1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 | let $deferred;
|
8 | function define(modules, callback) {
|
9 | $deferred = {modules, callback};
|
10 | }
|
11 | module.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 |
|
30 | define(['exports', 'typescript/lib/tsserverlibrary', 'typescript', 'path'], function (exports, tss, ts, path) { 'use strict';
|
31 |
|
32 | |
33 |
|
34 |
|
35 |
|
36 |
|
37 |
|
38 |
|
39 | function isNgLanguageService(ls) {
|
40 | return 'getTcb' in ls;
|
41 | }
|
42 |
|
43 | |
44 |
|
45 |
|
46 |
|
47 |
|
48 |
|
49 |
|
50 | var TagContentType;
|
51 | (function (TagContentType) {
|
52 | TagContentType[TagContentType["RAW_TEXT"] = 0] = "RAW_TEXT";
|
53 | TagContentType[TagContentType["ESCAPABLE_RAW_TEXT"] = 1] = "ESCAPABLE_RAW_TEXT";
|
54 | TagContentType[TagContentType["PARSABLE_DATA"] = 2] = "PARSABLE_DATA";
|
55 | })(TagContentType || (TagContentType = {}));
|
56 | function splitNsName(elementName) {
|
57 | if (elementName[0] != ':') {
|
58 | return [null, elementName];
|
59 | }
|
60 | const colonIndex = elementName.indexOf(':', 1);
|
61 | if (colonIndex == -1) {
|
62 | throw new Error(`Unsupported format "${elementName}" expecting ":namespace:name"`);
|
63 | }
|
64 | return [elementName.slice(1, colonIndex), elementName.slice(colonIndex + 1)];
|
65 | }
|
66 |
|
67 | function isNgContainer(tagName) {
|
68 | return splitNsName(tagName)[1] === 'ng-container';
|
69 | }
|
70 |
|
71 | function isNgContent(tagName) {
|
72 | return splitNsName(tagName)[1] === 'ng-content';
|
73 | }
|
74 |
|
75 | function isNgTemplate(tagName) {
|
76 | return splitNsName(tagName)[1] === 'ng-template';
|
77 | }
|
78 | function getNsPrefix(fullName) {
|
79 | return fullName === null ? null : splitNsName(fullName)[0];
|
80 | }
|
81 | function mergeNsAndName(prefix, localName) {
|
82 | return prefix ? `:${prefix}:${localName}` : localName;
|
83 | }
|
84 |
|
85 |
|
86 |
|
87 |
|
88 |
|
89 | const NAMED_ENTITIES = {
|
90 | 'Aacute': '\u00C1',
|
91 | 'aacute': '\u00E1',
|
92 | 'Acirc': '\u00C2',
|
93 | 'acirc': '\u00E2',
|
94 | 'acute': '\u00B4',
|
95 | 'AElig': '\u00C6',
|
96 | 'aelig': '\u00E6',
|
97 | 'Agrave': '\u00C0',
|
98 | 'agrave': '\u00E0',
|
99 | 'alefsym': '\u2135',
|
100 | 'Alpha': '\u0391',
|
101 | 'alpha': '\u03B1',
|
102 | 'amp': '&',
|
103 | 'and': '\u2227',
|
104 | 'ang': '\u2220',
|
105 | 'apos': '\u0027',
|
106 | 'Aring': '\u00C5',
|
107 | 'aring': '\u00E5',
|
108 | 'asymp': '\u2248',
|
109 | 'Atilde': '\u00C3',
|
110 | 'atilde': '\u00E3',
|
111 | 'Auml': '\u00C4',
|
112 | 'auml': '\u00E4',
|
113 | 'bdquo': '\u201E',
|
114 | 'Beta': '\u0392',
|
115 | 'beta': '\u03B2',
|
116 | 'brvbar': '\u00A6',
|
117 | 'bull': '\u2022',
|
118 | 'cap': '\u2229',
|
119 | 'Ccedil': '\u00C7',
|
120 | 'ccedil': '\u00E7',
|
121 | 'cedil': '\u00B8',
|
122 | 'cent': '\u00A2',
|
123 | 'Chi': '\u03A7',
|
124 | 'chi': '\u03C7',
|
125 | 'circ': '\u02C6',
|
126 | 'clubs': '\u2663',
|
127 | 'cong': '\u2245',
|
128 | 'copy': '\u00A9',
|
129 | 'crarr': '\u21B5',
|
130 | 'cup': '\u222A',
|
131 | 'curren': '\u00A4',
|
132 | 'dagger': '\u2020',
|
133 | 'Dagger': '\u2021',
|
134 | 'darr': '\u2193',
|
135 | 'dArr': '\u21D3',
|
136 | 'deg': '\u00B0',
|
137 | 'Delta': '\u0394',
|
138 | 'delta': '\u03B4',
|
139 | 'diams': '\u2666',
|
140 | 'divide': '\u00F7',
|
141 | 'Eacute': '\u00C9',
|
142 | 'eacute': '\u00E9',
|
143 | 'Ecirc': '\u00CA',
|
144 | 'ecirc': '\u00EA',
|
145 | 'Egrave': '\u00C8',
|
146 | 'egrave': '\u00E8',
|
147 | 'empty': '\u2205',
|
148 | 'emsp': '\u2003',
|
149 | 'ensp': '\u2002',
|
150 | 'Epsilon': '\u0395',
|
151 | 'epsilon': '\u03B5',
|
152 | 'equiv': '\u2261',
|
153 | 'Eta': '\u0397',
|
154 | 'eta': '\u03B7',
|
155 | 'ETH': '\u00D0',
|
156 | 'eth': '\u00F0',
|
157 | 'Euml': '\u00CB',
|
158 | 'euml': '\u00EB',
|
159 | 'euro': '\u20AC',
|
160 | 'exist': '\u2203',
|
161 | 'fnof': '\u0192',
|
162 | 'forall': '\u2200',
|
163 | 'frac12': '\u00BD',
|
164 | 'frac14': '\u00BC',
|
165 | 'frac34': '\u00BE',
|
166 | 'frasl': '\u2044',
|
167 | 'Gamma': '\u0393',
|
168 | 'gamma': '\u03B3',
|
169 | 'ge': '\u2265',
|
170 | 'gt': '>',
|
171 | 'harr': '\u2194',
|
172 | 'hArr': '\u21D4',
|
173 | 'hearts': '\u2665',
|
174 | 'hellip': '\u2026',
|
175 | 'Iacute': '\u00CD',
|
176 | 'iacute': '\u00ED',
|
177 | 'Icirc': '\u00CE',
|
178 | 'icirc': '\u00EE',
|
179 | 'iexcl': '\u00A1',
|
180 | 'Igrave': '\u00CC',
|
181 | 'igrave': '\u00EC',
|
182 | 'image': '\u2111',
|
183 | 'infin': '\u221E',
|
184 | 'int': '\u222B',
|
185 | 'Iota': '\u0399',
|
186 | 'iota': '\u03B9',
|
187 | 'iquest': '\u00BF',
|
188 | 'isin': '\u2208',
|
189 | 'Iuml': '\u00CF',
|
190 | 'iuml': '\u00EF',
|
191 | 'Kappa': '\u039A',
|
192 | 'kappa': '\u03BA',
|
193 | 'Lambda': '\u039B',
|
194 | 'lambda': '\u03BB',
|
195 | 'lang': '\u27E8',
|
196 | 'laquo': '\u00AB',
|
197 | 'larr': '\u2190',
|
198 | 'lArr': '\u21D0',
|
199 | 'lceil': '\u2308',
|
200 | 'ldquo': '\u201C',
|
201 | 'le': '\u2264',
|
202 | 'lfloor': '\u230A',
|
203 | 'lowast': '\u2217',
|
204 | 'loz': '\u25CA',
|
205 | 'lrm': '\u200E',
|
206 | 'lsaquo': '\u2039',
|
207 | 'lsquo': '\u2018',
|
208 | 'lt': '<',
|
209 | 'macr': '\u00AF',
|
210 | 'mdash': '\u2014',
|
211 | 'micro': '\u00B5',
|
212 | 'middot': '\u00B7',
|
213 | 'minus': '\u2212',
|
214 | 'Mu': '\u039C',
|
215 | 'mu': '\u03BC',
|
216 | 'nabla': '\u2207',
|
217 | 'nbsp': '\u00A0',
|
218 | 'ndash': '\u2013',
|
219 | 'ne': '\u2260',
|
220 | 'ni': '\u220B',
|
221 | 'not': '\u00AC',
|
222 | 'notin': '\u2209',
|
223 | 'nsub': '\u2284',
|
224 | 'Ntilde': '\u00D1',
|
225 | 'ntilde': '\u00F1',
|
226 | 'Nu': '\u039D',
|
227 | 'nu': '\u03BD',
|
228 | 'Oacute': '\u00D3',
|
229 | 'oacute': '\u00F3',
|
230 | 'Ocirc': '\u00D4',
|
231 | 'ocirc': '\u00F4',
|
232 | 'OElig': '\u0152',
|
233 | 'oelig': '\u0153',
|
234 | 'Ograve': '\u00D2',
|
235 | 'ograve': '\u00F2',
|
236 | 'oline': '\u203E',
|
237 | 'Omega': '\u03A9',
|
238 | 'omega': '\u03C9',
|
239 | 'Omicron': '\u039F',
|
240 | 'omicron': '\u03BF',
|
241 | 'oplus': '\u2295',
|
242 | 'or': '\u2228',
|
243 | 'ordf': '\u00AA',
|
244 | 'ordm': '\u00BA',
|
245 | 'Oslash': '\u00D8',
|
246 | 'oslash': '\u00F8',
|
247 | 'Otilde': '\u00D5',
|
248 | 'otilde': '\u00F5',
|
249 | 'otimes': '\u2297',
|
250 | 'Ouml': '\u00D6',
|
251 | 'ouml': '\u00F6',
|
252 | 'para': '\u00B6',
|
253 | 'permil': '\u2030',
|
254 | 'perp': '\u22A5',
|
255 | 'Phi': '\u03A6',
|
256 | 'phi': '\u03C6',
|
257 | 'Pi': '\u03A0',
|
258 | 'pi': '\u03C0',
|
259 | 'piv': '\u03D6',
|
260 | 'plusmn': '\u00B1',
|
261 | 'pound': '\u00A3',
|
262 | 'prime': '\u2032',
|
263 | 'Prime': '\u2033',
|
264 | 'prod': '\u220F',
|
265 | 'prop': '\u221D',
|
266 | 'Psi': '\u03A8',
|
267 | 'psi': '\u03C8',
|
268 | 'quot': '\u0022',
|
269 | 'radic': '\u221A',
|
270 | 'rang': '\u27E9',
|
271 | 'raquo': '\u00BB',
|
272 | 'rarr': '\u2192',
|
273 | 'rArr': '\u21D2',
|
274 | 'rceil': '\u2309',
|
275 | 'rdquo': '\u201D',
|
276 | 'real': '\u211C',
|
277 | 'reg': '\u00AE',
|
278 | 'rfloor': '\u230B',
|
279 | 'Rho': '\u03A1',
|
280 | 'rho': '\u03C1',
|
281 | 'rlm': '\u200F',
|
282 | 'rsaquo': '\u203A',
|
283 | 'rsquo': '\u2019',
|
284 | 'sbquo': '\u201A',
|
285 | 'Scaron': '\u0160',
|
286 | 'scaron': '\u0161',
|
287 | 'sdot': '\u22C5',
|
288 | 'sect': '\u00A7',
|
289 | 'shy': '\u00AD',
|
290 | 'Sigma': '\u03A3',
|
291 | 'sigma': '\u03C3',
|
292 | 'sigmaf': '\u03C2',
|
293 | 'sim': '\u223C',
|
294 | 'spades': '\u2660',
|
295 | 'sub': '\u2282',
|
296 | 'sube': '\u2286',
|
297 | 'sum': '\u2211',
|
298 | 'sup': '\u2283',
|
299 | 'sup1': '\u00B9',
|
300 | 'sup2': '\u00B2',
|
301 | 'sup3': '\u00B3',
|
302 | 'supe': '\u2287',
|
303 | 'szlig': '\u00DF',
|
304 | 'Tau': '\u03A4',
|
305 | 'tau': '\u03C4',
|
306 | 'there4': '\u2234',
|
307 | 'Theta': '\u0398',
|
308 | 'theta': '\u03B8',
|
309 | 'thetasym': '\u03D1',
|
310 | 'thinsp': '\u2009',
|
311 | 'THORN': '\u00DE',
|
312 | 'thorn': '\u00FE',
|
313 | 'tilde': '\u02DC',
|
314 | 'times': '\u00D7',
|
315 | 'trade': '\u2122',
|
316 | 'Uacute': '\u00DA',
|
317 | 'uacute': '\u00FA',
|
318 | 'uarr': '\u2191',
|
319 | 'uArr': '\u21D1',
|
320 | 'Ucirc': '\u00DB',
|
321 | 'ucirc': '\u00FB',
|
322 | 'Ugrave': '\u00D9',
|
323 | 'ugrave': '\u00F9',
|
324 | 'uml': '\u00A8',
|
325 | 'upsih': '\u03D2',
|
326 | 'Upsilon': '\u03A5',
|
327 | 'upsilon': '\u03C5',
|
328 | 'Uuml': '\u00DC',
|
329 | 'uuml': '\u00FC',
|
330 | 'weierp': '\u2118',
|
331 | 'Xi': '\u039E',
|
332 | 'xi': '\u03BE',
|
333 | 'Yacute': '\u00DD',
|
334 | 'yacute': '\u00FD',
|
335 | 'yen': '\u00A5',
|
336 | 'yuml': '\u00FF',
|
337 | 'Yuml': '\u0178',
|
338 | 'Zeta': '\u0396',
|
339 | 'zeta': '\u03B6',
|
340 | 'zwj': '\u200D',
|
341 | 'zwnj': '\u200C',
|
342 | };
|
343 |
|
344 |
|
345 | const NGSP_UNICODE = '\uE500';
|
346 | NAMED_ENTITIES['ngsp'] = NGSP_UNICODE;
|
347 |
|
348 | |
349 |
|
350 |
|
351 |
|
352 |
|
353 |
|
354 |
|
355 | class HtmlTagDefinition {
|
356 | constructor({ closedByChildren, implicitNamespacePrefix, contentType = TagContentType.PARSABLE_DATA, closedByParent = false, isVoid = false, ignoreFirstLf = false, preventNamespaceInheritance = false } = {}) {
|
357 | this.closedByChildren = {};
|
358 | this.closedByParent = false;
|
359 | this.canSelfClose = false;
|
360 | if (closedByChildren && closedByChildren.length > 0) {
|
361 | closedByChildren.forEach(tagName => this.closedByChildren[tagName] = true);
|
362 | }
|
363 | this.isVoid = isVoid;
|
364 | this.closedByParent = closedByParent || isVoid;
|
365 | this.implicitNamespacePrefix = implicitNamespacePrefix || null;
|
366 | this.contentType = contentType;
|
367 | this.ignoreFirstLf = ignoreFirstLf;
|
368 | this.preventNamespaceInheritance = preventNamespaceInheritance;
|
369 | }
|
370 | isClosedByChild(name) {
|
371 | return this.isVoid || name.toLowerCase() in this.closedByChildren;
|
372 | }
|
373 | getContentType(prefix) {
|
374 | if (typeof this.contentType === 'object') {
|
375 | const overrideType = prefix == null ? undefined : this.contentType[prefix];
|
376 | return overrideType !== null && overrideType !== void 0 ? overrideType : this.contentType.default;
|
377 | }
|
378 | return this.contentType;
|
379 | }
|
380 | }
|
381 | let _DEFAULT_TAG_DEFINITION;
|
382 |
|
383 |
|
384 | let TAG_DEFINITIONS;
|
385 | function getHtmlTagDefinition(tagName) {
|
386 | var _a, _b;
|
387 | if (!TAG_DEFINITIONS) {
|
388 | _DEFAULT_TAG_DEFINITION = new HtmlTagDefinition();
|
389 | TAG_DEFINITIONS = {
|
390 | 'base': new HtmlTagDefinition({ isVoid: true }),
|
391 | 'meta': new HtmlTagDefinition({ isVoid: true }),
|
392 | 'area': new HtmlTagDefinition({ isVoid: true }),
|
393 | 'embed': new HtmlTagDefinition({ isVoid: true }),
|
394 | 'link': new HtmlTagDefinition({ isVoid: true }),
|
395 | 'img': new HtmlTagDefinition({ isVoid: true }),
|
396 | 'input': new HtmlTagDefinition({ isVoid: true }),
|
397 | 'param': new HtmlTagDefinition({ isVoid: true }),
|
398 | 'hr': new HtmlTagDefinition({ isVoid: true }),
|
399 | 'br': new HtmlTagDefinition({ isVoid: true }),
|
400 | 'source': new HtmlTagDefinition({ isVoid: true }),
|
401 | 'track': new HtmlTagDefinition({ isVoid: true }),
|
402 | 'wbr': new HtmlTagDefinition({ isVoid: true }),
|
403 | 'p': new HtmlTagDefinition({
|
404 | closedByChildren: [
|
405 | 'address', 'article', 'aside', 'blockquote', 'div', 'dl', 'fieldset',
|
406 | 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5',
|
407 | 'h6', 'header', 'hgroup', 'hr', 'main', 'nav', 'ol',
|
408 | 'p', 'pre', 'section', 'table', 'ul'
|
409 | ],
|
410 | closedByParent: true
|
411 | }),
|
412 | 'thead': new HtmlTagDefinition({ closedByChildren: ['tbody', 'tfoot'] }),
|
413 | 'tbody': new HtmlTagDefinition({ closedByChildren: ['tbody', 'tfoot'], closedByParent: true }),
|
414 | 'tfoot': new HtmlTagDefinition({ closedByChildren: ['tbody'], closedByParent: true }),
|
415 | 'tr': new HtmlTagDefinition({ closedByChildren: ['tr'], closedByParent: true }),
|
416 | 'td': new HtmlTagDefinition({ closedByChildren: ['td', 'th'], closedByParent: true }),
|
417 | 'th': new HtmlTagDefinition({ closedByChildren: ['td', 'th'], closedByParent: true }),
|
418 | 'col': new HtmlTagDefinition({ isVoid: true }),
|
419 | 'svg': new HtmlTagDefinition({ implicitNamespacePrefix: 'svg' }),
|
420 | 'foreignObject': new HtmlTagDefinition({
|
421 |
|
422 |
|
423 |
|
424 |
|
425 |
|
426 | implicitNamespacePrefix: 'svg',
|
427 |
|
428 |
|
429 | preventNamespaceInheritance: true,
|
430 | }),
|
431 | 'math': new HtmlTagDefinition({ implicitNamespacePrefix: 'math' }),
|
432 | 'li': new HtmlTagDefinition({ closedByChildren: ['li'], closedByParent: true }),
|
433 | 'dt': new HtmlTagDefinition({ closedByChildren: ['dt', 'dd'] }),
|
434 | 'dd': new HtmlTagDefinition({ closedByChildren: ['dt', 'dd'], closedByParent: true }),
|
435 | 'rb': new HtmlTagDefinition({ closedByChildren: ['rb', 'rt', 'rtc', 'rp'], closedByParent: true }),
|
436 | 'rt': new HtmlTagDefinition({ closedByChildren: ['rb', 'rt', 'rtc', 'rp'], closedByParent: true }),
|
437 | 'rtc': new HtmlTagDefinition({ closedByChildren: ['rb', 'rtc', 'rp'], closedByParent: true }),
|
438 | 'rp': new HtmlTagDefinition({ closedByChildren: ['rb', 'rt', 'rtc', 'rp'], closedByParent: true }),
|
439 | 'optgroup': new HtmlTagDefinition({ closedByChildren: ['optgroup'], closedByParent: true }),
|
440 | 'option': new HtmlTagDefinition({ closedByChildren: ['option', 'optgroup'], closedByParent: true }),
|
441 | 'pre': new HtmlTagDefinition({ ignoreFirstLf: true }),
|
442 | 'listing': new HtmlTagDefinition({ ignoreFirstLf: true }),
|
443 | 'style': new HtmlTagDefinition({ contentType: TagContentType.RAW_TEXT }),
|
444 | 'script': new HtmlTagDefinition({ contentType: TagContentType.RAW_TEXT }),
|
445 | 'title': new HtmlTagDefinition({
|
446 |
|
447 |
|
448 | contentType: { default: TagContentType.ESCAPABLE_RAW_TEXT, svg: TagContentType.PARSABLE_DATA }
|
449 | }),
|
450 | 'textarea': new HtmlTagDefinition({ contentType: TagContentType.ESCAPABLE_RAW_TEXT, ignoreFirstLf: true }),
|
451 | };
|
452 | }
|
453 |
|
454 |
|
455 | return (_b = (_a = TAG_DEFINITIONS[tagName]) !== null && _a !== void 0 ? _a : TAG_DEFINITIONS[tagName.toLowerCase()]) !== null && _b !== void 0 ? _b : _DEFAULT_TAG_DEFINITION;
|
456 | }
|
457 |
|
458 | |
459 |
|
460 |
|
461 |
|
462 |
|
463 |
|
464 |
|
465 | const _SELECTOR_REGEXP = new RegExp('(\\:not\\()|' +
|
466 | '(([\\.\\#]?)[-\\w]+)|' +
|
467 |
|
468 |
|
469 | '(?:\\[([-.\\w*]+)(?:=([\"\']?)([^\\]\"\']*)\\5)?\\])|' +
|
470 |
|
471 |
|
472 | '(\\))|' +
|
473 | '(\\s*,\\s*)',
|
474 | 'g');
|
475 | |
476 |
|
477 |
|
478 |
|
479 |
|
480 | class CssSelector {
|
481 | constructor() {
|
482 | this.element = null;
|
483 | this.classNames = [];
|
484 | |
485 |
|
486 |
|
487 |
|
488 |
|
489 |
|
490 |
|
491 |
|
492 |
|
493 |
|
494 |
|
495 | this.attrs = [];
|
496 | this.notSelectors = [];
|
497 | }
|
498 | static parse(selector) {
|
499 | const results = [];
|
500 | const _addResult = (res, cssSel) => {
|
501 | if (cssSel.notSelectors.length > 0 && !cssSel.element && cssSel.classNames.length == 0 &&
|
502 | cssSel.attrs.length == 0) {
|
503 | cssSel.element = '*';
|
504 | }
|
505 | res.push(cssSel);
|
506 | };
|
507 | let cssSelector = new CssSelector();
|
508 | let match;
|
509 | let current = cssSelector;
|
510 | let inNot = false;
|
511 | _SELECTOR_REGEXP.lastIndex = 0;
|
512 | while (match = _SELECTOR_REGEXP.exec(selector)) {
|
513 | if (match[1 ]) {
|
514 | if (inNot) {
|
515 | throw new Error('Nesting :not in a selector is not allowed');
|
516 | }
|
517 | inNot = true;
|
518 | current = new CssSelector();
|
519 | cssSelector.notSelectors.push(current);
|
520 | }
|
521 | const tag = match[2 ];
|
522 | if (tag) {
|
523 | const prefix = match[3 ];
|
524 | if (prefix === '#') {
|
525 |
|
526 | current.addAttribute('id', tag.substr(1));
|
527 | }
|
528 | else if (prefix === '.') {
|
529 |
|
530 | current.addClassName(tag.substr(1));
|
531 | }
|
532 | else {
|
533 |
|
534 | current.setElement(tag);
|
535 | }
|
536 | }
|
537 | const attribute = match[4 ];
|
538 | if (attribute) {
|
539 | current.addAttribute(attribute, match[6 ]);
|
540 | }
|
541 | if (match[7 ]) {
|
542 | inNot = false;
|
543 | current = cssSelector;
|
544 | }
|
545 | if (match[8 ]) {
|
546 | if (inNot) {
|
547 | throw new Error('Multiple selectors in :not are not supported');
|
548 | }
|
549 | _addResult(results, cssSelector);
|
550 | cssSelector = current = new CssSelector();
|
551 | }
|
552 | }
|
553 | _addResult(results, cssSelector);
|
554 | return results;
|
555 | }
|
556 | isElementSelector() {
|
557 | return this.hasElementSelector() && this.classNames.length == 0 && this.attrs.length == 0 &&
|
558 | this.notSelectors.length === 0;
|
559 | }
|
560 | hasElementSelector() {
|
561 | return !!this.element;
|
562 | }
|
563 | setElement(element = null) {
|
564 | this.element = element;
|
565 | }
|
566 |
|
567 | getMatchingElementTemplate() {
|
568 | const tagName = this.element || 'div';
|
569 | const classAttr = this.classNames.length > 0 ? ` class="${this.classNames.join(' ')}"` : '';
|
570 | let attrs = '';
|
571 | for (let i = 0; i < this.attrs.length; i += 2) {
|
572 | const attrName = this.attrs[i];
|
573 | const attrValue = this.attrs[i + 1] !== '' ? `="${this.attrs[i + 1]}"` : '';
|
574 | attrs += ` ${attrName}${attrValue}`;
|
575 | }
|
576 | return getHtmlTagDefinition(tagName).isVoid ? `<${tagName}${classAttr}${attrs}/>` :
|
577 | `<${tagName}${classAttr}${attrs}></${tagName}>`;
|
578 | }
|
579 | getAttrs() {
|
580 | const result = [];
|
581 | if (this.classNames.length > 0) {
|
582 | result.push('class', this.classNames.join(' '));
|
583 | }
|
584 | return result.concat(this.attrs);
|
585 | }
|
586 | addAttribute(name, value = '') {
|
587 | this.attrs.push(name, value && value.toLowerCase() || '');
|
588 | }
|
589 | addClassName(name) {
|
590 | this.classNames.push(name.toLowerCase());
|
591 | }
|
592 | toString() {
|
593 | let res = this.element || '';
|
594 | if (this.classNames) {
|
595 | this.classNames.forEach(klass => res += `.${klass}`);
|
596 | }
|
597 | if (this.attrs) {
|
598 | for (let i = 0; i < this.attrs.length; i += 2) {
|
599 | const name = this.attrs[i];
|
600 | const value = this.attrs[i + 1];
|
601 | res += `[${name}${value ? '=' + value : ''}]`;
|
602 | }
|
603 | }
|
604 | this.notSelectors.forEach(notSelector => res += `:not(${notSelector})`);
|
605 | return res;
|
606 | }
|
607 | }
|
608 | |
609 |
|
610 |
|
611 |
|
612 | class SelectorMatcher {
|
613 | constructor() {
|
614 | this._elementMap = new Map();
|
615 | this._elementPartialMap = new Map();
|
616 | this._classMap = new Map();
|
617 | this._classPartialMap = new Map();
|
618 | this._attrValueMap = new Map();
|
619 | this._attrValuePartialMap = new Map();
|
620 | this._listContexts = [];
|
621 | }
|
622 | static createNotMatcher(notSelectors) {
|
623 | const notMatcher = new SelectorMatcher();
|
624 | notMatcher.addSelectables(notSelectors, null);
|
625 | return notMatcher;
|
626 | }
|
627 | addSelectables(cssSelectors, callbackCtxt) {
|
628 | let listContext = null;
|
629 | if (cssSelectors.length > 1) {
|
630 | listContext = new SelectorListContext(cssSelectors);
|
631 | this._listContexts.push(listContext);
|
632 | }
|
633 | for (let i = 0; i < cssSelectors.length; i++) {
|
634 | this._addSelectable(cssSelectors[i], callbackCtxt, listContext);
|
635 | }
|
636 | }
|
637 | |
638 |
|
639 |
|
640 |
|
641 |
|
642 | _addSelectable(cssSelector, callbackCtxt, listContext) {
|
643 | let matcher = this;
|
644 | const element = cssSelector.element;
|
645 | const classNames = cssSelector.classNames;
|
646 | const attrs = cssSelector.attrs;
|
647 | const selectable = new SelectorContext(cssSelector, callbackCtxt, listContext);
|
648 | if (element) {
|
649 | const isTerminal = attrs.length === 0 && classNames.length === 0;
|
650 | if (isTerminal) {
|
651 | this._addTerminal(matcher._elementMap, element, selectable);
|
652 | }
|
653 | else {
|
654 | matcher = this._addPartial(matcher._elementPartialMap, element);
|
655 | }
|
656 | }
|
657 | if (classNames) {
|
658 | for (let i = 0; i < classNames.length; i++) {
|
659 | const isTerminal = attrs.length === 0 && i === classNames.length - 1;
|
660 | const className = classNames[i];
|
661 | if (isTerminal) {
|
662 | this._addTerminal(matcher._classMap, className, selectable);
|
663 | }
|
664 | else {
|
665 | matcher = this._addPartial(matcher._classPartialMap, className);
|
666 | }
|
667 | }
|
668 | }
|
669 | if (attrs) {
|
670 | for (let i = 0; i < attrs.length; i += 2) {
|
671 | const isTerminal = i === attrs.length - 2;
|
672 | const name = attrs[i];
|
673 | const value = attrs[i + 1];
|
674 | if (isTerminal) {
|
675 | const terminalMap = matcher._attrValueMap;
|
676 | let terminalValuesMap = terminalMap.get(name);
|
677 | if (!terminalValuesMap) {
|
678 | terminalValuesMap = new Map();
|
679 | terminalMap.set(name, terminalValuesMap);
|
680 | }
|
681 | this._addTerminal(terminalValuesMap, value, selectable);
|
682 | }
|
683 | else {
|
684 | const partialMap = matcher._attrValuePartialMap;
|
685 | let partialValuesMap = partialMap.get(name);
|
686 | if (!partialValuesMap) {
|
687 | partialValuesMap = new Map();
|
688 | partialMap.set(name, partialValuesMap);
|
689 | }
|
690 | matcher = this._addPartial(partialValuesMap, value);
|
691 | }
|
692 | }
|
693 | }
|
694 | }
|
695 | _addTerminal(map, name, selectable) {
|
696 | let terminalList = map.get(name);
|
697 | if (!terminalList) {
|
698 | terminalList = [];
|
699 | map.set(name, terminalList);
|
700 | }
|
701 | terminalList.push(selectable);
|
702 | }
|
703 | _addPartial(map, name) {
|
704 | let matcher = map.get(name);
|
705 | if (!matcher) {
|
706 | matcher = new SelectorMatcher();
|
707 | map.set(name, matcher);
|
708 | }
|
709 | return matcher;
|
710 | }
|
711 | |
712 |
|
713 |
|
714 |
|
715 |
|
716 |
|
717 |
|
718 | match(cssSelector, matchedCallback) {
|
719 | let result = false;
|
720 | const element = cssSelector.element;
|
721 | const classNames = cssSelector.classNames;
|
722 | const attrs = cssSelector.attrs;
|
723 | for (let i = 0; i < this._listContexts.length; i++) {
|
724 | this._listContexts[i].alreadyMatched = false;
|
725 | }
|
726 | result = this._matchTerminal(this._elementMap, element, cssSelector, matchedCallback) || result;
|
727 | result = this._matchPartial(this._elementPartialMap, element, cssSelector, matchedCallback) ||
|
728 | result;
|
729 | if (classNames) {
|
730 | for (let i = 0; i < classNames.length; i++) {
|
731 | const className = classNames[i];
|
732 | result =
|
733 | this._matchTerminal(this._classMap, className, cssSelector, matchedCallback) || result;
|
734 | result =
|
735 | this._matchPartial(this._classPartialMap, className, cssSelector, matchedCallback) ||
|
736 | result;
|
737 | }
|
738 | }
|
739 | if (attrs) {
|
740 | for (let i = 0; i < attrs.length; i += 2) {
|
741 | const name = attrs[i];
|
742 | const value = attrs[i + 1];
|
743 | const terminalValuesMap = this._attrValueMap.get(name);
|
744 | if (value) {
|
745 | result =
|
746 | this._matchTerminal(terminalValuesMap, '', cssSelector, matchedCallback) || result;
|
747 | }
|
748 | result =
|
749 | this._matchTerminal(terminalValuesMap, value, cssSelector, matchedCallback) || result;
|
750 | const partialValuesMap = this._attrValuePartialMap.get(name);
|
751 | if (value) {
|
752 | result = this._matchPartial(partialValuesMap, '', cssSelector, matchedCallback) || result;
|
753 | }
|
754 | result =
|
755 | this._matchPartial(partialValuesMap, value, cssSelector, matchedCallback) || result;
|
756 | }
|
757 | }
|
758 | return result;
|
759 | }
|
760 |
|
761 | _matchTerminal(map, name, cssSelector, matchedCallback) {
|
762 | if (!map || typeof name !== 'string') {
|
763 | return false;
|
764 | }
|
765 | let selectables = map.get(name) || [];
|
766 | const starSelectables = map.get('*');
|
767 | if (starSelectables) {
|
768 | selectables = selectables.concat(starSelectables);
|
769 | }
|
770 | if (selectables.length === 0) {
|
771 | return false;
|
772 | }
|
773 | let selectable;
|
774 | let result = false;
|
775 | for (let i = 0; i < selectables.length; i++) {
|
776 | selectable = selectables[i];
|
777 | result = selectable.finalize(cssSelector, matchedCallback) || result;
|
778 | }
|
779 | return result;
|
780 | }
|
781 |
|
782 | _matchPartial(map, name, cssSelector, matchedCallback) {
|
783 | if (!map || typeof name !== 'string') {
|
784 | return false;
|
785 | }
|
786 | const nestedSelector = map.get(name);
|
787 | if (!nestedSelector) {
|
788 | return false;
|
789 | }
|
790 |
|
791 |
|
792 |
|
793 | return nestedSelector.match(cssSelector, matchedCallback);
|
794 | }
|
795 | }
|
796 | class SelectorListContext {
|
797 | constructor(selectors) {
|
798 | this.selectors = selectors;
|
799 | this.alreadyMatched = false;
|
800 | }
|
801 | }
|
802 |
|
803 | class SelectorContext {
|
804 | constructor(selector, cbContext, listContext) {
|
805 | this.selector = selector;
|
806 | this.cbContext = cbContext;
|
807 | this.listContext = listContext;
|
808 | this.notSelectors = selector.notSelectors;
|
809 | }
|
810 | finalize(cssSelector, callback) {
|
811 | let result = true;
|
812 | if (this.notSelectors.length > 0 && (!this.listContext || !this.listContext.alreadyMatched)) {
|
813 | const notMatcher = SelectorMatcher.createNotMatcher(this.notSelectors);
|
814 | result = !notMatcher.match(cssSelector, null);
|
815 | }
|
816 | if (result && callback && (!this.listContext || !this.listContext.alreadyMatched)) {
|
817 | if (this.listContext) {
|
818 | this.listContext.alreadyMatched = true;
|
819 | }
|
820 | callback(this.selector, this.cbContext);
|
821 | }
|
822 | return result;
|
823 | }
|
824 | }
|
825 |
|
826 | |
827 |
|
828 |
|
829 |
|
830 |
|
831 |
|
832 |
|
833 | const createInject = makeMetadataFactory('Inject', (token) => ({ token }));
|
834 | const createInjectionToken = makeMetadataFactory('InjectionToken', (desc) => ({ _desc: desc, ɵprov: undefined }));
|
835 | const createAttribute = makeMetadataFactory('Attribute', (attributeName) => ({ attributeName }));
|
836 |
|
837 |
|
838 |
|
839 | const emitDistinctChangesOnlyDefaultValue = false;
|
840 | const createContentChildren = makeMetadataFactory('ContentChildren', (selector, data = {}) => (Object.assign({ selector, first: false, isViewQuery: false, descendants: false, emitDistinctChangesOnly: emitDistinctChangesOnlyDefaultValue }, data)));
|
841 | const createContentChild = makeMetadataFactory('ContentChild', (selector, data = {}) => (Object.assign({ selector, first: true, isViewQuery: false, descendants: true }, data)));
|
842 | const createViewChildren = makeMetadataFactory('ViewChildren', (selector, data = {}) => (Object.assign({ selector, first: false, isViewQuery: true, descendants: true, emitDistinctChangesOnly: emitDistinctChangesOnlyDefaultValue }, data)));
|
843 | const createViewChild = makeMetadataFactory('ViewChild', (selector, data) => (Object.assign({ selector, first: true, isViewQuery: true, descendants: true }, data)));
|
844 | const createDirective = makeMetadataFactory('Directive', (dir = {}) => dir);
|
845 | var ViewEncapsulation;
|
846 | (function (ViewEncapsulation) {
|
847 | ViewEncapsulation[ViewEncapsulation["Emulated"] = 0] = "Emulated";
|
848 |
|
849 | ViewEncapsulation[ViewEncapsulation["None"] = 2] = "None";
|
850 | ViewEncapsulation[ViewEncapsulation["ShadowDom"] = 3] = "ShadowDom";
|
851 | })(ViewEncapsulation || (ViewEncapsulation = {}));
|
852 | var ChangeDetectionStrategy;
|
853 | (function (ChangeDetectionStrategy) {
|
854 | ChangeDetectionStrategy[ChangeDetectionStrategy["OnPush"] = 0] = "OnPush";
|
855 | ChangeDetectionStrategy[ChangeDetectionStrategy["Default"] = 1] = "Default";
|
856 | })(ChangeDetectionStrategy || (ChangeDetectionStrategy = {}));
|
857 | const createComponent = makeMetadataFactory('Component', (c = {}) => (Object.assign({ changeDetection: ChangeDetectionStrategy.Default }, c)));
|
858 | const createPipe = makeMetadataFactory('Pipe', (p) => (Object.assign({ pure: true }, p)));
|
859 | const createInput = makeMetadataFactory('Input', (bindingPropertyName) => ({ bindingPropertyName }));
|
860 | const createOutput = makeMetadataFactory('Output', (bindingPropertyName) => ({ bindingPropertyName }));
|
861 | const createHostBinding = makeMetadataFactory('HostBinding', (hostPropertyName) => ({ hostPropertyName }));
|
862 | const createHostListener = makeMetadataFactory('HostListener', (eventName, args) => ({ eventName, args }));
|
863 | const createNgModule = makeMetadataFactory('NgModule', (ngModule) => ngModule);
|
864 | const createInjectable = makeMetadataFactory('Injectable', (injectable = {}) => injectable);
|
865 | const CUSTOM_ELEMENTS_SCHEMA = {
|
866 | name: 'custom-elements'
|
867 | };
|
868 | const NO_ERRORS_SCHEMA = {
|
869 | name: 'no-errors-schema'
|
870 | };
|
871 | const createOptional = makeMetadataFactory('Optional');
|
872 | const createSelf = makeMetadataFactory('Self');
|
873 | const createSkipSelf = makeMetadataFactory('SkipSelf');
|
874 | const createHost = makeMetadataFactory('Host');
|
875 | const Type = Function;
|
876 | var SecurityContext;
|
877 | (function (SecurityContext) {
|
878 | SecurityContext[SecurityContext["NONE"] = 0] = "NONE";
|
879 | SecurityContext[SecurityContext["HTML"] = 1] = "HTML";
|
880 | SecurityContext[SecurityContext["STYLE"] = 2] = "STYLE";
|
881 | SecurityContext[SecurityContext["SCRIPT"] = 3] = "SCRIPT";
|
882 | SecurityContext[SecurityContext["URL"] = 4] = "URL";
|
883 | SecurityContext[SecurityContext["RESOURCE_URL"] = 5] = "RESOURCE_URL";
|
884 | })(SecurityContext || (SecurityContext = {}));
|
885 | var MissingTranslationStrategy;
|
886 | (function (MissingTranslationStrategy) {
|
887 | MissingTranslationStrategy[MissingTranslationStrategy["Error"] = 0] = "Error";
|
888 | MissingTranslationStrategy[MissingTranslationStrategy["Warning"] = 1] = "Warning";
|
889 | MissingTranslationStrategy[MissingTranslationStrategy["Ignore"] = 2] = "Ignore";
|
890 | })(MissingTranslationStrategy || (MissingTranslationStrategy = {}));
|
891 | function makeMetadataFactory(name, props) {
|
892 |
|
893 |
|
894 |
|
895 |
|
896 | function factory(...args) {
|
897 | const values = props ? props(...args) : {};
|
898 | return Object.assign({ ngMetadataName: name }, values);
|
899 | }
|
900 | factory.isTypeOf = (obj) => obj && obj.ngMetadataName === name;
|
901 | factory.ngMetadataName = name;
|
902 | return factory;
|
903 | }
|
904 | function parserSelectorToSimpleSelector(selector) {
|
905 | const classes = selector.classNames && selector.classNames.length ?
|
906 | [8 , ...selector.classNames] :
|
907 | [];
|
908 | const elementName = selector.element && selector.element !== '*' ? selector.element : '';
|
909 | return [elementName, ...selector.attrs, ...classes];
|
910 | }
|
911 | function parserSelectorToNegativeSelector(selector) {
|
912 | const classes = selector.classNames && selector.classNames.length ?
|
913 | [8 , ...selector.classNames] :
|
914 | [];
|
915 | if (selector.element) {
|
916 | return [
|
917 | 1 | 4 , selector.element, ...selector.attrs, ...classes
|
918 | ];
|
919 | }
|
920 | else if (selector.attrs.length) {
|
921 | return [1 | 2 , ...selector.attrs, ...classes];
|
922 | }
|
923 | else {
|
924 | return selector.classNames && selector.classNames.length ?
|
925 | [1 | 8 , ...selector.classNames] :
|
926 | [];
|
927 | }
|
928 | }
|
929 | function parserSelectorToR3Selector(selector) {
|
930 | const positive = parserSelectorToSimpleSelector(selector);
|
931 | const negative = selector.notSelectors && selector.notSelectors.length ?
|
932 | selector.notSelectors.map(notSelector => parserSelectorToNegativeSelector(notSelector)) :
|
933 | [];
|
934 | return positive.concat(...negative);
|
935 | }
|
936 | function parseSelectorToR3Selector(selector) {
|
937 | return selector ? CssSelector.parse(selector).map(parserSelectorToR3Selector) : [];
|
938 | }
|
939 |
|
940 | |
941 |
|
942 |
|
943 |
|
944 |
|
945 |
|
946 |
|
947 |
|
948 | var TypeModifier;
|
949 | (function (TypeModifier) {
|
950 | TypeModifier[TypeModifier["Const"] = 0] = "Const";
|
951 | })(TypeModifier || (TypeModifier = {}));
|
952 | class Type$1 {
|
953 | constructor(modifiers = []) {
|
954 | this.modifiers = modifiers;
|
955 | }
|
956 | hasModifier(modifier) {
|
957 | return this.modifiers.indexOf(modifier) !== -1;
|
958 | }
|
959 | }
|
960 | var BuiltinTypeName;
|
961 | (function (BuiltinTypeName) {
|
962 | BuiltinTypeName[BuiltinTypeName["Dynamic"] = 0] = "Dynamic";
|
963 | BuiltinTypeName[BuiltinTypeName["Bool"] = 1] = "Bool";
|
964 | BuiltinTypeName[BuiltinTypeName["String"] = 2] = "String";
|
965 | BuiltinTypeName[BuiltinTypeName["Int"] = 3] = "Int";
|
966 | BuiltinTypeName[BuiltinTypeName["Number"] = 4] = "Number";
|
967 | BuiltinTypeName[BuiltinTypeName["Function"] = 5] = "Function";
|
968 | BuiltinTypeName[BuiltinTypeName["Inferred"] = 6] = "Inferred";
|
969 | BuiltinTypeName[BuiltinTypeName["None"] = 7] = "None";
|
970 | })(BuiltinTypeName || (BuiltinTypeName = {}));
|
971 | class BuiltinType extends Type$1 {
|
972 | constructor(name, modifiers) {
|
973 | super(modifiers);
|
974 | this.name = name;
|
975 | }
|
976 | visitType(visitor, context) {
|
977 | return visitor.visitBuiltinType(this, context);
|
978 | }
|
979 | }
|
980 | class ExpressionType extends Type$1 {
|
981 | constructor(value, modifiers, typeParams = null) {
|
982 | super(modifiers);
|
983 | this.value = value;
|
984 | this.typeParams = typeParams;
|
985 | }
|
986 | visitType(visitor, context) {
|
987 | return visitor.visitExpressionType(this, context);
|
988 | }
|
989 | }
|
990 | const DYNAMIC_TYPE = new BuiltinType(BuiltinTypeName.Dynamic);
|
991 | const INFERRED_TYPE = new BuiltinType(BuiltinTypeName.Inferred);
|
992 | const BOOL_TYPE = new BuiltinType(BuiltinTypeName.Bool);
|
993 | const INT_TYPE = new BuiltinType(BuiltinTypeName.Int);
|
994 | const NUMBER_TYPE = new BuiltinType(BuiltinTypeName.Number);
|
995 | const STRING_TYPE = new BuiltinType(BuiltinTypeName.String);
|
996 | const FUNCTION_TYPE = new BuiltinType(BuiltinTypeName.Function);
|
997 | const NONE_TYPE = new BuiltinType(BuiltinTypeName.None);
|
998 |
|
999 | var UnaryOperator;
|
1000 | (function (UnaryOperator) {
|
1001 | UnaryOperator[UnaryOperator["Minus"] = 0] = "Minus";
|
1002 | UnaryOperator[UnaryOperator["Plus"] = 1] = "Plus";
|
1003 | })(UnaryOperator || (UnaryOperator = {}));
|
1004 | var BinaryOperator;
|
1005 | (function (BinaryOperator) {
|
1006 | BinaryOperator[BinaryOperator["Equals"] = 0] = "Equals";
|
1007 | BinaryOperator[BinaryOperator["NotEquals"] = 1] = "NotEquals";
|
1008 | BinaryOperator[BinaryOperator["Identical"] = 2] = "Identical";
|
1009 | BinaryOperator[BinaryOperator["NotIdentical"] = 3] = "NotIdentical";
|
1010 | BinaryOperator[BinaryOperator["Minus"] = 4] = "Minus";
|
1011 | BinaryOperator[BinaryOperator["Plus"] = 5] = "Plus";
|
1012 | BinaryOperator[BinaryOperator["Divide"] = 6] = "Divide";
|
1013 | BinaryOperator[BinaryOperator["Multiply"] = 7] = "Multiply";
|
1014 | BinaryOperator[BinaryOperator["Modulo"] = 8] = "Modulo";
|
1015 | BinaryOperator[BinaryOperator["And"] = 9] = "And";
|
1016 | BinaryOperator[BinaryOperator["Or"] = 10] = "Or";
|
1017 | BinaryOperator[BinaryOperator["BitwiseAnd"] = 11] = "BitwiseAnd";
|
1018 | BinaryOperator[BinaryOperator["Lower"] = 12] = "Lower";
|
1019 | BinaryOperator[BinaryOperator["LowerEquals"] = 13] = "LowerEquals";
|
1020 | BinaryOperator[BinaryOperator["Bigger"] = 14] = "Bigger";
|
1021 | BinaryOperator[BinaryOperator["BiggerEquals"] = 15] = "BiggerEquals";
|
1022 | })(BinaryOperator || (BinaryOperator = {}));
|
1023 | function nullSafeIsEquivalent(base, other) {
|
1024 | if (base == null || other == null) {
|
1025 | return base == other;
|
1026 | }
|
1027 | return base.isEquivalent(other);
|
1028 | }
|
1029 | function areAllEquivalentPredicate(base, other, equivalentPredicate) {
|
1030 | const len = base.length;
|
1031 | if (len !== other.length) {
|
1032 | return false;
|
1033 | }
|
1034 | for (let i = 0; i < len; i++) {
|
1035 | if (!equivalentPredicate(base[i], other[i])) {
|
1036 | return false;
|
1037 | }
|
1038 | }
|
1039 | return true;
|
1040 | }
|
1041 | function areAllEquivalent(base, other) {
|
1042 | return areAllEquivalentPredicate(base, other, (baseElement, otherElement) => baseElement.isEquivalent(otherElement));
|
1043 | }
|
1044 | class Expression {
|
1045 | constructor(type, sourceSpan) {
|
1046 | this.type = type || null;
|
1047 | this.sourceSpan = sourceSpan || null;
|
1048 | }
|
1049 | prop(name, sourceSpan) {
|
1050 | return new ReadPropExpr(this, name, null, sourceSpan);
|
1051 | }
|
1052 | key(index, type, sourceSpan) {
|
1053 | return new ReadKeyExpr(this, index, type, sourceSpan);
|
1054 | }
|
1055 | callMethod(name, params, sourceSpan) {
|
1056 | return new InvokeMethodExpr(this, name, params, null, sourceSpan);
|
1057 | }
|
1058 | callFn(params, sourceSpan, pure) {
|
1059 | return new InvokeFunctionExpr(this, params, null, sourceSpan, pure);
|
1060 | }
|
1061 | instantiate(params, type, sourceSpan) {
|
1062 | return new InstantiateExpr(this, params, type, sourceSpan);
|
1063 | }
|
1064 | conditional(trueCase, falseCase = null, sourceSpan) {
|
1065 | return new ConditionalExpr(this, trueCase, falseCase, null, sourceSpan);
|
1066 | }
|
1067 | equals(rhs, sourceSpan) {
|
1068 | return new BinaryOperatorExpr(BinaryOperator.Equals, this, rhs, null, sourceSpan);
|
1069 | }
|
1070 | notEquals(rhs, sourceSpan) {
|
1071 | return new BinaryOperatorExpr(BinaryOperator.NotEquals, this, rhs, null, sourceSpan);
|
1072 | }
|
1073 | identical(rhs, sourceSpan) {
|
1074 | return new BinaryOperatorExpr(BinaryOperator.Identical, this, rhs, null, sourceSpan);
|
1075 | }
|
1076 | notIdentical(rhs, sourceSpan) {
|
1077 | return new BinaryOperatorExpr(BinaryOperator.NotIdentical, this, rhs, null, sourceSpan);
|
1078 | }
|
1079 | minus(rhs, sourceSpan) {
|
1080 | return new BinaryOperatorExpr(BinaryOperator.Minus, this, rhs, null, sourceSpan);
|
1081 | }
|
1082 | plus(rhs, sourceSpan) {
|
1083 | return new BinaryOperatorExpr(BinaryOperator.Plus, this, rhs, null, sourceSpan);
|
1084 | }
|
1085 | divide(rhs, sourceSpan) {
|
1086 | return new BinaryOperatorExpr(BinaryOperator.Divide, this, rhs, null, sourceSpan);
|
1087 | }
|
1088 | multiply(rhs, sourceSpan) {
|
1089 | return new BinaryOperatorExpr(BinaryOperator.Multiply, this, rhs, null, sourceSpan);
|
1090 | }
|
1091 | modulo(rhs, sourceSpan) {
|
1092 | return new BinaryOperatorExpr(BinaryOperator.Modulo, this, rhs, null, sourceSpan);
|
1093 | }
|
1094 | and(rhs, sourceSpan) {
|
1095 | return new BinaryOperatorExpr(BinaryOperator.And, this, rhs, null, sourceSpan);
|
1096 | }
|
1097 | bitwiseAnd(rhs, sourceSpan, parens = true) {
|
1098 | return new BinaryOperatorExpr(BinaryOperator.BitwiseAnd, this, rhs, null, sourceSpan, parens);
|
1099 | }
|
1100 | or(rhs, sourceSpan) {
|
1101 | return new BinaryOperatorExpr(BinaryOperator.Or, this, rhs, null, sourceSpan);
|
1102 | }
|
1103 | lower(rhs, sourceSpan) {
|
1104 | return new BinaryOperatorExpr(BinaryOperator.Lower, this, rhs, null, sourceSpan);
|
1105 | }
|
1106 | lowerEquals(rhs, sourceSpan) {
|
1107 | return new BinaryOperatorExpr(BinaryOperator.LowerEquals, this, rhs, null, sourceSpan);
|
1108 | }
|
1109 | bigger(rhs, sourceSpan) {
|
1110 | return new BinaryOperatorExpr(BinaryOperator.Bigger, this, rhs, null, sourceSpan);
|
1111 | }
|
1112 | biggerEquals(rhs, sourceSpan) {
|
1113 | return new BinaryOperatorExpr(BinaryOperator.BiggerEquals, this, rhs, null, sourceSpan);
|
1114 | }
|
1115 | isBlank(sourceSpan) {
|
1116 |
|
1117 |
|
1118 | return this.equals(TYPED_NULL_EXPR, sourceSpan);
|
1119 | }
|
1120 | cast(type, sourceSpan) {
|
1121 | return new CastExpr(this, type, sourceSpan);
|
1122 | }
|
1123 | toStmt() {
|
1124 | return new ExpressionStatement(this, null);
|
1125 | }
|
1126 | }
|
1127 | var BuiltinVar;
|
1128 | (function (BuiltinVar) {
|
1129 | BuiltinVar[BuiltinVar["This"] = 0] = "This";
|
1130 | BuiltinVar[BuiltinVar["Super"] = 1] = "Super";
|
1131 | BuiltinVar[BuiltinVar["CatchError"] = 2] = "CatchError";
|
1132 | BuiltinVar[BuiltinVar["CatchStack"] = 3] = "CatchStack";
|
1133 | })(BuiltinVar || (BuiltinVar = {}));
|
1134 | class ReadVarExpr extends Expression {
|
1135 | constructor(name, type, sourceSpan) {
|
1136 | super(type, sourceSpan);
|
1137 | if (typeof name === 'string') {
|
1138 | this.name = name;
|
1139 | this.builtin = null;
|
1140 | }
|
1141 | else {
|
1142 | this.name = null;
|
1143 | this.builtin = name;
|
1144 | }
|
1145 | }
|
1146 | isEquivalent(e) {
|
1147 | return e instanceof ReadVarExpr && this.name === e.name && this.builtin === e.builtin;
|
1148 | }
|
1149 | isConstant() {
|
1150 | return false;
|
1151 | }
|
1152 | visitExpression(visitor, context) {
|
1153 | return visitor.visitReadVarExpr(this, context);
|
1154 | }
|
1155 | set(value) {
|
1156 | if (!this.name) {
|
1157 | throw new Error(`Built in variable ${this.builtin} can not be assigned to.`);
|
1158 | }
|
1159 | return new WriteVarExpr(this.name, value, null, this.sourceSpan);
|
1160 | }
|
1161 | }
|
1162 | class TypeofExpr extends Expression {
|
1163 | constructor(expr, type, sourceSpan) {
|
1164 | super(type, sourceSpan);
|
1165 | this.expr = expr;
|
1166 | }
|
1167 | visitExpression(visitor, context) {
|
1168 | return visitor.visitTypeofExpr(this, context);
|
1169 | }
|
1170 | isEquivalent(e) {
|
1171 | return e instanceof TypeofExpr && e.expr.isEquivalent(this.expr);
|
1172 | }
|
1173 | isConstant() {
|
1174 | return this.expr.isConstant();
|
1175 | }
|
1176 | }
|
1177 | class WrappedNodeExpr extends Expression {
|
1178 | constructor(node, type, sourceSpan) {
|
1179 | super(type, sourceSpan);
|
1180 | this.node = node;
|
1181 | }
|
1182 | isEquivalent(e) {
|
1183 | return e instanceof WrappedNodeExpr && this.node === e.node;
|
1184 | }
|
1185 | isConstant() {
|
1186 | return false;
|
1187 | }
|
1188 | visitExpression(visitor, context) {
|
1189 | return visitor.visitWrappedNodeExpr(this, context);
|
1190 | }
|
1191 | }
|
1192 | class WriteVarExpr extends Expression {
|
1193 | constructor(name, value, type, sourceSpan) {
|
1194 | super(type || value.type, sourceSpan);
|
1195 | this.name = name;
|
1196 | this.value = value;
|
1197 | }
|
1198 | isEquivalent(e) {
|
1199 | return e instanceof WriteVarExpr && this.name === e.name && this.value.isEquivalent(e.value);
|
1200 | }
|
1201 | isConstant() {
|
1202 | return false;
|
1203 | }
|
1204 | visitExpression(visitor, context) {
|
1205 | return visitor.visitWriteVarExpr(this, context);
|
1206 | }
|
1207 | toDeclStmt(type, modifiers) {
|
1208 | return new DeclareVarStmt(this.name, this.value, type, modifiers, this.sourceSpan);
|
1209 | }
|
1210 | toConstDecl() {
|
1211 | return this.toDeclStmt(INFERRED_TYPE, [StmtModifier.Final]);
|
1212 | }
|
1213 | }
|
1214 | class WriteKeyExpr extends Expression {
|
1215 | constructor(receiver, index, value, type, sourceSpan) {
|
1216 | super(type || value.type, sourceSpan);
|
1217 | this.receiver = receiver;
|
1218 | this.index = index;
|
1219 | this.value = value;
|
1220 | }
|
1221 | isEquivalent(e) {
|
1222 | return e instanceof WriteKeyExpr && this.receiver.isEquivalent(e.receiver) &&
|
1223 | this.index.isEquivalent(e.index) && this.value.isEquivalent(e.value);
|
1224 | }
|
1225 | isConstant() {
|
1226 | return false;
|
1227 | }
|
1228 | visitExpression(visitor, context) {
|
1229 | return visitor.visitWriteKeyExpr(this, context);
|
1230 | }
|
1231 | }
|
1232 | class WritePropExpr extends Expression {
|
1233 | constructor(receiver, name, value, type, sourceSpan) {
|
1234 | super(type || value.type, sourceSpan);
|
1235 | this.receiver = receiver;
|
1236 | this.name = name;
|
1237 | this.value = value;
|
1238 | }
|
1239 | isEquivalent(e) {
|
1240 | return e instanceof WritePropExpr && this.receiver.isEquivalent(e.receiver) &&
|
1241 | this.name === e.name && this.value.isEquivalent(e.value);
|
1242 | }
|
1243 | isConstant() {
|
1244 | return false;
|
1245 | }
|
1246 | visitExpression(visitor, context) {
|
1247 | return visitor.visitWritePropExpr(this, context);
|
1248 | }
|
1249 | }
|
1250 | var BuiltinMethod;
|
1251 | (function (BuiltinMethod) {
|
1252 | BuiltinMethod[BuiltinMethod["ConcatArray"] = 0] = "ConcatArray";
|
1253 | BuiltinMethod[BuiltinMethod["SubscribeObservable"] = 1] = "SubscribeObservable";
|
1254 | BuiltinMethod[BuiltinMethod["Bind"] = 2] = "Bind";
|
1255 | })(BuiltinMethod || (BuiltinMethod = {}));
|
1256 | class InvokeMethodExpr extends Expression {
|
1257 | constructor(receiver, method, args, type, sourceSpan) {
|
1258 | super(type, sourceSpan);
|
1259 | this.receiver = receiver;
|
1260 | this.args = args;
|
1261 | if (typeof method === 'string') {
|
1262 | this.name = method;
|
1263 | this.builtin = null;
|
1264 | }
|
1265 | else {
|
1266 | this.name = null;
|
1267 | this.builtin = method;
|
1268 | }
|
1269 | }
|
1270 | isEquivalent(e) {
|
1271 | return e instanceof InvokeMethodExpr && this.receiver.isEquivalent(e.receiver) &&
|
1272 | this.name === e.name && this.builtin === e.builtin && areAllEquivalent(this.args, e.args);
|
1273 | }
|
1274 | isConstant() {
|
1275 | return false;
|
1276 | }
|
1277 | visitExpression(visitor, context) {
|
1278 | return visitor.visitInvokeMethodExpr(this, context);
|
1279 | }
|
1280 | }
|
1281 | class InvokeFunctionExpr extends Expression {
|
1282 | constructor(fn, args, type, sourceSpan, pure = false) {
|
1283 | super(type, sourceSpan);
|
1284 | this.fn = fn;
|
1285 | this.args = args;
|
1286 | this.pure = pure;
|
1287 | }
|
1288 | isEquivalent(e) {
|
1289 | return e instanceof InvokeFunctionExpr && this.fn.isEquivalent(e.fn) &&
|
1290 | areAllEquivalent(this.args, e.args) && this.pure === e.pure;
|
1291 | }
|
1292 | isConstant() {
|
1293 | return false;
|
1294 | }
|
1295 | visitExpression(visitor, context) {
|
1296 | return visitor.visitInvokeFunctionExpr(this, context);
|
1297 | }
|
1298 | }
|
1299 | class TaggedTemplateExpr extends Expression {
|
1300 | constructor(tag, template, type, sourceSpan) {
|
1301 | super(type, sourceSpan);
|
1302 | this.tag = tag;
|
1303 | this.template = template;
|
1304 | }
|
1305 | isEquivalent(e) {
|
1306 | return e instanceof TaggedTemplateExpr && this.tag.isEquivalent(e.tag) &&
|
1307 | areAllEquivalentPredicate(this.template.elements, e.template.elements, (a, b) => a.text === b.text) &&
|
1308 | areAllEquivalent(this.template.expressions, e.template.expressions);
|
1309 | }
|
1310 | isConstant() {
|
1311 | return false;
|
1312 | }
|
1313 | visitExpression(visitor, context) {
|
1314 | return visitor.visitTaggedTemplateExpr(this, context);
|
1315 | }
|
1316 | }
|
1317 | class InstantiateExpr extends Expression {
|
1318 | constructor(classExpr, args, type, sourceSpan) {
|
1319 | super(type, sourceSpan);
|
1320 | this.classExpr = classExpr;
|
1321 | this.args = args;
|
1322 | }
|
1323 | isEquivalent(e) {
|
1324 | return e instanceof InstantiateExpr && this.classExpr.isEquivalent(e.classExpr) &&
|
1325 | areAllEquivalent(this.args, e.args);
|
1326 | }
|
1327 | isConstant() {
|
1328 | return false;
|
1329 | }
|
1330 | visitExpression(visitor, context) {
|
1331 | return visitor.visitInstantiateExpr(this, context);
|
1332 | }
|
1333 | }
|
1334 | class LiteralExpr extends Expression {
|
1335 | constructor(value, type, sourceSpan) {
|
1336 | super(type, sourceSpan);
|
1337 | this.value = value;
|
1338 | }
|
1339 | isEquivalent(e) {
|
1340 | return e instanceof LiteralExpr && this.value === e.value;
|
1341 | }
|
1342 | isConstant() {
|
1343 | return true;
|
1344 | }
|
1345 | visitExpression(visitor, context) {
|
1346 | return visitor.visitLiteralExpr(this, context);
|
1347 | }
|
1348 | }
|
1349 | class TemplateLiteral {
|
1350 | constructor(elements, expressions) {
|
1351 | this.elements = elements;
|
1352 | this.expressions = expressions;
|
1353 | }
|
1354 | }
|
1355 | class TemplateLiteralElement {
|
1356 | constructor(text, sourceSpan, rawText) {
|
1357 | var _a;
|
1358 | this.text = text;
|
1359 | this.sourceSpan = sourceSpan;
|
1360 |
|
1361 |
|
1362 |
|
1363 |
|
1364 |
|
1365 |
|
1366 | 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));
|
1367 | }
|
1368 | }
|
1369 | class MessagePiece {
|
1370 | constructor(text, sourceSpan) {
|
1371 | this.text = text;
|
1372 | this.sourceSpan = sourceSpan;
|
1373 | }
|
1374 | }
|
1375 | class LiteralPiece extends MessagePiece {
|
1376 | }
|
1377 | class PlaceholderPiece extends MessagePiece {
|
1378 | }
|
1379 | class LocalizedString extends Expression {
|
1380 | constructor(metaBlock, messageParts, placeHolderNames, expressions, sourceSpan) {
|
1381 | super(STRING_TYPE, sourceSpan);
|
1382 | this.metaBlock = metaBlock;
|
1383 | this.messageParts = messageParts;
|
1384 | this.placeHolderNames = placeHolderNames;
|
1385 | this.expressions = expressions;
|
1386 | }
|
1387 | isEquivalent(e) {
|
1388 |
|
1389 | return false;
|
1390 | }
|
1391 | isConstant() {
|
1392 | return false;
|
1393 | }
|
1394 | visitExpression(visitor, context) {
|
1395 | return visitor.visitLocalizedString(this, context);
|
1396 | }
|
1397 | |
1398 |
|
1399 |
|
1400 |
|
1401 |
|
1402 |
|
1403 |
|
1404 |
|
1405 | serializeI18nHead() {
|
1406 | const MEANING_SEPARATOR = '|';
|
1407 | const ID_SEPARATOR = '@@';
|
1408 | const LEGACY_ID_INDICATOR = '␟';
|
1409 | let metaBlock = this.metaBlock.description || '';
|
1410 | if (this.metaBlock.meaning) {
|
1411 | metaBlock = `${this.metaBlock.meaning}${MEANING_SEPARATOR}${metaBlock}`;
|
1412 | }
|
1413 | if (this.metaBlock.customId) {
|
1414 | metaBlock = `${metaBlock}${ID_SEPARATOR}${this.metaBlock.customId}`;
|
1415 | }
|
1416 | if (this.metaBlock.legacyIds) {
|
1417 | this.metaBlock.legacyIds.forEach(legacyId => {
|
1418 | metaBlock = `${metaBlock}${LEGACY_ID_INDICATOR}${legacyId}`;
|
1419 | });
|
1420 | }
|
1421 | return createCookedRawString(metaBlock, this.messageParts[0].text, this.getMessagePartSourceSpan(0));
|
1422 | }
|
1423 | getMessagePartSourceSpan(i) {
|
1424 | var _a, _b;
|
1425 | return (_b = (_a = this.messageParts[i]) === null || _a === void 0 ? void 0 : _a.sourceSpan) !== null && _b !== void 0 ? _b : this.sourceSpan;
|
1426 | }
|
1427 | getPlaceholderSourceSpan(i) {
|
1428 | var _a, _b, _c, _d;
|
1429 | 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;
|
1430 | }
|
1431 | |
1432 |
|
1433 |
|
1434 |
|
1435 |
|
1436 |
|
1437 |
|
1438 | serializeI18nTemplatePart(partIndex) {
|
1439 | const placeholderName = this.placeHolderNames[partIndex - 1].text;
|
1440 | const messagePart = this.messageParts[partIndex];
|
1441 | return createCookedRawString(placeholderName, messagePart.text, this.getMessagePartSourceSpan(partIndex));
|
1442 | }
|
1443 | }
|
1444 | const escapeSlashes = (str) => str.replace(/\\/g, '\\\\');
|
1445 | const escapeStartingColon = (str) => str.replace(/^:/, '\\:');
|
1446 | const escapeColons = (str) => str.replace(/:/g, '\\:');
|
1447 | const escapeForTemplateLiteral = (str) => str.replace(/`/g, '\\`').replace(/\${/g, '$\\{');
|
1448 | |
1449 |
|
1450 |
|
1451 |
|
1452 |
|
1453 |
|
1454 |
|
1455 |
|
1456 |
|
1457 |
|
1458 |
|
1459 |
|
1460 |
|
1461 |
|
1462 | function createCookedRawString(metaBlock, messagePart, range) {
|
1463 | if (metaBlock === '') {
|
1464 | return {
|
1465 | cooked: messagePart,
|
1466 | raw: escapeForTemplateLiteral(escapeStartingColon(escapeSlashes(messagePart))),
|
1467 | range,
|
1468 | };
|
1469 | }
|
1470 | else {
|
1471 | return {
|
1472 | cooked: `:${metaBlock}:${messagePart}`,
|
1473 | raw: escapeForTemplateLiteral(`:${escapeColons(escapeSlashes(metaBlock))}:${escapeSlashes(messagePart)}`),
|
1474 | range,
|
1475 | };
|
1476 | }
|
1477 | }
|
1478 | class ExternalExpr extends Expression {
|
1479 | constructor(value, type, typeParams = null, sourceSpan) {
|
1480 | super(type, sourceSpan);
|
1481 | this.value = value;
|
1482 | this.typeParams = typeParams;
|
1483 | }
|
1484 | isEquivalent(e) {
|
1485 | return e instanceof ExternalExpr && this.value.name === e.value.name &&
|
1486 | this.value.moduleName === e.value.moduleName && this.value.runtime === e.value.runtime;
|
1487 | }
|
1488 | isConstant() {
|
1489 | return false;
|
1490 | }
|
1491 | visitExpression(visitor, context) {
|
1492 | return visitor.visitExternalExpr(this, context);
|
1493 | }
|
1494 | }
|
1495 | class ConditionalExpr extends Expression {
|
1496 | constructor(condition, trueCase, falseCase = null, type, sourceSpan) {
|
1497 | super(type || trueCase.type, sourceSpan);
|
1498 | this.condition = condition;
|
1499 | this.falseCase = falseCase;
|
1500 | this.trueCase = trueCase;
|
1501 | }
|
1502 | isEquivalent(e) {
|
1503 | return e instanceof ConditionalExpr && this.condition.isEquivalent(e.condition) &&
|
1504 | this.trueCase.isEquivalent(e.trueCase) && nullSafeIsEquivalent(this.falseCase, e.falseCase);
|
1505 | }
|
1506 | isConstant() {
|
1507 | return false;
|
1508 | }
|
1509 | visitExpression(visitor, context) {
|
1510 | return visitor.visitConditionalExpr(this, context);
|
1511 | }
|
1512 | }
|
1513 | class NotExpr extends Expression {
|
1514 | constructor(condition, sourceSpan) {
|
1515 | super(BOOL_TYPE, sourceSpan);
|
1516 | this.condition = condition;
|
1517 | }
|
1518 | isEquivalent(e) {
|
1519 | return e instanceof NotExpr && this.condition.isEquivalent(e.condition);
|
1520 | }
|
1521 | isConstant() {
|
1522 | return false;
|
1523 | }
|
1524 | visitExpression(visitor, context) {
|
1525 | return visitor.visitNotExpr(this, context);
|
1526 | }
|
1527 | }
|
1528 | class AssertNotNull extends Expression {
|
1529 | constructor(condition, sourceSpan) {
|
1530 | super(condition.type, sourceSpan);
|
1531 | this.condition = condition;
|
1532 | }
|
1533 | isEquivalent(e) {
|
1534 | return e instanceof AssertNotNull && this.condition.isEquivalent(e.condition);
|
1535 | }
|
1536 | isConstant() {
|
1537 | return false;
|
1538 | }
|
1539 | visitExpression(visitor, context) {
|
1540 | return visitor.visitAssertNotNullExpr(this, context);
|
1541 | }
|
1542 | }
|
1543 | class CastExpr extends Expression {
|
1544 | constructor(value, type, sourceSpan) {
|
1545 | super(type, sourceSpan);
|
1546 | this.value = value;
|
1547 | }
|
1548 | isEquivalent(e) {
|
1549 | return e instanceof CastExpr && this.value.isEquivalent(e.value);
|
1550 | }
|
1551 | isConstant() {
|
1552 | return false;
|
1553 | }
|
1554 | visitExpression(visitor, context) {
|
1555 | return visitor.visitCastExpr(this, context);
|
1556 | }
|
1557 | }
|
1558 | class FnParam {
|
1559 | constructor(name, type = null) {
|
1560 | this.name = name;
|
1561 | this.type = type;
|
1562 | }
|
1563 | isEquivalent(param) {
|
1564 | return this.name === param.name;
|
1565 | }
|
1566 | }
|
1567 | class FunctionExpr extends Expression {
|
1568 | constructor(params, statements, type, sourceSpan, name) {
|
1569 | super(type, sourceSpan);
|
1570 | this.params = params;
|
1571 | this.statements = statements;
|
1572 | this.name = name;
|
1573 | }
|
1574 | isEquivalent(e) {
|
1575 | return e instanceof FunctionExpr && areAllEquivalent(this.params, e.params) &&
|
1576 | areAllEquivalent(this.statements, e.statements);
|
1577 | }
|
1578 | isConstant() {
|
1579 | return false;
|
1580 | }
|
1581 | visitExpression(visitor, context) {
|
1582 | return visitor.visitFunctionExpr(this, context);
|
1583 | }
|
1584 | toDeclStmt(name, modifiers) {
|
1585 | return new DeclareFunctionStmt(name, this.params, this.statements, this.type, modifiers, this.sourceSpan);
|
1586 | }
|
1587 | }
|
1588 | class UnaryOperatorExpr extends Expression {
|
1589 | constructor(operator, expr, type, sourceSpan, parens = true) {
|
1590 | super(type || NUMBER_TYPE, sourceSpan);
|
1591 | this.operator = operator;
|
1592 | this.expr = expr;
|
1593 | this.parens = parens;
|
1594 | }
|
1595 | isEquivalent(e) {
|
1596 | return e instanceof UnaryOperatorExpr && this.operator === e.operator &&
|
1597 | this.expr.isEquivalent(e.expr);
|
1598 | }
|
1599 | isConstant() {
|
1600 | return false;
|
1601 | }
|
1602 | visitExpression(visitor, context) {
|
1603 | return visitor.visitUnaryOperatorExpr(this, context);
|
1604 | }
|
1605 | }
|
1606 | class BinaryOperatorExpr extends Expression {
|
1607 | constructor(operator, lhs, rhs, type, sourceSpan, parens = true) {
|
1608 | super(type || lhs.type, sourceSpan);
|
1609 | this.operator = operator;
|
1610 | this.rhs = rhs;
|
1611 | this.parens = parens;
|
1612 | this.lhs = lhs;
|
1613 | }
|
1614 | isEquivalent(e) {
|
1615 | return e instanceof BinaryOperatorExpr && this.operator === e.operator &&
|
1616 | this.lhs.isEquivalent(e.lhs) && this.rhs.isEquivalent(e.rhs);
|
1617 | }
|
1618 | isConstant() {
|
1619 | return false;
|
1620 | }
|
1621 | visitExpression(visitor, context) {
|
1622 | return visitor.visitBinaryOperatorExpr(this, context);
|
1623 | }
|
1624 | }
|
1625 | class ReadPropExpr extends Expression {
|
1626 | constructor(receiver, name, type, sourceSpan) {
|
1627 | super(type, sourceSpan);
|
1628 | this.receiver = receiver;
|
1629 | this.name = name;
|
1630 | }
|
1631 | isEquivalent(e) {
|
1632 | return e instanceof ReadPropExpr && this.receiver.isEquivalent(e.receiver) &&
|
1633 | this.name === e.name;
|
1634 | }
|
1635 | isConstant() {
|
1636 | return false;
|
1637 | }
|
1638 | visitExpression(visitor, context) {
|
1639 | return visitor.visitReadPropExpr(this, context);
|
1640 | }
|
1641 | set(value) {
|
1642 | return new WritePropExpr(this.receiver, this.name, value, null, this.sourceSpan);
|
1643 | }
|
1644 | }
|
1645 | class ReadKeyExpr extends Expression {
|
1646 | constructor(receiver, index, type, sourceSpan) {
|
1647 | super(type, sourceSpan);
|
1648 | this.receiver = receiver;
|
1649 | this.index = index;
|
1650 | }
|
1651 | isEquivalent(e) {
|
1652 | return e instanceof ReadKeyExpr && this.receiver.isEquivalent(e.receiver) &&
|
1653 | this.index.isEquivalent(e.index);
|
1654 | }
|
1655 | isConstant() {
|
1656 | return false;
|
1657 | }
|
1658 | visitExpression(visitor, context) {
|
1659 | return visitor.visitReadKeyExpr(this, context);
|
1660 | }
|
1661 | set(value) {
|
1662 | return new WriteKeyExpr(this.receiver, this.index, value, null, this.sourceSpan);
|
1663 | }
|
1664 | }
|
1665 | class LiteralArrayExpr extends Expression {
|
1666 | constructor(entries, type, sourceSpan) {
|
1667 | super(type, sourceSpan);
|
1668 | this.entries = entries;
|
1669 | }
|
1670 | isConstant() {
|
1671 | return this.entries.every(e => e.isConstant());
|
1672 | }
|
1673 | isEquivalent(e) {
|
1674 | return e instanceof LiteralArrayExpr && areAllEquivalent(this.entries, e.entries);
|
1675 | }
|
1676 | visitExpression(visitor, context) {
|
1677 | return visitor.visitLiteralArrayExpr(this, context);
|
1678 | }
|
1679 | }
|
1680 | class LiteralMapEntry {
|
1681 | constructor(key, value, quoted) {
|
1682 | this.key = key;
|
1683 | this.value = value;
|
1684 | this.quoted = quoted;
|
1685 | }
|
1686 | isEquivalent(e) {
|
1687 | return this.key === e.key && this.value.isEquivalent(e.value);
|
1688 | }
|
1689 | }
|
1690 | class LiteralMapExpr extends Expression {
|
1691 | constructor(entries, type, sourceSpan) {
|
1692 | super(type, sourceSpan);
|
1693 | this.entries = entries;
|
1694 | this.valueType = null;
|
1695 | if (type) {
|
1696 | this.valueType = type.valueType;
|
1697 | }
|
1698 | }
|
1699 | isEquivalent(e) {
|
1700 | return e instanceof LiteralMapExpr && areAllEquivalent(this.entries, e.entries);
|
1701 | }
|
1702 | isConstant() {
|
1703 | return this.entries.every(e => e.value.isConstant());
|
1704 | }
|
1705 | visitExpression(visitor, context) {
|
1706 | return visitor.visitLiteralMapExpr(this, context);
|
1707 | }
|
1708 | }
|
1709 | const THIS_EXPR = new ReadVarExpr(BuiltinVar.This, null, null);
|
1710 | const SUPER_EXPR = new ReadVarExpr(BuiltinVar.Super, null, null);
|
1711 | const CATCH_ERROR_VAR = new ReadVarExpr(BuiltinVar.CatchError, null, null);
|
1712 | const CATCH_STACK_VAR = new ReadVarExpr(BuiltinVar.CatchStack, null, null);
|
1713 | const NULL_EXPR = new LiteralExpr(null, null, null);
|
1714 | const TYPED_NULL_EXPR = new LiteralExpr(null, INFERRED_TYPE, null);
|
1715 |
|
1716 | var StmtModifier;
|
1717 | (function (StmtModifier) {
|
1718 | StmtModifier[StmtModifier["Final"] = 0] = "Final";
|
1719 | StmtModifier[StmtModifier["Private"] = 1] = "Private";
|
1720 | StmtModifier[StmtModifier["Exported"] = 2] = "Exported";
|
1721 | StmtModifier[StmtModifier["Static"] = 3] = "Static";
|
1722 | })(StmtModifier || (StmtModifier = {}));
|
1723 | class LeadingComment {
|
1724 | constructor(text, multiline, trailingNewline) {
|
1725 | this.text = text;
|
1726 | this.multiline = multiline;
|
1727 | this.trailingNewline = trailingNewline;
|
1728 | }
|
1729 | toString() {
|
1730 | return this.multiline ? ` ${this.text} ` : this.text;
|
1731 | }
|
1732 | }
|
1733 | class JSDocComment extends LeadingComment {
|
1734 | constructor(tags) {
|
1735 | super('', true, true);
|
1736 | this.tags = tags;
|
1737 | }
|
1738 | toString() {
|
1739 | return serializeTags(this.tags);
|
1740 | }
|
1741 | }
|
1742 | class Statement {
|
1743 | constructor(modifiers = [], sourceSpan = null, leadingComments) {
|
1744 | this.modifiers = modifiers;
|
1745 | this.sourceSpan = sourceSpan;
|
1746 | this.leadingComments = leadingComments;
|
1747 | }
|
1748 | hasModifier(modifier) {
|
1749 | return this.modifiers.indexOf(modifier) !== -1;
|
1750 | }
|
1751 | addLeadingComment(leadingComment) {
|
1752 | var _a;
|
1753 | this.leadingComments = (_a = this.leadingComments) !== null && _a !== void 0 ? _a : [];
|
1754 | this.leadingComments.push(leadingComment);
|
1755 | }
|
1756 | }
|
1757 | class DeclareVarStmt extends Statement {
|
1758 | constructor(name, value, type, modifiers, sourceSpan, leadingComments) {
|
1759 | super(modifiers, sourceSpan, leadingComments);
|
1760 | this.name = name;
|
1761 | this.value = value;
|
1762 | this.type = type || (value && value.type) || null;
|
1763 | }
|
1764 | isEquivalent(stmt) {
|
1765 | return stmt instanceof DeclareVarStmt && this.name === stmt.name &&
|
1766 | (this.value ? !!stmt.value && this.value.isEquivalent(stmt.value) : !stmt.value);
|
1767 | }
|
1768 | visitStatement(visitor, context) {
|
1769 | return visitor.visitDeclareVarStmt(this, context);
|
1770 | }
|
1771 | }
|
1772 | class DeclareFunctionStmt extends Statement {
|
1773 | constructor(name, params, statements, type, modifiers, sourceSpan, leadingComments) {
|
1774 | super(modifiers, sourceSpan, leadingComments);
|
1775 | this.name = name;
|
1776 | this.params = params;
|
1777 | this.statements = statements;
|
1778 | this.type = type || null;
|
1779 | }
|
1780 | isEquivalent(stmt) {
|
1781 | return stmt instanceof DeclareFunctionStmt && areAllEquivalent(this.params, stmt.params) &&
|
1782 | areAllEquivalent(this.statements, stmt.statements);
|
1783 | }
|
1784 | visitStatement(visitor, context) {
|
1785 | return visitor.visitDeclareFunctionStmt(this, context);
|
1786 | }
|
1787 | }
|
1788 | class ExpressionStatement extends Statement {
|
1789 | constructor(expr, sourceSpan, leadingComments) {
|
1790 | super([], sourceSpan, leadingComments);
|
1791 | this.expr = expr;
|
1792 | }
|
1793 | isEquivalent(stmt) {
|
1794 | return stmt instanceof ExpressionStatement && this.expr.isEquivalent(stmt.expr);
|
1795 | }
|
1796 | visitStatement(visitor, context) {
|
1797 | return visitor.visitExpressionStmt(this, context);
|
1798 | }
|
1799 | }
|
1800 | class ReturnStatement extends Statement {
|
1801 | constructor(value, sourceSpan = null, leadingComments) {
|
1802 | super([], sourceSpan, leadingComments);
|
1803 | this.value = value;
|
1804 | }
|
1805 | isEquivalent(stmt) {
|
1806 | return stmt instanceof ReturnStatement && this.value.isEquivalent(stmt.value);
|
1807 | }
|
1808 | visitStatement(visitor, context) {
|
1809 | return visitor.visitReturnStmt(this, context);
|
1810 | }
|
1811 | }
|
1812 | class IfStmt extends Statement {
|
1813 | constructor(condition, trueCase, falseCase = [], sourceSpan, leadingComments) {
|
1814 | super([], sourceSpan, leadingComments);
|
1815 | this.condition = condition;
|
1816 | this.trueCase = trueCase;
|
1817 | this.falseCase = falseCase;
|
1818 | }
|
1819 | isEquivalent(stmt) {
|
1820 | return stmt instanceof IfStmt && this.condition.isEquivalent(stmt.condition) &&
|
1821 | areAllEquivalent(this.trueCase, stmt.trueCase) &&
|
1822 | areAllEquivalent(this.falseCase, stmt.falseCase);
|
1823 | }
|
1824 | visitStatement(visitor, context) {
|
1825 | return visitor.visitIfStmt(this, context);
|
1826 | }
|
1827 | }
|
1828 | function jsDocComment(tags = []) {
|
1829 | return new JSDocComment(tags);
|
1830 | }
|
1831 | function variable(name, type, sourceSpan) {
|
1832 | return new ReadVarExpr(name, type, sourceSpan);
|
1833 | }
|
1834 | function importExpr(id, typeParams = null, sourceSpan) {
|
1835 | return new ExternalExpr(id, null, typeParams, sourceSpan);
|
1836 | }
|
1837 | function expressionType(expr, typeModifiers, typeParams) {
|
1838 | return new ExpressionType(expr, typeModifiers, typeParams);
|
1839 | }
|
1840 | function typeofExpr(expr) {
|
1841 | return new TypeofExpr(expr);
|
1842 | }
|
1843 | function literalArr(values, type, sourceSpan) {
|
1844 | return new LiteralArrayExpr(values, type, sourceSpan);
|
1845 | }
|
1846 | function literalMap(values, type = null) {
|
1847 | return new LiteralMapExpr(values.map(e => new LiteralMapEntry(e.key, e.value, e.quoted)), type, null);
|
1848 | }
|
1849 | function not(expr, sourceSpan) {
|
1850 | return new NotExpr(expr, sourceSpan);
|
1851 | }
|
1852 | function assertNotNull(expr, sourceSpan) {
|
1853 | return new AssertNotNull(expr, sourceSpan);
|
1854 | }
|
1855 | function fn(params, body, type, sourceSpan, name) {
|
1856 | return new FunctionExpr(params, body, type, sourceSpan, name);
|
1857 | }
|
1858 | function ifStmt(condition, thenClause, elseClause, sourceSpan, leadingComments) {
|
1859 | return new IfStmt(condition, thenClause, elseClause, sourceSpan, leadingComments);
|
1860 | }
|
1861 | function taggedTemplate(tag, template, type, sourceSpan) {
|
1862 | return new TaggedTemplateExpr(tag, template, type, sourceSpan);
|
1863 | }
|
1864 | function literal(value, type, sourceSpan) {
|
1865 | return new LiteralExpr(value, type, sourceSpan);
|
1866 | }
|
1867 | function localizedString(metaBlock, messageParts, placeholderNames, expressions, sourceSpan) {
|
1868 | return new LocalizedString(metaBlock, messageParts, placeholderNames, expressions, sourceSpan);
|
1869 | }
|
1870 | function isNull(exp) {
|
1871 | return exp instanceof LiteralExpr && exp.value === null;
|
1872 | }
|
1873 | |
1874 |
|
1875 |
|
1876 |
|
1877 | function tagToString(tag) {
|
1878 | let out = '';
|
1879 | if (tag.tagName) {
|
1880 | out += ` @${tag.tagName}`;
|
1881 | }
|
1882 | if (tag.text) {
|
1883 | if (tag.text.match(/\/\*|\*\//)) {
|
1884 | throw new Error('JSDoc text cannot contain "/*" and "*/"');
|
1885 | }
|
1886 | out += ' ' + tag.text.replace(/@/g, '\\@');
|
1887 | }
|
1888 | return out;
|
1889 | }
|
1890 | function serializeTags(tags) {
|
1891 | if (tags.length === 0)
|
1892 | return '';
|
1893 | if (tags.length === 1 && tags[0].tagName && !tags[0].text) {
|
1894 |
|
1895 | return `*${tagToString(tags[0])} `;
|
1896 | }
|
1897 | let out = '*\n';
|
1898 | for (const tag of tags) {
|
1899 | out += ' *';
|
1900 |
|
1901 | out += tagToString(tag).replace(/\n/g, '\n * ');
|
1902 | out += '\n';
|
1903 | }
|
1904 | out += ' ';
|
1905 | return out;
|
1906 | }
|
1907 |
|
1908 | |
1909 |
|
1910 |
|
1911 |
|
1912 |
|
1913 |
|
1914 |
|
1915 | const CONSTANT_PREFIX = '_c';
|
1916 | |
1917 |
|
1918 |
|
1919 |
|
1920 |
|
1921 |
|
1922 |
|
1923 |
|
1924 | const UNKNOWN_VALUE_KEY = variable('<unknown>');
|
1925 | |
1926 |
|
1927 |
|
1928 |
|
1929 |
|
1930 |
|
1931 | const KEY_CONTEXT = {};
|
1932 | |
1933 |
|
1934 |
|
1935 |
|
1936 |
|
1937 | const POOL_INCLUSION_LENGTH_THRESHOLD_FOR_STRINGS = 50;
|
1938 | |
1939 |
|
1940 |
|
1941 |
|
1942 |
|
1943 |
|
1944 |
|
1945 |
|
1946 | class FixupExpression extends Expression {
|
1947 | constructor(resolved) {
|
1948 | super(resolved.type);
|
1949 | this.resolved = resolved;
|
1950 | this.original = resolved;
|
1951 | }
|
1952 | visitExpression(visitor, context) {
|
1953 | if (context === KEY_CONTEXT) {
|
1954 |
|
1955 |
|
1956 | return this.original.visitExpression(visitor, context);
|
1957 | }
|
1958 | else {
|
1959 | return this.resolved.visitExpression(visitor, context);
|
1960 | }
|
1961 | }
|
1962 | isEquivalent(e) {
|
1963 | return e instanceof FixupExpression && this.resolved.isEquivalent(e.resolved);
|
1964 | }
|
1965 | isConstant() {
|
1966 | return true;
|
1967 | }
|
1968 | fixup(expression) {
|
1969 | this.resolved = expression;
|
1970 | this.shared = true;
|
1971 | }
|
1972 | }
|
1973 | |
1974 |
|
1975 |
|
1976 |
|
1977 |
|
1978 | class ConstantPool {
|
1979 | constructor(isClosureCompilerEnabled = false) {
|
1980 | this.isClosureCompilerEnabled = isClosureCompilerEnabled;
|
1981 | this.statements = [];
|
1982 | this.literals = new Map();
|
1983 | this.literalFactories = new Map();
|
1984 | this.injectorDefinitions = new Map();
|
1985 | this.directiveDefinitions = new Map();
|
1986 | this.componentDefinitions = new Map();
|
1987 | this.pipeDefinitions = new Map();
|
1988 | this.nextNameIndex = 0;
|
1989 | }
|
1990 | getConstLiteral(literal, forceShared) {
|
1991 | if ((literal instanceof LiteralExpr && !isLongStringLiteral(literal)) ||
|
1992 | literal instanceof FixupExpression) {
|
1993 |
|
1994 |
|
1995 | return literal;
|
1996 | }
|
1997 | const key = this.keyOf(literal);
|
1998 | let fixup = this.literals.get(key);
|
1999 | let newValue = false;
|
2000 | if (!fixup) {
|
2001 | fixup = new FixupExpression(literal);
|
2002 | this.literals.set(key, fixup);
|
2003 | newValue = true;
|
2004 | }
|
2005 | if ((!newValue && !fixup.shared) || (newValue && forceShared)) {
|
2006 |
|
2007 | const name = this.freshName();
|
2008 | let definition;
|
2009 | let usage;
|
2010 | if (this.isClosureCompilerEnabled && isLongStringLiteral(literal)) {
|
2011 |
|
2012 |
|
2013 |
|
2014 |
|
2015 |
|
2016 |
|
2017 |
|
2018 |
|
2019 |
|
2020 |
|
2021 |
|
2022 |
|
2023 |
|
2024 | definition = variable(name).set(new FunctionExpr([],
|
2025 | [
|
2026 |
|
2027 | new ReturnStatement(literal),
|
2028 | ]));
|
2029 | usage = variable(name).callFn([]);
|
2030 | }
|
2031 | else {
|
2032 |
|
2033 |
|
2034 | definition = variable(name).set(literal);
|
2035 | usage = variable(name);
|
2036 | }
|
2037 | this.statements.push(definition.toDeclStmt(INFERRED_TYPE, [StmtModifier.Final]));
|
2038 | fixup.fixup(usage);
|
2039 | }
|
2040 | return fixup;
|
2041 | }
|
2042 | getDefinition(type, kind, ctx, forceShared = false) {
|
2043 | const definitions = this.definitionsOf(kind);
|
2044 | let fixup = definitions.get(type);
|
2045 | let newValue = false;
|
2046 | if (!fixup) {
|
2047 | const property = this.propertyNameOf(kind);
|
2048 | fixup = new FixupExpression(ctx.importExpr(type).prop(property));
|
2049 | definitions.set(type, fixup);
|
2050 | newValue = true;
|
2051 | }
|
2052 | if ((!newValue && !fixup.shared) || (newValue && forceShared)) {
|
2053 | const name = this.freshName();
|
2054 | this.statements.push(variable(name).set(fixup.resolved).toDeclStmt(INFERRED_TYPE, [StmtModifier.Final]));
|
2055 | fixup.fixup(variable(name));
|
2056 | }
|
2057 | return fixup;
|
2058 | }
|
2059 | getLiteralFactory(literal) {
|
2060 |
|
2061 | if (literal instanceof LiteralArrayExpr) {
|
2062 | const argumentsForKey = literal.entries.map(e => e.isConstant() ? e : UNKNOWN_VALUE_KEY);
|
2063 | const key = this.keyOf(literalArr(argumentsForKey));
|
2064 | return this._getLiteralFactory(key, literal.entries, entries => literalArr(entries));
|
2065 | }
|
2066 | else {
|
2067 | const expressionForKey = literalMap(literal.entries.map(e => ({
|
2068 | key: e.key,
|
2069 | value: e.value.isConstant() ? e.value : UNKNOWN_VALUE_KEY,
|
2070 | quoted: e.quoted
|
2071 | })));
|
2072 | const key = this.keyOf(expressionForKey);
|
2073 | return this._getLiteralFactory(key, literal.entries.map(e => e.value), entries => literalMap(entries.map((value, index) => ({
|
2074 | key: literal.entries[index].key,
|
2075 | value,
|
2076 | quoted: literal.entries[index].quoted
|
2077 | }))));
|
2078 | }
|
2079 | }
|
2080 | _getLiteralFactory(key, values, resultMap) {
|
2081 | let literalFactory = this.literalFactories.get(key);
|
2082 | const literalFactoryArguments = values.filter((e => !e.isConstant()));
|
2083 | if (!literalFactory) {
|
2084 | const resultExpressions = values.map((e, index) => e.isConstant() ? this.getConstLiteral(e, true) : variable(`a${index}`));
|
2085 | const parameters = resultExpressions.filter(isVariable).map(e => new FnParam(e.name, DYNAMIC_TYPE));
|
2086 | const pureFunctionDeclaration = fn(parameters, [new ReturnStatement(resultMap(resultExpressions))], INFERRED_TYPE);
|
2087 | const name = this.freshName();
|
2088 | this.statements.push(variable(name).set(pureFunctionDeclaration).toDeclStmt(INFERRED_TYPE, [
|
2089 | StmtModifier.Final
|
2090 | ]));
|
2091 | literalFactory = variable(name);
|
2092 | this.literalFactories.set(key, literalFactory);
|
2093 | }
|
2094 | return { literalFactory, literalFactoryArguments };
|
2095 | }
|
2096 | |
2097 |
|
2098 |
|
2099 |
|
2100 |
|
2101 |
|
2102 |
|
2103 | uniqueName(prefix) {
|
2104 | return `${prefix}${this.nextNameIndex++}`;
|
2105 | }
|
2106 | definitionsOf(kind) {
|
2107 | switch (kind) {
|
2108 | case 2 :
|
2109 | return this.componentDefinitions;
|
2110 | case 1 :
|
2111 | return this.directiveDefinitions;
|
2112 | case 0 :
|
2113 | return this.injectorDefinitions;
|
2114 | case 3 :
|
2115 | return this.pipeDefinitions;
|
2116 | }
|
2117 | }
|
2118 | propertyNameOf(kind) {
|
2119 | switch (kind) {
|
2120 | case 2 :
|
2121 | return 'ɵcmp';
|
2122 | case 1 :
|
2123 | return 'ɵdir';
|
2124 | case 0 :
|
2125 | return 'ɵinj';
|
2126 | case 3 :
|
2127 | return 'ɵpipe';
|
2128 | }
|
2129 | }
|
2130 | freshName() {
|
2131 | return this.uniqueName(CONSTANT_PREFIX);
|
2132 | }
|
2133 | keyOf(expression) {
|
2134 | return expression.visitExpression(new KeyVisitor(), KEY_CONTEXT);
|
2135 | }
|
2136 | }
|
2137 | |
2138 |
|
2139 |
|
2140 |
|
2141 |
|
2142 |
|
2143 | class KeyVisitor {
|
2144 | constructor() {
|
2145 | this.visitWrappedNodeExpr = invalid;
|
2146 | this.visitWriteVarExpr = invalid;
|
2147 | this.visitWriteKeyExpr = invalid;
|
2148 | this.visitWritePropExpr = invalid;
|
2149 | this.visitInvokeMethodExpr = invalid;
|
2150 | this.visitInvokeFunctionExpr = invalid;
|
2151 | this.visitTaggedTemplateExpr = invalid;
|
2152 | this.visitInstantiateExpr = invalid;
|
2153 | this.visitConditionalExpr = invalid;
|
2154 | this.visitNotExpr = invalid;
|
2155 | this.visitAssertNotNullExpr = invalid;
|
2156 | this.visitCastExpr = invalid;
|
2157 | this.visitFunctionExpr = invalid;
|
2158 | this.visitUnaryOperatorExpr = invalid;
|
2159 | this.visitBinaryOperatorExpr = invalid;
|
2160 | this.visitReadPropExpr = invalid;
|
2161 | this.visitReadKeyExpr = invalid;
|
2162 | this.visitCommaExpr = invalid;
|
2163 | this.visitLocalizedString = invalid;
|
2164 | }
|
2165 | visitLiteralExpr(ast) {
|
2166 | return `${typeof ast.value === 'string' ? '"' + ast.value + '"' : ast.value}`;
|
2167 | }
|
2168 | visitLiteralArrayExpr(ast, context) {
|
2169 | return `[${ast.entries.map(entry => entry.visitExpression(this, context)).join(',')}]`;
|
2170 | }
|
2171 | visitLiteralMapExpr(ast, context) {
|
2172 | const mapKey = (entry) => {
|
2173 | const quote = entry.quoted ? '"' : '';
|
2174 | return `${quote}${entry.key}${quote}`;
|
2175 | };
|
2176 | const mapEntry = (entry) => `${mapKey(entry)}:${entry.value.visitExpression(this, context)}`;
|
2177 | return `{${ast.entries.map(mapEntry).join(',')}`;
|
2178 | }
|
2179 | visitExternalExpr(ast) {
|
2180 | return ast.value.moduleName ? `EX:${ast.value.moduleName}:${ast.value.name}` :
|
2181 | `EX:${ast.value.runtime.name}`;
|
2182 | }
|
2183 | visitReadVarExpr(node) {
|
2184 | return `VAR:${node.name}`;
|
2185 | }
|
2186 | visitTypeofExpr(node, context) {
|
2187 | return `TYPEOF:${node.expr.visitExpression(this, context)}`;
|
2188 | }
|
2189 | }
|
2190 | function invalid(arg) {
|
2191 | throw new Error(`Invalid state: Visitor ${this.constructor.name} doesn't handle ${arg.constructor.name}`);
|
2192 | }
|
2193 | function isVariable(e) {
|
2194 | return e instanceof ReadVarExpr;
|
2195 | }
|
2196 | function isLongStringLiteral(expr) {
|
2197 | return expr instanceof LiteralExpr && typeof expr.value === 'string' &&
|
2198 | expr.value.length >= POOL_INCLUSION_LENGTH_THRESHOLD_FOR_STRINGS;
|
2199 | }
|
2200 |
|
2201 | |
2202 |
|
2203 |
|
2204 |
|
2205 |
|
2206 |
|
2207 |
|
2208 | const CORE = '@angular/core';
|
2209 | class Identifiers {
|
2210 | }
|
2211 | Identifiers.ANALYZE_FOR_ENTRY_COMPONENTS = {
|
2212 | name: 'ANALYZE_FOR_ENTRY_COMPONENTS',
|
2213 | moduleName: CORE,
|
2214 | };
|
2215 | Identifiers.ElementRef = { name: 'ElementRef', moduleName: CORE };
|
2216 | Identifiers.NgModuleRef = { name: 'NgModuleRef', moduleName: CORE };
|
2217 | Identifiers.ViewContainerRef = { name: 'ViewContainerRef', moduleName: CORE };
|
2218 | Identifiers.ChangeDetectorRef = {
|
2219 | name: 'ChangeDetectorRef',
|
2220 | moduleName: CORE,
|
2221 | };
|
2222 | Identifiers.QueryList = { name: 'QueryList', moduleName: CORE };
|
2223 | Identifiers.TemplateRef = { name: 'TemplateRef', moduleName: CORE };
|
2224 | Identifiers.Renderer2 = { name: 'Renderer2', moduleName: CORE };
|
2225 | Identifiers.CodegenComponentFactoryResolver = {
|
2226 | name: 'ɵCodegenComponentFactoryResolver',
|
2227 | moduleName: CORE,
|
2228 | };
|
2229 | Identifiers.ComponentFactoryResolver = {
|
2230 | name: 'ComponentFactoryResolver',
|
2231 | moduleName: CORE,
|
2232 | };
|
2233 | Identifiers.ComponentFactory = { name: 'ComponentFactory', moduleName: CORE };
|
2234 | Identifiers.ComponentRef = { name: 'ComponentRef', moduleName: CORE };
|
2235 | Identifiers.NgModuleFactory = { name: 'NgModuleFactory', moduleName: CORE };
|
2236 | Identifiers.createModuleFactory = {
|
2237 | name: 'ɵcmf',
|
2238 | moduleName: CORE,
|
2239 | };
|
2240 | Identifiers.moduleDef = {
|
2241 | name: 'ɵmod',
|
2242 | moduleName: CORE,
|
2243 | };
|
2244 | Identifiers.moduleProviderDef = {
|
2245 | name: 'ɵmpd',
|
2246 | moduleName: CORE,
|
2247 | };
|
2248 | Identifiers.RegisterModuleFactoryFn = {
|
2249 | name: 'ɵregisterModuleFactory',
|
2250 | moduleName: CORE,
|
2251 | };
|
2252 | Identifiers.inject = { name: 'ɵɵinject', moduleName: CORE };
|
2253 | Identifiers.directiveInject = { name: 'ɵɵdirectiveInject', moduleName: CORE };
|
2254 | Identifiers.INJECTOR = { name: 'INJECTOR', moduleName: CORE };
|
2255 | Identifiers.Injector = { name: 'Injector', moduleName: CORE };
|
2256 | Identifiers.ɵɵdefineInjectable = { name: 'ɵɵdefineInjectable', moduleName: CORE };
|
2257 | Identifiers.InjectableDef = { name: 'ɵɵInjectableDef', moduleName: CORE };
|
2258 | Identifiers.ViewEncapsulation = {
|
2259 | name: 'ViewEncapsulation',
|
2260 | moduleName: CORE,
|
2261 | };
|
2262 | Identifiers.ChangeDetectionStrategy = {
|
2263 | name: 'ChangeDetectionStrategy',
|
2264 | moduleName: CORE,
|
2265 | };
|
2266 | Identifiers.SecurityContext = {
|
2267 | name: 'SecurityContext',
|
2268 | moduleName: CORE,
|
2269 | };
|
2270 | Identifiers.LOCALE_ID = { name: 'LOCALE_ID', moduleName: CORE };
|
2271 | Identifiers.TRANSLATIONS_FORMAT = {
|
2272 | name: 'TRANSLATIONS_FORMAT',
|
2273 | moduleName: CORE,
|
2274 | };
|
2275 | Identifiers.inlineInterpolate = {
|
2276 | name: 'ɵinlineInterpolate',
|
2277 | moduleName: CORE,
|
2278 | };
|
2279 | Identifiers.interpolate = { name: 'ɵinterpolate', moduleName: CORE };
|
2280 | Identifiers.EMPTY_ARRAY = { name: 'ɵEMPTY_ARRAY', moduleName: CORE };
|
2281 | Identifiers.EMPTY_MAP = { name: 'ɵEMPTY_MAP', moduleName: CORE };
|
2282 | Identifiers.Renderer = { name: 'Renderer', moduleName: CORE };
|
2283 | Identifiers.viewDef = { name: 'ɵvid', moduleName: CORE };
|
2284 | Identifiers.elementDef = { name: 'ɵeld', moduleName: CORE };
|
2285 | Identifiers.anchorDef = { name: 'ɵand', moduleName: CORE };
|
2286 | Identifiers.textDef = { name: 'ɵted', moduleName: CORE };
|
2287 | Identifiers.directiveDef = { name: 'ɵdid', moduleName: CORE };
|
2288 | Identifiers.providerDef = { name: 'ɵprd', moduleName: CORE };
|
2289 | Identifiers.queryDef = { name: 'ɵqud', moduleName: CORE };
|
2290 | Identifiers.pureArrayDef = { name: 'ɵpad', moduleName: CORE };
|
2291 | Identifiers.pureObjectDef = { name: 'ɵpod', moduleName: CORE };
|
2292 | Identifiers.purePipeDef = { name: 'ɵppd', moduleName: CORE };
|
2293 | Identifiers.pipeDef = { name: 'ɵpid', moduleName: CORE };
|
2294 | Identifiers.nodeValue = { name: 'ɵnov', moduleName: CORE };
|
2295 | Identifiers.ngContentDef = { name: 'ɵncd', moduleName: CORE };
|
2296 | Identifiers.unwrapValue = { name: 'ɵunv', moduleName: CORE };
|
2297 | Identifiers.createRendererType2 = { name: 'ɵcrt', moduleName: CORE };
|
2298 |
|
2299 | Identifiers.RendererType2 = {
|
2300 | name: 'RendererType2',
|
2301 | moduleName: CORE,
|
2302 | };
|
2303 |
|
2304 | Identifiers.ViewDefinition = {
|
2305 | name: 'ɵViewDefinition',
|
2306 | moduleName: CORE,
|
2307 | };
|
2308 | Identifiers.createComponentFactory = { name: 'ɵccf', moduleName: CORE };
|
2309 | Identifiers.setClassMetadata = { name: 'ɵsetClassMetadata', moduleName: CORE };
|
2310 | function createTokenForReference(reference) {
|
2311 | return { identifier: { reference: reference } };
|
2312 | }
|
2313 | function createTokenForExternalReference(reflector, reference) {
|
2314 | return createTokenForReference(reflector.resolveExternalReference(reference));
|
2315 | }
|
2316 |
|
2317 | |
2318 |
|
2319 |
|
2320 |
|
2321 |
|
2322 |
|
2323 |
|
2324 | |
2325 |
|
2326 |
|
2327 |
|
2328 |
|
2329 | class StaticSymbol {
|
2330 | constructor(filePath, name, members) {
|
2331 | this.filePath = filePath;
|
2332 | this.name = name;
|
2333 | this.members = members;
|
2334 | }
|
2335 | assertNoMembers() {
|
2336 | if (this.members.length) {
|
2337 | throw new Error(`Illegal state: symbol without members expected, but got ${JSON.stringify(this)}.`);
|
2338 | }
|
2339 | }
|
2340 | }
|
2341 | |
2342 |
|
2343 |
|
2344 |
|
2345 | class StaticSymbolCache {
|
2346 | constructor() {
|
2347 | this.cache = new Map();
|
2348 | }
|
2349 | get(declarationFile, name, members) {
|
2350 | members = members || [];
|
2351 | const memberSuffix = members.length ? `.${members.join('.')}` : '';
|
2352 | const key = `"${declarationFile}".${name}${memberSuffix}`;
|
2353 | let result = this.cache.get(key);
|
2354 | if (!result) {
|
2355 | result = new StaticSymbol(declarationFile, name, members);
|
2356 | this.cache.set(key, result);
|
2357 | }
|
2358 | return result;
|
2359 | }
|
2360 | }
|
2361 |
|
2362 | |
2363 |
|
2364 |
|
2365 |
|
2366 |
|
2367 |
|
2368 |
|
2369 | const DASH_CASE_REGEXP = /-+([a-z0-9])/g;
|
2370 | function dashCaseToCamelCase(input) {
|
2371 | return input.replace(DASH_CASE_REGEXP, (...m) => m[1].toUpperCase());
|
2372 | }
|
2373 | function splitAtColon(input, defaultValues) {
|
2374 | return _splitAt(input, ':', defaultValues);
|
2375 | }
|
2376 | function splitAtPeriod(input, defaultValues) {
|
2377 | return _splitAt(input, '.', defaultValues);
|
2378 | }
|
2379 | function _splitAt(input, character, defaultValues) {
|
2380 | const characterIndex = input.indexOf(character);
|
2381 | if (characterIndex == -1)
|
2382 | return defaultValues;
|
2383 | return [input.slice(0, characterIndex).trim(), input.slice(characterIndex + 1).trim()];
|
2384 | }
|
2385 | function visitValue(value, visitor, context) {
|
2386 | if (Array.isArray(value)) {
|
2387 | return visitor.visitArray(value, context);
|
2388 | }
|
2389 | if (isStrictStringMap(value)) {
|
2390 | return visitor.visitStringMap(value, context);
|
2391 | }
|
2392 | if (value == null || typeof value == 'string' || typeof value == 'number' ||
|
2393 | typeof value == 'boolean') {
|
2394 | return visitor.visitPrimitive(value, context);
|
2395 | }
|
2396 | return visitor.visitOther(value, context);
|
2397 | }
|
2398 | function isDefined(val) {
|
2399 | return val !== null && val !== undefined;
|
2400 | }
|
2401 | function noUndefined(val) {
|
2402 | return val === undefined ? null : val;
|
2403 | }
|
2404 | class ValueTransformer {
|
2405 | visitArray(arr, context) {
|
2406 | return arr.map(value => visitValue(value, this, context));
|
2407 | }
|
2408 | visitStringMap(map, context) {
|
2409 | const result = {};
|
2410 | Object.keys(map).forEach(key => {
|
2411 | result[key] = visitValue(map[key], this, context);
|
2412 | });
|
2413 | return result;
|
2414 | }
|
2415 | visitPrimitive(value, context) {
|
2416 | return value;
|
2417 | }
|
2418 | visitOther(value, context) {
|
2419 | return value;
|
2420 | }
|
2421 | }
|
2422 | const SyncAsync = {
|
2423 | assertSync: (value) => {
|
2424 | if (isPromise(value)) {
|
2425 | throw new Error(`Illegal state: value cannot be a promise`);
|
2426 | }
|
2427 | return value;
|
2428 | },
|
2429 | then: (value, cb) => {
|
2430 | return isPromise(value) ? value.then(cb) : cb(value);
|
2431 | },
|
2432 | all: (syncAsyncValues) => {
|
2433 | return syncAsyncValues.some(isPromise) ? Promise.all(syncAsyncValues) : syncAsyncValues;
|
2434 | }
|
2435 | };
|
2436 | function error(msg) {
|
2437 | throw new Error(`Internal Error: ${msg}`);
|
2438 | }
|
2439 | function syntaxError(msg, parseErrors) {
|
2440 | const error = Error(msg);
|
2441 | error[ERROR_SYNTAX_ERROR] = true;
|
2442 | if (parseErrors)
|
2443 | error[ERROR_PARSE_ERRORS] = parseErrors;
|
2444 | return error;
|
2445 | }
|
2446 | const ERROR_SYNTAX_ERROR = 'ngSyntaxError';
|
2447 | const ERROR_PARSE_ERRORS = 'ngParseErrors';
|
2448 | const STRING_MAP_PROTO = Object.getPrototypeOf({});
|
2449 | function isStrictStringMap(obj) {
|
2450 | return typeof obj === 'object' && obj !== null && Object.getPrototypeOf(obj) === STRING_MAP_PROTO;
|
2451 | }
|
2452 | function utf8Encode(str) {
|
2453 | let encoded = [];
|
2454 | for (let index = 0; index < str.length; index++) {
|
2455 | let codePoint = str.charCodeAt(index);
|
2456 |
|
2457 |
|
2458 | if (codePoint >= 0xd800 && codePoint <= 0xdbff && str.length > (index + 1)) {
|
2459 | const low = str.charCodeAt(index + 1);
|
2460 | if (low >= 0xdc00 && low <= 0xdfff) {
|
2461 | index++;
|
2462 | codePoint = ((codePoint - 0xd800) << 10) + low - 0xdc00 + 0x10000;
|
2463 | }
|
2464 | }
|
2465 | if (codePoint <= 0x7f) {
|
2466 | encoded.push(codePoint);
|
2467 | }
|
2468 | else if (codePoint <= 0x7ff) {
|
2469 | encoded.push(((codePoint >> 6) & 0x1F) | 0xc0, (codePoint & 0x3f) | 0x80);
|
2470 | }
|
2471 | else if (codePoint <= 0xffff) {
|
2472 | encoded.push((codePoint >> 12) | 0xe0, ((codePoint >> 6) & 0x3f) | 0x80, (codePoint & 0x3f) | 0x80);
|
2473 | }
|
2474 | else if (codePoint <= 0x1fffff) {
|
2475 | encoded.push(((codePoint >> 18) & 0x07) | 0xf0, ((codePoint >> 12) & 0x3f) | 0x80, ((codePoint >> 6) & 0x3f) | 0x80, (codePoint & 0x3f) | 0x80);
|
2476 | }
|
2477 | }
|
2478 | return encoded;
|
2479 | }
|
2480 | function stringify(token) {
|
2481 | if (typeof token === 'string') {
|
2482 | return token;
|
2483 | }
|
2484 | if (Array.isArray(token)) {
|
2485 | return '[' + token.map(stringify).join(', ') + ']';
|
2486 | }
|
2487 | if (token == null) {
|
2488 | return '' + token;
|
2489 | }
|
2490 | if (token.overriddenName) {
|
2491 | return `${token.overriddenName}`;
|
2492 | }
|
2493 | if (token.name) {
|
2494 | return `${token.name}`;
|
2495 | }
|
2496 | if (!token.toString) {
|
2497 | return 'object';
|
2498 | }
|
2499 |
|
2500 |
|
2501 | const res = token.toString();
|
2502 | if (res == null) {
|
2503 | return '' + res;
|
2504 | }
|
2505 | const newLineIndex = res.indexOf('\n');
|
2506 | return newLineIndex === -1 ? res : res.substring(0, newLineIndex);
|
2507 | }
|
2508 | |
2509 |
|
2510 |
|
2511 | function resolveForwardRef(type) {
|
2512 | if (typeof type === 'function' && type.hasOwnProperty('__forward_ref__')) {
|
2513 | return type();
|
2514 | }
|
2515 | else {
|
2516 | return type;
|
2517 | }
|
2518 | }
|
2519 | |
2520 |
|
2521 |
|
2522 | function isPromise(obj) {
|
2523 |
|
2524 |
|
2525 | return !!obj && typeof obj.then === 'function';
|
2526 | }
|
2527 | class Version {
|
2528 | constructor(full) {
|
2529 | this.full = full;
|
2530 | const splits = full.split('.');
|
2531 | this.major = splits[0];
|
2532 | this.minor = splits[1];
|
2533 | this.patch = splits.slice(2).join('.');
|
2534 | }
|
2535 | }
|
2536 | const __window = typeof window !== 'undefined' && window;
|
2537 | const __self = typeof self !== 'undefined' && typeof WorkerGlobalScope !== 'undefined' &&
|
2538 | self instanceof WorkerGlobalScope && self;
|
2539 | const __global = typeof global !== 'undefined' && global;
|
2540 |
|
2541 |
|
2542 | const _global = __global || __window || __self;
|
2543 | function newArray(size, value) {
|
2544 | const list = [];
|
2545 | for (let i = 0; i < size; i++) {
|
2546 | list.push(value);
|
2547 | }
|
2548 | return list;
|
2549 | }
|
2550 | |
2551 |
|
2552 |
|
2553 |
|
2554 |
|
2555 |
|
2556 |
|
2557 |
|
2558 | function partitionArray(arr, conditionFn) {
|
2559 | const truthy = [];
|
2560 | const falsy = [];
|
2561 | for (const item of arr) {
|
2562 | (conditionFn(item) ? truthy : falsy).push(item);
|
2563 | }
|
2564 | return [truthy, falsy];
|
2565 | }
|
2566 |
|
2567 | |
2568 |
|
2569 |
|
2570 |
|
2571 |
|
2572 |
|
2573 |
|
2574 |
|
2575 |
|
2576 |
|
2577 |
|
2578 | const HOST_REG_EXP = /^(?:(?:\[([^\]]+)\])|(?:\(([^\)]+)\)))|(\@[-\w]+)$/;
|
2579 | function sanitizeIdentifier(name) {
|
2580 | return name.replace(/\W/g, '_');
|
2581 | }
|
2582 | let _anonymousTypeIndex = 0;
|
2583 | function identifierName(compileIdentifier) {
|
2584 | if (!compileIdentifier || !compileIdentifier.reference) {
|
2585 | return null;
|
2586 | }
|
2587 | const ref = compileIdentifier.reference;
|
2588 | if (ref instanceof StaticSymbol) {
|
2589 | return ref.name;
|
2590 | }
|
2591 | if (ref['__anonymousType']) {
|
2592 | return ref['__anonymousType'];
|
2593 | }
|
2594 | let identifier = stringify(ref);
|
2595 | if (identifier.indexOf('(') >= 0) {
|
2596 |
|
2597 | identifier = `anonymous_${_anonymousTypeIndex++}`;
|
2598 | ref['__anonymousType'] = identifier;
|
2599 | }
|
2600 | else {
|
2601 | identifier = sanitizeIdentifier(identifier);
|
2602 | }
|
2603 | return identifier;
|
2604 | }
|
2605 | function viewClassName(compType, embeddedTemplateIndex) {
|
2606 | return `View_${identifierName({ reference: compType })}_${embeddedTemplateIndex}`;
|
2607 | }
|
2608 | function rendererTypeName(compType) {
|
2609 | return `RenderType_${identifierName({ reference: compType })}`;
|
2610 | }
|
2611 | function hostViewClassName(compType) {
|
2612 | return `HostView_${identifierName({ reference: compType })}`;
|
2613 | }
|
2614 | function componentFactoryName(compType) {
|
2615 | return `${identifierName({ reference: compType })}NgFactory`;
|
2616 | }
|
2617 | var CompileSummaryKind;
|
2618 | (function (CompileSummaryKind) {
|
2619 | CompileSummaryKind[CompileSummaryKind["Pipe"] = 0] = "Pipe";
|
2620 | CompileSummaryKind[CompileSummaryKind["Directive"] = 1] = "Directive";
|
2621 | CompileSummaryKind[CompileSummaryKind["NgModule"] = 2] = "NgModule";
|
2622 | CompileSummaryKind[CompileSummaryKind["Injectable"] = 3] = "Injectable";
|
2623 | })(CompileSummaryKind || (CompileSummaryKind = {}));
|
2624 | function tokenName(token) {
|
2625 | return token.value != null ? sanitizeIdentifier(token.value) : identifierName(token.identifier);
|
2626 | }
|
2627 | function tokenReference(token) {
|
2628 | if (token.identifier != null) {
|
2629 | return token.identifier.reference;
|
2630 | }
|
2631 | else {
|
2632 | return token.value;
|
2633 | }
|
2634 | }
|
2635 | |
2636 |
|
2637 |
|
2638 | class CompileStylesheetMetadata {
|
2639 | constructor({ moduleUrl, styles, styleUrls } = {}) {
|
2640 | this.moduleUrl = moduleUrl || null;
|
2641 | this.styles = _normalizeArray(styles);
|
2642 | this.styleUrls = _normalizeArray(styleUrls);
|
2643 | }
|
2644 | }
|
2645 | |
2646 |
|
2647 |
|
2648 | class CompileTemplateMetadata {
|
2649 | constructor({ encapsulation, template, templateUrl, htmlAst, styles, styleUrls, externalStylesheets, animations, ngContentSelectors, interpolation, isInline, preserveWhitespaces }) {
|
2650 | this.encapsulation = encapsulation;
|
2651 | this.template = template;
|
2652 | this.templateUrl = templateUrl;
|
2653 | this.htmlAst = htmlAst;
|
2654 | this.styles = _normalizeArray(styles);
|
2655 | this.styleUrls = _normalizeArray(styleUrls);
|
2656 | this.externalStylesheets = _normalizeArray(externalStylesheets);
|
2657 | this.animations = animations ? flatten(animations) : [];
|
2658 | this.ngContentSelectors = ngContentSelectors || [];
|
2659 | if (interpolation && interpolation.length != 2) {
|
2660 | throw new Error(`'interpolation' should have a start and an end symbol.`);
|
2661 | }
|
2662 | this.interpolation = interpolation;
|
2663 | this.isInline = isInline;
|
2664 | this.preserveWhitespaces = preserveWhitespaces;
|
2665 | }
|
2666 | toSummary() {
|
2667 | return {
|
2668 | ngContentSelectors: this.ngContentSelectors,
|
2669 | encapsulation: this.encapsulation,
|
2670 | styles: this.styles,
|
2671 | animations: this.animations
|
2672 | };
|
2673 | }
|
2674 | }
|
2675 | |
2676 |
|
2677 |
|
2678 | class CompileDirectiveMetadata {
|
2679 | constructor({ isHost, type, isComponent, selector, exportAs, changeDetection, inputs, outputs, hostListeners, hostProperties, hostAttributes, providers, viewProviders, queries, guards, viewQueries, entryComponents, template, componentViewType, rendererType, componentFactory }) {
|
2680 | this.isHost = !!isHost;
|
2681 | this.type = type;
|
2682 | this.isComponent = isComponent;
|
2683 | this.selector = selector;
|
2684 | this.exportAs = exportAs;
|
2685 | this.changeDetection = changeDetection;
|
2686 | this.inputs = inputs;
|
2687 | this.outputs = outputs;
|
2688 | this.hostListeners = hostListeners;
|
2689 | this.hostProperties = hostProperties;
|
2690 | this.hostAttributes = hostAttributes;
|
2691 | this.providers = _normalizeArray(providers);
|
2692 | this.viewProviders = _normalizeArray(viewProviders);
|
2693 | this.queries = _normalizeArray(queries);
|
2694 | this.guards = guards;
|
2695 | this.viewQueries = _normalizeArray(viewQueries);
|
2696 | this.entryComponents = _normalizeArray(entryComponents);
|
2697 | this.template = template;
|
2698 | this.componentViewType = componentViewType;
|
2699 | this.rendererType = rendererType;
|
2700 | this.componentFactory = componentFactory;
|
2701 | }
|
2702 | static create({ isHost, type, isComponent, selector, exportAs, changeDetection, inputs, outputs, host, providers, viewProviders, queries, guards, viewQueries, entryComponents, template, componentViewType, rendererType, componentFactory }) {
|
2703 | const hostListeners = {};
|
2704 | const hostProperties = {};
|
2705 | const hostAttributes = {};
|
2706 | if (host != null) {
|
2707 | Object.keys(host).forEach(key => {
|
2708 | const value = host[key];
|
2709 | const matches = key.match(HOST_REG_EXP);
|
2710 | if (matches === null) {
|
2711 | hostAttributes[key] = value;
|
2712 | }
|
2713 | else if (matches[1] != null) {
|
2714 | hostProperties[matches[1]] = value;
|
2715 | }
|
2716 | else if (matches[2] != null) {
|
2717 | hostListeners[matches[2]] = value;
|
2718 | }
|
2719 | });
|
2720 | }
|
2721 | const inputsMap = {};
|
2722 | if (inputs != null) {
|
2723 | inputs.forEach((bindConfig) => {
|
2724 |
|
2725 |
|
2726 | const parts = splitAtColon(bindConfig, [bindConfig, bindConfig]);
|
2727 | inputsMap[parts[0]] = parts[1];
|
2728 | });
|
2729 | }
|
2730 | const outputsMap = {};
|
2731 | if (outputs != null) {
|
2732 | outputs.forEach((bindConfig) => {
|
2733 |
|
2734 |
|
2735 | const parts = splitAtColon(bindConfig, [bindConfig, bindConfig]);
|
2736 | outputsMap[parts[0]] = parts[1];
|
2737 | });
|
2738 | }
|
2739 | return new CompileDirectiveMetadata({
|
2740 | isHost,
|
2741 | type,
|
2742 | isComponent: !!isComponent,
|
2743 | selector,
|
2744 | exportAs,
|
2745 | changeDetection,
|
2746 | inputs: inputsMap,
|
2747 | outputs: outputsMap,
|
2748 | hostListeners,
|
2749 | hostProperties,
|
2750 | hostAttributes,
|
2751 | providers,
|
2752 | viewProviders,
|
2753 | queries,
|
2754 | guards,
|
2755 | viewQueries,
|
2756 | entryComponents,
|
2757 | template,
|
2758 | componentViewType,
|
2759 | rendererType,
|
2760 | componentFactory,
|
2761 | });
|
2762 | }
|
2763 | toSummary() {
|
2764 | return {
|
2765 | summaryKind: CompileSummaryKind.Directive,
|
2766 | type: this.type,
|
2767 | isComponent: this.isComponent,
|
2768 | selector: this.selector,
|
2769 | exportAs: this.exportAs,
|
2770 | inputs: this.inputs,
|
2771 | outputs: this.outputs,
|
2772 | hostListeners: this.hostListeners,
|
2773 | hostProperties: this.hostProperties,
|
2774 | hostAttributes: this.hostAttributes,
|
2775 | providers: this.providers,
|
2776 | viewProviders: this.viewProviders,
|
2777 | queries: this.queries,
|
2778 | guards: this.guards,
|
2779 | viewQueries: this.viewQueries,
|
2780 | entryComponents: this.entryComponents,
|
2781 | changeDetection: this.changeDetection,
|
2782 | template: this.template && this.template.toSummary(),
|
2783 | componentViewType: this.componentViewType,
|
2784 | rendererType: this.rendererType,
|
2785 | componentFactory: this.componentFactory
|
2786 | };
|
2787 | }
|
2788 | }
|
2789 | class CompilePipeMetadata {
|
2790 | constructor({ type, name, pure }) {
|
2791 | this.type = type;
|
2792 | this.name = name;
|
2793 | this.pure = !!pure;
|
2794 | }
|
2795 | toSummary() {
|
2796 | return {
|
2797 | summaryKind: CompileSummaryKind.Pipe,
|
2798 | type: this.type,
|
2799 | name: this.name,
|
2800 | pure: this.pure
|
2801 | };
|
2802 | }
|
2803 | }
|
2804 | |
2805 |
|
2806 |
|
2807 | class CompileNgModuleMetadata {
|
2808 | constructor({ type, providers, declaredDirectives, exportedDirectives, declaredPipes, exportedPipes, entryComponents, bootstrapComponents, importedModules, exportedModules, schemas, transitiveModule, id }) {
|
2809 | this.type = type || null;
|
2810 | this.declaredDirectives = _normalizeArray(declaredDirectives);
|
2811 | this.exportedDirectives = _normalizeArray(exportedDirectives);
|
2812 | this.declaredPipes = _normalizeArray(declaredPipes);
|
2813 | this.exportedPipes = _normalizeArray(exportedPipes);
|
2814 | this.providers = _normalizeArray(providers);
|
2815 | this.entryComponents = _normalizeArray(entryComponents);
|
2816 | this.bootstrapComponents = _normalizeArray(bootstrapComponents);
|
2817 | this.importedModules = _normalizeArray(importedModules);
|
2818 | this.exportedModules = _normalizeArray(exportedModules);
|
2819 | this.schemas = _normalizeArray(schemas);
|
2820 | this.id = id || null;
|
2821 | this.transitiveModule = transitiveModule || null;
|
2822 | }
|
2823 | toSummary() {
|
2824 | const module = this.transitiveModule;
|
2825 | return {
|
2826 | summaryKind: CompileSummaryKind.NgModule,
|
2827 | type: this.type,
|
2828 | entryComponents: module.entryComponents,
|
2829 | providers: module.providers,
|
2830 | modules: module.modules,
|
2831 | exportedDirectives: module.exportedDirectives,
|
2832 | exportedPipes: module.exportedPipes
|
2833 | };
|
2834 | }
|
2835 | }
|
2836 | class TransitiveCompileNgModuleMetadata {
|
2837 | constructor() {
|
2838 | this.directivesSet = new Set();
|
2839 | this.directives = [];
|
2840 | this.exportedDirectivesSet = new Set();
|
2841 | this.exportedDirectives = [];
|
2842 | this.pipesSet = new Set();
|
2843 | this.pipes = [];
|
2844 | this.exportedPipesSet = new Set();
|
2845 | this.exportedPipes = [];
|
2846 | this.modulesSet = new Set();
|
2847 | this.modules = [];
|
2848 | this.entryComponentsSet = new Set();
|
2849 | this.entryComponents = [];
|
2850 | this.providers = [];
|
2851 | }
|
2852 | addProvider(provider, module) {
|
2853 | this.providers.push({ provider: provider, module: module });
|
2854 | }
|
2855 | addDirective(id) {
|
2856 | if (!this.directivesSet.has(id.reference)) {
|
2857 | this.directivesSet.add(id.reference);
|
2858 | this.directives.push(id);
|
2859 | }
|
2860 | }
|
2861 | addExportedDirective(id) {
|
2862 | if (!this.exportedDirectivesSet.has(id.reference)) {
|
2863 | this.exportedDirectivesSet.add(id.reference);
|
2864 | this.exportedDirectives.push(id);
|
2865 | }
|
2866 | }
|
2867 | addPipe(id) {
|
2868 | if (!this.pipesSet.has(id.reference)) {
|
2869 | this.pipesSet.add(id.reference);
|
2870 | this.pipes.push(id);
|
2871 | }
|
2872 | }
|
2873 | addExportedPipe(id) {
|
2874 | if (!this.exportedPipesSet.has(id.reference)) {
|
2875 | this.exportedPipesSet.add(id.reference);
|
2876 | this.exportedPipes.push(id);
|
2877 | }
|
2878 | }
|
2879 | addModule(id) {
|
2880 | if (!this.modulesSet.has(id.reference)) {
|
2881 | this.modulesSet.add(id.reference);
|
2882 | this.modules.push(id);
|
2883 | }
|
2884 | }
|
2885 | addEntryComponent(ec) {
|
2886 | if (!this.entryComponentsSet.has(ec.componentType)) {
|
2887 | this.entryComponentsSet.add(ec.componentType);
|
2888 | this.entryComponents.push(ec);
|
2889 | }
|
2890 | }
|
2891 | }
|
2892 | function _normalizeArray(obj) {
|
2893 | return obj || [];
|
2894 | }
|
2895 | class ProviderMeta {
|
2896 | constructor(token, { useClass, useValue, useExisting, useFactory, deps, multi }) {
|
2897 | this.token = token;
|
2898 | this.useClass = useClass || null;
|
2899 | this.useValue = useValue;
|
2900 | this.useExisting = useExisting;
|
2901 | this.useFactory = useFactory || null;
|
2902 | this.dependencies = deps || null;
|
2903 | this.multi = !!multi;
|
2904 | }
|
2905 | }
|
2906 | function flatten(list) {
|
2907 | return list.reduce((flat, item) => {
|
2908 | const flatItem = Array.isArray(item) ? flatten(item) : item;
|
2909 | return flat.concat(flatItem);
|
2910 | }, []);
|
2911 | }
|
2912 | function jitSourceUrl(url) {
|
2913 |
|
2914 |
|
2915 | return url.replace(/(\w+:\/\/[\w:-]+)?(\/+)?/, 'ng:///');
|
2916 | }
|
2917 | function templateSourceUrl(ngModuleType, compMeta, templateMeta) {
|
2918 | let url;
|
2919 | if (templateMeta.isInline) {
|
2920 | if (compMeta.type.reference instanceof StaticSymbol) {
|
2921 |
|
2922 |
|
2923 | url = `${compMeta.type.reference.filePath}.${compMeta.type.reference.name}.html`;
|
2924 | }
|
2925 | else {
|
2926 | url = `${identifierName(ngModuleType)}/${identifierName(compMeta.type)}.html`;
|
2927 | }
|
2928 | }
|
2929 | else {
|
2930 | url = templateMeta.templateUrl;
|
2931 | }
|
2932 | return compMeta.type.reference instanceof StaticSymbol ? url : jitSourceUrl(url);
|
2933 | }
|
2934 |
|
2935 | |
2936 |
|
2937 |
|
2938 |
|
2939 |
|
2940 |
|
2941 |
|
2942 | const CORE$1 = '@angular/core';
|
2943 | class Identifiers$1 {
|
2944 | }
|
2945 |
|
2946 | Identifiers$1.NEW_METHOD = 'factory';
|
2947 | Identifiers$1.TRANSFORM_METHOD = 'transform';
|
2948 | Identifiers$1.PATCH_DEPS = 'patchedDeps';
|
2949 | Identifiers$1.core = { name: null, moduleName: CORE$1 };
|
2950 |
|
2951 | Identifiers$1.namespaceHTML = { name: 'ɵɵnamespaceHTML', moduleName: CORE$1 };
|
2952 | Identifiers$1.namespaceMathML = { name: 'ɵɵnamespaceMathML', moduleName: CORE$1 };
|
2953 | Identifiers$1.namespaceSVG = { name: 'ɵɵnamespaceSVG', moduleName: CORE$1 };
|
2954 | Identifiers$1.element = { name: 'ɵɵelement', moduleName: CORE$1 };
|
2955 | Identifiers$1.elementStart = { name: 'ɵɵelementStart', moduleName: CORE$1 };
|
2956 | Identifiers$1.elementEnd = { name: 'ɵɵelementEnd', moduleName: CORE$1 };
|
2957 | Identifiers$1.advance = { name: 'ɵɵadvance', moduleName: CORE$1 };
|
2958 | Identifiers$1.syntheticHostProperty = { name: 'ɵɵsyntheticHostProperty', moduleName: CORE$1 };
|
2959 | Identifiers$1.syntheticHostListener = { name: 'ɵɵsyntheticHostListener', moduleName: CORE$1 };
|
2960 | Identifiers$1.attribute = { name: 'ɵɵattribute', moduleName: CORE$1 };
|
2961 | Identifiers$1.attributeInterpolate1 = { name: 'ɵɵattributeInterpolate1', moduleName: CORE$1 };
|
2962 | Identifiers$1.attributeInterpolate2 = { name: 'ɵɵattributeInterpolate2', moduleName: CORE$1 };
|
2963 | Identifiers$1.attributeInterpolate3 = { name: 'ɵɵattributeInterpolate3', moduleName: CORE$1 };
|
2964 | Identifiers$1.attributeInterpolate4 = { name: 'ɵɵattributeInterpolate4', moduleName: CORE$1 };
|
2965 | Identifiers$1.attributeInterpolate5 = { name: 'ɵɵattributeInterpolate5', moduleName: CORE$1 };
|
2966 | Identifiers$1.attributeInterpolate6 = { name: 'ɵɵattributeInterpolate6', moduleName: CORE$1 };
|
2967 | Identifiers$1.attributeInterpolate7 = { name: 'ɵɵattributeInterpolate7', moduleName: CORE$1 };
|
2968 | Identifiers$1.attributeInterpolate8 = { name: 'ɵɵattributeInterpolate8', moduleName: CORE$1 };
|
2969 | Identifiers$1.attributeInterpolateV = { name: 'ɵɵattributeInterpolateV', moduleName: CORE$1 };
|
2970 | Identifiers$1.classProp = { name: 'ɵɵclassProp', moduleName: CORE$1 };
|
2971 | Identifiers$1.elementContainerStart = { name: 'ɵɵelementContainerStart', moduleName: CORE$1 };
|
2972 | Identifiers$1.elementContainerEnd = { name: 'ɵɵelementContainerEnd', moduleName: CORE$1 };
|
2973 | Identifiers$1.elementContainer = { name: 'ɵɵelementContainer', moduleName: CORE$1 };
|
2974 | Identifiers$1.styleMap = { name: 'ɵɵstyleMap', moduleName: CORE$1 };
|
2975 | Identifiers$1.styleMapInterpolate1 = { name: 'ɵɵstyleMapInterpolate1', moduleName: CORE$1 };
|
2976 | Identifiers$1.styleMapInterpolate2 = { name: 'ɵɵstyleMapInterpolate2', moduleName: CORE$1 };
|
2977 | Identifiers$1.styleMapInterpolate3 = { name: 'ɵɵstyleMapInterpolate3', moduleName: CORE$1 };
|
2978 | Identifiers$1.styleMapInterpolate4 = { name: 'ɵɵstyleMapInterpolate4', moduleName: CORE$1 };
|
2979 | Identifiers$1.styleMapInterpolate5 = { name: 'ɵɵstyleMapInterpolate5', moduleName: CORE$1 };
|
2980 | Identifiers$1.styleMapInterpolate6 = { name: 'ɵɵstyleMapInterpolate6', moduleName: CORE$1 };
|
2981 | Identifiers$1.styleMapInterpolate7 = { name: 'ɵɵstyleMapInterpolate7', moduleName: CORE$1 };
|
2982 | Identifiers$1.styleMapInterpolate8 = { name: 'ɵɵstyleMapInterpolate8', moduleName: CORE$1 };
|
2983 | Identifiers$1.styleMapInterpolateV = { name: 'ɵɵstyleMapInterpolateV', moduleName: CORE$1 };
|
2984 | Identifiers$1.classMap = { name: 'ɵɵclassMap', moduleName: CORE$1 };
|
2985 | Identifiers$1.classMapInterpolate1 = { name: 'ɵɵclassMapInterpolate1', moduleName: CORE$1 };
|
2986 | Identifiers$1.classMapInterpolate2 = { name: 'ɵɵclassMapInterpolate2', moduleName: CORE$1 };
|
2987 | Identifiers$1.classMapInterpolate3 = { name: 'ɵɵclassMapInterpolate3', moduleName: CORE$1 };
|
2988 | Identifiers$1.classMapInterpolate4 = { name: 'ɵɵclassMapInterpolate4', moduleName: CORE$1 };
|
2989 | Identifiers$1.classMapInterpolate5 = { name: 'ɵɵclassMapInterpolate5', moduleName: CORE$1 };
|
2990 | Identifiers$1.classMapInterpolate6 = { name: 'ɵɵclassMapInterpolate6', moduleName: CORE$1 };
|
2991 | Identifiers$1.classMapInterpolate7 = { name: 'ɵɵclassMapInterpolate7', moduleName: CORE$1 };
|
2992 | Identifiers$1.classMapInterpolate8 = { name: 'ɵɵclassMapInterpolate8', moduleName: CORE$1 };
|
2993 | Identifiers$1.classMapInterpolateV = { name: 'ɵɵclassMapInterpolateV', moduleName: CORE$1 };
|
2994 | Identifiers$1.styleProp = { name: 'ɵɵstyleProp', moduleName: CORE$1 };
|
2995 | Identifiers$1.stylePropInterpolate1 = { name: 'ɵɵstylePropInterpolate1', moduleName: CORE$1 };
|
2996 | Identifiers$1.stylePropInterpolate2 = { name: 'ɵɵstylePropInterpolate2', moduleName: CORE$1 };
|
2997 | Identifiers$1.stylePropInterpolate3 = { name: 'ɵɵstylePropInterpolate3', moduleName: CORE$1 };
|
2998 | Identifiers$1.stylePropInterpolate4 = { name: 'ɵɵstylePropInterpolate4', moduleName: CORE$1 };
|
2999 | Identifiers$1.stylePropInterpolate5 = { name: 'ɵɵstylePropInterpolate5', moduleName: CORE$1 };
|
3000 | Identifiers$1.stylePropInterpolate6 = { name: 'ɵɵstylePropInterpolate6', moduleName: CORE$1 };
|
3001 | Identifiers$1.stylePropInterpolate7 = { name: 'ɵɵstylePropInterpolate7', moduleName: CORE$1 };
|
3002 | Identifiers$1.stylePropInterpolate8 = { name: 'ɵɵstylePropInterpolate8', moduleName: CORE$1 };
|
3003 | Identifiers$1.stylePropInterpolateV = { name: 'ɵɵstylePropInterpolateV', moduleName: CORE$1 };
|
3004 | Identifiers$1.nextContext = { name: 'ɵɵnextContext', moduleName: CORE$1 };
|
3005 | Identifiers$1.templateCreate = { name: 'ɵɵtemplate', moduleName: CORE$1 };
|
3006 | Identifiers$1.text = { name: 'ɵɵtext', moduleName: CORE$1 };
|
3007 | Identifiers$1.enableBindings = { name: 'ɵɵenableBindings', moduleName: CORE$1 };
|
3008 | Identifiers$1.disableBindings = { name: 'ɵɵdisableBindings', moduleName: CORE$1 };
|
3009 | Identifiers$1.getCurrentView = { name: 'ɵɵgetCurrentView', moduleName: CORE$1 };
|
3010 | Identifiers$1.textInterpolate = { name: 'ɵɵtextInterpolate', moduleName: CORE$1 };
|
3011 | Identifiers$1.textInterpolate1 = { name: 'ɵɵtextInterpolate1', moduleName: CORE$1 };
|
3012 | Identifiers$1.textInterpolate2 = { name: 'ɵɵtextInterpolate2', moduleName: CORE$1 };
|
3013 | Identifiers$1.textInterpolate3 = { name: 'ɵɵtextInterpolate3', moduleName: CORE$1 };
|
3014 | Identifiers$1.textInterpolate4 = { name: 'ɵɵtextInterpolate4', moduleName: CORE$1 };
|
3015 | Identifiers$1.textInterpolate5 = { name: 'ɵɵtextInterpolate5', moduleName: CORE$1 };
|
3016 | Identifiers$1.textInterpolate6 = { name: 'ɵɵtextInterpolate6', moduleName: CORE$1 };
|
3017 | Identifiers$1.textInterpolate7 = { name: 'ɵɵtextInterpolate7', moduleName: CORE$1 };
|
3018 | Identifiers$1.textInterpolate8 = { name: 'ɵɵtextInterpolate8', moduleName: CORE$1 };
|
3019 | Identifiers$1.textInterpolateV = { name: 'ɵɵtextInterpolateV', moduleName: CORE$1 };
|
3020 | Identifiers$1.restoreView = { name: 'ɵɵrestoreView', moduleName: CORE$1 };
|
3021 | Identifiers$1.pureFunction0 = { name: 'ɵɵpureFunction0', moduleName: CORE$1 };
|
3022 | Identifiers$1.pureFunction1 = { name: 'ɵɵpureFunction1', moduleName: CORE$1 };
|
3023 | Identifiers$1.pureFunction2 = { name: 'ɵɵpureFunction2', moduleName: CORE$1 };
|
3024 | Identifiers$1.pureFunction3 = { name: 'ɵɵpureFunction3', moduleName: CORE$1 };
|
3025 | Identifiers$1.pureFunction4 = { name: 'ɵɵpureFunction4', moduleName: CORE$1 };
|
3026 | Identifiers$1.pureFunction5 = { name: 'ɵɵpureFunction5', moduleName: CORE$1 };
|
3027 | Identifiers$1.pureFunction6 = { name: 'ɵɵpureFunction6', moduleName: CORE$1 };
|
3028 | Identifiers$1.pureFunction7 = { name: 'ɵɵpureFunction7', moduleName: CORE$1 };
|
3029 | Identifiers$1.pureFunction8 = { name: 'ɵɵpureFunction8', moduleName: CORE$1 };
|
3030 | Identifiers$1.pureFunctionV = { name: 'ɵɵpureFunctionV', moduleName: CORE$1 };
|
3031 | Identifiers$1.pipeBind1 = { name: 'ɵɵpipeBind1', moduleName: CORE$1 };
|
3032 | Identifiers$1.pipeBind2 = { name: 'ɵɵpipeBind2', moduleName: CORE$1 };
|
3033 | Identifiers$1.pipeBind3 = { name: 'ɵɵpipeBind3', moduleName: CORE$1 };
|
3034 | Identifiers$1.pipeBind4 = { name: 'ɵɵpipeBind4', moduleName: CORE$1 };
|
3035 | Identifiers$1.pipeBindV = { name: 'ɵɵpipeBindV', moduleName: CORE$1 };
|
3036 | Identifiers$1.hostProperty = { name: 'ɵɵhostProperty', moduleName: CORE$1 };
|
3037 | Identifiers$1.property = { name: 'ɵɵproperty', moduleName: CORE$1 };
|
3038 | Identifiers$1.propertyInterpolate = { name: 'ɵɵpropertyInterpolate', moduleName: CORE$1 };
|
3039 | Identifiers$1.propertyInterpolate1 = { name: 'ɵɵpropertyInterpolate1', moduleName: CORE$1 };
|
3040 | Identifiers$1.propertyInterpolate2 = { name: 'ɵɵpropertyInterpolate2', moduleName: CORE$1 };
|
3041 | Identifiers$1.propertyInterpolate3 = { name: 'ɵɵpropertyInterpolate3', moduleName: CORE$1 };
|
3042 | Identifiers$1.propertyInterpolate4 = { name: 'ɵɵpropertyInterpolate4', moduleName: CORE$1 };
|
3043 | Identifiers$1.propertyInterpolate5 = { name: 'ɵɵpropertyInterpolate5', moduleName: CORE$1 };
|
3044 | Identifiers$1.propertyInterpolate6 = { name: 'ɵɵpropertyInterpolate6', moduleName: CORE$1 };
|
3045 | Identifiers$1.propertyInterpolate7 = { name: 'ɵɵpropertyInterpolate7', moduleName: CORE$1 };
|
3046 | Identifiers$1.propertyInterpolate8 = { name: 'ɵɵpropertyInterpolate8', moduleName: CORE$1 };
|
3047 | Identifiers$1.propertyInterpolateV = { name: 'ɵɵpropertyInterpolateV', moduleName: CORE$1 };
|
3048 | Identifiers$1.i18n = { name: 'ɵɵi18n', moduleName: CORE$1 };
|
3049 | Identifiers$1.i18nAttributes = { name: 'ɵɵi18nAttributes', moduleName: CORE$1 };
|
3050 | Identifiers$1.i18nExp = { name: 'ɵɵi18nExp', moduleName: CORE$1 };
|
3051 | Identifiers$1.i18nStart = { name: 'ɵɵi18nStart', moduleName: CORE$1 };
|
3052 | Identifiers$1.i18nEnd = { name: 'ɵɵi18nEnd', moduleName: CORE$1 };
|
3053 | Identifiers$1.i18nApply = { name: 'ɵɵi18nApply', moduleName: CORE$1 };
|
3054 | Identifiers$1.i18nPostprocess = { name: 'ɵɵi18nPostprocess', moduleName: CORE$1 };
|
3055 | Identifiers$1.pipe = { name: 'ɵɵpipe', moduleName: CORE$1 };
|
3056 | Identifiers$1.projection = { name: 'ɵɵprojection', moduleName: CORE$1 };
|
3057 | Identifiers$1.projectionDef = { name: 'ɵɵprojectionDef', moduleName: CORE$1 };
|
3058 | Identifiers$1.reference = { name: 'ɵɵreference', moduleName: CORE$1 };
|
3059 | Identifiers$1.inject = { name: 'ɵɵinject', moduleName: CORE$1 };
|
3060 | Identifiers$1.injectAttribute = { name: 'ɵɵinjectAttribute', moduleName: CORE$1 };
|
3061 | Identifiers$1.injectPipeChangeDetectorRef = { name: 'ɵɵinjectPipeChangeDetectorRef', moduleName: CORE$1 };
|
3062 | Identifiers$1.directiveInject = { name: 'ɵɵdirectiveInject', moduleName: CORE$1 };
|
3063 | Identifiers$1.invalidFactory = { name: 'ɵɵinvalidFactory', moduleName: CORE$1 };
|
3064 | Identifiers$1.invalidFactoryDep = { name: 'ɵɵinvalidFactoryDep', moduleName: CORE$1 };
|
3065 | Identifiers$1.templateRefExtractor = { name: 'ɵɵtemplateRefExtractor', moduleName: CORE$1 };
|
3066 | Identifiers$1.forwardRef = { name: 'forwardRef', moduleName: CORE$1 };
|
3067 | Identifiers$1.resolveForwardRef = { name: 'resolveForwardRef', moduleName: CORE$1 };
|
3068 | Identifiers$1.resolveWindow = { name: 'ɵɵresolveWindow', moduleName: CORE$1 };
|
3069 | Identifiers$1.resolveDocument = { name: 'ɵɵresolveDocument', moduleName: CORE$1 };
|
3070 | Identifiers$1.resolveBody = { name: 'ɵɵresolveBody', moduleName: CORE$1 };
|
3071 | Identifiers$1.defineComponent = { name: 'ɵɵdefineComponent', moduleName: CORE$1 };
|
3072 | Identifiers$1.declareComponent = { name: 'ɵɵngDeclareComponent', moduleName: CORE$1 };
|
3073 | Identifiers$1.setComponentScope = { name: 'ɵɵsetComponentScope', moduleName: CORE$1 };
|
3074 | Identifiers$1.ChangeDetectionStrategy = {
|
3075 | name: 'ChangeDetectionStrategy',
|
3076 | moduleName: CORE$1,
|
3077 | };
|
3078 | Identifiers$1.ViewEncapsulation = {
|
3079 | name: 'ViewEncapsulation',
|
3080 | moduleName: CORE$1,
|
3081 | };
|
3082 | Identifiers$1.ComponentDefWithMeta = {
|
3083 | name: 'ɵɵComponentDefWithMeta',
|
3084 | moduleName: CORE$1,
|
3085 | };
|
3086 | Identifiers$1.FactoryDef = {
|
3087 | name: 'ɵɵFactoryDef',
|
3088 | moduleName: CORE$1,
|
3089 | };
|
3090 | Identifiers$1.defineDirective = { name: 'ɵɵdefineDirective', moduleName: CORE$1 };
|
3091 | Identifiers$1.declareDirective = { name: 'ɵɵngDeclareDirective', moduleName: CORE$1 };
|
3092 | Identifiers$1.DirectiveDefWithMeta = {
|
3093 | name: 'ɵɵDirectiveDefWithMeta',
|
3094 | moduleName: CORE$1,
|
3095 | };
|
3096 | Identifiers$1.InjectorDef = {
|
3097 | name: 'ɵɵInjectorDef',
|
3098 | moduleName: CORE$1,
|
3099 | };
|
3100 | Identifiers$1.defineInjector = {
|
3101 | name: 'ɵɵdefineInjector',
|
3102 | moduleName: CORE$1,
|
3103 | };
|
3104 | Identifiers$1.NgModuleDefWithMeta = {
|
3105 | name: 'ɵɵNgModuleDefWithMeta',
|
3106 | moduleName: CORE$1,
|
3107 | };
|
3108 | Identifiers$1.ModuleWithProviders = {
|
3109 | name: 'ModuleWithProviders',
|
3110 | moduleName: CORE$1,
|
3111 | };
|
3112 | Identifiers$1.defineNgModule = { name: 'ɵɵdefineNgModule', moduleName: CORE$1 };
|
3113 | Identifiers$1.setNgModuleScope = { name: 'ɵɵsetNgModuleScope', moduleName: CORE$1 };
|
3114 | Identifiers$1.PipeDefWithMeta = { name: 'ɵɵPipeDefWithMeta', moduleName: CORE$1 };
|
3115 | Identifiers$1.definePipe = { name: 'ɵɵdefinePipe', moduleName: CORE$1 };
|
3116 | Identifiers$1.declarePipe = { name: 'ɵɵngDeclarePipe', moduleName: CORE$1 };
|
3117 | Identifiers$1.queryRefresh = { name: 'ɵɵqueryRefresh', moduleName: CORE$1 };
|
3118 | Identifiers$1.viewQuery = { name: 'ɵɵviewQuery', moduleName: CORE$1 };
|
3119 | Identifiers$1.loadQuery = { name: 'ɵɵloadQuery', moduleName: CORE$1 };
|
3120 | Identifiers$1.contentQuery = { name: 'ɵɵcontentQuery', moduleName: CORE$1 };
|
3121 | Identifiers$1.NgOnChangesFeature = { name: 'ɵɵNgOnChangesFeature', moduleName: CORE$1 };
|
3122 | Identifiers$1.InheritDefinitionFeature = { name: 'ɵɵInheritDefinitionFeature', moduleName: CORE$1 };
|
3123 | Identifiers$1.CopyDefinitionFeature = { name: 'ɵɵCopyDefinitionFeature', moduleName: CORE$1 };
|
3124 | Identifiers$1.ProvidersFeature = { name: 'ɵɵProvidersFeature', moduleName: CORE$1 };
|
3125 | Identifiers$1.listener = { name: 'ɵɵlistener', moduleName: CORE$1 };
|
3126 | Identifiers$1.getFactoryOf = {
|
3127 | name: 'ɵɵgetFactoryOf',
|
3128 | moduleName: CORE$1,
|
3129 | };
|
3130 | Identifiers$1.getInheritedFactory = {
|
3131 | name: 'ɵɵgetInheritedFactory',
|
3132 | moduleName: CORE$1,
|
3133 | };
|
3134 |
|
3135 | Identifiers$1.sanitizeHtml = { name: 'ɵɵsanitizeHtml', moduleName: CORE$1 };
|
3136 | Identifiers$1.sanitizeStyle = { name: 'ɵɵsanitizeStyle', moduleName: CORE$1 };
|
3137 | Identifiers$1.sanitizeResourceUrl = { name: 'ɵɵsanitizeResourceUrl', moduleName: CORE$1 };
|
3138 | Identifiers$1.sanitizeScript = { name: 'ɵɵsanitizeScript', moduleName: CORE$1 };
|
3139 | Identifiers$1.sanitizeUrl = { name: 'ɵɵsanitizeUrl', moduleName: CORE$1 };
|
3140 | Identifiers$1.sanitizeUrlOrResourceUrl = { name: 'ɵɵsanitizeUrlOrResourceUrl', moduleName: CORE$1 };
|
3141 | Identifiers$1.trustConstantHtml = { name: 'ɵɵtrustConstantHtml', moduleName: CORE$1 };
|
3142 | Identifiers$1.trustConstantResourceUrl = { name: 'ɵɵtrustConstantResourceUrl', moduleName: CORE$1 };
|
3143 |
|
3144 | |
3145 |
|
3146 |
|
3147 |
|
3148 |
|
3149 |
|
3150 |
|
3151 |
|
3152 | const VERSION = 3;
|
3153 | const JS_B64_PREFIX = '# sourceMappingURL=data:application/json;base64,';
|
3154 | class SourceMapGenerator {
|
3155 | constructor(file = null) {
|
3156 | this.file = file;
|
3157 | this.sourcesContent = new Map();
|
3158 | this.lines = [];
|
3159 | this.lastCol0 = 0;
|
3160 | this.hasMappings = false;
|
3161 | }
|
3162 |
|
3163 | addSource(url, content = null) {
|
3164 | if (!this.sourcesContent.has(url)) {
|
3165 | this.sourcesContent.set(url, content);
|
3166 | }
|
3167 | return this;
|
3168 | }
|
3169 | addLine() {
|
3170 | this.lines.push([]);
|
3171 | this.lastCol0 = 0;
|
3172 | return this;
|
3173 | }
|
3174 | addMapping(col0, sourceUrl, sourceLine0, sourceCol0) {
|
3175 | if (!this.currentLine) {
|
3176 | throw new Error(`A line must be added before mappings can be added`);
|
3177 | }
|
3178 | if (sourceUrl != null && !this.sourcesContent.has(sourceUrl)) {
|
3179 | throw new Error(`Unknown source file "${sourceUrl}"`);
|
3180 | }
|
3181 | if (col0 == null) {
|
3182 | throw new Error(`The column in the generated code must be provided`);
|
3183 | }
|
3184 | if (col0 < this.lastCol0) {
|
3185 | throw new Error(`Mapping should be added in output order`);
|
3186 | }
|
3187 | if (sourceUrl && (sourceLine0 == null || sourceCol0 == null)) {
|
3188 | throw new Error(`The source location must be provided when a source url is provided`);
|
3189 | }
|
3190 | this.hasMappings = true;
|
3191 | this.lastCol0 = col0;
|
3192 | this.currentLine.push({ col0, sourceUrl, sourceLine0, sourceCol0 });
|
3193 | return this;
|
3194 | }
|
3195 | |
3196 |
|
3197 |
|
3198 |
|
3199 | get currentLine() {
|
3200 | return this.lines.slice(-1)[0];
|
3201 | }
|
3202 | toJSON() {
|
3203 | if (!this.hasMappings) {
|
3204 | return null;
|
3205 | }
|
3206 | const sourcesIndex = new Map();
|
3207 | const sources = [];
|
3208 | const sourcesContent = [];
|
3209 | Array.from(this.sourcesContent.keys()).forEach((url, i) => {
|
3210 | sourcesIndex.set(url, i);
|
3211 | sources.push(url);
|
3212 | sourcesContent.push(this.sourcesContent.get(url) || null);
|
3213 | });
|
3214 | let mappings = '';
|
3215 | let lastCol0 = 0;
|
3216 | let lastSourceIndex = 0;
|
3217 | let lastSourceLine0 = 0;
|
3218 | let lastSourceCol0 = 0;
|
3219 | this.lines.forEach(segments => {
|
3220 | lastCol0 = 0;
|
3221 | mappings += segments
|
3222 | .map(segment => {
|
3223 |
|
3224 | let segAsStr = toBase64VLQ(segment.col0 - lastCol0);
|
3225 | lastCol0 = segment.col0;
|
3226 | if (segment.sourceUrl != null) {
|
3227 |
|
3228 | segAsStr +=
|
3229 | toBase64VLQ(sourcesIndex.get(segment.sourceUrl) - lastSourceIndex);
|
3230 | lastSourceIndex = sourcesIndex.get(segment.sourceUrl);
|
3231 |
|
3232 | segAsStr += toBase64VLQ(segment.sourceLine0 - lastSourceLine0);
|
3233 | lastSourceLine0 = segment.sourceLine0;
|
3234 |
|
3235 | segAsStr += toBase64VLQ(segment.sourceCol0 - lastSourceCol0);
|
3236 | lastSourceCol0 = segment.sourceCol0;
|
3237 | }
|
3238 | return segAsStr;
|
3239 | })
|
3240 | .join(',');
|
3241 | mappings += ';';
|
3242 | });
|
3243 | mappings = mappings.slice(0, -1);
|
3244 | return {
|
3245 | 'file': this.file || '',
|
3246 | 'version': VERSION,
|
3247 | 'sourceRoot': '',
|
3248 | 'sources': sources,
|
3249 | 'sourcesContent': sourcesContent,
|
3250 | 'mappings': mappings,
|
3251 | };
|
3252 | }
|
3253 | toJsComment() {
|
3254 | return this.hasMappings ? '//' + JS_B64_PREFIX + toBase64String(JSON.stringify(this, null, 0)) :
|
3255 | '';
|
3256 | }
|
3257 | }
|
3258 | function toBase64String(value) {
|
3259 | let b64 = '';
|
3260 | const encoded = utf8Encode(value);
|
3261 | for (let i = 0; i < encoded.length;) {
|
3262 | const i1 = encoded[i++];
|
3263 | const i2 = i < encoded.length ? encoded[i++] : null;
|
3264 | const i3 = i < encoded.length ? encoded[i++] : null;
|
3265 | b64 += toBase64Digit(i1 >> 2);
|
3266 | b64 += toBase64Digit(((i1 & 3) << 4) | (i2 === null ? 0 : i2 >> 4));
|
3267 | b64 += i2 === null ? '=' : toBase64Digit(((i2 & 15) << 2) | (i3 === null ? 0 : i3 >> 6));
|
3268 | b64 += i2 === null || i3 === null ? '=' : toBase64Digit(i3 & 63);
|
3269 | }
|
3270 | return b64;
|
3271 | }
|
3272 | function toBase64VLQ(value) {
|
3273 | value = value < 0 ? ((-value) << 1) + 1 : value << 1;
|
3274 | let out = '';
|
3275 | do {
|
3276 | let digit = value & 31;
|
3277 | value = value >> 5;
|
3278 | if (value > 0) {
|
3279 | digit = digit | 32;
|
3280 | }
|
3281 | out += toBase64Digit(digit);
|
3282 | } while (value > 0);
|
3283 | return out;
|
3284 | }
|
3285 | const B64_DIGITS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
3286 | function toBase64Digit(value) {
|
3287 | if (value < 0 || value >= 64) {
|
3288 | throw new Error(`Can only encode value in the range [0, 63]`);
|
3289 | }
|
3290 | return B64_DIGITS[value];
|
3291 | }
|
3292 |
|
3293 | |
3294 |
|
3295 |
|
3296 |
|
3297 |
|
3298 |
|
3299 |
|
3300 | const _SINGLE_QUOTE_ESCAPE_STRING_RE = /'|\\|\n|\r|\$/g;
|
3301 | const _LEGAL_IDENTIFIER_RE = /^[$A-Z_][0-9A-Z_$]*$/i;
|
3302 | const _INDENT_WITH = ' ';
|
3303 | const CATCH_ERROR_VAR$1 = variable('error', null, null);
|
3304 | const CATCH_STACK_VAR$1 = variable('stack', null, null);
|
3305 | class _EmittedLine {
|
3306 | constructor(indent) {
|
3307 | this.indent = indent;
|
3308 | this.partsLength = 0;
|
3309 | this.parts = [];
|
3310 | this.srcSpans = [];
|
3311 | }
|
3312 | }
|
3313 | class EmitterVisitorContext {
|
3314 | constructor(_indent) {
|
3315 | this._indent = _indent;
|
3316 | this._classes = [];
|
3317 | this._preambleLineCount = 0;
|
3318 | this._lines = [new _EmittedLine(_indent)];
|
3319 | }
|
3320 | static createRoot() {
|
3321 | return new EmitterVisitorContext(0);
|
3322 | }
|
3323 | |
3324 |
|
3325 |
|
3326 |
|
3327 | get _currentLine() {
|
3328 | return this._lines[this._lines.length - 1];
|
3329 | }
|
3330 | println(from, lastPart = '') {
|
3331 | this.print(from || null, lastPart, true);
|
3332 | }
|
3333 | lineIsEmpty() {
|
3334 | return this._currentLine.parts.length === 0;
|
3335 | }
|
3336 | lineLength() {
|
3337 | return this._currentLine.indent * _INDENT_WITH.length + this._currentLine.partsLength;
|
3338 | }
|
3339 | print(from, part, newLine = false) {
|
3340 | if (part.length > 0) {
|
3341 | this._currentLine.parts.push(part);
|
3342 | this._currentLine.partsLength += part.length;
|
3343 | this._currentLine.srcSpans.push(from && from.sourceSpan || null);
|
3344 | }
|
3345 | if (newLine) {
|
3346 | this._lines.push(new _EmittedLine(this._indent));
|
3347 | }
|
3348 | }
|
3349 | removeEmptyLastLine() {
|
3350 | if (this.lineIsEmpty()) {
|
3351 | this._lines.pop();
|
3352 | }
|
3353 | }
|
3354 | incIndent() {
|
3355 | this._indent++;
|
3356 | if (this.lineIsEmpty()) {
|
3357 | this._currentLine.indent = this._indent;
|
3358 | }
|
3359 | }
|
3360 | decIndent() {
|
3361 | this._indent--;
|
3362 | if (this.lineIsEmpty()) {
|
3363 | this._currentLine.indent = this._indent;
|
3364 | }
|
3365 | }
|
3366 | pushClass(clazz) {
|
3367 | this._classes.push(clazz);
|
3368 | }
|
3369 | popClass() {
|
3370 | return this._classes.pop();
|
3371 | }
|
3372 | get currentClass() {
|
3373 | return this._classes.length > 0 ? this._classes[this._classes.length - 1] : null;
|
3374 | }
|
3375 | toSource() {
|
3376 | return this.sourceLines
|
3377 | .map(l => l.parts.length > 0 ? _createIndent(l.indent) + l.parts.join('') : '')
|
3378 | .join('\n');
|
3379 | }
|
3380 | toSourceMapGenerator(genFilePath, startsAtLine = 0) {
|
3381 | const map = new SourceMapGenerator(genFilePath);
|
3382 | let firstOffsetMapped = false;
|
3383 | const mapFirstOffsetIfNeeded = () => {
|
3384 | if (!firstOffsetMapped) {
|
3385 |
|
3386 |
|
3387 |
|
3388 | map.addSource(genFilePath, ' ').addMapping(0, genFilePath, 0, 0);
|
3389 | firstOffsetMapped = true;
|
3390 | }
|
3391 | };
|
3392 | for (let i = 0; i < startsAtLine; i++) {
|
3393 | map.addLine();
|
3394 | mapFirstOffsetIfNeeded();
|
3395 | }
|
3396 | this.sourceLines.forEach((line, lineIdx) => {
|
3397 | map.addLine();
|
3398 | const spans = line.srcSpans;
|
3399 | const parts = line.parts;
|
3400 | let col0 = line.indent * _INDENT_WITH.length;
|
3401 | let spanIdx = 0;
|
3402 |
|
3403 | while (spanIdx < spans.length && !spans[spanIdx]) {
|
3404 | col0 += parts[spanIdx].length;
|
3405 | spanIdx++;
|
3406 | }
|
3407 | if (spanIdx < spans.length && lineIdx === 0 && col0 === 0) {
|
3408 | firstOffsetMapped = true;
|
3409 | }
|
3410 | else {
|
3411 | mapFirstOffsetIfNeeded();
|
3412 | }
|
3413 | while (spanIdx < spans.length) {
|
3414 | const span = spans[spanIdx];
|
3415 | const source = span.start.file;
|
3416 | const sourceLine = span.start.line;
|
3417 | const sourceCol = span.start.col;
|
3418 | map.addSource(source.url, source.content)
|
3419 | .addMapping(col0, source.url, sourceLine, sourceCol);
|
3420 | col0 += parts[spanIdx].length;
|
3421 | spanIdx++;
|
3422 |
|
3423 | while (spanIdx < spans.length && (span === spans[spanIdx] || !spans[spanIdx])) {
|
3424 | col0 += parts[spanIdx].length;
|
3425 | spanIdx++;
|
3426 | }
|
3427 | }
|
3428 | });
|
3429 | return map;
|
3430 | }
|
3431 | setPreambleLineCount(count) {
|
3432 | return this._preambleLineCount = count;
|
3433 | }
|
3434 | spanOf(line, column) {
|
3435 | const emittedLine = this._lines[line - this._preambleLineCount];
|
3436 | if (emittedLine) {
|
3437 | let columnsLeft = column - _createIndent(emittedLine.indent).length;
|
3438 | for (let partIndex = 0; partIndex < emittedLine.parts.length; partIndex++) {
|
3439 | const part = emittedLine.parts[partIndex];
|
3440 | if (part.length > columnsLeft) {
|
3441 | return emittedLine.srcSpans[partIndex];
|
3442 | }
|
3443 | columnsLeft -= part.length;
|
3444 | }
|
3445 | }
|
3446 | return null;
|
3447 | }
|
3448 | |
3449 |
|
3450 |
|
3451 |
|
3452 | get sourceLines() {
|
3453 | if (this._lines.length && this._lines[this._lines.length - 1].parts.length === 0) {
|
3454 | return this._lines.slice(0, -1);
|
3455 | }
|
3456 | return this._lines;
|
3457 | }
|
3458 | }
|
3459 | class AbstractEmitterVisitor {
|
3460 | constructor(_escapeDollarInStrings) {
|
3461 | this._escapeDollarInStrings = _escapeDollarInStrings;
|
3462 | }
|
3463 | printLeadingComments(stmt, ctx) {
|
3464 | if (stmt.leadingComments === undefined) {
|
3465 | return;
|
3466 | }
|
3467 | for (const comment of stmt.leadingComments) {
|
3468 | if (comment instanceof JSDocComment) {
|
3469 | ctx.print(stmt, `/*${comment.toString()}*/`, comment.trailingNewline);
|
3470 | }
|
3471 | else {
|
3472 | if (comment.multiline) {
|
3473 | ctx.print(stmt, `/* ${comment.text} */`, comment.trailingNewline);
|
3474 | }
|
3475 | else {
|
3476 | comment.text.split('\n').forEach((line) => {
|
3477 | ctx.println(stmt, `// ${line}`);
|
3478 | });
|
3479 | }
|
3480 | }
|
3481 | }
|
3482 | }
|
3483 | visitExpressionStmt(stmt, ctx) {
|
3484 | this.printLeadingComments(stmt, ctx);
|
3485 | stmt.expr.visitExpression(this, ctx);
|
3486 | ctx.println(stmt, ';');
|
3487 | return null;
|
3488 | }
|
3489 | visitReturnStmt(stmt, ctx) {
|
3490 | this.printLeadingComments(stmt, ctx);
|
3491 | ctx.print(stmt, `return `);
|
3492 | stmt.value.visitExpression(this, ctx);
|
3493 | ctx.println(stmt, ';');
|
3494 | return null;
|
3495 | }
|
3496 | visitIfStmt(stmt, ctx) {
|
3497 | this.printLeadingComments(stmt, ctx);
|
3498 | ctx.print(stmt, `if (`);
|
3499 | stmt.condition.visitExpression(this, ctx);
|
3500 | ctx.print(stmt, `) {`);
|
3501 | const hasElseCase = stmt.falseCase != null && stmt.falseCase.length > 0;
|
3502 | if (stmt.trueCase.length <= 1 && !hasElseCase) {
|
3503 | ctx.print(stmt, ` `);
|
3504 | this.visitAllStatements(stmt.trueCase, ctx);
|
3505 | ctx.removeEmptyLastLine();
|
3506 | ctx.print(stmt, ` `);
|
3507 | }
|
3508 | else {
|
3509 | ctx.println();
|
3510 | ctx.incIndent();
|
3511 | this.visitAllStatements(stmt.trueCase, ctx);
|
3512 | ctx.decIndent();
|
3513 | if (hasElseCase) {
|
3514 | ctx.println(stmt, `} else {`);
|
3515 | ctx.incIndent();
|
3516 | this.visitAllStatements(stmt.falseCase, ctx);
|
3517 | ctx.decIndent();
|
3518 | }
|
3519 | }
|
3520 | ctx.println(stmt, `}`);
|
3521 | return null;
|
3522 | }
|
3523 | visitThrowStmt(stmt, ctx) {
|
3524 | this.printLeadingComments(stmt, ctx);
|
3525 | ctx.print(stmt, `throw `);
|
3526 | stmt.error.visitExpression(this, ctx);
|
3527 | ctx.println(stmt, `;`);
|
3528 | return null;
|
3529 | }
|
3530 | visitWriteVarExpr(expr, ctx) {
|
3531 | const lineWasEmpty = ctx.lineIsEmpty();
|
3532 | if (!lineWasEmpty) {
|
3533 | ctx.print(expr, '(');
|
3534 | }
|
3535 | ctx.print(expr, `${expr.name} = `);
|
3536 | expr.value.visitExpression(this, ctx);
|
3537 | if (!lineWasEmpty) {
|
3538 | ctx.print(expr, ')');
|
3539 | }
|
3540 | return null;
|
3541 | }
|
3542 | visitWriteKeyExpr(expr, ctx) {
|
3543 | const lineWasEmpty = ctx.lineIsEmpty();
|
3544 | if (!lineWasEmpty) {
|
3545 | ctx.print(expr, '(');
|
3546 | }
|
3547 | expr.receiver.visitExpression(this, ctx);
|
3548 | ctx.print(expr, `[`);
|
3549 | expr.index.visitExpression(this, ctx);
|
3550 | ctx.print(expr, `] = `);
|
3551 | expr.value.visitExpression(this, ctx);
|
3552 | if (!lineWasEmpty) {
|
3553 | ctx.print(expr, ')');
|
3554 | }
|
3555 | return null;
|
3556 | }
|
3557 | visitWritePropExpr(expr, ctx) {
|
3558 | const lineWasEmpty = ctx.lineIsEmpty();
|
3559 | if (!lineWasEmpty) {
|
3560 | ctx.print(expr, '(');
|
3561 | }
|
3562 | expr.receiver.visitExpression(this, ctx);
|
3563 | ctx.print(expr, `.${expr.name} = `);
|
3564 | expr.value.visitExpression(this, ctx);
|
3565 | if (!lineWasEmpty) {
|
3566 | ctx.print(expr, ')');
|
3567 | }
|
3568 | return null;
|
3569 | }
|
3570 | visitInvokeMethodExpr(expr, ctx) {
|
3571 | expr.receiver.visitExpression(this, ctx);
|
3572 | let name = expr.name;
|
3573 | if (expr.builtin != null) {
|
3574 | name = this.getBuiltinMethodName(expr.builtin);
|
3575 | if (name == null) {
|
3576 |
|
3577 | return null;
|
3578 | }
|
3579 | }
|
3580 | ctx.print(expr, `.${name}(`);
|
3581 | this.visitAllExpressions(expr.args, ctx, `,`);
|
3582 | ctx.print(expr, `)`);
|
3583 | return null;
|
3584 | }
|
3585 | visitInvokeFunctionExpr(expr, ctx) {
|
3586 | expr.fn.visitExpression(this, ctx);
|
3587 | ctx.print(expr, `(`);
|
3588 | this.visitAllExpressions(expr.args, ctx, ',');
|
3589 | ctx.print(expr, `)`);
|
3590 | return null;
|
3591 | }
|
3592 | visitTaggedTemplateExpr(expr, ctx) {
|
3593 | expr.tag.visitExpression(this, ctx);
|
3594 | ctx.print(expr, '`' + expr.template.elements[0].rawText);
|
3595 | for (let i = 1; i < expr.template.elements.length; i++) {
|
3596 | ctx.print(expr, '${');
|
3597 | expr.template.expressions[i - 1].visitExpression(this, ctx);
|
3598 | ctx.print(expr, `}${expr.template.elements[i].rawText}`);
|
3599 | }
|
3600 | ctx.print(expr, '`');
|
3601 | return null;
|
3602 | }
|
3603 | visitWrappedNodeExpr(ast, ctx) {
|
3604 | throw new Error('Abstract emitter cannot visit WrappedNodeExpr.');
|
3605 | }
|
3606 | visitTypeofExpr(expr, ctx) {
|
3607 | ctx.print(expr, 'typeof ');
|
3608 | expr.expr.visitExpression(this, ctx);
|
3609 | }
|
3610 | visitReadVarExpr(ast, ctx) {
|
3611 | let varName = ast.name;
|
3612 | if (ast.builtin != null) {
|
3613 | switch (ast.builtin) {
|
3614 | case BuiltinVar.Super:
|
3615 | varName = 'super';
|
3616 | break;
|
3617 | case BuiltinVar.This:
|
3618 | varName = 'this';
|
3619 | break;
|
3620 | case BuiltinVar.CatchError:
|
3621 | varName = CATCH_ERROR_VAR$1.name;
|
3622 | break;
|
3623 | case BuiltinVar.CatchStack:
|
3624 | varName = CATCH_STACK_VAR$1.name;
|
3625 | break;
|
3626 | default:
|
3627 | throw new Error(`Unknown builtin variable ${ast.builtin}`);
|
3628 | }
|
3629 | }
|
3630 | ctx.print(ast, varName);
|
3631 | return null;
|
3632 | }
|
3633 | visitInstantiateExpr(ast, ctx) {
|
3634 | ctx.print(ast, `new `);
|
3635 | ast.classExpr.visitExpression(this, ctx);
|
3636 | ctx.print(ast, `(`);
|
3637 | this.visitAllExpressions(ast.args, ctx, ',');
|
3638 | ctx.print(ast, `)`);
|
3639 | return null;
|
3640 | }
|
3641 | visitLiteralExpr(ast, ctx) {
|
3642 | const value = ast.value;
|
3643 | if (typeof value === 'string') {
|
3644 | ctx.print(ast, escapeIdentifier(value, this._escapeDollarInStrings));
|
3645 | }
|
3646 | else {
|
3647 | ctx.print(ast, `${value}`);
|
3648 | }
|
3649 | return null;
|
3650 | }
|
3651 | visitLocalizedString(ast, ctx) {
|
3652 | const head = ast.serializeI18nHead();
|
3653 | ctx.print(ast, '$localize `' + head.raw);
|
3654 | for (let i = 1; i < ast.messageParts.length; i++) {
|
3655 | ctx.print(ast, '${');
|
3656 | ast.expressions[i - 1].visitExpression(this, ctx);
|
3657 | ctx.print(ast, `}${ast.serializeI18nTemplatePart(i).raw}`);
|
3658 | }
|
3659 | ctx.print(ast, '`');
|
3660 | return null;
|
3661 | }
|
3662 | visitConditionalExpr(ast, ctx) {
|
3663 | ctx.print(ast, `(`);
|
3664 | ast.condition.visitExpression(this, ctx);
|
3665 | ctx.print(ast, '? ');
|
3666 | ast.trueCase.visitExpression(this, ctx);
|
3667 | ctx.print(ast, ': ');
|
3668 | ast.falseCase.visitExpression(this, ctx);
|
3669 | ctx.print(ast, `)`);
|
3670 | return null;
|
3671 | }
|
3672 | visitNotExpr(ast, ctx) {
|
3673 | ctx.print(ast, '!');
|
3674 | ast.condition.visitExpression(this, ctx);
|
3675 | return null;
|
3676 | }
|
3677 | visitAssertNotNullExpr(ast, ctx) {
|
3678 | ast.condition.visitExpression(this, ctx);
|
3679 | return null;
|
3680 | }
|
3681 | visitUnaryOperatorExpr(ast, ctx) {
|
3682 | let opStr;
|
3683 | switch (ast.operator) {
|
3684 | case UnaryOperator.Plus:
|
3685 | opStr = '+';
|
3686 | break;
|
3687 | case UnaryOperator.Minus:
|
3688 | opStr = '-';
|
3689 | break;
|
3690 | default:
|
3691 | throw new Error(`Unknown operator ${ast.operator}`);
|
3692 | }
|
3693 | if (ast.parens)
|
3694 | ctx.print(ast, `(`);
|
3695 | ctx.print(ast, opStr);
|
3696 | ast.expr.visitExpression(this, ctx);
|
3697 | if (ast.parens)
|
3698 | ctx.print(ast, `)`);
|
3699 | return null;
|
3700 | }
|
3701 | visitBinaryOperatorExpr(ast, ctx) {
|
3702 | let opStr;
|
3703 | switch (ast.operator) {
|
3704 | case BinaryOperator.Equals:
|
3705 | opStr = '==';
|
3706 | break;
|
3707 | case BinaryOperator.Identical:
|
3708 | opStr = '===';
|
3709 | break;
|
3710 | case BinaryOperator.NotEquals:
|
3711 | opStr = '!=';
|
3712 | break;
|
3713 | case BinaryOperator.NotIdentical:
|
3714 | opStr = '!==';
|
3715 | break;
|
3716 | case BinaryOperator.And:
|
3717 | opStr = '&&';
|
3718 | break;
|
3719 | case BinaryOperator.BitwiseAnd:
|
3720 | opStr = '&';
|
3721 | break;
|
3722 | case BinaryOperator.Or:
|
3723 | opStr = '||';
|
3724 | break;
|
3725 | case BinaryOperator.Plus:
|
3726 | opStr = '+';
|
3727 | break;
|
3728 | case BinaryOperator.Minus:
|
3729 | opStr = '-';
|
3730 | break;
|
3731 | case BinaryOperator.Divide:
|
3732 | opStr = '/';
|
3733 | break;
|
3734 | case BinaryOperator.Multiply:
|
3735 | opStr = '*';
|
3736 | break;
|
3737 | case BinaryOperator.Modulo:
|
3738 | opStr = '%';
|
3739 | break;
|
3740 | case BinaryOperator.Lower:
|
3741 | opStr = '<';
|
3742 | break;
|
3743 | case BinaryOperator.LowerEquals:
|
3744 | opStr = '<=';
|
3745 | break;
|
3746 | case BinaryOperator.Bigger:
|
3747 | opStr = '>';
|
3748 | break;
|
3749 | case BinaryOperator.BiggerEquals:
|
3750 | opStr = '>=';
|
3751 | break;
|
3752 | default:
|
3753 | throw new Error(`Unknown operator ${ast.operator}`);
|
3754 | }
|
3755 | if (ast.parens)
|
3756 | ctx.print(ast, `(`);
|
3757 | ast.lhs.visitExpression(this, ctx);
|
3758 | ctx.print(ast, ` ${opStr} `);
|
3759 | ast.rhs.visitExpression(this, ctx);
|
3760 | if (ast.parens)
|
3761 | ctx.print(ast, `)`);
|
3762 | return null;
|
3763 | }
|
3764 | visitReadPropExpr(ast, ctx) {
|
3765 | ast.receiver.visitExpression(this, ctx);
|
3766 | ctx.print(ast, `.`);
|
3767 | ctx.print(ast, ast.name);
|
3768 | return null;
|
3769 | }
|
3770 | visitReadKeyExpr(ast, ctx) {
|
3771 | ast.receiver.visitExpression(this, ctx);
|
3772 | ctx.print(ast, `[`);
|
3773 | ast.index.visitExpression(this, ctx);
|
3774 | ctx.print(ast, `]`);
|
3775 | return null;
|
3776 | }
|
3777 | visitLiteralArrayExpr(ast, ctx) {
|
3778 | ctx.print(ast, `[`);
|
3779 | this.visitAllExpressions(ast.entries, ctx, ',');
|
3780 | ctx.print(ast, `]`);
|
3781 | return null;
|
3782 | }
|
3783 | visitLiteralMapExpr(ast, ctx) {
|
3784 | ctx.print(ast, `{`);
|
3785 | this.visitAllObjects(entry => {
|
3786 | ctx.print(ast, `${escapeIdentifier(entry.key, this._escapeDollarInStrings, entry.quoted)}:`);
|
3787 | entry.value.visitExpression(this, ctx);
|
3788 | }, ast.entries, ctx, ',');
|
3789 | ctx.print(ast, `}`);
|
3790 | return null;
|
3791 | }
|
3792 | visitCommaExpr(ast, ctx) {
|
3793 | ctx.print(ast, '(');
|
3794 | this.visitAllExpressions(ast.parts, ctx, ',');
|
3795 | ctx.print(ast, ')');
|
3796 | return null;
|
3797 | }
|
3798 | visitAllExpressions(expressions, ctx, separator) {
|
3799 | this.visitAllObjects(expr => expr.visitExpression(this, ctx), expressions, ctx, separator);
|
3800 | }
|
3801 | visitAllObjects(handler, expressions, ctx, separator) {
|
3802 | let incrementedIndent = false;
|
3803 | for (let i = 0; i < expressions.length; i++) {
|
3804 | if (i > 0) {
|
3805 | if (ctx.lineLength() > 80) {
|
3806 | ctx.print(null, separator, true);
|
3807 | if (!incrementedIndent) {
|
3808 |
|
3809 | ctx.incIndent();
|
3810 | ctx.incIndent();
|
3811 | incrementedIndent = true;
|
3812 | }
|
3813 | }
|
3814 | else {
|
3815 | ctx.print(null, separator, false);
|
3816 | }
|
3817 | }
|
3818 | handler(expressions[i]);
|
3819 | }
|
3820 | if (incrementedIndent) {
|
3821 |
|
3822 | ctx.decIndent();
|
3823 | ctx.decIndent();
|
3824 | }
|
3825 | }
|
3826 | visitAllStatements(statements, ctx) {
|
3827 | statements.forEach((stmt) => stmt.visitStatement(this, ctx));
|
3828 | }
|
3829 | }
|
3830 | function escapeIdentifier(input, escapeDollar, alwaysQuote = true) {
|
3831 | if (input == null) {
|
3832 | return null;
|
3833 | }
|
3834 | const body = input.replace(_SINGLE_QUOTE_ESCAPE_STRING_RE, (...match) => {
|
3835 | if (match[0] == '$') {
|
3836 | return escapeDollar ? '\\$' : '$';
|
3837 | }
|
3838 | else if (match[0] == '\n') {
|
3839 | return '\\n';
|
3840 | }
|
3841 | else if (match[0] == '\r') {
|
3842 | return '\\r';
|
3843 | }
|
3844 | else {
|
3845 | return `\\${match[0]}`;
|
3846 | }
|
3847 | });
|
3848 | const requiresQuotes = alwaysQuote || !_LEGAL_IDENTIFIER_RE.test(body);
|
3849 | return requiresQuotes ? `'${body}'` : body;
|
3850 | }
|
3851 | function _createIndent(count) {
|
3852 | let res = '';
|
3853 | for (let i = 0; i < count; i++) {
|
3854 | res += _INDENT_WITH;
|
3855 | }
|
3856 | return res;
|
3857 | }
|
3858 |
|
3859 | |
3860 |
|
3861 |
|
3862 |
|
3863 |
|
3864 |
|
3865 |
|
3866 | |
3867 |
|
3868 |
|
3869 | function mapToMapExpression(map) {
|
3870 | const result = Object.keys(map).map(key => ({
|
3871 | key,
|
3872 |
|
3873 |
|
3874 | value: map[key],
|
3875 | quoted: false,
|
3876 | }));
|
3877 | return literalMap(result);
|
3878 | }
|
3879 | function typeWithParameters(type, numParams) {
|
3880 | if (numParams === 0) {
|
3881 | return expressionType(type);
|
3882 | }
|
3883 | const params = [];
|
3884 | for (let i = 0; i < numParams; i++) {
|
3885 | params.push(DYNAMIC_TYPE);
|
3886 | }
|
3887 | return expressionType(type, undefined, params);
|
3888 | }
|
3889 | const ANIMATE_SYMBOL_PREFIX = '@';
|
3890 | function prepareSyntheticPropertyName(name) {
|
3891 | return `${ANIMATE_SYMBOL_PREFIX}${name}`;
|
3892 | }
|
3893 | function prepareSyntheticListenerName(name, phase) {
|
3894 | return `${ANIMATE_SYMBOL_PREFIX}${name}.${phase}`;
|
3895 | }
|
3896 | function getSafePropertyAccessString(accessor, name) {
|
3897 | const escapedName = escapeIdentifier(name, false, false);
|
3898 | return escapedName !== name ? `${accessor}[${escapedName}]` : `${accessor}.${name}`;
|
3899 | }
|
3900 | function prepareSyntheticListenerFunctionName(name, phase) {
|
3901 | return `animation_${name}_${phase}`;
|
3902 | }
|
3903 | function jitOnlyGuardedExpression(expr) {
|
3904 | return guardedExpression('ngJitMode', expr);
|
3905 | }
|
3906 | function guardedExpression(guard, expr) {
|
3907 | const guardExpr = new ExternalExpr({ name: guard, moduleName: null });
|
3908 | const guardNotDefined = new BinaryOperatorExpr(BinaryOperator.Identical, new TypeofExpr(guardExpr), literal('undefined'));
|
3909 | const guardUndefinedOrTrue = new BinaryOperatorExpr(BinaryOperator.Or, guardNotDefined, guardExpr, undefined,
|
3910 | undefined, true);
|
3911 | return new BinaryOperatorExpr(BinaryOperator.And, guardUndefinedOrTrue, expr);
|
3912 | }
|
3913 |
|
3914 | |
3915 |
|
3916 |
|
3917 |
|
3918 |
|
3919 |
|
3920 |
|
3921 | class Text {
|
3922 | constructor(value, sourceSpan) {
|
3923 | this.value = value;
|
3924 | this.sourceSpan = sourceSpan;
|
3925 | }
|
3926 | visit(visitor) {
|
3927 | return visitor.visitText(this);
|
3928 | }
|
3929 | }
|
3930 | class BoundText {
|
3931 | constructor(value, sourceSpan, i18n) {
|
3932 | this.value = value;
|
3933 | this.sourceSpan = sourceSpan;
|
3934 | this.i18n = i18n;
|
3935 | }
|
3936 | visit(visitor) {
|
3937 | return visitor.visitBoundText(this);
|
3938 | }
|
3939 | }
|
3940 | |
3941 |
|
3942 |
|
3943 |
|
3944 |
|
3945 |
|
3946 | class TextAttribute {
|
3947 | constructor(name, value, sourceSpan, keySpan, valueSpan, i18n) {
|
3948 | this.name = name;
|
3949 | this.value = value;
|
3950 | this.sourceSpan = sourceSpan;
|
3951 | this.keySpan = keySpan;
|
3952 | this.valueSpan = valueSpan;
|
3953 | this.i18n = i18n;
|
3954 | }
|
3955 | visit(visitor) {
|
3956 | return visitor.visitTextAttribute(this);
|
3957 | }
|
3958 | }
|
3959 | class BoundAttribute {
|
3960 | constructor(name, type, securityContext, value, unit, sourceSpan, keySpan, valueSpan, i18n) {
|
3961 | this.name = name;
|
3962 | this.type = type;
|
3963 | this.securityContext = securityContext;
|
3964 | this.value = value;
|
3965 | this.unit = unit;
|
3966 | this.sourceSpan = sourceSpan;
|
3967 | this.keySpan = keySpan;
|
3968 | this.valueSpan = valueSpan;
|
3969 | this.i18n = i18n;
|
3970 | }
|
3971 | static fromBoundElementProperty(prop, i18n) {
|
3972 | if (prop.keySpan === undefined) {
|
3973 | throw new Error(`Unexpected state: keySpan must be defined for bound attributes but was not for ${prop.name}: ${prop.sourceSpan}`);
|
3974 | }
|
3975 | return new BoundAttribute(prop.name, prop.type, prop.securityContext, prop.value, prop.unit, prop.sourceSpan, prop.keySpan, prop.valueSpan, i18n);
|
3976 | }
|
3977 | visit(visitor) {
|
3978 | return visitor.visitBoundAttribute(this);
|
3979 | }
|
3980 | }
|
3981 | class BoundEvent {
|
3982 | constructor(name, type, handler, target, phase, sourceSpan, handlerSpan, keySpan) {
|
3983 | this.name = name;
|
3984 | this.type = type;
|
3985 | this.handler = handler;
|
3986 | this.target = target;
|
3987 | this.phase = phase;
|
3988 | this.sourceSpan = sourceSpan;
|
3989 | this.handlerSpan = handlerSpan;
|
3990 | this.keySpan = keySpan;
|
3991 | }
|
3992 | static fromParsedEvent(event) {
|
3993 | const target = event.type === 0 ? event.targetOrPhase : null;
|
3994 | const phase = event.type === 1 ? event.targetOrPhase : null;
|
3995 | if (event.keySpan === undefined) {
|
3996 | throw new Error(`Unexpected state: keySpan must be defined for bound event but was not for ${event.name}: ${event.sourceSpan}`);
|
3997 | }
|
3998 | return new BoundEvent(event.name, event.type, event.handler, target, phase, event.sourceSpan, event.handlerSpan, event.keySpan);
|
3999 | }
|
4000 | visit(visitor) {
|
4001 | return visitor.visitBoundEvent(this);
|
4002 | }
|
4003 | }
|
4004 | class Element {
|
4005 | constructor(name, attributes, inputs, outputs, children, references, sourceSpan, startSourceSpan, endSourceSpan, i18n) {
|
4006 | this.name = name;
|
4007 | this.attributes = attributes;
|
4008 | this.inputs = inputs;
|
4009 | this.outputs = outputs;
|
4010 | this.children = children;
|
4011 | this.references = references;
|
4012 | this.sourceSpan = sourceSpan;
|
4013 | this.startSourceSpan = startSourceSpan;
|
4014 | this.endSourceSpan = endSourceSpan;
|
4015 | this.i18n = i18n;
|
4016 | }
|
4017 | visit(visitor) {
|
4018 | return visitor.visitElement(this);
|
4019 | }
|
4020 | }
|
4021 | class Template {
|
4022 | constructor(tagName, attributes, inputs, outputs, templateAttrs, children, references, variables, sourceSpan, startSourceSpan, endSourceSpan, i18n) {
|
4023 | this.tagName = tagName;
|
4024 | this.attributes = attributes;
|
4025 | this.inputs = inputs;
|
4026 | this.outputs = outputs;
|
4027 | this.templateAttrs = templateAttrs;
|
4028 | this.children = children;
|
4029 | this.references = references;
|
4030 | this.variables = variables;
|
4031 | this.sourceSpan = sourceSpan;
|
4032 | this.startSourceSpan = startSourceSpan;
|
4033 | this.endSourceSpan = endSourceSpan;
|
4034 | this.i18n = i18n;
|
4035 | }
|
4036 | visit(visitor) {
|
4037 | return visitor.visitTemplate(this);
|
4038 | }
|
4039 | }
|
4040 | class Content {
|
4041 | constructor(selector, attributes, sourceSpan, i18n) {
|
4042 | this.selector = selector;
|
4043 | this.attributes = attributes;
|
4044 | this.sourceSpan = sourceSpan;
|
4045 | this.i18n = i18n;
|
4046 | this.name = 'ng-content';
|
4047 | }
|
4048 | visit(visitor) {
|
4049 | return visitor.visitContent(this);
|
4050 | }
|
4051 | }
|
4052 | class Variable {
|
4053 | constructor(name, value, sourceSpan, keySpan, valueSpan) {
|
4054 | this.name = name;
|
4055 | this.value = value;
|
4056 | this.sourceSpan = sourceSpan;
|
4057 | this.keySpan = keySpan;
|
4058 | this.valueSpan = valueSpan;
|
4059 | }
|
4060 | visit(visitor) {
|
4061 | return visitor.visitVariable(this);
|
4062 | }
|
4063 | }
|
4064 | class Reference {
|
4065 | constructor(name, value, sourceSpan, keySpan, valueSpan) {
|
4066 | this.name = name;
|
4067 | this.value = value;
|
4068 | this.sourceSpan = sourceSpan;
|
4069 | this.keySpan = keySpan;
|
4070 | this.valueSpan = valueSpan;
|
4071 | }
|
4072 | visit(visitor) {
|
4073 | return visitor.visitReference(this);
|
4074 | }
|
4075 | }
|
4076 | class Icu {
|
4077 | constructor(vars, placeholders, sourceSpan, i18n) {
|
4078 | this.vars = vars;
|
4079 | this.placeholders = placeholders;
|
4080 | this.sourceSpan = sourceSpan;
|
4081 | this.i18n = i18n;
|
4082 | }
|
4083 | visit(visitor) {
|
4084 | return visitor.visitIcu(this);
|
4085 | }
|
4086 | }
|
4087 | function visitAll(visitor, nodes) {
|
4088 | const result = [];
|
4089 | if (visitor.visit) {
|
4090 | for (const node of nodes) {
|
4091 | const newNode = visitor.visit(node) || node.visit(visitor);
|
4092 | }
|
4093 | }
|
4094 | else {
|
4095 | for (const node of nodes) {
|
4096 | const newNode = node.visit(visitor);
|
4097 | if (newNode) {
|
4098 | result.push(newNode);
|
4099 | }
|
4100 | }
|
4101 | }
|
4102 | return result;
|
4103 | }
|
4104 |
|
4105 | |
4106 |
|
4107 |
|
4108 |
|
4109 |
|
4110 |
|
4111 |
|
4112 | class Message {
|
4113 | |
4114 |
|
4115 |
|
4116 |
|
4117 |
|
4118 |
|
4119 |
|
4120 |
|
4121 | constructor(nodes, placeholders, placeholderToMessage, meaning, description, customId) {
|
4122 | this.nodes = nodes;
|
4123 | this.placeholders = placeholders;
|
4124 | this.placeholderToMessage = placeholderToMessage;
|
4125 | this.meaning = meaning;
|
4126 | this.description = description;
|
4127 | this.customId = customId;
|
4128 | this.id = this.customId;
|
4129 |
|
4130 | this.legacyIds = [];
|
4131 | if (nodes.length) {
|
4132 | this.sources = [{
|
4133 | filePath: nodes[0].sourceSpan.start.file.url,
|
4134 | startLine: nodes[0].sourceSpan.start.line + 1,
|
4135 | startCol: nodes[0].sourceSpan.start.col + 1,
|
4136 | endLine: nodes[nodes.length - 1].sourceSpan.end.line + 1,
|
4137 | endCol: nodes[0].sourceSpan.start.col + 1
|
4138 | }];
|
4139 | }
|
4140 | else {
|
4141 | this.sources = [];
|
4142 | }
|
4143 | }
|
4144 | }
|
4145 | class Text$1 {
|
4146 | constructor(value, sourceSpan) {
|
4147 | this.value = value;
|
4148 | this.sourceSpan = sourceSpan;
|
4149 | }
|
4150 | visit(visitor, context) {
|
4151 | return visitor.visitText(this, context);
|
4152 | }
|
4153 | }
|
4154 |
|
4155 | class Container {
|
4156 | constructor(children, sourceSpan) {
|
4157 | this.children = children;
|
4158 | this.sourceSpan = sourceSpan;
|
4159 | }
|
4160 | visit(visitor, context) {
|
4161 | return visitor.visitContainer(this, context);
|
4162 | }
|
4163 | }
|
4164 | class Icu$1 {
|
4165 | constructor(expression, type, cases, sourceSpan) {
|
4166 | this.expression = expression;
|
4167 | this.type = type;
|
4168 | this.cases = cases;
|
4169 | this.sourceSpan = sourceSpan;
|
4170 | }
|
4171 | visit(visitor, context) {
|
4172 | return visitor.visitIcu(this, context);
|
4173 | }
|
4174 | }
|
4175 | class TagPlaceholder {
|
4176 | constructor(tag, attrs, startName, closeName, children, isVoid,
|
4177 | // TODO sourceSpan should cover all (we need a startSourceSpan and endSourceSpan)
|
4178 | sourceSpan, startSourceSpan, endSourceSpan) {
|
4179 | this.tag = tag;
|
4180 | this.attrs = attrs;
|
4181 | this.startName = startName;
|
4182 | this.closeName = closeName;
|
4183 | this.children = children;
|
4184 | this.isVoid = isVoid;
|
4185 | this.sourceSpan = sourceSpan;
|
4186 | this.startSourceSpan = startSourceSpan;
|
4187 | this.endSourceSpan = endSourceSpan;
|
4188 | }
|
4189 | visit(visitor, context) {
|
4190 | return visitor.visitTagPlaceholder(this, context);
|
4191 | }
|
4192 | }
|
4193 | class Placeholder {
|
4194 | constructor(value, name, sourceSpan) {
|
4195 | this.value = value;
|
4196 | this.name = name;
|
4197 | this.sourceSpan = sourceSpan;
|
4198 | }
|
4199 | visit(visitor, context) {
|
4200 | return visitor.visitPlaceholder(this, context);
|
4201 | }
|
4202 | }
|
4203 | class IcuPlaceholder {
|
4204 | constructor(value, name, sourceSpan) {
|
4205 | this.value = value;
|
4206 | this.name = name;
|
4207 | this.sourceSpan = sourceSpan;
|
4208 | }
|
4209 | visit(visitor, context) {
|
4210 | return visitor.visitIcuPlaceholder(this, context);
|
4211 | }
|
4212 | }
|
4213 |
|
4214 | |
4215 |
|
4216 |
|
4217 |
|
4218 |
|
4219 |
|
4220 |
|
4221 | |
4222 |
|
4223 |
|
4224 |
|
4225 |
|
4226 |
|
4227 |
|
4228 | class BigInteger {
|
4229 | |
4230 |
|
4231 |
|
4232 | constructor(digits) {
|
4233 | this.digits = digits;
|
4234 | }
|
4235 | static zero() {
|
4236 | return new BigInteger([0]);
|
4237 | }
|
4238 | static one() {
|
4239 | return new BigInteger([1]);
|
4240 | }
|
4241 | |
4242 |
|
4243 |
|
4244 | clone() {
|
4245 | return new BigInteger(this.digits.slice());
|
4246 | }
|
4247 | |
4248 |
|
4249 |
|
4250 |
|
4251 | add(other) {
|
4252 | const result = this.clone();
|
4253 | result.addToSelf(other);
|
4254 | return result;
|
4255 | }
|
4256 | |
4257 |
|
4258 |
|
4259 | addToSelf(other) {
|
4260 | const maxNrOfDigits = Math.max(this.digits.length, other.digits.length);
|
4261 | let carry = 0;
|
4262 | for (let i = 0; i < maxNrOfDigits; i++) {
|
4263 | let digitSum = carry;
|
4264 | if (i < this.digits.length) {
|
4265 | digitSum += this.digits[i];
|
4266 | }
|
4267 | if (i < other.digits.length) {
|
4268 | digitSum += other.digits[i];
|
4269 | }
|
4270 | if (digitSum >= 10) {
|
4271 | this.digits[i] = digitSum - 10;
|
4272 | carry = 1;
|
4273 | }
|
4274 | else {
|
4275 | this.digits[i] = digitSum;
|
4276 | carry = 0;
|
4277 | }
|
4278 | }
|
4279 |
|
4280 | if (carry > 0) {
|
4281 | this.digits[maxNrOfDigits] = 1;
|
4282 | }
|
4283 | }
|
4284 | |
4285 |
|
4286 |
|
4287 |
|
4288 | toString() {
|
4289 | let res = '';
|
4290 | for (let i = this.digits.length - 1; i >= 0; i--) {
|
4291 | res += this.digits[i];
|
4292 | }
|
4293 | return res;
|
4294 | }
|
4295 | }
|
4296 | |
4297 |
|
4298 |
|
4299 |
|
4300 | class BigIntForMultiplication {
|
4301 | constructor(value) {
|
4302 | this.powerOfTwos = [value];
|
4303 | }
|
4304 | |
4305 |
|
4306 |
|
4307 | getValue() {
|
4308 | return this.powerOfTwos[0];
|
4309 | }
|
4310 | |
4311 |
|
4312 |
|
4313 |
|
4314 |
|
4315 |
|
4316 |
|
4317 |
|
4318 |
|
4319 |
|
4320 |
|
4321 |
|
4322 |
|
4323 |
|
4324 |
|
4325 |
|
4326 |
|
4327 |
|
4328 |
|
4329 |
|
4330 |
|
4331 |
|
4332 |
|
4333 |
|
4334 |
|
4335 |
|
4336 | multiplyBy(num) {
|
4337 | const product = BigInteger.zero();
|
4338 | this.multiplyByAndAddTo(num, product);
|
4339 | return product;
|
4340 | }
|
4341 | |
4342 |
|
4343 |
|
4344 |
|
4345 | multiplyByAndAddTo(num, result) {
|
4346 | for (let exponent = 0; num !== 0; num = num >>> 1, exponent++) {
|
4347 | if (num & 1) {
|
4348 | const value = this.getMultipliedByPowerOfTwo(exponent);
|
4349 | result.addToSelf(value);
|
4350 | }
|
4351 | }
|
4352 | }
|
4353 | |
4354 |
|
4355 |
|
4356 | getMultipliedByPowerOfTwo(exponent) {
|
4357 |
|
4358 |
|
4359 |
|
4360 | for (let i = this.powerOfTwos.length; i <= exponent; i++) {
|
4361 | const previousPower = this.powerOfTwos[i - 1];
|
4362 | this.powerOfTwos[i] = previousPower.add(previousPower);
|
4363 | }
|
4364 | return this.powerOfTwos[exponent];
|
4365 | }
|
4366 | }
|
4367 | |
4368 |
|
4369 |
|
4370 |
|
4371 |
|
4372 |
|
4373 | class BigIntExponentiation {
|
4374 | constructor(base) {
|
4375 | this.base = base;
|
4376 | this.exponents = [new BigIntForMultiplication(BigInteger.one())];
|
4377 | }
|
4378 | |
4379 |
|
4380 |
|
4381 |
|
4382 | toThePowerOf(exponent) {
|
4383 |
|
4384 |
|
4385 |
|
4386 | for (let i = this.exponents.length; i <= exponent; i++) {
|
4387 | const value = this.exponents[i - 1].multiplyBy(this.base);
|
4388 | this.exponents[i] = new BigIntForMultiplication(value);
|
4389 | }
|
4390 | return this.exponents[exponent];
|
4391 | }
|
4392 | }
|
4393 |
|
4394 | |
4395 |
|
4396 |
|
4397 |
|
4398 |
|
4399 |
|
4400 |
|
4401 | |
4402 |
|
4403 |
|
4404 | function computeDigest(message) {
|
4405 | return sha1(serializeNodes(message.nodes).join('') + `[${message.meaning}]`);
|
4406 | }
|
4407 | |
4408 |
|
4409 |
|
4410 | function decimalDigest(message) {
|
4411 | return message.id || computeDecimalDigest(message);
|
4412 | }
|
4413 | |
4414 |
|
4415 |
|
4416 | function computeDecimalDigest(message) {
|
4417 | const visitor = new _SerializerIgnoreIcuExpVisitor();
|
4418 | const parts = message.nodes.map(a => a.visit(visitor, null));
|
4419 | return computeMsgId(parts.join(''), message.meaning);
|
4420 | }
|
4421 | |
4422 |
|
4423 |
|
4424 |
|
4425 |
|
4426 |
|
4427 |
|
4428 | class _SerializerVisitor {
|
4429 | visitText(text, context) {
|
4430 | return text.value;
|
4431 | }
|
4432 | visitContainer(container, context) {
|
4433 | return `[${container.children.map(child => child.visit(this)).join(', ')}]`;
|
4434 | }
|
4435 | visitIcu(icu, context) {
|
4436 | const strCases = Object.keys(icu.cases).map((k) => `${k} {${icu.cases[k].visit(this)}}`);
|
4437 | return `{${icu.expression}, ${icu.type}, ${strCases.join(', ')}}`;
|
4438 | }
|
4439 | visitTagPlaceholder(ph, context) {
|
4440 | return ph.isVoid ?
|
4441 | `<ph tag name="${ph.startName}"/>` :
|
4442 | `<ph tag name="${ph.startName}">${ph.children.map(child => child.visit(this)).join(', ')}</ph name="${ph.closeName}">`;
|
4443 | }
|
4444 | visitPlaceholder(ph, context) {
|
4445 | return ph.value ? `<ph name="${ph.name}">${ph.value}</ph>` : `<ph name="${ph.name}"/>`;
|
4446 | }
|
4447 | visitIcuPlaceholder(ph, context) {
|
4448 | return `<ph icu name="${ph.name}">${ph.value.visit(this)}</ph>`;
|
4449 | }
|
4450 | }
|
4451 | const serializerVisitor = new _SerializerVisitor();
|
4452 | function serializeNodes(nodes) {
|
4453 | return nodes.map(a => a.visit(serializerVisitor, null));
|
4454 | }
|
4455 | |
4456 |
|
4457 |
|
4458 |
|
4459 |
|
4460 |
|
4461 |
|
4462 | class _SerializerIgnoreIcuExpVisitor extends _SerializerVisitor {
|
4463 | visitIcu(icu, context) {
|
4464 | let strCases = Object.keys(icu.cases).map((k) => `${k} {${icu.cases[k].visit(this)}}`);
|
4465 |
|
4466 | return `{${icu.type}, ${strCases.join(', ')}}`;
|
4467 | }
|
4468 | }
|
4469 | |
4470 |
|
4471 |
|
4472 |
|
4473 |
|
4474 |
|
4475 |
|
4476 |
|
4477 | function sha1(str) {
|
4478 | const utf8 = utf8Encode(str);
|
4479 | const words32 = bytesToWords32(utf8, Endian.Big);
|
4480 | const len = utf8.length * 8;
|
4481 | const w = newArray(80);
|
4482 | let a = 0x67452301, b = 0xefcdab89, c = 0x98badcfe, d = 0x10325476, e = 0xc3d2e1f0;
|
4483 | words32[len >> 5] |= 0x80 << (24 - len % 32);
|
4484 | words32[((len + 64 >> 9) << 4) + 15] = len;
|
4485 | for (let i = 0; i < words32.length; i += 16) {
|
4486 | const h0 = a, h1 = b, h2 = c, h3 = d, h4 = e;
|
4487 | for (let j = 0; j < 80; j++) {
|
4488 | if (j < 16) {
|
4489 | w[j] = words32[i + j];
|
4490 | }
|
4491 | else {
|
4492 | w[j] = rol32(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1);
|
4493 | }
|
4494 | const fkVal = fk(j, b, c, d);
|
4495 | const f = fkVal[0];
|
4496 | const k = fkVal[1];
|
4497 | const temp = [rol32(a, 5), f, e, k, w[j]].reduce(add32);
|
4498 | e = d;
|
4499 | d = c;
|
4500 | c = rol32(b, 30);
|
4501 | b = a;
|
4502 | a = temp;
|
4503 | }
|
4504 | a = add32(a, h0);
|
4505 | b = add32(b, h1);
|
4506 | c = add32(c, h2);
|
4507 | d = add32(d, h3);
|
4508 | e = add32(e, h4);
|
4509 | }
|
4510 | return bytesToHexString(words32ToByteString([a, b, c, d, e]));
|
4511 | }
|
4512 | function fk(index, b, c, d) {
|
4513 | if (index < 20) {
|
4514 | return [(b & c) | (~b & d), 0x5a827999];
|
4515 | }
|
4516 | if (index < 40) {
|
4517 | return [b ^ c ^ d, 0x6ed9eba1];
|
4518 | }
|
4519 | if (index < 60) {
|
4520 | return [(b & c) | (b & d) | (c & d), 0x8f1bbcdc];
|
4521 | }
|
4522 | return [b ^ c ^ d, 0xca62c1d6];
|
4523 | }
|
4524 | |
4525 |
|
4526 |
|
4527 |
|
4528 |
|
4529 |
|
4530 |
|
4531 |
|
4532 | function fingerprint(str) {
|
4533 | const utf8 = utf8Encode(str);
|
4534 | let hi = hash32(utf8, 0);
|
4535 | let lo = hash32(utf8, 102072);
|
4536 | if (hi == 0 && (lo == 0 || lo == 1)) {
|
4537 | hi = hi ^ 0x130f9bef;
|
4538 | lo = lo ^ -0x6b5f56d8;
|
4539 | }
|
4540 | return [hi, lo];
|
4541 | }
|
4542 | function computeMsgId(msg, meaning = '') {
|
4543 | let msgFingerprint = fingerprint(msg);
|
4544 | if (meaning) {
|
4545 | const meaningFingerprint = fingerprint(meaning);
|
4546 | msgFingerprint = add64(rol64(msgFingerprint, 1), meaningFingerprint);
|
4547 | }
|
4548 | const hi = msgFingerprint[0];
|
4549 | const lo = msgFingerprint[1];
|
4550 | return wordsToDecimalString(hi & 0x7fffffff, lo);
|
4551 | }
|
4552 | function hash32(bytes, c) {
|
4553 | let a = 0x9e3779b9, b = 0x9e3779b9;
|
4554 | let i;
|
4555 | const len = bytes.length;
|
4556 | for (i = 0; i + 12 <= len; i += 12) {
|
4557 | a = add32(a, wordAt(bytes, i, Endian.Little));
|
4558 | b = add32(b, wordAt(bytes, i + 4, Endian.Little));
|
4559 | c = add32(c, wordAt(bytes, i + 8, Endian.Little));
|
4560 | const res = mix(a, b, c);
|
4561 | a = res[0], b = res[1], c = res[2];
|
4562 | }
|
4563 | a = add32(a, wordAt(bytes, i, Endian.Little));
|
4564 | b = add32(b, wordAt(bytes, i + 4, Endian.Little));
|
4565 |
|
4566 | c = add32(c, len);
|
4567 | c = add32(c, wordAt(bytes, i + 8, Endian.Little) << 8);
|
4568 | return mix(a, b, c)[2];
|
4569 | }
|
4570 |
|
4571 | function mix(a, b, c) {
|
4572 | a = sub32(a, b);
|
4573 | a = sub32(a, c);
|
4574 | a ^= c >>> 13;
|
4575 | b = sub32(b, c);
|
4576 | b = sub32(b, a);
|
4577 | b ^= a << 8;
|
4578 | c = sub32(c, a);
|
4579 | c = sub32(c, b);
|
4580 | c ^= b >>> 13;
|
4581 | a = sub32(a, b);
|
4582 | a = sub32(a, c);
|
4583 | a ^= c >>> 12;
|
4584 | b = sub32(b, c);
|
4585 | b = sub32(b, a);
|
4586 | b ^= a << 16;
|
4587 | c = sub32(c, a);
|
4588 | c = sub32(c, b);
|
4589 | c ^= b >>> 5;
|
4590 | a = sub32(a, b);
|
4591 | a = sub32(a, c);
|
4592 | a ^= c >>> 3;
|
4593 | b = sub32(b, c);
|
4594 | b = sub32(b, a);
|
4595 | b ^= a << 10;
|
4596 | c = sub32(c, a);
|
4597 | c = sub32(c, b);
|
4598 | c ^= b >>> 15;
|
4599 | return [a, b, c];
|
4600 | }
|
4601 |
|
4602 |
|
4603 | var Endian;
|
4604 | (function (Endian) {
|
4605 | Endian[Endian["Little"] = 0] = "Little";
|
4606 | Endian[Endian["Big"] = 1] = "Big";
|
4607 | })(Endian || (Endian = {}));
|
4608 | function add32(a, b) {
|
4609 | return add32to64(a, b)[1];
|
4610 | }
|
4611 | function add32to64(a, b) {
|
4612 | const low = (a & 0xffff) + (b & 0xffff);
|
4613 | const high = (a >>> 16) + (b >>> 16) + (low >>> 16);
|
4614 | return [high >>> 16, (high << 16) | (low & 0xffff)];
|
4615 | }
|
4616 | function add64(a, b) {
|
4617 | const ah = a[0], al = a[1];
|
4618 | const bh = b[0], bl = b[1];
|
4619 | const result = add32to64(al, bl);
|
4620 | const carry = result[0];
|
4621 | const l = result[1];
|
4622 | const h = add32(add32(ah, bh), carry);
|
4623 | return [h, l];
|
4624 | }
|
4625 | function sub32(a, b) {
|
4626 | const low = (a & 0xffff) - (b & 0xffff);
|
4627 | const high = (a >> 16) - (b >> 16) + (low >> 16);
|
4628 | return (high << 16) | (low & 0xffff);
|
4629 | }
|
4630 |
|
4631 | function rol32(a, count) {
|
4632 | return (a << count) | (a >>> (32 - count));
|
4633 | }
|
4634 |
|
4635 | function rol64(num, count) {
|
4636 | const hi = num[0], lo = num[1];
|
4637 | const h = (hi << count) | (lo >>> (32 - count));
|
4638 | const l = (lo << count) | (hi >>> (32 - count));
|
4639 | return [h, l];
|
4640 | }
|
4641 | function bytesToWords32(bytes, endian) {
|
4642 | const size = (bytes.length + 3) >>> 2;
|
4643 | const words32 = [];
|
4644 | for (let i = 0; i < size; i++) {
|
4645 | words32[i] = wordAt(bytes, i * 4, endian);
|
4646 | }
|
4647 | return words32;
|
4648 | }
|
4649 | function byteAt(bytes, index) {
|
4650 | return index >= bytes.length ? 0 : bytes[index];
|
4651 | }
|
4652 | function wordAt(bytes, index, endian) {
|
4653 | let word = 0;
|
4654 | if (endian === Endian.Big) {
|
4655 | for (let i = 0; i < 4; i++) {
|
4656 | word += byteAt(bytes, index + i) << (24 - 8 * i);
|
4657 | }
|
4658 | }
|
4659 | else {
|
4660 | for (let i = 0; i < 4; i++) {
|
4661 | word += byteAt(bytes, index + i) << 8 * i;
|
4662 | }
|
4663 | }
|
4664 | return word;
|
4665 | }
|
4666 | function words32ToByteString(words32) {
|
4667 | return words32.reduce((bytes, word) => bytes.concat(word32ToByteString(word)), []);
|
4668 | }
|
4669 | function word32ToByteString(word) {
|
4670 | let bytes = [];
|
4671 | for (let i = 0; i < 4; i++) {
|
4672 | bytes.push((word >>> 8 * (3 - i)) & 0xff);
|
4673 | }
|
4674 | return bytes;
|
4675 | }
|
4676 | function bytesToHexString(bytes) {
|
4677 | let hex = '';
|
4678 | for (let i = 0; i < bytes.length; i++) {
|
4679 | const b = byteAt(bytes, i);
|
4680 | hex += (b >>> 4).toString(16) + (b & 0x0f).toString(16);
|
4681 | }
|
4682 | return hex.toLowerCase();
|
4683 | }
|
4684 | |
4685 |
|
4686 |
|
4687 |
|
4688 |
|
4689 |
|
4690 |
|
4691 |
|
4692 | const base256 = new BigIntExponentiation(256);
|
4693 | |
4694 |
|
4695 |
|
4696 |
|
4697 |
|
4698 |
|
4699 | function wordsToDecimalString(hi, lo) {
|
4700 |
|
4701 |
|
4702 |
|
4703 | const decimal = base256.toThePowerOf(0).multiplyBy(lo);
|
4704 |
|
4705 |
|
4706 | base256.toThePowerOf(4).multiplyByAndAddTo(hi, decimal);
|
4707 | return decimal.toString();
|
4708 | }
|
4709 |
|
4710 | |
4711 |
|
4712 |
|
4713 |
|
4714 |
|
4715 |
|
4716 |
|
4717 |
|
4718 | function toPublicName(internalName) {
|
4719 | return internalName.toUpperCase().replace(/[^A-Z0-9_]/g, '_');
|
4720 | }
|
4721 |
|
4722 | |
4723 |
|
4724 |
|
4725 |
|
4726 |
|
4727 |
|
4728 |
|
4729 |
|
4730 | const CLOSURE_TRANSLATION_VAR_PREFIX = 'MSG_';
|
4731 | |
4732 |
|
4733 |
|
4734 |
|
4735 |
|
4736 | const TRANSLATION_VAR_PREFIX = 'i18n_';
|
4737 |
|
4738 | const I18N_ATTR = 'i18n';
|
4739 | const I18N_ATTR_PREFIX = 'i18n-';
|
4740 |
|
4741 | const I18N_ICU_VAR_PREFIX = 'VAR_';
|
4742 |
|
4743 | const I18N_ICU_MAPPING_PREFIX = 'I18N_EXP_';
|
4744 |
|
4745 | const I18N_PLACEHOLDER_SYMBOL = '�';
|
4746 | function isI18nAttribute(name) {
|
4747 | return name === I18N_ATTR || name.startsWith(I18N_ATTR_PREFIX);
|
4748 | }
|
4749 | function isI18nRootNode(meta) {
|
4750 | return meta instanceof Message;
|
4751 | }
|
4752 | function isSingleI18nIcu(meta) {
|
4753 | return isI18nRootNode(meta) && meta.nodes.length === 1 && meta.nodes[0] instanceof Icu$1;
|
4754 | }
|
4755 | function hasI18nMeta(node) {
|
4756 | return !!node.i18n;
|
4757 | }
|
4758 | function hasI18nAttrs(element) {
|
4759 | return element.attrs.some((attr) => isI18nAttribute(attr.name));
|
4760 | }
|
4761 | function icuFromI18nMessage(message) {
|
4762 | return message.nodes[0];
|
4763 | }
|
4764 | function wrapI18nPlaceholder(content, contextId = 0) {
|
4765 | const blockId = contextId > 0 ? `:${contextId}` : '';
|
4766 | return `${I18N_PLACEHOLDER_SYMBOL}${content}${blockId}${I18N_PLACEHOLDER_SYMBOL}`;
|
4767 | }
|
4768 | function assembleI18nBoundString(strings, bindingStartIndex = 0, contextId = 0) {
|
4769 | if (!strings.length)
|
4770 | return '';
|
4771 | let acc = '';
|
4772 | const lastIdx = strings.length - 1;
|
4773 | for (let i = 0; i < lastIdx; i++) {
|
4774 | acc += `${strings[i]}${wrapI18nPlaceholder(bindingStartIndex + i, contextId)}`;
|
4775 | }
|
4776 | acc += strings[lastIdx];
|
4777 | return acc;
|
4778 | }
|
4779 | function getSeqNumberGenerator(startsAt = 0) {
|
4780 | let current = startsAt;
|
4781 | return () => current++;
|
4782 | }
|
4783 | function placeholdersToParams(placeholders) {
|
4784 | const params = {};
|
4785 | placeholders.forEach((values, key) => {
|
4786 | params[key] = literal(values.length > 1 ? `[${values.join('|')}]` : values[0]);
|
4787 | });
|
4788 | return params;
|
4789 | }
|
4790 | function updatePlaceholderMap(map, name, ...values) {
|
4791 | const current = map.get(name) || [];
|
4792 | current.push(...values);
|
4793 | map.set(name, current);
|
4794 | }
|
4795 | function assembleBoundTextPlaceholders(meta, bindingStartIndex = 0, contextId = 0) {
|
4796 | const startIdx = bindingStartIndex;
|
4797 | const placeholders = new Map();
|
4798 | const node = meta instanceof Message ? meta.nodes.find(node => node instanceof Container) : meta;
|
4799 | if (node) {
|
4800 | node
|
4801 | .children
|
4802 | .filter((child) => child instanceof Placeholder)
|
4803 | .forEach((child, idx) => {
|
4804 | const content = wrapI18nPlaceholder(startIdx + idx, contextId);
|
4805 | updatePlaceholderMap(placeholders, child.name, content);
|
4806 | });
|
4807 | }
|
4808 | return placeholders;
|
4809 | }
|
4810 | |
4811 |
|
4812 |
|
4813 |
|
4814 |
|
4815 |
|
4816 |
|
4817 |
|
4818 |
|
4819 |
|
4820 | function i18nFormatPlaceholderNames(params = {}, useCamelCase) {
|
4821 | const _params = {};
|
4822 | if (params && Object.keys(params).length) {
|
4823 | Object.keys(params).forEach(key => _params[formatI18nPlaceholderName(key, useCamelCase)] = params[key]);
|
4824 | }
|
4825 | return _params;
|
4826 | }
|
4827 | |
4828 |
|
4829 |
|
4830 |
|
4831 |
|
4832 |
|
4833 |
|
4834 |
|
4835 | function formatI18nPlaceholderName(name, useCamelCase = true) {
|
4836 | const publicName = toPublicName(name);
|
4837 | if (!useCamelCase) {
|
4838 | return publicName;
|
4839 | }
|
4840 | const chunks = publicName.split('_');
|
4841 | if (chunks.length === 1) {
|
4842 |
|
4843 | return name.toLowerCase();
|
4844 | }
|
4845 | let postfix;
|
4846 |
|
4847 | if (/^\d+$/.test(chunks[chunks.length - 1])) {
|
4848 | postfix = chunks.pop();
|
4849 | }
|
4850 | let raw = chunks.shift().toLowerCase();
|
4851 | if (chunks.length) {
|
4852 | raw += chunks.map(c => c.charAt(0).toUpperCase() + c.slice(1).toLowerCase()).join('');
|
4853 | }
|
4854 | return postfix ? `${raw}_${postfix}` : raw;
|
4855 | }
|
4856 | |
4857 |
|
4858 |
|
4859 |
|
4860 |
|
4861 |
|
4862 | function getTranslationConstPrefix(extra) {
|
4863 | return `${CLOSURE_TRANSLATION_VAR_PREFIX}${extra}`.toUpperCase();
|
4864 | }
|
4865 | |
4866 |
|
4867 |
|
4868 |
|
4869 | function declareI18nVariable(variable) {
|
4870 | return new DeclareVarStmt(variable.name, undefined, INFERRED_TYPE, undefined, variable.sourceSpan);
|
4871 | }
|
4872 |
|
4873 | |
4874 |
|
4875 |
|
4876 |
|
4877 |
|
4878 |
|
4879 |
|
4880 | |
4881 |
|
4882 |
|
4883 |
|
4884 |
|
4885 |
|
4886 |
|
4887 |
|
4888 | const UNSAFE_OBJECT_KEY_NAME_REGEXP = /[-.]/;
|
4889 |
|
4890 | const TEMPORARY_NAME = '_t';
|
4891 |
|
4892 | const CONTEXT_NAME = 'ctx';
|
4893 |
|
4894 | const RENDER_FLAGS = 'rf';
|
4895 |
|
4896 | const REFERENCE_PREFIX = '_r';
|
4897 |
|
4898 | const IMPLICIT_REFERENCE = '$implicit';
|
4899 |
|
4900 | const NON_BINDABLE_ATTR = 'ngNonBindable';
|
4901 | |
4902 |
|
4903 |
|
4904 |
|
4905 |
|
4906 | function temporaryAllocator(statements, name) {
|
4907 | let temp = null;
|
4908 | return () => {
|
4909 | if (!temp) {
|
4910 | statements.push(new DeclareVarStmt(TEMPORARY_NAME, undefined, DYNAMIC_TYPE));
|
4911 | temp = variable(name);
|
4912 | }
|
4913 | return temp;
|
4914 | };
|
4915 | }
|
4916 | function unsupported(feature) {
|
4917 | if (this) {
|
4918 | throw new Error(`Builder ${this.constructor.name} doesn't support ${feature} yet`);
|
4919 | }
|
4920 | throw new Error(`Feature ${feature} is not supported yet`);
|
4921 | }
|
4922 | function invalid$1(arg) {
|
4923 | throw new Error(`Invalid state: Visitor ${this.constructor.name} doesn't handle ${arg.constructor.name}`);
|
4924 | }
|
4925 | function asLiteral(value) {
|
4926 | if (Array.isArray(value)) {
|
4927 | return literalArr(value.map(asLiteral));
|
4928 | }
|
4929 | return literal(value, INFERRED_TYPE);
|
4930 | }
|
4931 | function conditionallyCreateMapObjectLiteral(keys, keepDeclared) {
|
4932 | if (Object.getOwnPropertyNames(keys).length > 0) {
|
4933 | return mapToExpression(keys, keepDeclared);
|
4934 | }
|
4935 | return null;
|
4936 | }
|
4937 | function mapToExpression(map, keepDeclared) {
|
4938 | return literalMap(Object.getOwnPropertyNames(map).map(key => {
|
4939 |
|
4940 |
|
4941 | const value = map[key];
|
4942 | let declaredName;
|
4943 | let publicName;
|
4944 | let minifiedName;
|
4945 | let needsDeclaredName;
|
4946 | if (Array.isArray(value)) {
|
4947 | [publicName, declaredName] = value;
|
4948 | minifiedName = key;
|
4949 | needsDeclaredName = publicName !== declaredName;
|
4950 | }
|
4951 | else {
|
4952 | [declaredName, publicName] = splitAtColon(key, [key, value]);
|
4953 | minifiedName = declaredName;
|
4954 |
|
4955 |
|
4956 |
|
4957 | needsDeclaredName = publicName !== declaredName && key.includes(':');
|
4958 | }
|
4959 | return {
|
4960 | key: minifiedName,
|
4961 |
|
4962 | quoted: UNSAFE_OBJECT_KEY_NAME_REGEXP.test(minifiedName),
|
4963 | value: (keepDeclared && needsDeclaredName) ?
|
4964 | literalArr([asLiteral(publicName), asLiteral(declaredName)]) :
|
4965 | asLiteral(publicName)
|
4966 | };
|
4967 | }));
|
4968 | }
|
4969 | |
4970 |
|
4971 |
|
4972 | function trimTrailingNulls(parameters) {
|
4973 | while (isNull(parameters[parameters.length - 1])) {
|
4974 | parameters.pop();
|
4975 | }
|
4976 | return parameters;
|
4977 | }
|
4978 | function getQueryPredicate(query, constantPool) {
|
4979 | if (Array.isArray(query.predicate)) {
|
4980 | let predicate = [];
|
4981 | query.predicate.forEach((selector) => {
|
4982 |
|
4983 |
|
4984 |
|
4985 | const selectors = selector.split(',').map(token => literal(token.trim()));
|
4986 | predicate.push(...selectors);
|
4987 | });
|
4988 | return constantPool.getConstLiteral(literalArr(predicate), true);
|
4989 | }
|
4990 | else {
|
4991 | return query.predicate;
|
4992 | }
|
4993 | }
|
4994 | |
4995 |
|
4996 |
|
4997 |
|
4998 |
|
4999 | class DefinitionMap {
|
5000 | constructor() {
|
5001 | this.values = [];
|
5002 | }
|
5003 | set(key, value) {
|
5004 | if (value) {
|
5005 | this.values.push({ key: key, value, quoted: false });
|
5006 | }
|
5007 | }
|
5008 | toLiteralMap() {
|
5009 | return literalMap(this.values);
|
5010 | }
|
5011 | }
|
5012 | |
5013 |
|
5014 |
|
5015 |
|
5016 |
|
5017 |
|
5018 |
|
5019 |
|
5020 |
|
5021 | function getAttrsForDirectiveMatching(elOrTpl) {
|
5022 | const attributesMap = {};
|
5023 | if (elOrTpl instanceof Template && elOrTpl.tagName !== 'ng-template') {
|
5024 | elOrTpl.templateAttrs.forEach(a => attributesMap[a.name] = '');
|
5025 | }
|
5026 | else {
|
5027 | elOrTpl.attributes.forEach(a => {
|
5028 | if (!isI18nAttribute(a.name)) {
|
5029 | attributesMap[a.name] = a.value;
|
5030 | }
|
5031 | });
|
5032 | elOrTpl.inputs.forEach(i => {
|
5033 | attributesMap[i.name] = '';
|
5034 | });
|
5035 | elOrTpl.outputs.forEach(o => {
|
5036 | attributesMap[o.name] = '';
|
5037 | });
|
5038 | }
|
5039 | return attributesMap;
|
5040 | }
|
5041 |
|
5042 | function chainedInstruction(reference, calls, span) {
|
5043 | let expression = importExpr(reference, null, span);
|
5044 | if (calls.length > 0) {
|
5045 | for (let i = 0; i < calls.length; i++) {
|
5046 | expression = expression.callFn(calls[i], span);
|
5047 | }
|
5048 | }
|
5049 | else {
|
5050 |
|
5051 | expression = expression.callFn([], span);
|
5052 | }
|
5053 | return expression;
|
5054 | }
|
5055 | |
5056 |
|
5057 |
|
5058 |
|
5059 |
|
5060 | function getInterpolationArgsLength(interpolation) {
|
5061 | const { expressions, strings } = interpolation;
|
5062 | if (expressions.length === 1 && strings.length === 2 && strings[0] === '' && strings[1] === '') {
|
5063 |
|
5064 |
|
5065 |
|
5066 | return 1;
|
5067 | }
|
5068 | else {
|
5069 | return expressions.length + strings.length;
|
5070 | }
|
5071 | }
|
5072 |
|
5073 | |
5074 |
|
5075 |
|
5076 |
|
5077 |
|
5078 |
|
5079 |
|
5080 | var R3FactoryDelegateType;
|
5081 | (function (R3FactoryDelegateType) {
|
5082 | R3FactoryDelegateType[R3FactoryDelegateType["Class"] = 0] = "Class";
|
5083 | R3FactoryDelegateType[R3FactoryDelegateType["Function"] = 1] = "Function";
|
5084 | R3FactoryDelegateType[R3FactoryDelegateType["Factory"] = 2] = "Factory";
|
5085 | })(R3FactoryDelegateType || (R3FactoryDelegateType = {}));
|
5086 | var R3FactoryTarget;
|
5087 | (function (R3FactoryTarget) {
|
5088 | R3FactoryTarget[R3FactoryTarget["Directive"] = 0] = "Directive";
|
5089 | R3FactoryTarget[R3FactoryTarget["Component"] = 1] = "Component";
|
5090 | R3FactoryTarget[R3FactoryTarget["Injectable"] = 2] = "Injectable";
|
5091 | R3FactoryTarget[R3FactoryTarget["Pipe"] = 3] = "Pipe";
|
5092 | R3FactoryTarget[R3FactoryTarget["NgModule"] = 4] = "NgModule";
|
5093 | })(R3FactoryTarget || (R3FactoryTarget = {}));
|
5094 | |
5095 |
|
5096 |
|
5097 |
|
5098 |
|
5099 |
|
5100 |
|
5101 |
|
5102 | var R3ResolvedDependencyType;
|
5103 | (function (R3ResolvedDependencyType) {
|
5104 | |
5105 |
|
5106 |
|
5107 | R3ResolvedDependencyType[R3ResolvedDependencyType["Token"] = 0] = "Token";
|
5108 | |
5109 |
|
5110 |
|
5111 |
|
5112 |
|
5113 | R3ResolvedDependencyType[R3ResolvedDependencyType["Attribute"] = 1] = "Attribute";
|
5114 | |
5115 |
|
5116 |
|
5117 | R3ResolvedDependencyType[R3ResolvedDependencyType["ChangeDetectorRef"] = 2] = "ChangeDetectorRef";
|
5118 | |
5119 |
|
5120 |
|
5121 | R3ResolvedDependencyType[R3ResolvedDependencyType["Invalid"] = 3] = "Invalid";
|
5122 | })(R3ResolvedDependencyType || (R3ResolvedDependencyType = {}));
|
5123 | |
5124 |
|
5125 |
|
5126 | function compileFactoryFunction(meta) {
|
5127 | const t = variable('t');
|
5128 | const statements = [];
|
5129 | let ctorDepsType = NONE_TYPE;
|
5130 |
|
5131 |
|
5132 |
|
5133 |
|
5134 |
|
5135 | const typeForCtor = !isDelegatedMetadata(meta) ?
|
5136 | new BinaryOperatorExpr(BinaryOperator.Or, t, meta.internalType) :
|
5137 | t;
|
5138 | let ctorExpr = null;
|
5139 | if (meta.deps !== null) {
|
5140 |
|
5141 | if (meta.deps !== 'invalid') {
|
5142 | ctorExpr = new InstantiateExpr(typeForCtor, injectDependencies(meta.deps, meta.injectFn, meta.target === R3FactoryTarget.Pipe));
|
5143 | ctorDepsType = createCtorDepsType(meta.deps);
|
5144 | }
|
5145 | }
|
5146 | else {
|
5147 | const baseFactory = variable(`ɵ${meta.name}_BaseFactory`);
|
5148 | const getInheritedFactory = importExpr(Identifiers$1.getInheritedFactory);
|
5149 | const baseFactoryStmt = baseFactory
|
5150 | .set(getInheritedFactory.callFn([meta.internalType], undefined, true))
|
5151 | .toDeclStmt(INFERRED_TYPE, [StmtModifier.Exported, StmtModifier.Final]);
|
5152 | statements.push(baseFactoryStmt);
|
5153 |
|
5154 | ctorExpr = baseFactory.callFn([typeForCtor]);
|
5155 | }
|
5156 | const ctorExprFinal = ctorExpr;
|
5157 | const body = [];
|
5158 | let retExpr = null;
|
5159 | function makeConditionalFactory(nonCtorExpr) {
|
5160 | const r = variable('r');
|
5161 | body.push(r.set(NULL_EXPR).toDeclStmt());
|
5162 | let ctorStmt = null;
|
5163 | if (ctorExprFinal !== null) {
|
5164 | ctorStmt = r.set(ctorExprFinal).toStmt();
|
5165 | }
|
5166 | else {
|
5167 | ctorStmt = importExpr(Identifiers$1.invalidFactory).callFn([]).toStmt();
|
5168 | }
|
5169 | body.push(ifStmt(t, [ctorStmt], [r.set(nonCtorExpr).toStmt()]));
|
5170 | return r;
|
5171 | }
|
5172 | if (isDelegatedMetadata(meta) && meta.delegateType === R3FactoryDelegateType.Factory) {
|
5173 | const delegateFactory = variable(`ɵ${meta.name}_BaseFactory`);
|
5174 | const getFactoryOf = importExpr(Identifiers$1.getFactoryOf);
|
5175 | if (meta.delegate.isEquivalent(meta.internalType)) {
|
5176 | throw new Error(`Illegal state: compiling factory that delegates to itself`);
|
5177 | }
|
5178 | const delegateFactoryStmt = delegateFactory.set(getFactoryOf.callFn([meta.delegate])).toDeclStmt(INFERRED_TYPE, [
|
5179 | StmtModifier.Exported, StmtModifier.Final
|
5180 | ]);
|
5181 | statements.push(delegateFactoryStmt);
|
5182 | retExpr = makeConditionalFactory(delegateFactory.callFn([]));
|
5183 | }
|
5184 | else if (isDelegatedMetadata(meta)) {
|
5185 |
|
5186 |
|
5187 | const delegateArgs = injectDependencies(meta.delegateDeps, meta.injectFn, meta.target === R3FactoryTarget.Pipe);
|
5188 |
|
5189 | const factoryExpr = new (meta.delegateType === R3FactoryDelegateType.Class ?
|
5190 | InstantiateExpr :
|
5191 | InvokeFunctionExpr)(meta.delegate, delegateArgs);
|
5192 | retExpr = makeConditionalFactory(factoryExpr);
|
5193 | }
|
5194 | else if (isExpressionFactoryMetadata(meta)) {
|
5195 |
|
5196 | retExpr = makeConditionalFactory(meta.expression);
|
5197 | }
|
5198 | else {
|
5199 | retExpr = ctorExpr;
|
5200 | }
|
5201 | if (retExpr !== null) {
|
5202 | body.push(new ReturnStatement(retExpr));
|
5203 | }
|
5204 | else {
|
5205 | body.push(importExpr(Identifiers$1.invalidFactory).callFn([]).toStmt());
|
5206 | }
|
5207 | return {
|
5208 | factory: fn([new FnParam('t', DYNAMIC_TYPE)], body, INFERRED_TYPE, undefined, `${meta.name}_Factory`),
|
5209 | statements,
|
5210 | type: expressionType(importExpr(Identifiers$1.FactoryDef, [typeWithParameters(meta.type.type, meta.typeArgumentCount), ctorDepsType]))
|
5211 | };
|
5212 | }
|
5213 | function injectDependencies(deps, injectFn, isPipe) {
|
5214 | return deps.map((dep, index) => compileInjectDependency(dep, injectFn, isPipe, index));
|
5215 | }
|
5216 | function compileInjectDependency(dep, injectFn, isPipe, index) {
|
5217 |
|
5218 | switch (dep.resolved) {
|
5219 | case R3ResolvedDependencyType.Token:
|
5220 | case R3ResolvedDependencyType.ChangeDetectorRef:
|
5221 |
|
5222 | const flags = 0 | (dep.self ? 2 : 0) |
|
5223 | (dep.skipSelf ? 4 : 0) | (dep.host ? 1 : 0) |
|
5224 | (dep.optional ? 8 : 0);
|
5225 |
|
5226 |
|
5227 |
|
5228 | let flagsParam = (flags !== 0 || dep.optional) ? literal(flags) : null;
|
5229 |
|
5230 | if (isPipe && dep.resolved === R3ResolvedDependencyType.ChangeDetectorRef) {
|
5231 | return importExpr(Identifiers$1.injectPipeChangeDetectorRef).callFn(flagsParam ? [flagsParam] : []);
|
5232 | }
|
5233 |
|
5234 | const injectArgs = [dep.token];
|
5235 | if (flagsParam) {
|
5236 | injectArgs.push(flagsParam);
|
5237 | }
|
5238 | return importExpr(injectFn).callFn(injectArgs);
|
5239 | case R3ResolvedDependencyType.Attribute:
|
5240 |
|
5241 | return importExpr(Identifiers$1.injectAttribute).callFn([dep.token]);
|
5242 | case R3ResolvedDependencyType.Invalid:
|
5243 | return importExpr(Identifiers$1.invalidFactoryDep).callFn([literal(index)]);
|
5244 | default:
|
5245 | return unsupported(`Unknown R3ResolvedDependencyType: ${R3ResolvedDependencyType[dep.resolved]}`);
|
5246 | }
|
5247 | }
|
5248 | function createCtorDepsType(deps) {
|
5249 | let hasTypes = false;
|
5250 | const attributeTypes = deps.map(dep => {
|
5251 | const type = createCtorDepType(dep);
|
5252 | if (type !== null) {
|
5253 | hasTypes = true;
|
5254 | return type;
|
5255 | }
|
5256 | else {
|
5257 | return literal(null);
|
5258 | }
|
5259 | });
|
5260 | if (hasTypes) {
|
5261 | return expressionType(literalArr(attributeTypes));
|
5262 | }
|
5263 | else {
|
5264 | return NONE_TYPE;
|
5265 | }
|
5266 | }
|
5267 | function createCtorDepType(dep) {
|
5268 | const entries = [];
|
5269 | if (dep.resolved === R3ResolvedDependencyType.Attribute) {
|
5270 | if (dep.attribute !== null) {
|
5271 | entries.push({ key: 'attribute', value: dep.attribute, quoted: false });
|
5272 | }
|
5273 | }
|
5274 | if (dep.optional) {
|
5275 | entries.push({ key: 'optional', value: literal(true), quoted: false });
|
5276 | }
|
5277 | if (dep.host) {
|
5278 | entries.push({ key: 'host', value: literal(true), quoted: false });
|
5279 | }
|
5280 | if (dep.self) {
|
5281 | entries.push({ key: 'self', value: literal(true), quoted: false });
|
5282 | }
|
5283 | if (dep.skipSelf) {
|
5284 | entries.push({ key: 'skipSelf', value: literal(true), quoted: false });
|
5285 | }
|
5286 | return entries.length > 0 ? literalMap(entries) : null;
|
5287 | }
|
5288 | function isDelegatedMetadata(meta) {
|
5289 | return meta.delegateType !== undefined;
|
5290 | }
|
5291 | function isExpressionFactoryMetadata(meta) {
|
5292 | return meta.expression !== undefined;
|
5293 | }
|
5294 |
|
5295 | |
5296 |
|
5297 |
|
5298 |
|
5299 |
|
5300 |
|
5301 |
|
5302 | function compileInjectable(meta) {
|
5303 | let result = null;
|
5304 | const factoryMeta = {
|
5305 | name: meta.name,
|
5306 | type: meta.type,
|
5307 | internalType: meta.internalType,
|
5308 | typeArgumentCount: meta.typeArgumentCount,
|
5309 | deps: [],
|
5310 | injectFn: Identifiers.inject,
|
5311 | target: R3FactoryTarget.Injectable,
|
5312 | };
|
5313 | if (meta.useClass !== undefined) {
|
5314 |
|
5315 |
|
5316 |
|
5317 |
|
5318 |
|
5319 |
|
5320 | const useClassOnSelf = meta.useClass.isEquivalent(meta.internalType);
|
5321 | let deps = undefined;
|
5322 | if (meta.userDeps !== undefined) {
|
5323 | deps = meta.userDeps;
|
5324 | }
|
5325 | if (deps !== undefined) {
|
5326 |
|
5327 | result = compileFactoryFunction(Object.assign(Object.assign({}, factoryMeta), { delegate: meta.useClass, delegateDeps: deps, delegateType: R3FactoryDelegateType.Class }));
|
5328 | }
|
5329 | else if (useClassOnSelf) {
|
5330 | result = compileFactoryFunction(factoryMeta);
|
5331 | }
|
5332 | else {
|
5333 | result = delegateToFactory(meta.type.value, meta.useClass);
|
5334 | }
|
5335 | }
|
5336 | else if (meta.useFactory !== undefined) {
|
5337 | if (meta.userDeps !== undefined) {
|
5338 | result = compileFactoryFunction(Object.assign(Object.assign({}, factoryMeta), { delegate: meta.useFactory, delegateDeps: meta.userDeps || [], delegateType: R3FactoryDelegateType.Function }));
|
5339 | }
|
5340 | else {
|
5341 | result = {
|
5342 | statements: [],
|
5343 | factory: fn([], [new ReturnStatement(meta.useFactory.callFn([]))])
|
5344 | };
|
5345 | }
|
5346 | }
|
5347 | else if (meta.useValue !== undefined) {
|
5348 |
|
5349 |
|
5350 |
|
5351 | result = compileFactoryFunction(Object.assign(Object.assign({}, factoryMeta), { expression: meta.useValue }));
|
5352 | }
|
5353 | else if (meta.useExisting !== undefined) {
|
5354 |
|
5355 | result = compileFactoryFunction(Object.assign(Object.assign({}, factoryMeta), { expression: importExpr(Identifiers.inject).callFn([meta.useExisting]) }));
|
5356 | }
|
5357 | else {
|
5358 | result = delegateToFactory(meta.type.value, meta.internalType);
|
5359 | }
|
5360 | const token = meta.internalType;
|
5361 | const injectableProps = { token, factory: result.factory };
|
5362 |
|
5363 | if (meta.providedIn.value !== null) {
|
5364 | injectableProps.providedIn = meta.providedIn;
|
5365 | }
|
5366 | const expression = importExpr(Identifiers.ɵɵdefineInjectable).callFn([mapToMapExpression(injectableProps)]);
|
5367 | const type = new ExpressionType(importExpr(Identifiers.InjectableDef, [typeWithParameters(meta.type.type, meta.typeArgumentCount)]));
|
5368 | return {
|
5369 | expression,
|
5370 | type,
|
5371 | statements: result.statements,
|
5372 | };
|
5373 | }
|
5374 | function delegateToFactory(type, internalType) {
|
5375 | return {
|
5376 | statements: [],
|
5377 |
|
5378 |
|
5379 |
|
5380 | factory: type.node === internalType.node ?
|
5381 | internalType.prop('ɵfac') :
|
5382 | fn([new FnParam('t', DYNAMIC_TYPE)], [new ReturnStatement(internalType.callMethod('ɵfac', [variable('t')]))])
|
5383 | };
|
5384 | }
|
5385 |
|
5386 | |
5387 |
|
5388 |
|
5389 |
|
5390 |
|
5391 |
|
5392 |
|
5393 | function assertArrayOfStrings(identifier, value) {
|
5394 | if (value == null) {
|
5395 | return;
|
5396 | }
|
5397 | if (!Array.isArray(value)) {
|
5398 | throw new Error(`Expected '${identifier}' to be an array of strings.`);
|
5399 | }
|
5400 | for (let i = 0; i < value.length; i += 1) {
|
5401 | if (typeof value[i] !== 'string') {
|
5402 | throw new Error(`Expected '${identifier}' to be an array of strings.`);
|
5403 | }
|
5404 | }
|
5405 | }
|
5406 | const UNUSABLE_INTERPOLATION_REGEXPS = [
|
5407 | /^\s*$/,
|
5408 | /[<>]/,
|
5409 | /^[{}]$/,
|
5410 | /&(#|[a-z])/i,
|
5411 | /^\/\//,
|
5412 | ];
|
5413 | function assertInterpolationSymbols(identifier, value) {
|
5414 | if (value != null && !(Array.isArray(value) && value.length == 2)) {
|
5415 | throw new Error(`Expected '${identifier}' to be an array, [start, end].`);
|
5416 | }
|
5417 | else if (value != null) {
|
5418 | const start = value[0];
|
5419 | const end = value[1];
|
5420 | // Check for unusable interpolation symbols
|
5421 | UNUSABLE_INTERPOLATION_REGEXPS.forEach(regexp => {
|
5422 | if (regexp.test(start) || regexp.test(end)) {
|
5423 | throw new Error(`['${start}', '${end}'] contains unusable interpolation symbol.`);
|
5424 | }
|
5425 | });
|
5426 | }
|
5427 | }
|
5428 |
|
5429 | /**
|
5430 | * @license
|
5431 | * Copyright Google LLC All Rights Reserved.
|
5432 | *
|
5433 | * Use of this source code is governed by an MIT-style license that can be
|
5434 | * found in the LICENSE file at https://angular.io/license
|
5435 | */
|
5436 | class InterpolationConfig {
|
5437 | constructor(start, end) {
|
5438 | this.start = start;
|
5439 | this.end = end;
|
5440 | }
|
5441 | static fromArray(markers) {
|
5442 | if (!markers) {
|
5443 | return DEFAULT_INTERPOLATION_CONFIG;
|
5444 | }
|
5445 | assertInterpolationSymbols('interpolation', markers);
|
5446 | return new InterpolationConfig(markers[0], markers[1]);
|
5447 | }
|
5448 | }
|
5449 | const DEFAULT_INTERPOLATION_CONFIG = new InterpolationConfig('{{', '}}');
|
5450 |
|
5451 | /**
|
5452 | * @license
|
5453 | * Copyright Google LLC All Rights Reserved.
|
5454 | *
|
5455 | * Use of this source code is governed by an MIT-style license that can be
|
5456 | * found in the LICENSE file at https://angular.io/license
|
5457 | */
|
5458 | /**
|
5459 | * In TypeScript, tagged template functions expect a "template object", which is an array of
|
5460 | * "cooked" strings plus a `raw` property that contains an array of "raw" strings. This is
|
5461 | * typically constructed with a function called `__makeTemplateObject(cooked, raw)`, but it may not
|
5462 | * be available in all environments.
|
5463 | *
|
5464 | * This is a JavaScript polyfill that uses __makeTemplateObject when it's available, but otherwise
|
5465 | * creates an inline helper with the same functionality.
|
5466 | *
|
5467 | * In the inline function, if `Object.defineProperty` is available we use that to attach the `raw`
|
5468 | * array.
|
5469 | */
|
5470 | const makeTemplateObjectPolyfill = '(this&&this.__makeTemplateObject||function(e,t){return Object.defineProperty?Object.defineProperty(e,"raw",{value:t}):e.raw=t,e})';
|
5471 | class AbstractJsEmitterVisitor extends AbstractEmitterVisitor {
|
5472 | constructor() {
|
5473 | super(false);
|
5474 | }
|
5475 | visitDeclareClassStmt(stmt, ctx) {
|
5476 | ctx.pushClass(stmt);
|
5477 | this._visitClassConstructor(stmt, ctx);
|
5478 | if (stmt.parent != null) {
|
5479 | ctx.print(stmt, `${stmt.name}.prototype = Object.create(`);
|
5480 | stmt.parent.visitExpression(this, ctx);
|
5481 | ctx.println(stmt, `.prototype);`);
|
5482 | }
|
5483 | stmt.getters.forEach((getter) => this._visitClassGetter(stmt, getter, ctx));
|
5484 | stmt.methods.forEach((method) => this._visitClassMethod(stmt, method, ctx));
|
5485 | ctx.popClass();
|
5486 | return null;
|
5487 | }
|
5488 | _visitClassConstructor(stmt, ctx) {
|
5489 | ctx.print(stmt, `function ${stmt.name}(`);
|
5490 | if (stmt.constructorMethod != null) {
|
5491 | this._visitParams(stmt.constructorMethod.params, ctx);
|
5492 | }
|
5493 | ctx.println(stmt, `) {`);
|
5494 | ctx.incIndent();
|
5495 | if (stmt.constructorMethod != null) {
|
5496 | if (stmt.constructorMethod.body.length > 0) {
|
5497 | ctx.println(stmt, `var self = this;`);
|
5498 | this.visitAllStatements(stmt.constructorMethod.body, ctx);
|
5499 | }
|
5500 | }
|
5501 | ctx.decIndent();
|
5502 | ctx.println(stmt, `}`);
|
5503 | }
|
5504 | _visitClassGetter(stmt, getter, ctx) {
|
5505 | ctx.println(stmt, `Object.defineProperty(${stmt.name}.prototype, '${getter.name}', { get: function() {`);
|
5506 | ctx.incIndent();
|
5507 | if (getter.body.length > 0) {
|
5508 | ctx.println(stmt, `var self = this;`);
|
5509 | this.visitAllStatements(getter.body, ctx);
|
5510 | }
|
5511 | ctx.decIndent();
|
5512 | ctx.println(stmt, `}});`);
|
5513 | }
|
5514 | _visitClassMethod(stmt, method, ctx) {
|
5515 | ctx.print(stmt, `${stmt.name}.prototype.${method.name} = function(`);
|
5516 | this._visitParams(method.params, ctx);
|
5517 | ctx.println(stmt, `) {`);
|
5518 | ctx.incIndent();
|
5519 | if (method.body.length > 0) {
|
5520 | ctx.println(stmt, `var self = this;`);
|
5521 | this.visitAllStatements(method.body, ctx);
|
5522 | }
|
5523 | ctx.decIndent();
|
5524 | ctx.println(stmt, `};`);
|
5525 | }
|
5526 | visitWrappedNodeExpr(ast, ctx) {
|
5527 | throw new Error('Cannot emit a WrappedNodeExpr in Javascript.');
|
5528 | }
|
5529 | visitReadVarExpr(ast, ctx) {
|
5530 | if (ast.builtin === BuiltinVar.This) {
|
5531 | ctx.print(ast, 'self');
|
5532 | }
|
5533 | else if (ast.builtin === BuiltinVar.Super) {
|
5534 | throw new Error(`'super' needs to be handled at a parent ast node, not at the variable level!`);
|
5535 | }
|
5536 | else {
|
5537 | super.visitReadVarExpr(ast, ctx);
|
5538 | }
|
5539 | return null;
|
5540 | }
|
5541 | visitDeclareVarStmt(stmt, ctx) {
|
5542 | ctx.print(stmt, `var ${stmt.name}`);
|
5543 | if (stmt.value) {
|
5544 | ctx.print(stmt, ' = ');
|
5545 | stmt.value.visitExpression(this, ctx);
|
5546 | }
|
5547 | ctx.println(stmt, `;`);
|
5548 | return null;
|
5549 | }
|
5550 | visitCastExpr(ast, ctx) {
|
5551 | ast.value.visitExpression(this, ctx);
|
5552 | return null;
|
5553 | }
|
5554 | visitInvokeFunctionExpr(expr, ctx) {
|
5555 | const fnExpr = expr.fn;
|
5556 | if (fnExpr instanceof ReadVarExpr && fnExpr.builtin === BuiltinVar.Super) {
|
5557 | ctx.currentClass.parent.visitExpression(this, ctx);
|
5558 | ctx.print(expr, `.call(this`);
|
5559 | if (expr.args.length > 0) {
|
5560 | ctx.print(expr, `, `);
|
5561 | this.visitAllExpressions(expr.args, ctx, ',');
|
5562 | }
|
5563 | ctx.print(expr, `)`);
|
5564 | }
|
5565 | else {
|
5566 | super.visitInvokeFunctionExpr(expr, ctx);
|
5567 | }
|
5568 | return null;
|
5569 | }
|
5570 | visitTaggedTemplateExpr(ast, ctx) {
|
5571 | // The following convoluted piece of code is effectively the downlevelled equivalent of
|
5572 | // ```
|
5573 | // tag`...`
|
5574 | // ```
|
5575 | // which is effectively like:
|
5576 | // ```
|
5577 | // tag(__makeTemplateObject(cooked, raw), expression1, expression2, ...);
|
5578 | // ```
|
5579 | const elements = ast.template.elements;
|
5580 | ast.tag.visitExpression(this, ctx);
|
5581 | ctx.print(ast, `(${makeTemplateObjectPolyfill}(`);
|
5582 | ctx.print(ast, `[${elements.map(part => escapeIdentifier(part.text, false)).join(', ')}], `);
|
5583 | ctx.print(ast, `[${elements.map(part => escapeIdentifier(part.rawText, false)).join(', ')}])`);
|
5584 | ast.template.expressions.forEach(expression => {
|
5585 | ctx.print(ast, ', ');
|
5586 | expression.visitExpression(this, ctx);
|
5587 | });
|
5588 | ctx.print(ast, ')');
|
5589 | return null;
|
5590 | }
|
5591 | visitFunctionExpr(ast, ctx) {
|
5592 | ctx.print(ast, `function${ast.name ? ' ' + ast.name : ''}(`);
|
5593 | this._visitParams(ast.params, ctx);
|
5594 | ctx.println(ast, `) {`);
|
5595 | ctx.incIndent();
|
5596 | this.visitAllStatements(ast.statements, ctx);
|
5597 | ctx.decIndent();
|
5598 | ctx.print(ast, `}`);
|
5599 | return null;
|
5600 | }
|
5601 | visitDeclareFunctionStmt(stmt, ctx) {
|
5602 | ctx.print(stmt, `function ${stmt.name}(`);
|
5603 | this._visitParams(stmt.params, ctx);
|
5604 | ctx.println(stmt, `) {`);
|
5605 | ctx.incIndent();
|
5606 | this.visitAllStatements(stmt.statements, ctx);
|
5607 | ctx.decIndent();
|
5608 | ctx.println(stmt, `}`);
|
5609 | return null;
|
5610 | }
|
5611 | visitTryCatchStmt(stmt, ctx) {
|
5612 | ctx.println(stmt, `try {`);
|
5613 | ctx.incIndent();
|
5614 | this.visitAllStatements(stmt.bodyStmts, ctx);
|
5615 | ctx.decIndent();
|
5616 | ctx.println(stmt, `} catch (${CATCH_ERROR_VAR$1.name}) {`);
|
5617 | ctx.incIndent();
|
5618 | const catchStmts = [CATCH_STACK_VAR$1.set(CATCH_ERROR_VAR$1.prop('stack')).toDeclStmt(null, [
|
5619 | StmtModifier.Final
|
5620 | ])].concat(stmt.catchStmts);
|
5621 | this.visitAllStatements(catchStmts, ctx);
|
5622 | ctx.decIndent();
|
5623 | ctx.println(stmt, `}`);
|
5624 | return null;
|
5625 | }
|
5626 | visitLocalizedString(ast, ctx) {
|
5627 | // The following convoluted piece of code is effectively the downlevelled equivalent of
|
5628 | // ```
|
5629 | // $localize `...`
|
5630 | // ```
|
5631 | // which is effectively like:
|
5632 | // ```
|
5633 | // $localize(__makeTemplateObject(cooked, raw), expression1, expression2, ...);
|
5634 | // ```
|
5635 | ctx.print(ast, `$localize(${makeTemplateObjectPolyfill}(`);
|
5636 | const parts = [ast.serializeI18nHead()];
|
5637 | for (let i = 1; i < ast.messageParts.length; i++) {
|
5638 | parts.push(ast.serializeI18nTemplatePart(i));
|
5639 | }
|
5640 | ctx.print(ast, `[${parts.map(part => escapeIdentifier(part.cooked, false)).join(', ')}], `);
|
5641 | ctx.print(ast, `[${parts.map(part => escapeIdentifier(part.raw, false)).join(', ')}])`);
|
5642 | ast.expressions.forEach(expression => {
|
5643 | ctx.print(ast, ', ');
|
5644 | expression.visitExpression(this, ctx);
|
5645 | });
|
5646 | ctx.print(ast, ')');
|
5647 | return null;
|
5648 | }
|
5649 | _visitParams(params, ctx) {
|
5650 | this.visitAllObjects(param => ctx.print(null, param.name), params, ctx, ',');
|
5651 | }
|
5652 | getBuiltinMethodName(method) {
|
5653 | let name;
|
5654 | switch (method) {
|
5655 | case BuiltinMethod.ConcatArray:
|
5656 | name = 'concat';
|
5657 | break;
|
5658 | case BuiltinMethod.SubscribeObservable:
|
5659 | name = 'subscribe';
|
5660 | break;
|
5661 | case BuiltinMethod.Bind:
|
5662 | name = 'bind';
|
5663 | break;
|
5664 | default:
|
5665 | throw new Error(`Unknown builtin method: ${method}`);
|
5666 | }
|
5667 | return name;
|
5668 | }
|
5669 | }
|
5670 |
|
5671 | /**
|
5672 | * @license
|
5673 | * Copyright Google LLC All Rights Reserved.
|
5674 | *
|
5675 | * Use of this source code is governed by an MIT-style license that can be
|
5676 | * found in the LICENSE file at https://angular.io/license
|
5677 | */
|
5678 | /**
|
5679 | * The Trusted Types policy, or null if Trusted Types are not
|
5680 | * enabled/supported, or undefined if the policy has not been created yet.
|
5681 | */
|
5682 | let policy;
|
5683 | /**
|
5684 | * Returns the Trusted Types policy, or null if Trusted Types are not
|
5685 | * enabled/supported. The first call to this function will create the policy.
|
5686 | */
|
5687 | function getPolicy() {
|
5688 | if (policy === undefined) {
|
5689 | policy = null;
|
5690 | if (_global.trustedTypes) {
|
5691 | try {
|
5692 | policy =
|
5693 | _global.trustedTypes.createPolicy('angular#unsafe-jit', {
|
5694 | createScript: (s) => s,
|
5695 | });
|
5696 | }
|
5697 | catch (_a) {
|
5698 | // trustedTypes.createPolicy throws if called with a name that is
|
5699 | // already registered, even in report-only mode. Until the API changes,
|
5700 | // catch the error not to break the applications functionally. In such
|
5701 | // cases, the code will fall back to using strings.
|
5702 | }
|
5703 | }
|
5704 | }
|
5705 | return policy;
|
5706 | }
|
5707 | /**
|
5708 | * Unsafely promote a string to a TrustedScript, falling back to strings when
|
5709 | * Trusted Types are not available.
|
5710 | * @security In particular, it must be assured that the provided string will
|
5711 | * never cause an XSS vulnerability if used in a context that will be
|
5712 | * interpreted and executed as a script by a browser, e.g. when calling eval.
|
5713 | */
|
5714 | function trustedScriptFromString(script) {
|
5715 | var _a;
|
5716 | return ((_a = getPolicy()) === null || _a === void 0 ? void 0 : _a.createScript(script)) || script;
|
5717 | }
|
5718 | /**
|
5719 | * Unsafely call the Function constructor with the given string arguments.
|
5720 | * @security This is a security-sensitive function; any use of this function
|
5721 | * must go through security review. In particular, it must be assured that it
|
5722 | * is only called from the JIT compiler, as use in other code can lead to XSS
|
5723 | * vulnerabilities.
|
5724 | */
|
5725 | function newTrustedFunctionForJIT(...args) {
|
5726 | if (!_global.trustedTypes) {
|
5727 | // In environments that don't support Trusted Types, fall back to the most
|
5728 | // straightforward implementation:
|
5729 | return new Function(...args);
|
5730 | }
|
5731 | // Chrome currently does not support passing TrustedScript to the Function
|
5732 | // constructor. The following implements the workaround proposed on the page
|
5733 | // below, where the Chromium bug is also referenced:
|
5734 | // https://github.com/w3c/webappsec-trusted-types/wiki/Trusted-Types-for-function-constructor
|
5735 | const fnArgs = args.slice(0, -1).join(',');
|
5736 | const fnBody = args[args.length - 1];
|
5737 | const body = `(function anonymous(${fnArgs}
|
5738 | ) { ${fnBody}
|
5739 | })`;
|
5740 | // Using eval directly confuses the compiler and prevents this module from
|
5741 | // being stripped out of JS binaries even if not used. The global['eval']
|
5742 | // indirection fixes that.
|
5743 | const fn = _global['eval'](trustedScriptFromString(body));
|
5744 | if (fn.bind === undefined) {
|
5745 | // Workaround for a browser bug that only exists in Chrome 83, where passing
|
5746 | // a TrustedScript to eval just returns the TrustedScript back without
|
5747 | // evaluating it. In that case, fall back to the most straightforward
|
5748 | // implementation:
|
5749 | return new Function(...args);
|
5750 | }
|
5751 | // To completely mimic the behavior of calling "new Function", two more
|
5752 | // things need to happen:
|
5753 | // 1. Stringifying the resulting function should return its source code
|
5754 | fn.toString = () => body;
|
5755 | // 2. When calling the resulting function, `this` should refer to `global`
|
5756 | return fn.bind(_global);
|
5757 | // When Trusted Types support in Function constructors is widely available,
|
5758 | // the implementation of this function can be simplified to:
|
5759 | // return new Function(...args.map(a => trustedScriptFromString(a)));
|
5760 | }
|
5761 |
|
5762 | /**
|
5763 | * @license
|
5764 | * Copyright Google LLC All Rights Reserved.
|
5765 | *
|
5766 | * Use of this source code is governed by an MIT-style license that can be
|
5767 | * found in the LICENSE file at https://angular.io/license
|
5768 | */
|
5769 | /**
|
5770 | * A helper class to manage the evaluation of JIT generated code.
|
5771 | */
|
5772 | class JitEvaluator {
|
5773 | /**
|
5774 | *
|
5775 | * @param sourceUrl The URL of the generated code.
|
5776 | * @param statements An array of Angular statement AST nodes to be evaluated.
|
5777 | * @param reflector A helper used when converting the statements to executable code.
|
5778 | * @param createSourceMaps If true then create a source-map for the generated code and include it
|
5779 | * inline as a source-map comment.
|
5780 | * @returns A map of all the variables in the generated code.
|
5781 | */
|
5782 | evaluateStatements(sourceUrl, statements, reflector, createSourceMaps) {
|
5783 | const converter = new JitEmitterVisitor(reflector);
|
5784 | const ctx = EmitterVisitorContext.createRoot();
|
5785 | // Ensure generated code is in strict mode
|
5786 | if (statements.length > 0 && !isUseStrictStatement(statements[0])) {
|
5787 | statements = [
|
5788 | literal('use strict').toStmt(),
|
5789 | ...statements,
|
5790 | ];
|
5791 | }
|
5792 | converter.visitAllStatements(statements, ctx);
|
5793 | converter.createReturnStmt(ctx);
|
5794 | return this.evaluateCode(sourceUrl, ctx, converter.getArgs(), createSourceMaps);
|
5795 | }
|
5796 | /**
|
5797 | * Evaluate a piece of JIT generated code.
|
5798 | * @param sourceUrl The URL of this generated code.
|
5799 | * @param ctx A context object that contains an AST of the code to be evaluated.
|
5800 | * @param vars A map containing the names and values of variables that the evaluated code might
|
5801 | * reference.
|
5802 | * @param createSourceMap If true then create a source-map for the generated code and include it
|
5803 | * inline as a source-map comment.
|
5804 | * @returns The result of evaluating the code.
|
5805 | */
|
5806 | evaluateCode(sourceUrl, ctx, vars, createSourceMap) {
|
5807 | let fnBody = `"use strict";${ctx.toSource()}\n//# sourceURL=${sourceUrl}`;
|
5808 | const fnArgNames = [];
|
5809 | const fnArgValues = [];
|
5810 | for (const argName in vars) {
|
5811 | fnArgValues.push(vars[argName]);
|
5812 | fnArgNames.push(argName);
|
5813 | }
|
5814 | if (createSourceMap) {
|
5815 | // using `new Function(...)` generates a header, 1 line of no arguments, 2 lines otherwise
|
5816 | // E.g. ```
|
5817 | // function anonymous(a,b,c
|
5818 | // /**/) { ... }```
|
5819 | // We don't want to hard code this fact, so we auto detect it via an empty function first.
|
5820 | const emptyFn = newTrustedFunctionForJIT(...fnArgNames.concat('return null;')).toString();
|
5821 | const headerLines = emptyFn.slice(0, emptyFn.indexOf('return null;')).split('\n').length - 1;
|
5822 | fnBody += `\n${ctx.toSourceMapGenerator(sourceUrl, headerLines).toJsComment()}`;
|
5823 | }
|
5824 | const fn = newTrustedFunctionForJIT(...fnArgNames.concat(fnBody));
|
5825 | return this.executeFunction(fn, fnArgValues);
|
5826 | }
|
5827 | /**
|
5828 | * Execute a JIT generated function by calling it.
|
5829 | *
|
5830 | * This method can be overridden in tests to capture the functions that are generated
|
5831 | * by this `JitEvaluator` class.
|
5832 | *
|
5833 | * @param fn A function to execute.
|
5834 | * @param args The arguments to pass to the function being executed.
|
5835 | * @returns The return value of the executed function.
|
5836 | */
|
5837 | executeFunction(fn, args) {
|
5838 | return fn(...args);
|
5839 | }
|
5840 | }
|
5841 | /**
|
5842 | * An Angular AST visitor that converts AST nodes into executable JavaScript code.
|
5843 | */
|
5844 | class JitEmitterVisitor extends AbstractJsEmitterVisitor {
|
5845 | constructor(reflector) {
|
5846 | super();
|
5847 | this.reflector = reflector;
|
5848 | this._evalArgNames = [];
|
5849 | this._evalArgValues = [];
|
5850 | this._evalExportedVars = [];
|
5851 | }
|
5852 | createReturnStmt(ctx) {
|
5853 | const stmt = new ReturnStatement(new LiteralMapExpr(this._evalExportedVars.map(resultVar => new LiteralMapEntry(resultVar, variable(resultVar), false))));
|
5854 | stmt.visitStatement(this, ctx);
|
5855 | }
|
5856 | getArgs() {
|
5857 | const result = {};
|
5858 | for (let i = 0; i < this._evalArgNames.length; i++) {
|
5859 | result[this._evalArgNames[i]] = this._evalArgValues[i];
|
5860 | }
|
5861 | return result;
|
5862 | }
|
5863 | visitExternalExpr(ast, ctx) {
|
5864 | this._emitReferenceToExternal(ast, this.reflector.resolveExternalReference(ast.value), ctx);
|
5865 | return null;
|
5866 | }
|
5867 | visitWrappedNodeExpr(ast, ctx) {
|
5868 | this._emitReferenceToExternal(ast, ast.node, ctx);
|
5869 | return null;
|
5870 | }
|
5871 | visitDeclareVarStmt(stmt, ctx) {
|
5872 | if (stmt.hasModifier(StmtModifier.Exported)) {
|
5873 | this._evalExportedVars.push(stmt.name);
|
5874 | }
|
5875 | return super.visitDeclareVarStmt(stmt, ctx);
|
5876 | }
|
5877 | visitDeclareFunctionStmt(stmt, ctx) {
|
5878 | if (stmt.hasModifier(StmtModifier.Exported)) {
|
5879 | this._evalExportedVars.push(stmt.name);
|
5880 | }
|
5881 | return super.visitDeclareFunctionStmt(stmt, ctx);
|
5882 | }
|
5883 | visitDeclareClassStmt(stmt, ctx) {
|
5884 | if (stmt.hasModifier(StmtModifier.Exported)) {
|
5885 | this._evalExportedVars.push(stmt.name);
|
5886 | }
|
5887 | return super.visitDeclareClassStmt(stmt, ctx);
|
5888 | }
|
5889 | _emitReferenceToExternal(ast, value, ctx) {
|
5890 | let id = this._evalArgValues.indexOf(value);
|
5891 | if (id === -1) {
|
5892 | id = this._evalArgValues.length;
|
5893 | this._evalArgValues.push(value);
|
5894 | const name = identifierName({ reference: value }) || 'val';
|
5895 | this._evalArgNames.push(`jit_${name}_${id}`);
|
5896 | }
|
5897 | ctx.print(ast, this._evalArgNames[id]);
|
5898 | }
|
5899 | }
|
5900 | function isUseStrictStatement(statement) {
|
5901 | return statement.isEquivalent(literal('use strict').toStmt());
|
5902 | }
|
5903 |
|
5904 | /**
|
5905 | * @license
|
5906 | * Copyright Google LLC All Rights Reserved.
|
5907 | *
|
5908 | * Use of this source code is governed by an MIT-style license that can be
|
5909 | * found in the LICENSE file at https://angular.io/license
|
5910 | */
|
5911 | const $EOF = 0;
|
5912 | const $BSPACE = 8;
|
5913 | const $TAB = 9;
|
5914 | const $LF = 10;
|
5915 | const $VTAB = 11;
|
5916 | const $FF = 12;
|
5917 | const $CR = 13;
|
5918 | const $SPACE = 32;
|
5919 | const $BANG = 33;
|
5920 | const $DQ = 34;
|
5921 | const $HASH = 35;
|
5922 | const $$ = 36;
|
5923 | const $PERCENT = 37;
|
5924 | const $AMPERSAND = 38;
|
5925 | const $SQ = 39;
|
5926 | const $LPAREN = 40;
|
5927 | const $RPAREN = 41;
|
5928 | const $STAR = 42;
|
5929 | const $PLUS = 43;
|
5930 | const $COMMA = 44;
|
5931 | const $MINUS = 45;
|
5932 | const $PERIOD = 46;
|
5933 | const $SLASH = 47;
|
5934 | const $COLON = 58;
|
5935 | const $SEMICOLON = 59;
|
5936 | const $LT = 60;
|
5937 | const $EQ = 61;
|
5938 | const $GT = 62;
|
5939 | const $QUESTION = 63;
|
5940 | const $0 = 48;
|
5941 | const $7 = 55;
|
5942 | const $9 = 57;
|
5943 | const $A = 65;
|
5944 | const $E = 69;
|
5945 | const $F = 70;
|
5946 | const $X = 88;
|
5947 | const $Z = 90;
|
5948 | const $LBRACKET = 91;
|
5949 | const $BACKSLASH = 92;
|
5950 | const $RBRACKET = 93;
|
5951 | const $CARET = 94;
|
5952 | const $_ = 95;
|
5953 | const $a = 97;
|
5954 | const $b = 98;
|
5955 | const $e = 101;
|
5956 | const $f = 102;
|
5957 | const $n = 110;
|
5958 | const $r = 114;
|
5959 | const $t = 116;
|
5960 | const $u = 117;
|
5961 | const $v = 118;
|
5962 | const $x = 120;
|
5963 | const $z = 122;
|
5964 | const $LBRACE = 123;
|
5965 | const $BAR = 124;
|
5966 | const $RBRACE = 125;
|
5967 | const $NBSP = 160;
|
5968 | const $BT = 96;
|
5969 | function isWhitespace(code) {
|
5970 | return (code >= $TAB && code <= $SPACE) || (code == $NBSP);
|
5971 | }
|
5972 | function isDigit(code) {
|
5973 | return $0 <= code && code <= $9;
|
5974 | }
|
5975 | function isAsciiLetter(code) {
|
5976 | return code >= $a && code <= $z || code >= $A && code <= $Z;
|
5977 | }
|
5978 | function isAsciiHexDigit(code) {
|
5979 | return code >= $a && code <= $f || code >= $A && code <= $F || isDigit(code);
|
5980 | }
|
5981 | function isNewLine(code) {
|
5982 | return code === $LF || code === $CR;
|
5983 | }
|
5984 | function isOctalDigit(code) {
|
5985 | return $0 <= code && code <= $7;
|
5986 | }
|
5987 |
|
5988 | /**
|
5989 | * @license
|
5990 | * Copyright Google LLC All Rights Reserved.
|
5991 | *
|
5992 | * Use of this source code is governed by an MIT-style license that can be
|
5993 | * found in the LICENSE file at https://angular.io/license
|
5994 | */
|
5995 | class ParseLocation {
|
5996 | constructor(file, offset, line, col) {
|
5997 | this.file = file;
|
5998 | this.offset = offset;
|
5999 | this.line = line;
|
6000 | this.col = col;
|
6001 | }
|
6002 | toString() {
|
6003 | return this.offset != null ? `${this.file.url}@${this.line}:${this.col}` : this.file.url;
|
6004 | }
|
6005 | moveBy(delta) {
|
6006 | const source = this.file.content;
|
6007 | const len = source.length;
|
6008 | let offset = this.offset;
|
6009 | let line = this.line;
|
6010 | let col = this.col;
|
6011 | while (offset > 0 && delta < 0) {
|
6012 | offset--;
|
6013 | delta++;
|
6014 | const ch = source.charCodeAt(offset);
|
6015 | if (ch == $LF) {
|
6016 | line--;
|
6017 | const priorLine = source.substr(0, offset - 1).lastIndexOf(String.fromCharCode($LF));
|
6018 | col = priorLine > 0 ? offset - priorLine : offset;
|
6019 | }
|
6020 | else {
|
6021 | col--;
|
6022 | }
|
6023 | }
|
6024 | while (offset < len && delta > 0) {
|
6025 | const ch = source.charCodeAt(offset);
|
6026 | offset++;
|
6027 | delta--;
|
6028 | if (ch == $LF) {
|
6029 | line++;
|
6030 | col = 0;
|
6031 | }
|
6032 | else {
|
6033 | col++;
|
6034 | }
|
6035 | }
|
6036 | return new ParseLocation(this.file, offset, line, col);
|
6037 | }
|
6038 | // Return the source around the location
|
6039 | // Up to `maxChars` or `maxLines` on each side of the location
|
6040 | getContext(maxChars, maxLines) {
|
6041 | const content = this.file.content;
|
6042 | let startOffset = this.offset;
|
6043 | if (startOffset != null) {
|
6044 | if (startOffset > content.length - 1) {
|
6045 | startOffset = content.length - 1;
|
6046 | }
|
6047 | let endOffset = startOffset;
|
6048 | let ctxChars = 0;
|
6049 | let ctxLines = 0;
|
6050 | while (ctxChars < maxChars && startOffset > 0) {
|
6051 | startOffset--;
|
6052 | ctxChars++;
|
6053 | if (content[startOffset] == '\n') {
|
6054 | if (++ctxLines == maxLines) {
|
6055 | break;
|
6056 | }
|
6057 | }
|
6058 | }
|
6059 | ctxChars = 0;
|
6060 | ctxLines = 0;
|
6061 | while (ctxChars < maxChars && endOffset < content.length - 1) {
|
6062 | endOffset++;
|
6063 | ctxChars++;
|
6064 | if (content[endOffset] == '\n') {
|
6065 | if (++ctxLines == maxLines) {
|
6066 | break;
|
6067 | }
|
6068 | }
|
6069 | }
|
6070 | return {
|
6071 | before: content.substring(startOffset, this.offset),
|
6072 | after: content.substring(this.offset, endOffset + 1),
|
6073 | };
|
6074 | }
|
6075 | return null;
|
6076 | }
|
6077 | }
|
6078 | class ParseSourceFile {
|
6079 | constructor(content, url) {
|
6080 | this.content = content;
|
6081 | this.url = url;
|
6082 | }
|
6083 | }
|
6084 | class ParseSourceSpan {
|
6085 | /**
|
6086 | * Create an object that holds information about spans of tokens/nodes captured during
|
6087 | * lexing/parsing of text.
|
6088 | *
|
6089 | * @param start
|
6090 | * The location of the start of the span (having skipped leading trivia).
|
6091 | * Skipping leading trivia makes source-spans more "user friendly", since things like HTML
|
6092 | * elements will appear to begin at the start of the opening tag, rather than at the start of any
|
6093 | * leading trivia, which could include newlines.
|
6094 | *
|
6095 | * @param end
|
6096 | * The location of the end of the span.
|
6097 | *
|
6098 | * @param fullStart
|
6099 | * The start of the token without skipping the leading trivia.
|
6100 | * This is used by tooling that splits tokens further, such as extracting Angular interpolations
|
6101 | * from text tokens. Such tooling creates new source-spans relative to the original token's
|
6102 | * source-span. If leading trivia characters have been skipped then the new source-spans may be
|
6103 | * incorrectly offset.
|
6104 | *
|
6105 | * @param details
|
6106 | * Additional information (such as identifier names) that should be associated with the span.
|
6107 | */
|
6108 | constructor(start, end, fullStart = start, details = null) {
|
6109 | this.start = start;
|
6110 | this.end = end;
|
6111 | this.fullStart = fullStart;
|
6112 | this.details = details;
|
6113 | }
|
6114 | toString() {
|
6115 | return this.start.file.content.substring(this.start.offset, this.end.offset);
|
6116 | }
|
6117 | }
|
6118 | var ParseErrorLevel;
|
6119 | (function (ParseErrorLevel) {
|
6120 | ParseErrorLevel[ParseErrorLevel["WARNING"] = 0] = "WARNING";
|
6121 | ParseErrorLevel[ParseErrorLevel["ERROR"] = 1] = "ERROR";
|
6122 | })(ParseErrorLevel || (ParseErrorLevel = {}));
|
6123 | class ParseError {
|
6124 | constructor(span, msg, level = ParseErrorLevel.ERROR) {
|
6125 | this.span = span;
|
6126 | this.msg = msg;
|
6127 | this.level = level;
|
6128 | }
|
6129 | contextualMessage() {
|
6130 | const ctx = this.span.start.getContext(100, 3);
|
6131 | return ctx ? `${this.msg} ("${ctx.before}[${ParseErrorLevel[this.level]} ->]${ctx.after}")` :
|
6132 | this.msg;
|
6133 | }
|
6134 | toString() {
|
6135 | const details = this.span.details ? `, ${this.span.details}` : '';
|
6136 | return `${this.contextualMessage()}: ${this.span.start}${details}`;
|
6137 | }
|
6138 | }
|
6139 | /**
|
6140 | * Generates Source Span object for a given R3 Type for JIT mode.
|
6141 | *
|
6142 | * @param kind Component or Directive.
|
6143 | * @param typeName name of the Component or Directive.
|
6144 | * @param sourceUrl reference to Component or Directive source.
|
6145 | * @returns instance of ParseSourceSpan that represent a given Component or Directive.
|
6146 | */
|
6147 | function r3JitTypeSourceSpan(kind, typeName, sourceUrl) {
|
6148 | const sourceFileName = `in ${kind} ${typeName} in ${sourceUrl}`;
|
6149 | const sourceFile = new ParseSourceFile('', sourceFileName);
|
6150 | return new ParseSourceSpan(new ParseLocation(sourceFile, -1, -1, -1), new ParseLocation(sourceFile, -1, -1, -1));
|
6151 | }
|
6152 |
|
6153 | /**
|
6154 | * @license
|
6155 | * Copyright Google LLC All Rights Reserved.
|
6156 | *
|
6157 | * Use of this source code is governed by an MIT-style license that can be
|
6158 | * found in the LICENSE file at https://angular.io/license
|
6159 | */
|
6160 | /**
|
6161 | * Implementation of `CompileReflector` which resolves references to @angular/core
|
6162 | * symbols at runtime, according to a consumer-provided mapping.
|
6163 | *
|
6164 | * Only supports `resolveExternalReference`, all other methods throw.
|
6165 | */
|
6166 | class R3JitReflector {
|
6167 | constructor(context) {
|
6168 | this.context = context;
|
6169 | }
|
6170 | resolveExternalReference(ref) {
|
6171 | // This reflector only handles @angular/core imports.
|
6172 | if (ref.moduleName !== '@angular/core') {
|
6173 | throw new Error(`Cannot resolve external reference to ${ref.moduleName}, only references to @angular/core are supported.`);
|
6174 | }
|
6175 | if (!this.context.hasOwnProperty(ref.name)) {
|
6176 | throw new Error(`No value provided for @angular/core symbol '${ref.name}'.`);
|
6177 | }
|
6178 | return this.context[ref.name];
|
6179 | }
|
6180 | parameters(typeOrFunc) {
|
6181 | throw new Error('Not implemented.');
|
6182 | }
|
6183 | annotations(typeOrFunc) {
|
6184 | throw new Error('Not implemented.');
|
6185 | }
|
6186 | shallowAnnotations(typeOrFunc) {
|
6187 | throw new Error('Not implemented.');
|
6188 | }
|
6189 | tryAnnotations(typeOrFunc) {
|
6190 | throw new Error('Not implemented.');
|
6191 | }
|
6192 | propMetadata(typeOrFunc) {
|
6193 | throw new Error('Not implemented.');
|
6194 | }
|
6195 | hasLifecycleHook(type, lcProperty) {
|
6196 | throw new Error('Not implemented.');
|
6197 | }
|
6198 | guards(typeOrFunc) {
|
6199 | throw new Error('Not implemented.');
|
6200 | }
|
6201 | componentModuleUrl(type, cmpMetadata) {
|
6202 | throw new Error('Not implemented.');
|
6203 | }
|
6204 | }
|
6205 |
|
6206 | /**
|
6207 | * @license
|
6208 | * Copyright Google LLC All Rights Reserved.
|
6209 | *
|
6210 | * Use of this source code is governed by an MIT-style license that can be
|
6211 | * found in the LICENSE file at https://angular.io/license
|
6212 | */
|
6213 | function mapLiteral(obj, quoted = false) {
|
6214 | return literalMap(Object.keys(obj).map(key => ({
|
6215 | key,
|
6216 | quoted,
|
6217 | value: obj[key],
|
6218 | })));
|
6219 | }
|
6220 |
|
6221 | /**
|
6222 | * @license
|
6223 | * Copyright Google LLC All Rights Reserved.
|
6224 | *
|
6225 | * Use of this source code is governed by an MIT-style license that can be
|
6226 | * found in the LICENSE file at https://angular.io/license
|
6227 | */
|
6228 | /**
|
6229 | * Construct an `R3NgModuleDef` for the given `R3NgModuleMetadata`.
|
6230 | */
|
6231 | function compileNgModule(meta) {
|
6232 | const { internalType, type: moduleType, bootstrap, declarations, imports, exports, schemas, containsForwardDecls, emitInline, id } = meta;
|
6233 | const additionalStatements = [];
|
6234 | const definitionMap = { type: internalType };
|
6235 | // Only generate the keys in the metadata if the arrays have values.
|
6236 | if (bootstrap.length) {
|
6237 | definitionMap.bootstrap = refsToArray(bootstrap, containsForwardDecls);
|
6238 | }
|
6239 | // If requested to emit scope information inline, pass the declarations, imports and exports to
|
6240 | // the `ɵɵdefineNgModule` call. The JIT compilation uses this.
|
6241 | if (emitInline) {
|
6242 | if (declarations.length) {
|
6243 | definitionMap.declarations = refsToArray(declarations, containsForwardDecls);
|
6244 | }
|
6245 | if (imports.length) {
|
6246 | definitionMap.imports = refsToArray(imports, containsForwardDecls);
|
6247 | }
|
6248 | if (exports.length) {
|
6249 | definitionMap.exports = refsToArray(exports, containsForwardDecls);
|
6250 | }
|
6251 | }
|
6252 | // If not emitting inline, the scope information is not passed into `ɵɵdefineNgModule` as it would
|
6253 | // prevent tree-shaking of the declarations, imports and exports references.
|
6254 | else {
|
6255 | const setNgModuleScopeCall = generateSetNgModuleScopeCall(meta);
|
6256 | if (setNgModuleScopeCall !== null) {
|
6257 | additionalStatements.push(setNgModuleScopeCall);
|
6258 | }
|
6259 | }
|
6260 | if (schemas && schemas.length) {
|
6261 | definitionMap.schemas = literalArr(schemas.map(ref => ref.value));
|
6262 | }
|
6263 | if (id) {
|
6264 | definitionMap.id = id;
|
6265 | }
|
6266 | const expression = importExpr(Identifiers$1.defineNgModule).callFn([mapToMapExpression(definitionMap)]);
|
6267 | const type = new ExpressionType(importExpr(Identifiers$1.NgModuleDefWithMeta, [
|
6268 | new ExpressionType(moduleType.type), tupleTypeOf(declarations), tupleTypeOf(imports),
|
6269 | tupleTypeOf(exports)
|
6270 | ]));
|
6271 | return { expression, type, additionalStatements };
|
6272 | }
|
6273 | /**
|
6274 | * Generates a function call to `ɵɵsetNgModuleScope` with all necessary information so that the
|
6275 | * transitive module scope can be computed during runtime in JIT mode. This call is marked pure
|
6276 | * such that the references to declarations, imports and exports may be elided causing these
|
6277 | * symbols to become tree-shakeable.
|
6278 | */
|
6279 | function generateSetNgModuleScopeCall(meta) {
|
6280 | const { adjacentType: moduleType, declarations, imports, exports, containsForwardDecls } = meta;
|
6281 | const scopeMap = {};
|
6282 | if (declarations.length) {
|
6283 | scopeMap.declarations = refsToArray(declarations, containsForwardDecls);
|
6284 | }
|
6285 | if (imports.length) {
|
6286 | scopeMap.imports = refsToArray(imports, containsForwardDecls);
|
6287 | }
|
6288 | if (exports.length) {
|
6289 | scopeMap.exports = refsToArray(exports, containsForwardDecls);
|
6290 | }
|
6291 | if (Object.keys(scopeMap).length === 0) {
|
6292 | return null;
|
6293 | }
|
6294 | // setNgModuleScope(...)
|
6295 | const fnCall = new InvokeFunctionExpr(
|
6296 | /* fn */ importExpr(Identifiers$1.setNgModuleScope),
|
6297 | /* args */ [moduleType, mapToMapExpression(scopeMap)]);
|
6298 | // (ngJitMode guard) && setNgModuleScope(...)
|
6299 | const guardedCall = jitOnlyGuardedExpression(fnCall);
|
6300 | // function() { (ngJitMode guard) && setNgModuleScope(...); }
|
6301 | const iife = new FunctionExpr(
|
6302 | /* params */ [],
|
6303 | /* statements */ [guardedCall.toStmt()]);
|
6304 | // (function() { (ngJitMode guard) && setNgModuleScope(...); })()
|
6305 | const iifeCall = new InvokeFunctionExpr(
|
6306 | /* fn */ iife,
|
6307 | /* args */ []);
|
6308 | return iifeCall.toStmt();
|
6309 | }
|
6310 | function compileInjector(meta) {
|
6311 | const result = compileFactoryFunction({
|
6312 | name: meta.name,
|
6313 | type: meta.type,
|
6314 | internalType: meta.internalType,
|
6315 | typeArgumentCount: 0,
|
6316 | deps: meta.deps,
|
6317 | injectFn: Identifiers$1.inject,
|
6318 | target: R3FactoryTarget.NgModule,
|
6319 | });
|
6320 | const definitionMap = {
|
6321 | factory: result.factory,
|
6322 | };
|
6323 | if (meta.providers !== null) {
|
6324 | definitionMap.providers = meta.providers;
|
6325 | }
|
6326 | if (meta.imports.length > 0) {
|
6327 | definitionMap.imports = literalArr(meta.imports);
|
6328 | }
|
6329 | const expression = importExpr(Identifiers$1.defineInjector).callFn([mapToMapExpression(definitionMap)]);
|
6330 | const type = new ExpressionType(importExpr(Identifiers$1.InjectorDef, [new ExpressionType(meta.type.type)]));
|
6331 | return { expression, type, statements: result.statements };
|
6332 | }
|
6333 | function tupleTypeOf(exp) {
|
6334 | const types = exp.map(ref => typeofExpr(ref.type));
|
6335 | return exp.length > 0 ? expressionType(literalArr(types)) : NONE_TYPE;
|
6336 | }
|
6337 | function refsToArray(refs, shouldForwardDeclare) {
|
6338 | const values = literalArr(refs.map(ref => ref.value));
|
6339 | return shouldForwardDeclare ? fn([], [new ReturnStatement(values)]) : values;
|
6340 | }
|
6341 |
|
6342 | /**
|
6343 | * @license
|
6344 | * Copyright Google LLC All Rights Reserved.
|
6345 | *
|
6346 | * Use of this source code is governed by an MIT-style license that can be
|
6347 | * found in the LICENSE file at https://angular.io/license
|
6348 | */
|
6349 | function compilePipeFromMetadata(metadata) {
|
6350 | const definitionMapValues = [];
|
6351 | // e.g. `name: 'myPipe'`
|
6352 | definitionMapValues.push({ key: 'name', value: literal(metadata.pipeName), quoted: false });
|
6353 | // e.g. `type: MyPipe`
|
6354 | definitionMapValues.push({ key: 'type', value: metadata.type.value, quoted: false });
|
6355 | // e.g. `pure: true`
|
6356 | definitionMapValues.push({ key: 'pure', value: literal(metadata.pure), quoted: false });
|
6357 | const expression = importExpr(Identifiers$1.definePipe).callFn([literalMap(definitionMapValues)]);
|
6358 | const type = createPipeType(metadata);
|
6359 | return { expression, type };
|
6360 | }
|
6361 | function createPipeType(metadata) {
|
6362 | return new ExpressionType(importExpr(Identifiers$1.PipeDefWithMeta, [
|
6363 | typeWithParameters(metadata.type.type, metadata.typeArgumentCount),
|
6364 | new ExpressionType(new LiteralExpr(metadata.pipeName)),
|
6365 | ]));
|
6366 | }
|
6367 |
|
6368 | /**
|
6369 | * @license
|
6370 | * Copyright Google LLC All Rights Reserved.
|
6371 | *
|
6372 | * Use of this source code is governed by an MIT-style license that can be
|
6373 | * found in the LICENSE file at https://angular.io/license
|
6374 | */
|
6375 | class ParserError {
|
6376 | constructor(message, input, errLocation, ctxLocation) {
|
6377 | this.input = input;
|
6378 | this.errLocation = errLocation;
|
6379 | this.ctxLocation = ctxLocation;
|
6380 | this.message = `Parser Error: ${message} ${errLocation} [${input}] in ${ctxLocation}`;
|
6381 | }
|
6382 | }
|
6383 | class ParseSpan {
|
6384 | constructor(start, end) {
|
6385 | this.start = start;
|
6386 | this.end = end;
|
6387 | }
|
6388 | toAbsolute(absoluteOffset) {
|
6389 | return new AbsoluteSourceSpan(absoluteOffset + this.start, absoluteOffset + this.end);
|
6390 | }
|
6391 | }
|
6392 | class AST {
|
6393 | constructor(span,
|
6394 | /**
|
6395 | * Absolute location of the expression AST in a source code file.
|
6396 | */
|
6397 | sourceSpan) {
|
6398 | this.span = span;
|
6399 | this.sourceSpan = sourceSpan;
|
6400 | }
|
6401 | visit(visitor, context = null) {
|
6402 | return null;
|
6403 | }
|
6404 | toString() {
|
6405 | return 'AST';
|
6406 | }
|
6407 | }
|
6408 | class ASTWithName extends AST {
|
6409 | constructor(span, sourceSpan, nameSpan) {
|
6410 | super(span, sourceSpan);
|
6411 | this.nameSpan = nameSpan;
|
6412 | }
|
6413 | }
|
6414 | /**
|
6415 | * Represents a quoted expression of the form:
|
6416 | *
|
6417 | * quote = prefix `:` uninterpretedExpression
|
6418 | * prefix = identifier
|
6419 | * uninterpretedExpression = arbitrary string
|
6420 | *
|
6421 | * A quoted expression is meant to be pre-processed by an AST transformer that
|
6422 | * converts it into another AST that no longer contains quoted expressions.
|
6423 | * It is meant to allow third-party developers to extend Angular template
|
6424 | * expression language. The `uninterpretedExpression` part of the quote is
|
6425 | * therefore not interpreted by the Angular's own expression parser.
|
6426 | */
|
6427 | class Quote extends AST {
|
6428 | constructor(span, sourceSpan, prefix, uninterpretedExpression, location) {
|
6429 | super(span, sourceSpan);
|
6430 | this.prefix = prefix;
|
6431 | this.uninterpretedExpression = uninterpretedExpression;
|
6432 | this.location = location;
|
6433 | }
|
6434 | visit(visitor, context = null) {
|
6435 | return visitor.visitQuote(this, context);
|
6436 | }
|
6437 | toString() {
|
6438 | return 'Quote';
|
6439 | }
|
6440 | }
|
6441 | class EmptyExpr extends AST {
|
6442 | visit(visitor, context = null) {
|
6443 | // do nothing
|
6444 | }
|
6445 | }
|
6446 | class ImplicitReceiver extends AST {
|
6447 | visit(visitor, context = null) {
|
6448 | return visitor.visitImplicitReceiver(this, context);
|
6449 | }
|
6450 | }
|
6451 | /**
|
6452 | * Receiver when something is accessed through `this` (e.g. `this.foo`). Note that this class
|
6453 | * inherits from `ImplicitReceiver`, because accessing something through `this` is treated the
|
6454 | * same as accessing it implicitly inside of an Angular template (e.g. `[attr.title]="this.title"`
|
6455 | * is the same as `[attr.title]="title"`.). Inheriting allows for the `this` accesses to be treated
|
6456 | * the same as implicit ones, except for a couple of exceptions like `$event` and `$any`.
|
6457 | * TODO: we should find a way for this class not to extend from `ImplicitReceiver` in the future.
|
6458 | */
|
6459 | class ThisReceiver extends ImplicitReceiver {
|
6460 | visit(visitor, context = null) {
|
6461 | var _a;
|
6462 | return (_a = visitor.visitThisReceiver) === null || _a === void 0 ? void 0 : _a.call(visitor, this, context);
|
6463 | }
|
6464 | }
|
6465 | /**
|
6466 | * Multiple expressions separated by a semicolon.
|
6467 | */
|
6468 | class Chain extends AST {
|
6469 | constructor(span, sourceSpan, expressions) {
|
6470 | super(span, sourceSpan);
|
6471 | this.expressions = expressions;
|
6472 | }
|
6473 | visit(visitor, context = null) {
|
6474 | return visitor.visitChain(this, context);
|
6475 | }
|
6476 | }
|
6477 | class Conditional extends AST {
|
6478 | constructor(span, sourceSpan, condition, trueExp, falseExp) {
|
6479 | super(span, sourceSpan);
|
6480 | this.condition = condition;
|
6481 | this.trueExp = trueExp;
|
6482 | this.falseExp = falseExp;
|
6483 | }
|
6484 | visit(visitor, context = null) {
|
6485 | return visitor.visitConditional(this, context);
|
6486 | }
|
6487 | }
|
6488 | class PropertyRead extends ASTWithName {
|
6489 | constructor(span, sourceSpan, nameSpan, receiver, name) {
|
6490 | super(span, sourceSpan, nameSpan);
|
6491 | this.receiver = receiver;
|
6492 | this.name = name;
|
6493 | }
|
6494 | visit(visitor, context = null) {
|
6495 | return visitor.visitPropertyRead(this, context);
|
6496 | }
|
6497 | }
|
6498 | class PropertyWrite extends ASTWithName {
|
6499 | constructor(span, sourceSpan, nameSpan, receiver, name, value) {
|
6500 | super(span, sourceSpan, nameSpan);
|
6501 | this.receiver = receiver;
|
6502 | this.name = name;
|
6503 | this.value = value;
|
6504 | }
|
6505 | visit(visitor, context = null) {
|
6506 | return visitor.visitPropertyWrite(this, context);
|
6507 | }
|
6508 | }
|
6509 | class SafePropertyRead extends ASTWithName {
|
6510 | constructor(span, sourceSpan, nameSpan, receiver, name) {
|
6511 | super(span, sourceSpan, nameSpan);
|
6512 | this.receiver = receiver;
|
6513 | this.name = name;
|
6514 | }
|
6515 | visit(visitor, context = null) {
|
6516 | return visitor.visitSafePropertyRead(this, context);
|
6517 | }
|
6518 | }
|
6519 | class KeyedRead extends AST {
|
6520 | constructor(span, sourceSpan, obj, key) {
|
6521 | super(span, sourceSpan);
|
6522 | this.obj = obj;
|
6523 | this.key = key;
|
6524 | }
|
6525 | visit(visitor, context = null) {
|
6526 | return visitor.visitKeyedRead(this, context);
|
6527 | }
|
6528 | }
|
6529 | class KeyedWrite extends AST {
|
6530 | constructor(span, sourceSpan, obj, key, value) {
|
6531 | super(span, sourceSpan);
|
6532 | this.obj = obj;
|
6533 | this.key = key;
|
6534 | this.value = value;
|
6535 | }
|
6536 | visit(visitor, context = null) {
|
6537 | return visitor.visitKeyedWrite(this, context);
|
6538 | }
|
6539 | }
|
6540 | class BindingPipe extends ASTWithName {
|
6541 | constructor(span, sourceSpan, exp, name, args, nameSpan) {
|
6542 | super(span, sourceSpan, nameSpan);
|
6543 | this.exp = exp;
|
6544 | this.name = name;
|
6545 | this.args = args;
|
6546 | }
|
6547 | visit(visitor, context = null) {
|
6548 | return visitor.visitPipe(this, context);
|
6549 | }
|
6550 | }
|
6551 | class LiteralPrimitive extends AST {
|
6552 | constructor(span, sourceSpan, value) {
|
6553 | super(span, sourceSpan);
|
6554 | this.value = value;
|
6555 | }
|
6556 | visit(visitor, context = null) {
|
6557 | return visitor.visitLiteralPrimitive(this, context);
|
6558 | }
|
6559 | }
|
6560 | class LiteralArray extends AST {
|
6561 | constructor(span, sourceSpan, expressions) {
|
6562 | super(span, sourceSpan);
|
6563 | this.expressions = expressions;
|
6564 | }
|
6565 | visit(visitor, context = null) {
|
6566 | return visitor.visitLiteralArray(this, context);
|
6567 | }
|
6568 | }
|
6569 | class LiteralMap extends AST {
|
6570 | constructor(span, sourceSpan, keys, values) {
|
6571 | super(span, sourceSpan);
|
6572 | this.keys = keys;
|
6573 | this.values = values;
|
6574 | }
|
6575 | visit(visitor, context = null) {
|
6576 | return visitor.visitLiteralMap(this, context);
|
6577 | }
|
6578 | }
|
6579 | class Interpolation extends AST {
|
6580 | constructor(span, sourceSpan, strings, expressions) {
|
6581 | super(span, sourceSpan);
|
6582 | this.strings = strings;
|
6583 | this.expressions = expressions;
|
6584 | }
|
6585 | visit(visitor, context = null) {
|
6586 | return visitor.visitInterpolation(this, context);
|
6587 | }
|
6588 | }
|
6589 | class Binary extends AST {
|
6590 | constructor(span, sourceSpan, operation, left, right) {
|
6591 | super(span, sourceSpan);
|
6592 | this.operation = operation;
|
6593 | this.left = left;
|
6594 | this.right = right;
|
6595 | }
|
6596 | visit(visitor, context = null) {
|
6597 | return visitor.visitBinary(this, context);
|
6598 | }
|
6599 | }
|
6600 | /**
|
6601 | * For backwards compatibility reasons, `Unary` inherits from `Binary` and mimics the binary AST
|
6602 | * node that was originally used. This inheritance relation can be deleted in some future major,
|
6603 | * after consumers have been given a chance to fully support Unary.
|
6604 | */
|
6605 | class Unary extends Binary {
|
6606 | /**
|
6607 | * During the deprecation period this constructor is private, to avoid consumers from creating
|
6608 | * a `Unary` with the fallback properties for `Binary`.
|
6609 | */
|
6610 | constructor(span, sourceSpan, operator, expr, binaryOp, binaryLeft, binaryRight) {
|
6611 | super(span, sourceSpan, binaryOp, binaryLeft, binaryRight);
|
6612 | this.operator = operator;
|
6613 | this.expr = expr;
|
6614 | }
|
6615 | /**
|
6616 | * Creates a unary minus expression "-x", represented as `Binary` using "0 - x".
|
6617 | */
|
6618 | static createMinus(span, sourceSpan, expr) {
|
6619 | return new Unary(span, sourceSpan, '-', expr, '-', new LiteralPrimitive(span, sourceSpan, 0), expr);
|
6620 | }
|
6621 | /**
|
6622 | * Creates a unary plus expression "+x", represented as `Binary` using "x - 0".
|
6623 | */
|
6624 | static createPlus(span, sourceSpan, expr) {
|
6625 | return new Unary(span, sourceSpan, '+', expr, '-', expr, new LiteralPrimitive(span, sourceSpan, 0));
|
6626 | }
|
6627 | visit(visitor, context = null) {
|
6628 | if (visitor.visitUnary !== undefined) {
|
6629 | return visitor.visitUnary(this, context);
|
6630 | }
|
6631 | return visitor.visitBinary(this, context);
|
6632 | }
|
6633 | }
|
6634 | class PrefixNot extends AST {
|
6635 | constructor(span, sourceSpan, expression) {
|
6636 | super(span, sourceSpan);
|
6637 | this.expression = expression;
|
6638 | }
|
6639 | visit(visitor, context = null) {
|
6640 | return visitor.visitPrefixNot(this, context);
|
6641 | }
|
6642 | }
|
6643 | class NonNullAssert extends AST {
|
6644 | constructor(span, sourceSpan, expression) {
|
6645 | super(span, sourceSpan);
|
6646 | this.expression = expression;
|
6647 | }
|
6648 | visit(visitor, context = null) {
|
6649 | return visitor.visitNonNullAssert(this, context);
|
6650 | }
|
6651 | }
|
6652 | class MethodCall extends ASTWithName {
|
6653 | constructor(span, sourceSpan, nameSpan, receiver, name, args) {
|
6654 | super(span, sourceSpan, nameSpan);
|
6655 | this.receiver = receiver;
|
6656 | this.name = name;
|
6657 | this.args = args;
|
6658 | }
|
6659 | visit(visitor, context = null) {
|
6660 | return visitor.visitMethodCall(this, context);
|
6661 | }
|
6662 | }
|
6663 | class SafeMethodCall extends ASTWithName {
|
6664 | constructor(span, sourceSpan, nameSpan, receiver, name, args) {
|
6665 | super(span, sourceSpan, nameSpan);
|
6666 | this.receiver = receiver;
|
6667 | this.name = name;
|
6668 | this.args = args;
|
6669 | }
|
6670 | visit(visitor, context = null) {
|
6671 | return visitor.visitSafeMethodCall(this, context);
|
6672 | }
|
6673 | }
|
6674 | class FunctionCall extends AST {
|
6675 | constructor(span, sourceSpan, target, args) {
|
6676 | super(span, sourceSpan);
|
6677 | this.target = target;
|
6678 | this.args = args;
|
6679 | }
|
6680 | visit(visitor, context = null) {
|
6681 | return visitor.visitFunctionCall(this, context);
|
6682 | }
|
6683 | }
|
6684 | /**
|
6685 | * Records the absolute position of a text span in a source file, where `start` and `end` are the
|
6686 | * starting and ending byte offsets, respectively, of the text span in a source file.
|
6687 | */
|
6688 | class AbsoluteSourceSpan {
|
6689 | constructor(start, end) {
|
6690 | this.start = start;
|
6691 | this.end = end;
|
6692 | }
|
6693 | }
|
6694 | class ASTWithSource extends AST {
|
6695 | constructor(ast, source, location, absoluteOffset, errors) {
|
6696 | super(new ParseSpan(0, source === null ? 0 : source.length), new AbsoluteSourceSpan(absoluteOffset, source === null ? absoluteOffset : absoluteOffset + source.length));
|
6697 | this.ast = ast;
|
6698 | this.source = source;
|
6699 | this.location = location;
|
6700 | this.errors = errors;
|
6701 | }
|
6702 | visit(visitor, context = null) {
|
6703 | if (visitor.visitASTWithSource) {
|
6704 | return visitor.visitASTWithSource(this, context);
|
6705 | }
|
6706 | return this.ast.visit(visitor, context);
|
6707 | }
|
6708 | toString() {
|
6709 | return `${this.source} in ${this.location}`;
|
6710 | }
|
6711 | }
|
6712 | class VariableBinding {
|
6713 | /**
|
6714 | * @param sourceSpan entire span of the binding.
|
6715 | * @param key name of the LHS along with its span.
|
6716 | * @param value optional value for the RHS along with its span.
|
6717 | */
|
6718 | constructor(sourceSpan, key, value) {
|
6719 | this.sourceSpan = sourceSpan;
|
6720 | this.key = key;
|
6721 | this.value = value;
|
6722 | }
|
6723 | }
|
6724 | class ExpressionBinding {
|
6725 | /**
|
6726 | * @param sourceSpan entire span of the binding.
|
6727 | * @param key binding name, like ngForOf, ngForTrackBy, ngIf, along with its
|
6728 | * span. Note that the length of the span may not be the same as
|
6729 | * `key.source.length`. For example,
|
6730 | * 1. key.source = ngFor, key.span is for "ngFor"
|
6731 | * 2. key.source = ngForOf, key.span is for "of"
|
6732 | * 3. key.source = ngForTrackBy, key.span is for "trackBy"
|
6733 | * @param value optional expression for the RHS.
|
6734 | */
|
6735 | constructor(sourceSpan, key, value) {
|
6736 | this.sourceSpan = sourceSpan;
|
6737 | this.key = key;
|
6738 | this.value = value;
|
6739 | }
|
6740 | }
|
6741 | class RecursiveAstVisitor {
|
6742 | visit(ast, context) {
|
6743 | // The default implementation just visits every node.
|
6744 | // Classes that extend RecursiveAstVisitor should override this function
|
6745 | // to selectively visit the specified node.
|
6746 | ast.visit(this, context);
|
6747 | }
|
6748 | visitUnary(ast, context) {
|
6749 | this.visit(ast.expr, context);
|
6750 | }
|
6751 | visitBinary(ast, context) {
|
6752 | this.visit(ast.left, context);
|
6753 | this.visit(ast.right, context);
|
6754 | }
|
6755 | visitChain(ast, context) {
|
6756 | this.visitAll(ast.expressions, context);
|
6757 | }
|
6758 | visitConditional(ast, context) {
|
6759 | this.visit(ast.condition, context);
|
6760 | this.visit(ast.trueExp, context);
|
6761 | this.visit(ast.falseExp, context);
|
6762 | }
|
6763 | visitPipe(ast, context) {
|
6764 | this.visit(ast.exp, context);
|
6765 | this.visitAll(ast.args, context);
|
6766 | }
|
6767 | visitFunctionCall(ast, context) {
|
6768 | if (ast.target) {
|
6769 | this.visit(ast.target, context);
|
6770 | }
|
6771 | this.visitAll(ast.args, context);
|
6772 | }
|
6773 | visitImplicitReceiver(ast, context) { }
|
6774 | visitThisReceiver(ast, context) { }
|
6775 | visitInterpolation(ast, context) {
|
6776 | this.visitAll(ast.expressions, context);
|
6777 | }
|
6778 | visitKeyedRead(ast, context) {
|
6779 | this.visit(ast.obj, context);
|
6780 | this.visit(ast.key, context);
|
6781 | }
|
6782 | visitKeyedWrite(ast, context) {
|
6783 | this.visit(ast.obj, context);
|
6784 | this.visit(ast.key, context);
|
6785 | this.visit(ast.value, context);
|
6786 | }
|
6787 | visitLiteralArray(ast, context) {
|
6788 | this.visitAll(ast.expressions, context);
|
6789 | }
|
6790 | visitLiteralMap(ast, context) {
|
6791 | this.visitAll(ast.values, context);
|
6792 | }
|
6793 | visitLiteralPrimitive(ast, context) { }
|
6794 | visitMethodCall(ast, context) {
|
6795 | this.visit(ast.receiver, context);
|
6796 | this.visitAll(ast.args, context);
|
6797 | }
|
6798 | visitPrefixNot(ast, context) {
|
6799 | this.visit(ast.expression, context);
|
6800 | }
|
6801 | visitNonNullAssert(ast, context) {
|
6802 | this.visit(ast.expression, context);
|
6803 | }
|
6804 | visitPropertyRead(ast, context) {
|
6805 | this.visit(ast.receiver, context);
|
6806 | }
|
6807 | visitPropertyWrite(ast, context) {
|
6808 | this.visit(ast.receiver, context);
|
6809 | this.visit(ast.value, context);
|
6810 | }
|
6811 | visitSafePropertyRead(ast, context) {
|
6812 | this.visit(ast.receiver, context);
|
6813 | }
|
6814 | visitSafeMethodCall(ast, context) {
|
6815 | this.visit(ast.receiver, context);
|
6816 | this.visitAll(ast.args, context);
|
6817 | }
|
6818 | visitQuote(ast, context) { }
|
6819 | // This is not part of the AstVisitor interface, just a helper method
|
6820 | visitAll(asts, context) {
|
6821 | for (const ast of asts) {
|
6822 | this.visit(ast, context);
|
6823 | }
|
6824 | }
|
6825 | }
|
6826 | class AstTransformer {
|
6827 | visitImplicitReceiver(ast, context) {
|
6828 | return ast;
|
6829 | }
|
6830 | visitThisReceiver(ast, context) {
|
6831 | return ast;
|
6832 | }
|
6833 | visitInterpolation(ast, context) {
|
6834 | return new Interpolation(ast.span, ast.sourceSpan, ast.strings, this.visitAll(ast.expressions));
|
6835 | }
|
6836 | visitLiteralPrimitive(ast, context) {
|
6837 | return new LiteralPrimitive(ast.span, ast.sourceSpan, ast.value);
|
6838 | }
|
6839 | visitPropertyRead(ast, context) {
|
6840 | return new PropertyRead(ast.span, ast.sourceSpan, ast.nameSpan, ast.receiver.visit(this), ast.name);
|
6841 | }
|
6842 | visitPropertyWrite(ast, context) {
|
6843 | return new PropertyWrite(ast.span, ast.sourceSpan, ast.nameSpan, ast.receiver.visit(this), ast.name, ast.value.visit(this));
|
6844 | }
|
6845 | visitSafePropertyRead(ast, context) {
|
6846 | return new SafePropertyRead(ast.span, ast.sourceSpan, ast.nameSpan, ast.receiver.visit(this), ast.name);
|
6847 | }
|
6848 | visitMethodCall(ast, context) {
|
6849 | return new MethodCall(ast.span, ast.sourceSpan, ast.nameSpan, ast.receiver.visit(this), ast.name, this.visitAll(ast.args));
|
6850 | }
|
6851 | visitSafeMethodCall(ast, context) {
|
6852 | return new SafeMethodCall(ast.span, ast.sourceSpan, ast.nameSpan, ast.receiver.visit(this), ast.name, this.visitAll(ast.args));
|
6853 | }
|
6854 | visitFunctionCall(ast, context) {
|
6855 | return new FunctionCall(ast.span, ast.sourceSpan, ast.target.visit(this), this.visitAll(ast.args));
|
6856 | }
|
6857 | visitLiteralArray(ast, context) {
|
6858 | return new LiteralArray(ast.span, ast.sourceSpan, this.visitAll(ast.expressions));
|
6859 | }
|
6860 | visitLiteralMap(ast, context) {
|
6861 | return new LiteralMap(ast.span, ast.sourceSpan, ast.keys, this.visitAll(ast.values));
|
6862 | }
|
6863 | visitUnary(ast, context) {
|
6864 | switch (ast.operator) {
|
6865 | case '+':
|
6866 | return Unary.createPlus(ast.span, ast.sourceSpan, ast.expr.visit(this));
|
6867 | case '-':
|
6868 | return Unary.createMinus(ast.span, ast.sourceSpan, ast.expr.visit(this));
|
6869 | default:
|
6870 | throw new Error(`Unknown unary operator ${ast.operator}`);
|
6871 | }
|
6872 | }
|
6873 | visitBinary(ast, context) {
|
6874 | return new Binary(ast.span, ast.sourceSpan, ast.operation, ast.left.visit(this), ast.right.visit(this));
|
6875 | }
|
6876 | visitPrefixNot(ast, context) {
|
6877 | return new PrefixNot(ast.span, ast.sourceSpan, ast.expression.visit(this));
|
6878 | }
|
6879 | visitNonNullAssert(ast, context) {
|
6880 | return new NonNullAssert(ast.span, ast.sourceSpan, ast.expression.visit(this));
|
6881 | }
|
6882 | visitConditional(ast, context) {
|
6883 | return new Conditional(ast.span, ast.sourceSpan, ast.condition.visit(this), ast.trueExp.visit(this), ast.falseExp.visit(this));
|
6884 | }
|
6885 | visitPipe(ast, context) {
|
6886 | return new BindingPipe(ast.span, ast.sourceSpan, ast.exp.visit(this), ast.name, this.visitAll(ast.args), ast.nameSpan);
|
6887 | }
|
6888 | visitKeyedRead(ast, context) {
|
6889 | return new KeyedRead(ast.span, ast.sourceSpan, ast.obj.visit(this), ast.key.visit(this));
|
6890 | }
|
6891 | visitKeyedWrite(ast, context) {
|
6892 | return new KeyedWrite(ast.span, ast.sourceSpan, ast.obj.visit(this), ast.key.visit(this), ast.value.visit(this));
|
6893 | }
|
6894 | visitAll(asts) {
|
6895 | const res = [];
|
6896 | for (let i = 0; i < asts.length; ++i) {
|
6897 | res[i] = asts[i].visit(this);
|
6898 | }
|
6899 | return res;
|
6900 | }
|
6901 | visitChain(ast, context) {
|
6902 | return new Chain(ast.span, ast.sourceSpan, this.visitAll(ast.expressions));
|
6903 | }
|
6904 | visitQuote(ast, context) {
|
6905 | return new Quote(ast.span, ast.sourceSpan, ast.prefix, ast.uninterpretedExpression, ast.location);
|
6906 | }
|
6907 | }
|
6908 | // A transformer that only creates new nodes if the transformer makes a change or
|
6909 | // a change is made a child node.
|
6910 | class AstMemoryEfficientTransformer {
|
6911 | visitImplicitReceiver(ast, context) {
|
6912 | return ast;
|
6913 | }
|
6914 | visitThisReceiver(ast, context) {
|
6915 | return ast;
|
6916 | }
|
6917 | visitInterpolation(ast, context) {
|
6918 | const expressions = this.visitAll(ast.expressions);
|
6919 | if (expressions !== ast.expressions)
|
6920 | return new Interpolation(ast.span, ast.sourceSpan, ast.strings, expressions);
|
6921 | return ast;
|
6922 | }
|
6923 | visitLiteralPrimitive(ast, context) {
|
6924 | return ast;
|
6925 | }
|
6926 | visitPropertyRead(ast, context) {
|
6927 | const receiver = ast.receiver.visit(this);
|
6928 | if (receiver !== ast.receiver) {
|
6929 | return new PropertyRead(ast.span, ast.sourceSpan, ast.nameSpan, receiver, ast.name);
|
6930 | }
|
6931 | return ast;
|
6932 | }
|
6933 | visitPropertyWrite(ast, context) {
|
6934 | const receiver = ast.receiver.visit(this);
|
6935 | const value = ast.value.visit(this);
|
6936 | if (receiver !== ast.receiver || value !== ast.value) {
|
6937 | return new PropertyWrite(ast.span, ast.sourceSpan, ast.nameSpan, receiver, ast.name, value);
|
6938 | }
|
6939 | return ast;
|
6940 | }
|
6941 | visitSafePropertyRead(ast, context) {
|
6942 | const receiver = ast.receiver.visit(this);
|
6943 | if (receiver !== ast.receiver) {
|
6944 | return new SafePropertyRead(ast.span, ast.sourceSpan, ast.nameSpan, receiver, ast.name);
|
6945 | }
|
6946 | return ast;
|
6947 | }
|
6948 | visitMethodCall(ast, context) {
|
6949 | const receiver = ast.receiver.visit(this);
|
6950 | const args = this.visitAll(ast.args);
|
6951 | if (receiver !== ast.receiver || args !== ast.args) {
|
6952 | return new MethodCall(ast.span, ast.sourceSpan, ast.nameSpan, receiver, ast.name, args);
|
6953 | }
|
6954 | return ast;
|
6955 | }
|
6956 | visitSafeMethodCall(ast, context) {
|
6957 | const receiver = ast.receiver.visit(this);
|
6958 | const args = this.visitAll(ast.args);
|
6959 | if (receiver !== ast.receiver || args !== ast.args) {
|
6960 | return new SafeMethodCall(ast.span, ast.sourceSpan, ast.nameSpan, receiver, ast.name, args);
|
6961 | }
|
6962 | return ast;
|
6963 | }
|
6964 | visitFunctionCall(ast, context) {
|
6965 | const target = ast.target && ast.target.visit(this);
|
6966 | const args = this.visitAll(ast.args);
|
6967 | if (target !== ast.target || args !== ast.args) {
|
6968 | return new FunctionCall(ast.span, ast.sourceSpan, target, args);
|
6969 | }
|
6970 | return ast;
|
6971 | }
|
6972 | visitLiteralArray(ast, context) {
|
6973 | const expressions = this.visitAll(ast.expressions);
|
6974 | if (expressions !== ast.expressions) {
|
6975 | return new LiteralArray(ast.span, ast.sourceSpan, expressions);
|
6976 | }
|
6977 | return ast;
|
6978 | }
|
6979 | visitLiteralMap(ast, context) {
|
6980 | const values = this.visitAll(ast.values);
|
6981 | if (values !== ast.values) {
|
6982 | return new LiteralMap(ast.span, ast.sourceSpan, ast.keys, values);
|
6983 | }
|
6984 | return ast;
|
6985 | }
|
6986 | visitUnary(ast, context) {
|
6987 | const expr = ast.expr.visit(this);
|
6988 | if (expr !== ast.expr) {
|
6989 | switch (ast.operator) {
|
6990 | case '+':
|
6991 | return Unary.createPlus(ast.span, ast.sourceSpan, expr);
|
6992 | case '-':
|
6993 | return Unary.createMinus(ast.span, ast.sourceSpan, expr);
|
6994 | default:
|
6995 | throw new Error(`Unknown unary operator ${ast.operator}`);
|
6996 | }
|
6997 | }
|
6998 | return ast;
|
6999 | }
|
7000 | visitBinary(ast, context) {
|
7001 | const left = ast.left.visit(this);
|
7002 | const right = ast.right.visit(this);
|
7003 | if (left !== ast.left || right !== ast.right) {
|
7004 | return new Binary(ast.span, ast.sourceSpan, ast.operation, left, right);
|
7005 | }
|
7006 | return ast;
|
7007 | }
|
7008 | visitPrefixNot(ast, context) {
|
7009 | const expression = ast.expression.visit(this);
|
7010 | if (expression !== ast.expression) {
|
7011 | return new PrefixNot(ast.span, ast.sourceSpan, expression);
|
7012 | }
|
7013 | return ast;
|
7014 | }
|
7015 | visitNonNullAssert(ast, context) {
|
7016 | const expression = ast.expression.visit(this);
|
7017 | if (expression !== ast.expression) {
|
7018 | return new NonNullAssert(ast.span, ast.sourceSpan, expression);
|
7019 | }
|
7020 | return ast;
|
7021 | }
|
7022 | visitConditional(ast, context) {
|
7023 | const condition = ast.condition.visit(this);
|
7024 | const trueExp = ast.trueExp.visit(this);
|
7025 | const falseExp = ast.falseExp.visit(this);
|
7026 | if (condition !== ast.condition || trueExp !== ast.trueExp || falseExp !== ast.falseExp) {
|
7027 | return new Conditional(ast.span, ast.sourceSpan, condition, trueExp, falseExp);
|
7028 | }
|
7029 | return ast;
|
7030 | }
|
7031 | visitPipe(ast, context) {
|
7032 | const exp = ast.exp.visit(this);
|
7033 | const args = this.visitAll(ast.args);
|
7034 | if (exp !== ast.exp || args !== ast.args) {
|
7035 | return new BindingPipe(ast.span, ast.sourceSpan, exp, ast.name, args, ast.nameSpan);
|
7036 | }
|
7037 | return ast;
|
7038 | }
|
7039 | visitKeyedRead(ast, context) {
|
7040 | const obj = ast.obj.visit(this);
|
7041 | const key = ast.key.visit(this);
|
7042 | if (obj !== ast.obj || key !== ast.key) {
|
7043 | return new KeyedRead(ast.span, ast.sourceSpan, obj, key);
|
7044 | }
|
7045 | return ast;
|
7046 | }
|
7047 | visitKeyedWrite(ast, context) {
|
7048 | const obj = ast.obj.visit(this);
|
7049 | const key = ast.key.visit(this);
|
7050 | const value = ast.value.visit(this);
|
7051 | if (obj !== ast.obj || key !== ast.key || value !== ast.value) {
|
7052 | return new KeyedWrite(ast.span, ast.sourceSpan, obj, key, value);
|
7053 | }
|
7054 | return ast;
|
7055 | }
|
7056 | visitAll(asts) {
|
7057 | const res = [];
|
7058 | let modified = false;
|
7059 | for (let i = 0; i < asts.length; ++i) {
|
7060 | const original = asts[i];
|
7061 | const value = original.visit(this);
|
7062 | res[i] = value;
|
7063 | modified = modified || value !== original;
|
7064 | }
|
7065 | return modified ? res : asts;
|
7066 | }
|
7067 | visitChain(ast, context) {
|
7068 | const expressions = this.visitAll(ast.expressions);
|
7069 | if (expressions !== ast.expressions) {
|
7070 | return new Chain(ast.span, ast.sourceSpan, expressions);
|
7071 | }
|
7072 | return ast;
|
7073 | }
|
7074 | visitQuote(ast, context) {
|
7075 | return ast;
|
7076 | }
|
7077 | }
|
7078 | // Bindings
|
7079 | class ParsedProperty {
|
7080 | constructor(name, expression, type,
|
7081 | // TODO(FW-2095): `keySpan` should really be required but allows `undefined` so VE does
|
7082 | // not need to be updated. Make `keySpan` required when VE is removed.
|
7083 | sourceSpan, keySpan, valueSpan) {
|
7084 | this.name = name;
|
7085 | this.expression = expression;
|
7086 | this.type = type;
|
7087 | this.sourceSpan = sourceSpan;
|
7088 | this.keySpan = keySpan;
|
7089 | this.valueSpan = valueSpan;
|
7090 | this.isLiteral = this.type === ParsedPropertyType.LITERAL_ATTR;
|
7091 | this.isAnimation = this.type === ParsedPropertyType.ANIMATION;
|
7092 | }
|
7093 | }
|
7094 | var ParsedPropertyType;
|
7095 | (function (ParsedPropertyType) {
|
7096 | ParsedPropertyType[ParsedPropertyType["DEFAULT"] = 0] = "DEFAULT";
|
7097 | ParsedPropertyType[ParsedPropertyType["LITERAL_ATTR"] = 1] = "LITERAL_ATTR";
|
7098 | ParsedPropertyType[ParsedPropertyType["ANIMATION"] = 2] = "ANIMATION";
|
7099 | })(ParsedPropertyType || (ParsedPropertyType = {}));
|
7100 | class ParsedEvent {
|
7101 | // Regular events have a target
|
7102 | // Animation events have a phase
|
7103 | constructor(name, targetOrPhase, type, handler, sourceSpan,
|
7104 | // TODO(FW-2095): keySpan should be required but was made optional to avoid changing VE
|
7105 | handlerSpan, keySpan) {
|
7106 | this.name = name;
|
7107 | this.targetOrPhase = targetOrPhase;
|
7108 | this.type = type;
|
7109 | this.handler = handler;
|
7110 | this.sourceSpan = sourceSpan;
|
7111 | this.handlerSpan = handlerSpan;
|
7112 | this.keySpan = keySpan;
|
7113 | }
|
7114 | }
|
7115 | /**
|
7116 | * ParsedVariable represents a variable declaration in a microsyntax expression.
|
7117 | */
|
7118 | class ParsedVariable {
|
7119 | constructor(name, value, sourceSpan, keySpan, valueSpan) {
|
7120 | this.name = name;
|
7121 | this.value = value;
|
7122 | this.sourceSpan = sourceSpan;
|
7123 | this.keySpan = keySpan;
|
7124 | this.valueSpan = valueSpan;
|
7125 | }
|
7126 | }
|
7127 | class BoundElementProperty {
|
7128 | constructor(name, type, securityContext, value, unit, sourceSpan, keySpan, valueSpan) {
|
7129 | this.name = name;
|
7130 | this.type = type;
|
7131 | this.securityContext = securityContext;
|
7132 | this.value = value;
|
7133 | this.unit = unit;
|
7134 | this.sourceSpan = sourceSpan;
|
7135 | this.keySpan = keySpan;
|
7136 | this.valueSpan = valueSpan;
|
7137 | }
|
7138 | }
|
7139 |
|
7140 | /**
|
7141 | * @license
|
7142 | * Copyright Google LLC All Rights Reserved.
|
7143 | *
|
7144 | * Use of this source code is governed by an MIT-style license that can be
|
7145 | * found in the LICENSE file at https://angular.io/license
|
7146 | */
|
7147 | class EventHandlerVars {
|
7148 | }
|
7149 | EventHandlerVars.event = variable('$event');
|
7150 | class ConvertActionBindingResult {
|
7151 | constructor(
|
7152 | /**
|
7153 | * Render2 compatible statements,
|
7154 | */
|
7155 | stmts,
|
7156 | /**
|
7157 | * Variable name used with render2 compatible statements.
|
7158 | */
|
7159 | allowDefault) {
|
7160 | this.stmts = stmts;
|
7161 | this.allowDefault = allowDefault;
|
7162 | /**
|
7163 | * This is bit of a hack. It converts statements which render2 expects to statements which are
|
7164 | * expected by render3.
|
7165 | *
|
7166 | * Example: `<div click="doSomething($event)">` will generate:
|
7167 | *
|
7168 | * Render3:
|
7169 | * ```
|
7170 | * const pd_b:any = ((<any>ctx.doSomething($event)) !== false);
|
7171 | * return pd_b;
|
7172 | * ```
|
7173 | *
|
7174 | * but render2 expects:
|
7175 | * ```
|
7176 | * return ctx.doSomething($event);
|
7177 | * ```
|
7178 | */
|
7179 | // TODO(misko): remove this hack once we no longer support ViewEngine.
|
7180 | this.render3Stmts = stmts.map((statement) => {
|
7181 | if (statement instanceof DeclareVarStmt && statement.name == allowDefault.name &&
|
7182 | statement.value instanceof BinaryOperatorExpr) {
|
7183 | const lhs = statement.value.lhs;
|
7184 | return new ReturnStatement(lhs.value);
|
7185 | }
|
7186 | return statement;
|
7187 | });
|
7188 | }
|
7189 | }
|
7190 | /**
|
7191 | * Converts the given expression AST into an executable output AST, assuming the expression is
|
7192 | * used in an action binding (e.g. an event handler).
|
7193 | */
|
7194 | function convertActionBinding(localResolver, implicitReceiver, action, bindingId, interpolationFunction, baseSourceSpan, implicitReceiverAccesses, globals) {
|
7195 | if (!localResolver) {
|
7196 | localResolver = new DefaultLocalResolver(globals);
|
7197 | }
|
7198 | const actionWithoutBuiltins = convertPropertyBindingBuiltins({
|
7199 | createLiteralArrayConverter: (argCount) => {
|
7200 | // Note: no caching for literal arrays in actions.
|
7201 | return (args) => literalArr(args);
|
7202 | },
|
7203 | createLiteralMapConverter: (keys) => {
|
7204 | // Note: no caching for literal maps in actions.
|
7205 | return (values) => {
|
7206 | const entries = keys.map((k, i) => ({
|
7207 | key: k.key,
|
7208 | value: values[i],
|
7209 | quoted: k.quoted,
|
7210 | }));
|
7211 | return literalMap(entries);
|
7212 | };
|
7213 | },
|
7214 | createPipeConverter: (name) => {
|
7215 | throw new Error(`Illegal State: Actions are not allowed to contain pipes. Pipe: ${name}`);
|
7216 | }
|
7217 | }, action);
|
7218 | const visitor = new _AstToIrVisitor(localResolver, implicitReceiver, bindingId, interpolationFunction, baseSourceSpan, implicitReceiverAccesses);
|
7219 | const actionStmts = [];
|
7220 | flattenStatements(actionWithoutBuiltins.visit(visitor, _Mode.Statement), actionStmts);
|
7221 | prependTemporaryDecls(visitor.temporaryCount, bindingId, actionStmts);
|
7222 | if (visitor.usesImplicitReceiver) {
|
7223 | localResolver.notifyImplicitReceiverUse();
|
7224 | }
|
7225 | const lastIndex = actionStmts.length - 1;
|
7226 | let preventDefaultVar = null;
|
7227 | if (lastIndex >= 0) {
|
7228 | const lastStatement = actionStmts[lastIndex];
|
7229 | const returnExpr = convertStmtIntoExpression(lastStatement);
|
7230 | if (returnExpr) {
|
7231 | // Note: We need to cast the result of the method call to dynamic,
|
7232 | // as it might be a void method!
|
7233 | preventDefaultVar = createPreventDefaultVar(bindingId);
|
7234 | actionStmts[lastIndex] =
|
7235 | preventDefaultVar.set(returnExpr.cast(DYNAMIC_TYPE).notIdentical(literal(false)))
|
7236 | .toDeclStmt(null, [StmtModifier.Final]);
|
7237 | }
|
7238 | }
|
7239 | return new ConvertActionBindingResult(actionStmts, preventDefaultVar);
|
7240 | }
|
7241 | function convertPropertyBindingBuiltins(converterFactory, ast) {
|
7242 | return convertBuiltins(converterFactory, ast);
|
7243 | }
|
7244 | class ConvertPropertyBindingResult {
|
7245 | constructor(stmts, currValExpr) {
|
7246 | this.stmts = stmts;
|
7247 | this.currValExpr = currValExpr;
|
7248 | }
|
7249 | }
|
7250 | var BindingForm;
|
7251 | (function (BindingForm) {
|
7252 | // The general form of binding expression, supports all expressions.
|
7253 | BindingForm[BindingForm["General"] = 0] = "General";
|
7254 | // Try to generate a simple binding (no temporaries or statements)
|
7255 | // otherwise generate a general binding
|
7256 | BindingForm[BindingForm["TrySimple"] = 1] = "TrySimple";
|
7257 | // Inlines assignment of temporaries into the generated expression. The result may still
|
7258 | // have statements attached for declarations of temporary variables.
|
7259 | // This is the only relevant form for Ivy, the other forms are only used in ViewEngine.
|
7260 | BindingForm[BindingForm["Expression"] = 2] = "Expression";
|
7261 | })(BindingForm || (BindingForm = {}));
|
7262 | /**
|
7263 | * Converts the given expression AST into an executable output AST, assuming the expression
|
7264 | * is used in property binding. The expression has to be preprocessed via
|
7265 | * `convertPropertyBindingBuiltins`.
|
7266 | */
|
7267 | function convertPropertyBinding(localResolver, implicitReceiver, expressionWithoutBuiltins, bindingId, form, interpolationFunction) {
|
7268 | if (!localResolver) {
|
7269 | localResolver = new DefaultLocalResolver();
|
7270 | }
|
7271 | const visitor = new _AstToIrVisitor(localResolver, implicitReceiver, bindingId, interpolationFunction);
|
7272 | const outputExpr = expressionWithoutBuiltins.visit(visitor, _Mode.Expression);
|
7273 | const stmts = getStatementsFromVisitor(visitor, bindingId);
|
7274 | if (visitor.usesImplicitReceiver) {
|
7275 | localResolver.notifyImplicitReceiverUse();
|
7276 | }
|
7277 | if (visitor.temporaryCount === 0 && form == BindingForm.TrySimple) {
|
7278 | return new ConvertPropertyBindingResult([], outputExpr);
|
7279 | }
|
7280 | else if (form === BindingForm.Expression) {
|
7281 | return new ConvertPropertyBindingResult(stmts, outputExpr);
|
7282 | }
|
7283 | const currValExpr = createCurrValueExpr(bindingId);
|
7284 | stmts.push(currValExpr.set(outputExpr).toDeclStmt(DYNAMIC_TYPE, [StmtModifier.Final]));
|
7285 | return new ConvertPropertyBindingResult(stmts, currValExpr);
|
7286 | }
|
7287 | /**
|
7288 | * Given some expression, such as a binding or interpolation expression, and a context expression to
|
7289 | * look values up on, visit each facet of the given expression resolving values from the context
|
7290 | * expression such that a list of arguments can be derived from the found values that can be used as
|
7291 | * arguments to an external update instruction.
|
7292 | *
|
7293 | * @param localResolver The resolver to use to look up expressions by name appropriately
|
7294 | * @param contextVariableExpression The expression representing the context variable used to create
|
7295 | * the final argument expressions
|
7296 | * @param expressionWithArgumentsToExtract The expression to visit to figure out what values need to
|
7297 | * be resolved and what arguments list to build.
|
7298 | * @param bindingId A name prefix used to create temporary variable names if they're needed for the
|
7299 | * arguments generated
|
7300 | * @returns An array of expressions that can be passed as arguments to instruction expressions like
|
7301 | * `o.importExpr(R3.propertyInterpolate).callFn(result)`
|
7302 | */
|
7303 | function convertUpdateArguments(localResolver, contextVariableExpression, expressionWithArgumentsToExtract, bindingId) {
|
7304 | const visitor = new _AstToIrVisitor(localResolver, contextVariableExpression, bindingId, undefined);
|
7305 | const outputExpr = expressionWithArgumentsToExtract.visit(visitor, _Mode.Expression);
|
7306 | if (visitor.usesImplicitReceiver) {
|
7307 | localResolver.notifyImplicitReceiverUse();
|
7308 | }
|
7309 | const stmts = getStatementsFromVisitor(visitor, bindingId);
|
7310 | // Removing the first argument, because it was a length for ViewEngine, not Ivy.
|
7311 | let args = outputExpr.args.slice(1);
|
7312 | if (expressionWithArgumentsToExtract instanceof Interpolation) {
|
7313 | // If we're dealing with an interpolation of 1 value with an empty prefix and suffix, reduce the
|
7314 | // args returned to just the value, because we're going to pass it to a special instruction.
|
7315 | const strings = expressionWithArgumentsToExtract.strings;
|
7316 | if (args.length === 3 && strings[0] === '' && strings[1] === '') {
|
7317 | // Single argument interpolate instructions.
|
7318 | args = [args[1]];
|
7319 | }
|
7320 | else if (args.length >= 19) {
|
7321 | // 19 or more arguments must be passed to the `interpolateV`-style instructions, which accept
|
7322 | // an array of arguments
|
7323 | args = [literalArr(args)];
|
7324 | }
|
7325 | }
|
7326 | return { stmts, args };
|
7327 | }
|
7328 | function getStatementsFromVisitor(visitor, bindingId) {
|
7329 | const stmts = [];
|
7330 | for (let i = 0; i < visitor.temporaryCount; i++) {
|
7331 | stmts.push(temporaryDeclaration(bindingId, i));
|
7332 | }
|
7333 | return stmts;
|
7334 | }
|
7335 | function convertBuiltins(converterFactory, ast) {
|
7336 | const visitor = new _BuiltinAstConverter(converterFactory);
|
7337 | return ast.visit(visitor);
|
7338 | }
|
7339 | function temporaryName(bindingId, temporaryNumber) {
|
7340 | return `tmp_${bindingId}_${temporaryNumber}`;
|
7341 | }
|
7342 | function temporaryDeclaration(bindingId, temporaryNumber) {
|
7343 | return new DeclareVarStmt(temporaryName(bindingId, temporaryNumber), NULL_EXPR);
|
7344 | }
|
7345 | function prependTemporaryDecls(temporaryCount, bindingId, statements) {
|
7346 | for (let i = temporaryCount - 1; i >= 0; i--) {
|
7347 | statements.unshift(temporaryDeclaration(bindingId, i));
|
7348 | }
|
7349 | }
|
7350 | var _Mode;
|
7351 | (function (_Mode) {
|
7352 | _Mode[_Mode["Statement"] = 0] = "Statement";
|
7353 | _Mode[_Mode["Expression"] = 1] = "Expression";
|
7354 | })(_Mode || (_Mode = {}));
|
7355 | function ensureStatementMode(mode, ast) {
|
7356 | if (mode !== _Mode.Statement) {
|
7357 | throw new Error(`Expected a statement, but saw ${ast}`);
|
7358 | }
|
7359 | }
|
7360 | function ensureExpressionMode(mode, ast) {
|
7361 | if (mode !== _Mode.Expression) {
|
7362 | throw new Error(`Expected an expression, but saw ${ast}`);
|
7363 | }
|
7364 | }
|
7365 | function convertToStatementIfNeeded(mode, expr) {
|
7366 | if (mode === _Mode.Statement) {
|
7367 | return expr.toStmt();
|
7368 | }
|
7369 | else {
|
7370 | return expr;
|
7371 | }
|
7372 | }
|
7373 | class _BuiltinAstConverter extends AstTransformer {
|
7374 | constructor(_converterFactory) {
|
7375 | super();
|
7376 | this._converterFactory = _converterFactory;
|
7377 | }
|
7378 | visitPipe(ast, context) {
|
7379 | const args = [ast.exp, ...ast.args].map(ast => ast.visit(this, context));
|
7380 | return new BuiltinFunctionCall(ast.span, ast.sourceSpan, args, this._converterFactory.createPipeConverter(ast.name, args.length));
|
7381 | }
|
7382 | visitLiteralArray(ast, context) {
|
7383 | const args = ast.expressions.map(ast => ast.visit(this, context));
|
7384 | return new BuiltinFunctionCall(ast.span, ast.sourceSpan, args, this._converterFactory.createLiteralArrayConverter(ast.expressions.length));
|
7385 | }
|
7386 | visitLiteralMap(ast, context) {
|
7387 | const args = ast.values.map(ast => ast.visit(this, context));
|
7388 | return new BuiltinFunctionCall(ast.span, ast.sourceSpan, args, this._converterFactory.createLiteralMapConverter(ast.keys));
|
7389 | }
|
7390 | }
|
7391 | class _AstToIrVisitor {
|
7392 | constructor(_localResolver, _implicitReceiver, bindingId, interpolationFunction, baseSourceSpan, implicitReceiverAccesses) {
|
7393 | this._localResolver = _localResolver;
|
7394 | this._implicitReceiver = _implicitReceiver;
|
7395 | this.bindingId = bindingId;
|
7396 | this.interpolationFunction = interpolationFunction;
|
7397 | this.baseSourceSpan = baseSourceSpan;
|
7398 | this.implicitReceiverAccesses = implicitReceiverAccesses;
|
7399 | this._nodeMap = new Map();
|
7400 | this._resultMap = new Map();
|
7401 | this._currentTemporary = 0;
|
7402 | this.temporaryCount = 0;
|
7403 | this.usesImplicitReceiver = false;
|
7404 | }
|
7405 | visitUnary(ast, mode) {
|
7406 | let op;
|
7407 | switch (ast.operator) {
|
7408 | case '+':
|
7409 | op = UnaryOperator.Plus;
|
7410 | break;
|
7411 | case '-':
|
7412 | op = UnaryOperator.Minus;
|
7413 | break;
|
7414 | default:
|
7415 | throw new Error(`Unsupported operator ${ast.operator}`);
|
7416 | }
|
7417 | return convertToStatementIfNeeded(mode, new UnaryOperatorExpr(op, this._visit(ast.expr, _Mode.Expression), undefined, this.convertSourceSpan(ast.span)));
|
7418 | }
|
7419 | visitBinary(ast, mode) {
|
7420 | let op;
|
7421 | switch (ast.operation) {
|
7422 | case '+':
|
7423 | op = BinaryOperator.Plus;
|
7424 | break;
|
7425 | case '-':
|
7426 | op = BinaryOperator.Minus;
|
7427 | break;
|
7428 | case '*':
|
7429 | op = BinaryOperator.Multiply;
|
7430 | break;
|
7431 | case '/':
|
7432 | op = BinaryOperator.Divide;
|
7433 | break;
|
7434 | case '%':
|
7435 | op = BinaryOperator.Modulo;
|
7436 | break;
|
7437 | case '&&':
|
7438 | op = BinaryOperator.And;
|
7439 | break;
|
7440 | case '||':
|
7441 | op = BinaryOperator.Or;
|
7442 | break;
|
7443 | case '==':
|
7444 | op = BinaryOperator.Equals;
|
7445 | break;
|
7446 | case '!=':
|
7447 | op = BinaryOperator.NotEquals;
|
7448 | break;
|
7449 | case '===':
|
7450 | op = BinaryOperator.Identical;
|
7451 | break;
|
7452 | case '!==':
|
7453 | op = BinaryOperator.NotIdentical;
|
7454 | break;
|
7455 | case '<':
|
7456 | op = BinaryOperator.Lower;
|
7457 | break;
|
7458 | case '>':
|
7459 | op = BinaryOperator.Bigger;
|
7460 | break;
|
7461 | case '<=':
|
7462 | op = BinaryOperator.LowerEquals;
|
7463 | break;
|
7464 | case '>=':
|
7465 | op = BinaryOperator.BiggerEquals;
|
7466 | break;
|
7467 | default:
|
7468 | throw new Error(`Unsupported operation ${ast.operation}`);
|
7469 | }
|
7470 | return convertToStatementIfNeeded(mode, new BinaryOperatorExpr(op, this._visit(ast.left, _Mode.Expression), this._visit(ast.right, _Mode.Expression), undefined, this.convertSourceSpan(ast.span)));
|
7471 | }
|
7472 | visitChain(ast, mode) {
|
7473 | ensureStatementMode(mode, ast);
|
7474 | return this.visitAll(ast.expressions, mode);
|
7475 | }
|
7476 | visitConditional(ast, mode) {
|
7477 | const value = this._visit(ast.condition, _Mode.Expression);
|
7478 | return convertToStatementIfNeeded(mode, value.conditional(this._visit(ast.trueExp, _Mode.Expression), this._visit(ast.falseExp, _Mode.Expression), this.convertSourceSpan(ast.span)));
|
7479 | }
|
7480 | visitPipe(ast, mode) {
|
7481 | throw new Error(`Illegal state: Pipes should have been converted into functions. Pipe: ${ast.name}`);
|
7482 | }
|
7483 | visitFunctionCall(ast, mode) {
|
7484 | const convertedArgs = this.visitAll(ast.args, _Mode.Expression);
|
7485 | let fnResult;
|
7486 | if (ast instanceof BuiltinFunctionCall) {
|
7487 | fnResult = ast.converter(convertedArgs);
|
7488 | }
|
7489 | else {
|
7490 | fnResult = this._visit(ast.target, _Mode.Expression)
|
7491 | .callFn(convertedArgs, this.convertSourceSpan(ast.span));
|
7492 | }
|
7493 | return convertToStatementIfNeeded(mode, fnResult);
|
7494 | }
|
7495 | visitImplicitReceiver(ast, mode) {
|
7496 | ensureExpressionMode(mode, ast);
|
7497 | this.usesImplicitReceiver = true;
|
7498 | return this._implicitReceiver;
|
7499 | }
|
7500 | visitThisReceiver(ast, mode) {
|
7501 | return this.visitImplicitReceiver(ast, mode);
|
7502 | }
|
7503 | visitInterpolation(ast, mode) {
|
7504 | ensureExpressionMode(mode, ast);
|
7505 | const args = [literal(ast.expressions.length)];
|
7506 | for (let i = 0; i < ast.strings.length - 1; i++) {
|
7507 | args.push(literal(ast.strings[i]));
|
7508 | args.push(this._visit(ast.expressions[i], _Mode.Expression));
|
7509 | }
|
7510 | args.push(literal(ast.strings[ast.strings.length - 1]));
|
7511 | if (this.interpolationFunction) {
|
7512 | return this.interpolationFunction(args);
|
7513 | }
|
7514 | return ast.expressions.length <= 9 ?
|
7515 | importExpr(Identifiers.inlineInterpolate).callFn(args) :
|
7516 | importExpr(Identifiers.interpolate).callFn([
|
7517 | args[0], literalArr(args.slice(1), undefined, this.convertSourceSpan(ast.span))
|
7518 | ]);
|
7519 | }
|
7520 | visitKeyedRead(ast, mode) {
|
7521 | const leftMostSafe = this.leftMostSafeNode(ast);
|
7522 | if (leftMostSafe) {
|
7523 | return this.convertSafeAccess(ast, leftMostSafe, mode);
|
7524 | }
|
7525 | else {
|
7526 | return convertToStatementIfNeeded(mode, this._visit(ast.obj, _Mode.Expression).key(this._visit(ast.key, _Mode.Expression)));
|
7527 | }
|
7528 | }
|
7529 | visitKeyedWrite(ast, mode) {
|
7530 | const obj = this._visit(ast.obj, _Mode.Expression);
|
7531 | const key = this._visit(ast.key, _Mode.Expression);
|
7532 | const value = this._visit(ast.value, _Mode.Expression);
|
7533 | return convertToStatementIfNeeded(mode, obj.key(key).set(value));
|
7534 | }
|
7535 | visitLiteralArray(ast, mode) {
|
7536 | throw new Error(`Illegal State: literal arrays should have been converted into functions`);
|
7537 | }
|
7538 | visitLiteralMap(ast, mode) {
|
7539 | throw new Error(`Illegal State: literal maps should have been converted into functions`);
|
7540 | }
|
7541 | visitLiteralPrimitive(ast, mode) {
|
7542 | // For literal values of null, undefined, true, or false allow type interference
|
7543 | // to infer the type.
|
7544 | const type = ast.value === null || ast.value === undefined || ast.value === true || ast.value === true ?
|
7545 | INFERRED_TYPE :
|
7546 | undefined;
|
7547 | return convertToStatementIfNeeded(mode, literal(ast.value, type, this.convertSourceSpan(ast.span)));
|
7548 | }
|
7549 | _getLocal(name, receiver) {
|
7550 | var _a;
|
7551 | if (((_a = this._localResolver.globals) === null || _a === void 0 ? void 0 : _a.has(name)) && receiver instanceof ThisReceiver) {
|
7552 | return null;
|
7553 | }
|
7554 | return this._localResolver.getLocal(name);
|
7555 | }
|
7556 | visitMethodCall(ast, mode) {
|
7557 | if (ast.receiver instanceof ImplicitReceiver &&
|
7558 | !(ast.receiver instanceof ThisReceiver) && ast.name === '$any') {
|
7559 | const args = this.visitAll(ast.args, _Mode.Expression);
|
7560 | if (args.length != 1) {
|
7561 | throw new Error(`Invalid call to $any, expected 1 argument but received ${args.length || 'none'}`);
|
7562 | }
|
7563 | return args[0].cast(DYNAMIC_TYPE, this.convertSourceSpan(ast.span));
|
7564 | }
|
7565 | const leftMostSafe = this.leftMostSafeNode(ast);
|
7566 | if (leftMostSafe) {
|
7567 | return this.convertSafeAccess(ast, leftMostSafe, mode);
|
7568 | }
|
7569 | else {
|
7570 | const args = this.visitAll(ast.args, _Mode.Expression);
|
7571 | const prevUsesImplicitReceiver = this.usesImplicitReceiver;
|
7572 | let result = null;
|
7573 | const receiver = this._visit(ast.receiver, _Mode.Expression);
|
7574 | if (receiver === this._implicitReceiver) {
|
7575 | const varExpr = this._getLocal(ast.name, ast.receiver);
|
7576 | if (varExpr) {
|
7577 | // Restore the previous "usesImplicitReceiver" state since the implicit
|
7578 | // receiver has been replaced with a resolved local expression.
|
7579 | this.usesImplicitReceiver = prevUsesImplicitReceiver;
|
7580 | result = varExpr.callFn(args);
|
7581 | this.addImplicitReceiverAccess(ast.name);
|
7582 | }
|
7583 | }
|
7584 | if (result == null) {
|
7585 | result = receiver.callMethod(ast.name, args, this.convertSourceSpan(ast.span));
|
7586 | }
|
7587 | return convertToStatementIfNeeded(mode, result);
|
7588 | }
|
7589 | }
|
7590 | visitPrefixNot(ast, mode) {
|
7591 | return convertToStatementIfNeeded(mode, not(this._visit(ast.expression, _Mode.Expression)));
|
7592 | }
|
7593 | visitNonNullAssert(ast, mode) {
|
7594 | return convertToStatementIfNeeded(mode, assertNotNull(this._visit(ast.expression, _Mode.Expression)));
|
7595 | }
|
7596 | visitPropertyRead(ast, mode) {
|
7597 | const leftMostSafe = this.leftMostSafeNode(ast);
|
7598 | if (leftMostSafe) {
|
7599 | return this.convertSafeAccess(ast, leftMostSafe, mode);
|
7600 | }
|
7601 | else {
|
7602 | let result = null;
|
7603 | const prevUsesImplicitReceiver = this.usesImplicitReceiver;
|
7604 | const receiver = this._visit(ast.receiver, _Mode.Expression);
|
7605 | if (receiver === this._implicitReceiver) {
|
7606 | result = this._getLocal(ast.name, ast.receiver);
|
7607 | if (result) {
|
7608 | // Restore the previous "usesImplicitReceiver" state since the implicit
|
7609 | // receiver has been replaced with a resolved local expression.
|
7610 | this.usesImplicitReceiver = prevUsesImplicitReceiver;
|
7611 | this.addImplicitReceiverAccess(ast.name);
|
7612 | }
|
7613 | }
|
7614 | if (result == null) {
|
7615 | result = receiver.prop(ast.name);
|
7616 | }
|
7617 | return convertToStatementIfNeeded(mode, result);
|
7618 | }
|
7619 | }
|
7620 | visitPropertyWrite(ast, mode) {
|
7621 | const receiver = this._visit(ast.receiver, _Mode.Expression);
|
7622 | const prevUsesImplicitReceiver = this.usesImplicitReceiver;
|
7623 | let varExpr = null;
|
7624 | if (receiver === this._implicitReceiver) {
|
7625 | const localExpr = this._getLocal(ast.name, ast.receiver);
|
7626 | if (localExpr) {
|
7627 | if (localExpr instanceof ReadPropExpr) {
|
7628 | // If the local variable is a property read expression, it's a reference
|
7629 | // to a 'context.property' value and will be used as the target of the
|
7630 | // write expression.
|
7631 | varExpr = localExpr;
|
7632 | // Restore the previous "usesImplicitReceiver" state since the implicit
|
7633 | // receiver has been replaced with a resolved local expression.
|
7634 | this.usesImplicitReceiver = prevUsesImplicitReceiver;
|
7635 | this.addImplicitReceiverAccess(ast.name);
|
7636 | }
|
7637 | else {
|
7638 | // Otherwise it's an error.
|
7639 | const receiver = ast.name;
|
7640 | const value = (ast.value instanceof PropertyRead) ? ast.value.name : undefined;
|
7641 | throw new Error(`Cannot assign value "${value}" to template variable "${receiver}". Template variables are read-only.`);
|
7642 | }
|
7643 | }
|
7644 | }
|
7645 | // If no local expression could be produced, use the original receiver's
|
7646 | // property as the target.
|
7647 | if (varExpr === null) {
|
7648 | varExpr = receiver.prop(ast.name);
|
7649 | }
|
7650 | return convertToStatementIfNeeded(mode, varExpr.set(this._visit(ast.value, _Mode.Expression)));
|
7651 | }
|
7652 | visitSafePropertyRead(ast, mode) {
|
7653 | return this.convertSafeAccess(ast, this.leftMostSafeNode(ast), mode);
|
7654 | }
|
7655 | visitSafeMethodCall(ast, mode) {
|
7656 | return this.convertSafeAccess(ast, this.leftMostSafeNode(ast), mode);
|
7657 | }
|
7658 | visitAll(asts, mode) {
|
7659 | return asts.map(ast => this._visit(ast, mode));
|
7660 | }
|
7661 | visitQuote(ast, mode) {
|
7662 | throw new Error(`Quotes are not supported for evaluation!
|
7663 | Statement: ${ast.uninterpretedExpression} located at ${ast.location}`);
|
7664 | }
|
7665 | _visit(ast, mode) {
|
7666 | const result = this._resultMap.get(ast);
|
7667 | if (result)
|
7668 | return result;
|
7669 | return (this._nodeMap.get(ast) || ast).visit(this, mode);
|
7670 | }
|
7671 | convertSafeAccess(ast, leftMostSafe, mode) {
|
7672 | // If the expression contains a safe access node on the left it needs to be converted to
|
7673 | // an expression that guards the access to the member by checking the receiver for blank. As
|
7674 | // execution proceeds from left to right, the left most part of the expression must be guarded
|
7675 | // first but, because member access is left associative, the right side of the expression is at
|
7676 | // the top of the AST. The desired result requires lifting a copy of the left part of the
|
7677 | // expression up to test it for blank before generating the unguarded version.
|
7678 | // Consider, for example the following expression: a?.b.c?.d.e
|
7679 | // This results in the ast:
|
7680 | // .
|
7681 | // / \
|
7682 | // ?. e
|
7683 | // / \
|
7684 | // . d
|
7685 | // / \
|
7686 | // ?. c
|
7687 | // / \
|
7688 | // a b
|
7689 | // The following tree should be generated:
|
7690 | //
|
7691 | // /---- ? ----\
|
7692 | // / | \
|
7693 | // a /--- ? ---\ null
|
7694 | // / | \
|
7695 | // . . null
|
7696 | // / \ / \
|
7697 | // . c . e
|
7698 | // / \ / \
|
7699 | // a b . d
|
7700 | // / \
|
7701 | // . c
|
7702 | // / \
|
7703 | // a b
|
7704 | //
|
7705 | // Notice that the first guard condition is the left hand of the left most safe access node
|
7706 | // which comes in as leftMostSafe to this routine.
|
7707 | let guardedExpression = this._visit(leftMostSafe.receiver, _Mode.Expression);
|
7708 | let temporary = undefined;
|
7709 | if (this.needsTemporary(leftMostSafe.receiver)) {
|
7710 | // If the expression has method calls or pipes then we need to save the result into a
|
7711 | // temporary variable to avoid calling stateful or impure code more than once.
|
7712 | temporary = this.allocateTemporary();
|
7713 | // Preserve the result in the temporary variable
|
7714 | guardedExpression = temporary.set(guardedExpression);
|
7715 | // Ensure all further references to the guarded expression refer to the temporary instead.
|
7716 | this._resultMap.set(leftMostSafe.receiver, temporary);
|
7717 | }
|
7718 | const condition = guardedExpression.isBlank();
|
7719 | // Convert the ast to an unguarded access to the receiver's member. The map will substitute
|
7720 | // leftMostNode with its unguarded version in the call to `this.visit()`.
|
7721 | if (leftMostSafe instanceof SafeMethodCall) {
|
7722 | this._nodeMap.set(leftMostSafe, new MethodCall(leftMostSafe.span, leftMostSafe.sourceSpan, leftMostSafe.nameSpan, leftMostSafe.receiver, leftMostSafe.name, leftMostSafe.args));
|
7723 | }
|
7724 | else {
|
7725 | this._nodeMap.set(leftMostSafe, new PropertyRead(leftMostSafe.span, leftMostSafe.sourceSpan, leftMostSafe.nameSpan, leftMostSafe.receiver, leftMostSafe.name));
|
7726 | }
|
7727 | // Recursively convert the node now without the guarded member access.
|
7728 | const access = this._visit(ast, _Mode.Expression);
|
7729 | // Remove the mapping. This is not strictly required as the converter only traverses each node
|
7730 | // once but is safer if the conversion is changed to traverse the nodes more than once.
|
7731 | this._nodeMap.delete(leftMostSafe);
|
7732 | // If we allocated a temporary, release it.
|
7733 | if (temporary) {
|
7734 | this.releaseTemporary(temporary);
|
7735 | }
|
7736 | // Produce the conditional
|
7737 | return convertToStatementIfNeeded(mode, condition.conditional(literal(null), access));
|
7738 | }
|
7739 | // Given an expression of the form a?.b.c?.d.e then the left most safe node is
|
7740 | // the (a?.b). The . and ?. are left associative thus can be rewritten as:
|
7741 | // ((((a?.c).b).c)?.d).e. This returns the most deeply nested safe read or
|
7742 | // safe method call as this needs to be transformed initially to:
|
7743 | // a == null ? null : a.c.b.c?.d.e
|
7744 | // then to:
|
7745 | // a == null ? null : a.b.c == null ? null : a.b.c.d.e
|
7746 | leftMostSafeNode(ast) {
|
7747 | const visit = (visitor, ast) => {
|
7748 | return (this._nodeMap.get(ast) || ast).visit(visitor);
|
7749 | };
|
7750 | return ast.visit({
|
7751 | visitUnary(ast) {
|
7752 | return null;
|
7753 | },
|
7754 | visitBinary(ast) {
|
7755 | return null;
|
7756 | },
|
7757 | visitChain(ast) {
|
7758 | return null;
|
7759 | },
|
7760 | visitConditional(ast) {
|
7761 | return null;
|
7762 | },
|
7763 | visitFunctionCall(ast) {
|
7764 | return null;
|
7765 | },
|
7766 | visitImplicitReceiver(ast) {
|
7767 | return null;
|
7768 | },
|
7769 | visitThisReceiver(ast) {
|
7770 | return null;
|
7771 | },
|
7772 | visitInterpolation(ast) {
|
7773 | return null;
|
7774 | },
|
7775 | visitKeyedRead(ast) {
|
7776 | return visit(this, ast.obj);
|
7777 | },
|
7778 | visitKeyedWrite(ast) {
|
7779 | return null;
|
7780 | },
|
7781 | visitLiteralArray(ast) {
|
7782 | return null;
|
7783 | },
|
7784 | visitLiteralMap(ast) {
|
7785 | return null;
|
7786 | },
|
7787 | visitLiteralPrimitive(ast) {
|
7788 | return null;
|
7789 | },
|
7790 | visitMethodCall(ast) {
|
7791 | return visit(this, ast.receiver);
|
7792 | },
|
7793 | visitPipe(ast) {
|
7794 | return null;
|
7795 | },
|
7796 | visitPrefixNot(ast) {
|
7797 | return null;
|
7798 | },
|
7799 | visitNonNullAssert(ast) {
|
7800 | return null;
|
7801 | },
|
7802 | visitPropertyRead(ast) {
|
7803 | return visit(this, ast.receiver);
|
7804 | },
|
7805 | visitPropertyWrite(ast) {
|
7806 | return null;
|
7807 | },
|
7808 | visitQuote(ast) {
|
7809 | return null;
|
7810 | },
|
7811 | visitSafeMethodCall(ast) {
|
7812 | return visit(this, ast.receiver) || ast;
|
7813 | },
|
7814 | visitSafePropertyRead(ast) {
|
7815 | return visit(this, ast.receiver) || ast;
|
7816 | }
|
7817 | });
|
7818 | }
|
7819 | // Returns true of the AST includes a method or a pipe indicating that, if the
|
7820 | // expression is used as the target of a safe property or method access then
|
7821 | // the expression should be stored into a temporary variable.
|
7822 | needsTemporary(ast) {
|
7823 | const visit = (visitor, ast) => {
|
7824 | return ast && (this._nodeMap.get(ast) || ast).visit(visitor);
|
7825 | };
|
7826 | const visitSome = (visitor, ast) => {
|
7827 | return ast.some(ast => visit(visitor, ast));
|
7828 | };
|
7829 | return ast.visit({
|
7830 | visitUnary(ast) {
|
7831 | return visit(this, ast.expr);
|
7832 | },
|
7833 | visitBinary(ast) {
|
7834 | return visit(this, ast.left) || visit(this, ast.right);
|
7835 | },
|
7836 | visitChain(ast) {
|
7837 | return false;
|
7838 | },
|
7839 | visitConditional(ast) {
|
7840 | return visit(this, ast.condition) || visit(this, ast.trueExp) || visit(this, ast.falseExp);
|
7841 | },
|
7842 | visitFunctionCall(ast) {
|
7843 | return true;
|
7844 | },
|
7845 | visitImplicitReceiver(ast) {
|
7846 | return false;
|
7847 | },
|
7848 | visitThisReceiver(ast) {
|
7849 | return false;
|
7850 | },
|
7851 | visitInterpolation(ast) {
|
7852 | return visitSome(this, ast.expressions);
|
7853 | },
|
7854 | visitKeyedRead(ast) {
|
7855 | return false;
|
7856 | },
|
7857 | visitKeyedWrite(ast) {
|
7858 | return false;
|
7859 | },
|
7860 | visitLiteralArray(ast) {
|
7861 | return true;
|
7862 | },
|
7863 | visitLiteralMap(ast) {
|
7864 | return true;
|
7865 | },
|
7866 | visitLiteralPrimitive(ast) {
|
7867 | return false;
|
7868 | },
|
7869 | visitMethodCall(ast) {
|
7870 | return true;
|
7871 | },
|
7872 | visitPipe(ast) {
|
7873 | return true;
|
7874 | },
|
7875 | visitPrefixNot(ast) {
|
7876 | return visit(this, ast.expression);
|
7877 | },
|
7878 | visitNonNullAssert(ast) {
|
7879 | return visit(this, ast.expression);
|
7880 | },
|
7881 | visitPropertyRead(ast) {
|
7882 | return false;
|
7883 | },
|
7884 | visitPropertyWrite(ast) {
|
7885 | return false;
|
7886 | },
|
7887 | visitQuote(ast) {
|
7888 | return false;
|
7889 | },
|
7890 | visitSafeMethodCall(ast) {
|
7891 | return true;
|
7892 | },
|
7893 | visitSafePropertyRead(ast) {
|
7894 | return false;
|
7895 | }
|
7896 | });
|
7897 | }
|
7898 | allocateTemporary() {
|
7899 | const tempNumber = this._currentTemporary++;
|
7900 | this.temporaryCount = Math.max(this._currentTemporary, this.temporaryCount);
|
7901 | return new ReadVarExpr(temporaryName(this.bindingId, tempNumber));
|
7902 | }
|
7903 | releaseTemporary(temporary) {
|
7904 | this._currentTemporary--;
|
7905 | if (temporary.name != temporaryName(this.bindingId, this._currentTemporary)) {
|
7906 | throw new Error(`Temporary ${temporary.name} released out of order`);
|
7907 | }
|
7908 | }
|
7909 | /**
|
7910 | * Creates an absolute `ParseSourceSpan` from the relative `ParseSpan`.
|
7911 | *
|
7912 | * `ParseSpan` objects are relative to the start of the expression.
|
7913 | * This method converts these to full `ParseSourceSpan` objects that
|
7914 | * show where the span is within the overall source file.
|
7915 | *
|
7916 | * @param span the relative span to convert.
|
7917 | * @returns a `ParseSourceSpan` for the given span or null if no
|
7918 | * `baseSourceSpan` was provided to this class.
|
7919 | */
|
7920 | convertSourceSpan(span) {
|
7921 | if (this.baseSourceSpan) {
|
7922 | const start = this.baseSourceSpan.start.moveBy(span.start);
|
7923 | const end = this.baseSourceSpan.start.moveBy(span.end);
|
7924 | const fullStart = this.baseSourceSpan.fullStart.moveBy(span.start);
|
7925 | return new ParseSourceSpan(start, end, fullStart);
|
7926 | }
|
7927 | else {
|
7928 | return null;
|
7929 | }
|
7930 | }
|
7931 | /** Adds the name of an AST to the list of implicit receiver accesses. */
|
7932 | addImplicitReceiverAccess(name) {
|
7933 | if (this.implicitReceiverAccesses) {
|
7934 | this.implicitReceiverAccesses.add(name);
|
7935 | }
|
7936 | }
|
7937 | }
|
7938 | function flattenStatements(arg, output) {
|
7939 | if (Array.isArray(arg)) {
|
7940 | arg.forEach((entry) => flattenStatements(entry, output));
|
7941 | }
|
7942 | else {
|
7943 | output.push(arg);
|
7944 | }
|
7945 | }
|
7946 | class DefaultLocalResolver {
|
7947 | constructor(globals) {
|
7948 | this.globals = globals;
|
7949 | }
|
7950 | notifyImplicitReceiverUse() { }
|
7951 | getLocal(name) {
|
7952 | if (name === EventHandlerVars.event.name) {
|
7953 | return EventHandlerVars.event;
|
7954 | }
|
7955 | return null;
|
7956 | }
|
7957 | }
|
7958 | function createCurrValueExpr(bindingId) {
|
7959 | return variable(`currVal_${bindingId}`); // fix syntax highlighting: `
|
7960 | }
|
7961 | function createPreventDefaultVar(bindingId) {
|
7962 | return variable(`pd_${bindingId}`);
|
7963 | }
|
7964 | function convertStmtIntoExpression(stmt) {
|
7965 | if (stmt instanceof ExpressionStatement) {
|
7966 | return stmt.expr;
|
7967 | }
|
7968 | else if (stmt instanceof ReturnStatement) {
|
7969 | return stmt.value;
|
7970 | }
|
7971 | return null;
|
7972 | }
|
7973 | class BuiltinFunctionCall extends FunctionCall {
|
7974 | constructor(span, sourceSpan, args, converter) {
|
7975 | super(span, sourceSpan, null, args);
|
7976 | this.args = args;
|
7977 | this.converter = converter;
|
7978 | }
|
7979 | }
|
7980 |
|
7981 | /**
|
7982 | * @license
|
7983 | * Copyright Google LLC All Rights Reserved.
|
7984 | *
|
7985 | * Use of this source code is governed by an MIT-style license that can be
|
7986 | * found in the LICENSE file at https://angular.io/license
|
7987 | */
|
7988 | /**
|
7989 | * This file is a port of shadowCSS from webcomponents.js to TypeScript.
|
7990 | *
|
7991 | * Please make sure to keep to edits in sync with the source file.
|
7992 | *
|
7993 | * Source:
|
7994 | * https://github.com/webcomponents/webcomponentsjs/blob/4efecd7e0e/src/ShadowCSS/ShadowCSS.js
|
7995 | *
|
7996 | * The original file level comment is reproduced below
|
7997 | */
|
7998 | /*
|
7999 | This is a limited shim for ShadowDOM css styling.
|
8000 | https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#styles
|
8001 |
|
8002 | The intention here is to support only the styling features which can be
|
8003 | relatively simply implemented. The goal is to allow users to avoid the
|
8004 | most obvious pitfalls and do so without compromising performance significantly.
|
8005 | For ShadowDOM styling that's not covered here, a set of best practices
|
8006 | can be provided that should allow users to accomplish more complex styling.
|
8007 |
|
8008 | The following is a list of specific ShadowDOM styling features and a brief
|
8009 | discussion of the approach used to shim.
|
8010 |
|
8011 | Shimmed features:
|
8012 |
|
8013 | * :host, :host-context: ShadowDOM allows styling of the shadowRoot's host
|
8014 | element using the :host rule. To shim this feature, the :host styles are
|
8015 | reformatted and prefixed with a given scope name and promoted to a
|
8016 | document level stylesheet.
|
8017 | For example, given a scope name of .foo, a rule like this:
|
8018 |
|
8019 | :host {
|
8020 | background: red;
|
8021 | }
|
8022 | }
|
8023 |
|
8024 | becomes:
|
8025 |
|
8026 | .foo {
|
8027 | background: red;
|
8028 | }
|
8029 |
|
8030 | * encapsulation: Styles defined within ShadowDOM, apply only to
|
8031 | dom inside the ShadowDOM. Polymer uses one of two techniques to implement
|
8032 | this feature.
|
8033 |
|
8034 | By default, rules are prefixed with the host element tag name
|
8035 | as a descendant selector. This ensures styling does not leak out of the 'top'
|
8036 | of the element's ShadowDOM. For example,
|
8037 |
|
8038 | div {
|
8039 | font-weight: bold;
|
8040 | }
|
8041 |
|
8042 | becomes:
|
8043 |
|
8044 | x-foo div {
|
8045 | font-weight: bold;
|
8046 | }
|
8047 |
|
8048 | becomes:
|
8049 |
|
8050 |
|
8051 | Alternatively, if WebComponents.ShadowCSS.strictStyling is set to true then
|
8052 | selectors are scoped by adding an attribute selector suffix to each
|
8053 | simple selector that contains the host element tag name. Each element
|
8054 | in the element's ShadowDOM template is also given the scope attribute.
|
8055 | Thus, these rules match only elements that have the scope attribute.
|
8056 | For example, given a scope name of x-foo, a rule like this:
|
8057 |
|
8058 | div {
|
8059 | font-weight: bold;
|
8060 | }
|
8061 |
|
8062 | becomes:
|
8063 |
|
8064 | div[x-foo] {
|
8065 | font-weight: bold;
|
8066 | }
|
8067 |
|
8068 | Note that elements that are dynamically added to a scope must have the scope
|
8069 | selector added to them manually.
|
8070 |
|
8071 | * upper/lower bound encapsulation: Styles which are defined outside a
|
8072 | shadowRoot should not cross the ShadowDOM boundary and should not apply
|
8073 | inside a shadowRoot.
|
8074 |
|
8075 | This styling behavior is not emulated. Some possible ways to do this that
|
8076 | were rejected due to complexity and/or performance concerns include: (1) reset
|
8077 | every possible property for every possible selector for a given scope name;
|
8078 | (2) re-implement css in javascript.
|
8079 |
|
8080 | As an alternative, users should make sure to use selectors
|
8081 | specific to the scope in which they are working.
|
8082 |
|
8083 | * ::distributed: This behavior is not emulated. It's often not necessary
|
8084 | to style the contents of a specific insertion point and instead, descendants
|
8085 | of the host element can be styled selectively. Users can also create an
|
8086 | extra node around an insertion point and style that node's contents
|
8087 | via descendent selectors. For example, with a shadowRoot like this:
|
8088 |
|
8089 | <style>
|
8090 | ::content(div) {
|
8091 | background: red;
|
8092 | }
|
8093 | </style>
|
8094 | <content></content>
|
8095 |
|
8096 | could become:
|
8097 |
|
8098 | <style>
|
8099 | / *@polyfill .content-container div * /
|
8100 | ::content(div) {
|
8101 | background: red;
|
8102 | }
|
8103 | </style>
|
8104 | <div class="content-container">
|
8105 | <content></content>
|
8106 | </div>
|
8107 |
|
8108 | Note the use of @polyfill in the comment above a ShadowDOM specific style
|
8109 | declaration. This is a directive to the styling shim to use the selector
|
8110 | in comments in lieu of the next selector when running under polyfill.
|
8111 | */
|
8112 | class ShadowCss {
|
8113 | constructor() {
|
8114 | this.strictStyling = true;
|
8115 | }
|
8116 | /*
|
8117 | * Shim some cssText with the given selector. Returns cssText that can
|
8118 | * be included in the document via WebComponents.ShadowCSS.addCssToDocument(css).
|
8119 | *
|
8120 | * When strictStyling is true:
|
8121 | * - selector is the attribute added to all elements inside the host,
|
8122 | * - hostSelector is the attribute added to the host itself.
|
8123 | */
|
8124 | shimCssText(cssText, selector, hostSelector = '') {
|
8125 | const commentsWithHash = extractCommentsWithHash(cssText);
|
8126 | cssText = stripComments(cssText);
|
8127 | cssText = this._insertDirectives(cssText);
|
8128 | const scopedCssText = this._scopeCssText(cssText, selector, hostSelector);
|
8129 | return [scopedCssText, ...commentsWithHash].join('\n');
|
8130 | }
|
8131 | _insertDirectives(cssText) {
|
8132 | cssText = this._insertPolyfillDirectivesInCssText(cssText);
|
8133 | return this._insertPolyfillRulesInCssText(cssText);
|
8134 | }
|
8135 | /*
|
8136 | * Process styles to convert native ShadowDOM rules that will trip
|
8137 | * up the css parser; we rely on decorating the stylesheet with inert rules.
|
8138 | *
|
8139 | * For example, we convert this rule:
|
8140 | *
|
8141 | * polyfill-next-selector { content: ':host menu-item'; }
|
8142 | * ::content menu-item {
|
8143 | *
|
8144 | * to this:
|
8145 | *
|
8146 | * scopeName menu-item {
|
8147 | *
|
8148 | **/
|
8149 | _insertPolyfillDirectivesInCssText(cssText) {
|
8150 | // Difference with webcomponents.js: does not handle comments
|
8151 | return cssText.replace(_cssContentNextSelectorRe, function (...m) {
|
8152 | return m[2] + '{';
|
8153 | });
|
8154 | }
|
8155 | /*
|
8156 | * Process styles to add rules which will only apply under the polyfill
|
8157 | *
|
8158 | * For example, we convert this rule:
|
8159 | *
|
8160 | * polyfill-rule {
|
8161 | * content: ':host menu-item';
|
8162 | * ...
|
8163 | * }
|
8164 | *
|
8165 | * to this:
|
8166 | *
|
8167 | * scopeName menu-item {...}
|
8168 | *
|
8169 | **/
|
8170 | _insertPolyfillRulesInCssText(cssText) {
|
8171 | // Difference with webcomponents.js: does not handle comments
|
8172 | return cssText.replace(_cssContentRuleRe, (...m) => {
|
8173 | const rule = m[0].replace(m[1], '').replace(m[2], '');
|
8174 | return m[4] + rule;
|
8175 | });
|
8176 | }
|
8177 | /* Ensure styles are scoped. Pseudo-scoping takes a rule like:
|
8178 | *
|
8179 | * .foo {... }
|
8180 | *
|
8181 | * and converts this to
|
8182 | *
|
8183 | * scopeName .foo { ... }
|
8184 | */
|
8185 | _scopeCssText(cssText, scopeSelector, hostSelector) {
|
8186 | const unscopedRules = this._extractUnscopedRulesFromCssText(cssText);
|
8187 | // replace :host and :host-context -shadowcsshost and -shadowcsshost respectively
|
8188 | cssText = this._insertPolyfillHostInCssText(cssText);
|
8189 | cssText = this._convertColonHost(cssText);
|
8190 | cssText = this._convertColonHostContext(cssText);
|
8191 | cssText = this._convertShadowDOMSelectors(cssText);
|
8192 | if (scopeSelector) {
|
8193 | cssText = this._scopeSelectors(cssText, scopeSelector, hostSelector);
|
8194 | }
|
8195 | cssText = cssText + '\n' + unscopedRules;
|
8196 | return cssText.trim();
|
8197 | }
|
8198 | /*
|
8199 | * Process styles to add rules which will only apply under the polyfill
|
8200 | * and do not process via CSSOM. (CSSOM is destructive to rules on rare
|
8201 | * occasions, e.g. -webkit-calc on Safari.)
|
8202 | * For example, we convert this rule:
|
8203 | *
|
8204 | * @polyfill-unscoped-rule {
|
8205 | * content: 'menu-item';
|
8206 | * ... }
|
8207 | *
|
8208 | * to this:
|
8209 | *
|
8210 | * menu-item {...}
|
8211 | *
|
8212 | **/
|
8213 | _extractUnscopedRulesFromCssText(cssText) {
|
8214 | // Difference with webcomponents.js: does not handle comments
|
8215 | let r = '';
|
8216 | let m;
|
8217 | _cssContentUnscopedRuleRe.lastIndex = 0;
|
8218 | while ((m = _cssContentUnscopedRuleRe.exec(cssText)) !== null) {
|
8219 | const rule = m[0].replace(m[2], '').replace(m[1], m[4]);
|
8220 | r += rule + '\n\n';
|
8221 | }
|
8222 | return r;
|
8223 | }
|
8224 | /*
|
8225 | * convert a rule like :host(.foo) > .bar { }
|
8226 | *
|
8227 | * to
|
8228 | *
|
8229 | * .foo<scopeName> > .bar
|
8230 | */
|
8231 | _convertColonHost(cssText) {
|
8232 | return cssText.replace(_cssColonHostRe, (_, hostSelectors, otherSelectors) => {
|
8233 | if (hostSelectors) {
|
8234 | const convertedSelectors = [];
|
8235 | const hostSelectorArray = hostSelectors.split(',').map(p => p.trim());
|
8236 | for (const hostSelector of hostSelectorArray) {
|
8237 | if (!hostSelector)
|
8238 | break;
|
8239 | const convertedSelector = _polyfillHostNoCombinator + hostSelector.replace(_polyfillHost, '') + otherSelectors;
|
8240 | convertedSelectors.push(convertedSelector);
|
8241 | }
|
8242 | return convertedSelectors.join(',');
|
8243 | }
|
8244 | else {
|
8245 | return _polyfillHostNoCombinator + otherSelectors;
|
8246 | }
|
8247 | });
|
8248 | }
|
8249 | /*
|
8250 | * convert a rule like :host-context(.foo) > .bar { }
|
8251 | *
|
8252 | * to
|
8253 | *
|
8254 | * .foo<scopeName> > .bar, .foo <scopeName> > .bar { }
|
8255 | *
|
8256 | * and
|
8257 | *
|
8258 | * :host-context(.foo:host) .bar { ... }
|
8259 | *
|
8260 | * to
|
8261 | *
|
8262 | * .foo<scopeName> .bar { ... }
|
8263 | */
|
8264 | _convertColonHostContext(cssText) {
|
8265 | return cssText.replace(_cssColonHostContextReGlobal, selectorText => {
|
8266 | // We have captured a selector that contains a `:host-context` rule.
|
8267 | var _a;
|
8268 | // For backward compatibility `:host-context` may contain a comma separated list of selectors.
|
8269 | // Each context selector group will contain a list of host-context selectors that must match
|
8270 | // an ancestor of the host.
|
8271 | // (Normally `contextSelectorGroups` will only contain a single array of context selectors.)
|
8272 | const contextSelectorGroups = [[]];
|
8273 | // There may be more than `:host-context` in this selector so `selectorText` could look like:
|
8274 | // `:host-context(.one):host-context(.two)`.
|
8275 | // Execute `_cssColonHostContextRe` over and over until we have extracted all the
|
8276 | // `:host-context` selectors from this selector.
|
8277 | let match;
|
8278 | while (match = _cssColonHostContextRe.exec(selectorText)) {
|
8279 | // `match` = [':host-context(<selectors>)<rest>', <selectors>, <rest>]
|
8280 | // The `<selectors>` could actually be a comma separated list: `:host-context(.one, .two)`.
|
8281 | const newContextSelectors = ((_a = match[1]) !== null && _a !== void 0 ? _a : '').trim().split(',').map(m => m.trim()).filter(m => m !== '');
|
8282 | // We must duplicate the current selector group for each of these new selectors.
|
8283 | // For example if the current groups are:
|
8284 | // ```
|
8285 | // [
|
8286 | // ['a', 'b', 'c'],
|
8287 | // ['x', 'y', 'z'],
|
8288 | // ]
|
8289 | // ```
|
8290 | // And we have a new set of comma separated selectors: `:host-context(m,n)` then the new
|
8291 | // groups are:
|
8292 | // ```
|
8293 | // [
|
8294 | // ['a', 'b', 'c', 'm'],
|
8295 | // ['x', 'y', 'z', 'm'],
|
8296 | // ['a', 'b', 'c', 'n'],
|
8297 | // ['x', 'y', 'z', 'n'],
|
8298 | // ]
|
8299 | // ```
|
8300 | const contextSelectorGroupsLength = contextSelectorGroups.length;
|
8301 | repeatGroups(contextSelectorGroups, newContextSelectors.length);
|
8302 | for (let i = 0; i < newContextSelectors.length; i++) {
|
8303 | for (let j = 0; j < contextSelectorGroupsLength; j++) {
|
8304 | contextSelectorGroups[j + (i * contextSelectorGroupsLength)].push(newContextSelectors[i]);
|
8305 | }
|
8306 | }
|
8307 | // Update the `selectorText` and see repeat to see if there are more `:host-context`s.
|
8308 | selectorText = match[2];
|
8309 | }
|
8310 | // The context selectors now must be combined with each other to capture all the possible
|
8311 | // selectors that `:host-context` can match. See `combineHostContextSelectors()` for more
|
8312 | // info about how this is done.
|
8313 | return contextSelectorGroups
|
8314 | .map(contextSelectors => combineHostContextSelectors(contextSelectors, selectorText))
|
8315 | .join(', ');
|
8316 | });
|
8317 | }
|
8318 | /*
|
8319 | * Convert combinators like ::shadow and pseudo-elements like ::content
|
8320 | * by replacing with space.
|
8321 | */
|
8322 | _convertShadowDOMSelectors(cssText) {
|
8323 | return _shadowDOMSelectorsRe.reduce((result, pattern) => result.replace(pattern, ' '), cssText);
|
8324 | }
|
8325 | // change a selector like 'div' to 'name div'
|
8326 | _scopeSelectors(cssText, scopeSelector, hostSelector) {
|
8327 | return processRules(cssText, (rule) => {
|
8328 | let selector = rule.selector;
|
8329 | let content = rule.content;
|
8330 | if (rule.selector[0] != '@') {
|
8331 | selector =
|
8332 | this._scopeSelector(rule.selector, scopeSelector, hostSelector, this.strictStyling);
|
8333 | }
|
8334 | else if (rule.selector.startsWith('@media') || rule.selector.startsWith('@supports') ||
|
8335 | rule.selector.startsWith('@page') || rule.selector.startsWith('@document')) {
|
8336 | content = this._scopeSelectors(rule.content, scopeSelector, hostSelector);
|
8337 | }
|
8338 | return new CssRule(selector, content);
|
8339 | });
|
8340 | }
|
8341 | _scopeSelector(selector, scopeSelector, hostSelector, strict) {
|
8342 | return selector.split(',')
|
8343 | .map(part => part.trim().split(_shadowDeepSelectors))
|
8344 | .map((deepParts) => {
|
8345 | const [shallowPart, ...otherParts] = deepParts;
|
8346 | const applyScope = (shallowPart) => {
|
8347 | if (this._selectorNeedsScoping(shallowPart, scopeSelector)) {
|
8348 | return strict ?
|
8349 | this._applyStrictSelectorScope(shallowPart, scopeSelector, hostSelector) :
|
8350 | this._applySelectorScope(shallowPart, scopeSelector, hostSelector);
|
8351 | }
|
8352 | else {
|
8353 | return shallowPart;
|
8354 | }
|
8355 | };
|
8356 | return [applyScope(shallowPart), ...otherParts].join(' ');
|
8357 | })
|
8358 | .join(', ');
|
8359 | }
|
8360 | _selectorNeedsScoping(selector, scopeSelector) {
|
8361 | const re = this._makeScopeMatcher(scopeSelector);
|
8362 | return !re.test(selector);
|
8363 | }
|
8364 | _makeScopeMatcher(scopeSelector) {
|
8365 | const lre = /\[/g;
|
8366 | const rre = /\]/g;
|
8367 | scopeSelector = scopeSelector.replace(lre, '\\[').replace(rre, '\\]');
|
8368 | return new RegExp('^(' + scopeSelector + ')' + _selectorReSuffix, 'm');
|
8369 | }
|
8370 | _applySelectorScope(selector, scopeSelector, hostSelector) {
|
8371 | // Difference from webcomponents.js: scopeSelector could not be an array
|
8372 | return this._applySimpleSelectorScope(selector, scopeSelector, hostSelector);
|
8373 | }
|
8374 | // scope via name and [is=name]
|
8375 | _applySimpleSelectorScope(selector, scopeSelector, hostSelector) {
|
8376 | // In Android browser, the lastIndex is not reset when the regex is used in String.replace()
|
8377 | _polyfillHostRe.lastIndex = 0;
|
8378 | if (_polyfillHostRe.test(selector)) {
|
8379 | const replaceBy = this.strictStyling ? `[${hostSelector}]` : scopeSelector;
|
8380 | return selector
|
8381 | .replace(_polyfillHostNoCombinatorRe, (hnc, selector) => {
|
8382 | return selector.replace(/([^:]*)(:*)(.*)/, (_, before, colon, after) => {
|
8383 | return before + replaceBy + colon + after;
|
8384 | });
|
8385 | })
|
8386 | .replace(_polyfillHostRe, replaceBy + ' ');
|
8387 | }
|
8388 | return scopeSelector + ' ' + selector;
|
8389 | }
|
8390 | // return a selector with [name] suffix on each simple selector
|
8391 | // e.g. .foo.bar > .zot becomes .foo[name].bar[name] > .zot[name] /** @internal */
|
8392 | _applyStrictSelectorScope(selector, scopeSelector, hostSelector) {
|
8393 | const isRe = /\[is=([^\]]*)\]/g;
|
8394 | scopeSelector = scopeSelector.replace(isRe, (_, ...parts) => parts[0]);
|
8395 | const attrName = '[' + scopeSelector + ']';
|
8396 | const _scopeSelectorPart = (p) => {
|
8397 | let scopedP = p.trim();
|
8398 | if (!scopedP) {
|
8399 | return '';
|
8400 | }
|
8401 | if (p.indexOf(_polyfillHostNoCombinator) > -1) {
|
8402 | scopedP = this._applySimpleSelectorScope(p, scopeSelector, hostSelector);
|
8403 | }
|
8404 | else {
|
8405 | // remove :host since it should be unnecessary
|
8406 | const t = p.replace(_polyfillHostRe, '');
|
8407 | if (t.length > 0) {
|
8408 | const matches = t.match(/([^:]*)(:*)(.*)/);
|
8409 | if (matches) {
|
8410 | scopedP = matches[1] + attrName + matches[2] + matches[3];
|
8411 | }
|
8412 | }
|
8413 | }
|
8414 | return scopedP;
|
8415 | };
|
8416 | const safeContent = new SafeSelector(selector);
|
8417 | selector = safeContent.content();
|
8418 | let scopedSelector = '';
|
8419 | let startIndex = 0;
|
8420 | let res;
|
8421 | const sep = /( |>|\+|~(?!=))\s*/g;
|
8422 | // If a selector appears before :host it should not be shimmed as it
|
8423 | // matches on ancestor elements and not on elements in the host's shadow
|
8424 | // `:host-context(div)` is transformed to
|
8425 | // `-shadowcsshost-no-combinatordiv, div -shadowcsshost-no-combinator`
|
8426 | // the `div` is not part of the component in the 2nd selectors and should not be scoped.
|
8427 | // Historically `component-tag:host` was matching the component so we also want to preserve
|
8428 | // this behavior to avoid breaking legacy apps (it should not match).
|
8429 | // The behavior should be:
|
8430 | // - `tag:host` -> `tag[h]` (this is to avoid breaking legacy apps, should not match anything)
|
8431 | // - `tag :host` -> `tag [h]` (`tag` is not scoped because it's considered part of a
|
8432 | // `:host-context(tag)`)
|
8433 | const hasHost = selector.indexOf(_polyfillHostNoCombinator) > -1;
|
8434 | // Only scope parts after the first `-shadowcsshost-no-combinator` when it is present
|
8435 | let shouldScope = !hasHost;
|
8436 | while ((res = sep.exec(selector)) !== null) {
|
8437 | const separator = res[1];
|
8438 | const part = selector.slice(startIndex, res.index).trim();
|
8439 | shouldScope = shouldScope || part.indexOf(_polyfillHostNoCombinator) > -1;
|
8440 | const scopedPart = shouldScope ? _scopeSelectorPart(part) : part;
|
8441 | scopedSelector += `${scopedPart} ${separator} `;
|
8442 | startIndex = sep.lastIndex;
|
8443 | }
|
8444 | const part = selector.substring(startIndex);
|
8445 | shouldScope = shouldScope || part.indexOf(_polyfillHostNoCombinator) > -1;
|
8446 | scopedSelector += shouldScope ? _scopeSelectorPart(part) : part;
|
8447 | // replace the placeholders with their original values
|
8448 | return safeContent.restore(scopedSelector);
|
8449 | }
|
8450 | _insertPolyfillHostInCssText(selector) {
|
8451 | return selector.replace(_colonHostContextRe, _polyfillHostContext)
|
8452 | .replace(_colonHostRe, _polyfillHost);
|
8453 | }
|
8454 | }
|
8455 | class SafeSelector {
|
8456 | constructor(selector) {
|
8457 | this.placeholders = [];
|
8458 | this.index = 0;
|
8459 | // Replaces attribute selectors with placeholders.
|
8460 | // The WS in [attr="va lue"] would otherwise be interpreted as a selector separator.
|
8461 | selector = this._escapeRegexMatches(selector, /(\[[^\]]*\])/g);
|
8462 | // CSS allows for certain special characters to be used in selectors if they're escaped.
|
8463 | // E.g. `.foo:blue` won't match a class called `foo:blue`, because the colon denotes a
|
8464 | // pseudo-class, but writing `.foo\:blue` will match, because the colon was escaped.
|
8465 | // Replace all escape sequences (`\` followed by a character) with a placeholder so
|
8466 | // that our handling of pseudo-selectors doesn't mess with them.
|
8467 | selector = this._escapeRegexMatches(selector, /(\\.)/g);
|
8468 | // Replaces the expression in `:nth-child(2n + 1)` with a placeholder.
|
8469 | // WS and "+" would otherwise be interpreted as selector separators.
|
8470 | this._content = selector.replace(/(:nth-[-\w]+)(\([^)]+\))/g, (_, pseudo, exp) => {
|
8471 | const replaceBy = `__ph-${this.index}__`;
|
8472 | this.placeholders.push(exp);
|
8473 | this.index++;
|
8474 | return pseudo + replaceBy;
|
8475 | });
|
8476 | }
|
8477 | restore(content) {
|
8478 | return content.replace(/__ph-(\d+)__/g, (_ph, index) => this.placeholders[+index]);
|
8479 | }
|
8480 | content() {
|
8481 | return this._content;
|
8482 | }
|
8483 | /**
|
8484 | * Replaces all of the substrings that match a regex within a
|
8485 | * special string (e.g. `__ph-0__`, `__ph-1__`, etc).
|
8486 | */
|
8487 | _escapeRegexMatches(content, pattern) {
|
8488 | return content.replace(pattern, (_, keep) => {
|
8489 | const replaceBy = `__ph-${this.index}__`;
|
8490 | this.placeholders.push(keep);
|
8491 | this.index++;
|
8492 | return replaceBy;
|
8493 | });
|
8494 | }
|
8495 | }
|
8496 | const _cssContentNextSelectorRe = /polyfill-next-selector[^}]*content:[\s]*?(['"])(.*?)\1[;\s]*}([^{]*?){/gim;
|
8497 | const _cssContentRuleRe = /(polyfill-rule)[^}]*(content:[\s]*(['"])(.*?)\3)[;\s]*[^}]*}/gim;
|
8498 | const _cssContentUnscopedRuleRe = /(polyfill-unscoped-rule)[^}]*(content:[\s]*(['"])(.*?)\3)[;\s]*[^}]*}/gim;
|
8499 | const _polyfillHost = '-shadowcsshost';
|
8500 | // note: :host-context pre-processed to -shadowcsshostcontext.
|
8501 | const _polyfillHostContext = '-shadowcsscontext';
|
8502 | const _parenSuffix = '(?:\\((' +
|
8503 | '(?:\\([^)(]*\\)|[^)(]*)+?' +
|
8504 | ')\\))?([^,{]*)';
|
8505 | const _cssColonHostRe = new RegExp(_polyfillHost + _parenSuffix, 'gim');
|
8506 | const _cssColonHostContextReGlobal = new RegExp(_polyfillHostContext + _parenSuffix, 'gim');
|
8507 | const _cssColonHostContextRe = new RegExp(_polyfillHostContext + _parenSuffix, 'im');
|
8508 | const _polyfillHostNoCombinator = _polyfillHost + '-no-combinator';
|
8509 | const _polyfillHostNoCombinatorRe = /-shadowcsshost-no-combinator([^\s]*)/;
|
8510 | const _shadowDOMSelectorsRe = [
|
8511 | /::shadow/g,
|
8512 | /::content/g,
|
8513 | // Deprecated selectors
|
8514 | /\/shadow-deep\//g,
|
8515 | /\/shadow\//g,
|
8516 | ];
|
8517 | // The deep combinator is deprecated in the CSS spec
|
8518 | // Support for `>>>`, `deep`, `::ng-deep` is then also deprecated and will be removed in the future.
|
8519 | // see https://github.com/angular/angular/pull/17677
|
8520 | const _shadowDeepSelectors = /(?:>>>)|(?:\/deep\/)|(?:::ng-deep)/g;
|
8521 | const _selectorReSuffix = '([>\\s~+\[.,{:][\\s\\S]*)?$';
|
8522 | const _polyfillHostRe = /-shadowcsshost/gim;
|
8523 | const _colonHostRe = /:host/gim;
|
8524 | const _colonHostContextRe = /:host-context/gim;
|
8525 | const _commentRe = /\/\*\s*[\s\S]*?\*\//g;
|
8526 | function stripComments(input) {
|
8527 | return input.replace(_commentRe, '');
|
8528 | }
|
8529 | const _commentWithHashRe = /\/\*\s*#\s*source(Mapping)?URL=[\s\S]+?\*\//g;
|
8530 | function extractCommentsWithHash(input) {
|
8531 | return input.match(_commentWithHashRe) || [];
|
8532 | }
|
8533 | const BLOCK_PLACEHOLDER = '%BLOCK%';
|
8534 | const QUOTE_PLACEHOLDER = '%QUOTED%';
|
8535 | const _ruleRe = /(\s*)([^;\{\}]+?)(\s*)((?:{%BLOCK%}?\s*;?)|(?:\s*;))/g;
|
8536 | const _quotedRe = /%QUOTED%/g;
|
8537 | const CONTENT_PAIRS = new Map([['{', '}']]);
|
8538 | const QUOTE_PAIRS = new Map([[`"`, `"`], [`'`, `'`]]);
|
8539 | class CssRule {
|
8540 | constructor(selector, content) {
|
8541 | this.selector = selector;
|
8542 | this.content = content;
|
8543 | }
|
8544 | }
|
8545 | function processRules(input, ruleCallback) {
|
8546 | const inputWithEscapedQuotes = escapeBlocks(input, QUOTE_PAIRS, QUOTE_PLACEHOLDER);
|
8547 | const inputWithEscapedBlocks = escapeBlocks(inputWithEscapedQuotes.escapedString, CONTENT_PAIRS, BLOCK_PLACEHOLDER);
|
8548 | let nextBlockIndex = 0;
|
8549 | let nextQuoteIndex = 0;
|
8550 | return inputWithEscapedBlocks.escapedString
|
8551 | .replace(_ruleRe, (...m) => {
|
8552 | const selector = m[2];
|
8553 | let content = '';
|
8554 | let suffix = m[4];
|
8555 | let contentPrefix = '';
|
8556 | if (suffix && suffix.startsWith('{' + BLOCK_PLACEHOLDER)) {
|
8557 | content = inputWithEscapedBlocks.blocks[nextBlockIndex++];
|
8558 | suffix = suffix.substring(BLOCK_PLACEHOLDER.length + 1);
|
8559 | contentPrefix = '{';
|
8560 | }
|
8561 | const rule = ruleCallback(new CssRule(selector, content));
|
8562 | return `${m[1]}${rule.selector}${m[3]}${contentPrefix}${rule.content}${suffix}`;
|
8563 | })
|
8564 | .replace(_quotedRe, () => inputWithEscapedQuotes.blocks[nextQuoteIndex++]);
|
8565 | }
|
8566 | class StringWithEscapedBlocks {
|
8567 | constructor(escapedString, blocks) {
|
8568 | this.escapedString = escapedString;
|
8569 | this.blocks = blocks;
|
8570 | }
|
8571 | }
|
8572 | function escapeBlocks(input, charPairs, placeholder) {
|
8573 | const resultParts = [];
|
8574 | const escapedBlocks = [];
|
8575 | let openCharCount = 0;
|
8576 | let nonBlockStartIndex = 0;
|
8577 | let blockStartIndex = -1;
|
8578 | let openChar;
|
8579 | let closeChar;
|
8580 | for (let i = 0; i < input.length; i++) {
|
8581 | const char = input[i];
|
8582 | if (char === '\\') {
|
8583 | i++;
|
8584 | }
|
8585 | else if (char === closeChar) {
|
8586 | openCharCount--;
|
8587 | if (openCharCount === 0) {
|
8588 | escapedBlocks.push(input.substring(blockStartIndex, i));
|
8589 | resultParts.push(placeholder);
|
8590 | nonBlockStartIndex = i;
|
8591 | blockStartIndex = -1;
|
8592 | openChar = closeChar = undefined;
|
8593 | }
|
8594 | }
|
8595 | else if (char === openChar) {
|
8596 | openCharCount++;
|
8597 | }
|
8598 | else if (openCharCount === 0 && charPairs.has(char)) {
|
8599 | openChar = char;
|
8600 | closeChar = charPairs.get(char);
|
8601 | openCharCount = 1;
|
8602 | blockStartIndex = i + 1;
|
8603 | resultParts.push(input.substring(nonBlockStartIndex, blockStartIndex));
|
8604 | }
|
8605 | }
|
8606 | if (blockStartIndex !== -1) {
|
8607 | escapedBlocks.push(input.substring(blockStartIndex));
|
8608 | resultParts.push(placeholder);
|
8609 | }
|
8610 | else {
|
8611 | resultParts.push(input.substring(nonBlockStartIndex));
|
8612 | }
|
8613 | return new StringWithEscapedBlocks(resultParts.join(''), escapedBlocks);
|
8614 | }
|
8615 | /**
|
8616 | * Combine the `contextSelectors` with the `hostMarker` and the `otherSelectors`
|
8617 | * to create a selector that matches the same as `:host-context()`.
|
8618 | *
|
8619 | * Given a single context selector `A` we need to output selectors that match on the host and as an
|
8620 | * ancestor of the host:
|
8621 | *
|
8622 | * ```
|
8623 | * A <hostMarker>, A<hostMarker> {}
|
8624 | * ```
|
8625 | *
|
8626 | * When there is more than one context selector we also have to create combinations of those
|
8627 | * selectors with each other. For example if there are `A` and `B` selectors the output is:
|
8628 | *
|
8629 | * ```
|
8630 | * AB<hostMarker>, AB <hostMarker>, A B<hostMarker>,
|
8631 | * B A<hostMarker>, A B <hostMarker>, B A <hostMarker> {}
|
8632 | * ```
|
8633 | *
|
8634 | * And so on...
|
8635 | *
|
8636 | * @param hostMarker the string that selects the host element.
|
8637 | * @param contextSelectors an array of context selectors that will be combined.
|
8638 | * @param otherSelectors the rest of the selectors that are not context selectors.
|
8639 | */
|
8640 | function combineHostContextSelectors(contextSelectors, otherSelectors) {
|
8641 | const hostMarker = _polyfillHostNoCombinator;
|
8642 | const otherSelectorsHasHost = _polyfillHostRe.test(otherSelectors);
|
8643 | // If there are no context selectors then just output a host marker
|
8644 | if (contextSelectors.length === 0) {
|
8645 | return hostMarker + otherSelectors;
|
8646 | }
|
8647 | const combined = [contextSelectors.pop() || ''];
|
8648 | while (contextSelectors.length > 0) {
|
8649 | const length = combined.length;
|
8650 | const contextSelector = contextSelectors.pop();
|
8651 | for (let i = 0; i < length; i++) {
|
8652 | const previousSelectors = combined[i];
|
8653 | // Add the new selector as a descendant of the previous selectors
|
8654 | combined[length * 2 + i] = previousSelectors + ' ' + contextSelector;
|
8655 | // Add the new selector as an ancestor of the previous selectors
|
8656 | combined[length + i] = contextSelector + ' ' + previousSelectors;
|
8657 | // Add the new selector to act on the same element as the previous selectors
|
8658 | combined[i] = contextSelector + previousSelectors;
|
8659 | }
|
8660 | }
|
8661 | // Finally connect the selector to the `hostMarker`s: either acting directly on the host
|
8662 | // (A<hostMarker>) or as an ancestor (A <hostMarker>).
|
8663 | return combined
|
8664 | .map(s => otherSelectorsHasHost ?
|
8665 | `${s}${otherSelectors}` :
|
8666 | `${s}${hostMarker}${otherSelectors}, ${s} ${hostMarker}${otherSelectors}`)
|
8667 | .join(',');
|
8668 | }
|
8669 | /**
|
8670 | * Mutate the given `groups` array so that there are `multiples` clones of the original array
|
8671 | * stored.
|
8672 | *
|
8673 | * For example `repeatGroups([a, b], 3)` will result in `[a, b, a, b, a, b]` - but importantly the
|
8674 | * newly added groups will be clones of the original.
|
8675 | *
|
8676 | * @param groups An array of groups of strings that will be repeated. This array is mutated
|
8677 | * in-place.
|
8678 | * @param multiples The number of times the current groups should appear.
|
8679 | */
|
8680 | function repeatGroups(groups, multiples) {
|
8681 | const length = groups.length;
|
8682 | for (let i = 1; i < multiples; i++) {
|
8683 | for (let j = 0; j < length; j++) {
|
8684 | groups[j + (i * length)] = groups[j].slice(0);
|
8685 | }
|
8686 | }
|
8687 | }
|
8688 |
|
8689 | /**
|
8690 | * @license
|
8691 | * Copyright Google LLC All Rights Reserved.
|
8692 | *
|
8693 | * Use of this source code is governed by an MIT-style license that can be
|
8694 | * found in the LICENSE file at https://angular.io/license
|
8695 | */
|
8696 | const COMPONENT_VARIABLE = '%COMP%';
|
8697 | const HOST_ATTR = `_nghost-${COMPONENT_VARIABLE}`;
|
8698 | const CONTENT_ATTR = `_ngcontent-${COMPONENT_VARIABLE}`;
|
8699 |
|
8700 | /**
|
8701 | * @license
|
8702 | * Copyright Google LLC All Rights Reserved.
|
8703 | *
|
8704 | * Use of this source code is governed by an MIT-style license that can be
|
8705 | * found in the LICENSE file at https://angular.io/license
|
8706 | */
|
8707 | /**
|
8708 | * A path is an ordered set of elements. Typically a path is to a
|
8709 | * particular offset in a source file. The head of the list is the top
|
8710 | * most node. The tail is the node that contains the offset directly.
|
8711 | *
|
8712 | * For example, the expression `a + b + c` might have an ast that looks
|
8713 | * like:
|
8714 | * +
|
8715 | * / \
|
8716 | * a +
|
8717 | * / \
|
8718 | * b c
|
8719 | *
|
8720 | * The path to the node at offset 9 would be `['+' at 1-10, '+' at 7-10,
|
8721 | * 'c' at 9-10]` and the path the node at offset 1 would be
|
8722 | * `['+' at 1-10, 'a' at 1-2]`.
|
8723 | */
|
8724 | class AstPath {
|
8725 | constructor(path, position = -1) {
|
8726 | this.path = path;
|
8727 | this.position = position;
|
8728 | }
|
8729 | get empty() {
|
8730 | return !this.path || !this.path.length;
|
8731 | }
|
8732 | get head() {
|
8733 | return this.path[0];
|
8734 | }
|
8735 | get tail() {
|
8736 | return this.path[this.path.length - 1];
|
8737 | }
|
8738 | parentOf(node) {
|
8739 | return node && this.path[this.path.indexOf(node) - 1];
|
8740 | }
|
8741 | childOf(node) {
|
8742 | return this.path[this.path.indexOf(node) + 1];
|
8743 | }
|
8744 | first(ctor) {
|
8745 | for (let i = this.path.length - 1; i >= 0; i--) {
|
8746 | let item = this.path[i];
|
8747 | if (item instanceof ctor)
|
8748 | return item;
|
8749 | }
|
8750 | }
|
8751 | push(node) {
|
8752 | this.path.push(node);
|
8753 | }
|
8754 | pop() {
|
8755 | return this.path.pop();
|
8756 | }
|
8757 | }
|
8758 |
|
8759 | /**
|
8760 | * @license
|
8761 | * Copyright Google LLC All Rights Reserved.
|
8762 | *
|
8763 | * Use of this source code is governed by an MIT-style license that can be
|
8764 | * found in the LICENSE file at https://angular.io/license
|
8765 | */
|
8766 | class NodeWithI18n {
|
8767 | constructor(sourceSpan, i18n) {
|
8768 | this.sourceSpan = sourceSpan;
|
8769 | this.i18n = i18n;
|
8770 | }
|
8771 | }
|
8772 | class Text$2 extends NodeWithI18n {
|
8773 | constructor(value, sourceSpan, i18n) {
|
8774 | super(sourceSpan, i18n);
|
8775 | this.value = value;
|
8776 | }
|
8777 | visit(visitor, context) {
|
8778 | return visitor.visitText(this, context);
|
8779 | }
|
8780 | }
|
8781 | class Expansion extends NodeWithI18n {
|
8782 | constructor(switchValue, type, cases, sourceSpan, switchValueSourceSpan, i18n) {
|
8783 | super(sourceSpan, i18n);
|
8784 | this.switchValue = switchValue;
|
8785 | this.type = type;
|
8786 | this.cases = cases;
|
8787 | this.switchValueSourceSpan = switchValueSourceSpan;
|
8788 | }
|
8789 | visit(visitor, context) {
|
8790 | return visitor.visitExpansion(this, context);
|
8791 | }
|
8792 | }
|
8793 | class ExpansionCase {
|
8794 | constructor(value, expression, sourceSpan, valueSourceSpan, expSourceSpan) {
|
8795 | this.value = value;
|
8796 | this.expression = expression;
|
8797 | this.sourceSpan = sourceSpan;
|
8798 | this.valueSourceSpan = valueSourceSpan;
|
8799 | this.expSourceSpan = expSourceSpan;
|
8800 | }
|
8801 | visit(visitor, context) {
|
8802 | return visitor.visitExpansionCase(this, context);
|
8803 | }
|
8804 | }
|
8805 | class Attribute extends NodeWithI18n {
|
8806 | constructor(name, value, sourceSpan, keySpan, valueSpan, i18n) {
|
8807 | super(sourceSpan, i18n);
|
8808 | this.name = name;
|
8809 | this.value = value;
|
8810 | this.keySpan = keySpan;
|
8811 | this.valueSpan = valueSpan;
|
8812 | }
|
8813 | visit(visitor, context) {
|
8814 | return visitor.visitAttribute(this, context);
|
8815 | }
|
8816 | }
|
8817 | class Element$1 extends NodeWithI18n {
|
8818 | constructor(name, attrs, children, sourceSpan, startSourceSpan, endSourceSpan = null, i18n) {
|
8819 | super(sourceSpan, i18n);
|
8820 | this.name = name;
|
8821 | this.attrs = attrs;
|
8822 | this.children = children;
|
8823 | this.startSourceSpan = startSourceSpan;
|
8824 | this.endSourceSpan = endSourceSpan;
|
8825 | }
|
8826 | visit(visitor, context) {
|
8827 | return visitor.visitElement(this, context);
|
8828 | }
|
8829 | }
|
8830 | class Comment {
|
8831 | constructor(value, sourceSpan) {
|
8832 | this.value = value;
|
8833 | this.sourceSpan = sourceSpan;
|
8834 | }
|
8835 | visit(visitor, context) {
|
8836 | return visitor.visitComment(this, context);
|
8837 | }
|
8838 | }
|
8839 | function visitAll$1(visitor, nodes, context = null) {
|
8840 | const result = [];
|
8841 | const visit = visitor.visit ?
|
8842 | (ast) => visitor.visit(ast, context) || ast.visit(visitor, context) :
|
8843 | (ast) => ast.visit(visitor, context);
|
8844 | nodes.forEach(ast => {
|
8845 | const astResult = visit(ast);
|
8846 | if (astResult) {
|
8847 | result.push(astResult);
|
8848 | }
|
8849 | });
|
8850 | return result;
|
8851 | }
|
8852 | class RecursiveVisitor {
|
8853 | constructor() { }
|
8854 | visitElement(ast, context) {
|
8855 | this.visitChildren(context, visit => {
|
8856 | visit(ast.attrs);
|
8857 | visit(ast.children);
|
8858 | });
|
8859 | }
|
8860 | visitAttribute(ast, context) { }
|
8861 | visitText(ast, context) { }
|
8862 | visitComment(ast, context) { }
|
8863 | visitExpansion(ast, context) {
|
8864 | return this.visitChildren(context, visit => {
|
8865 | visit(ast.cases);
|
8866 | });
|
8867 | }
|
8868 | visitExpansionCase(ast, context) { }
|
8869 | visitChildren(context, cb) {
|
8870 | let results = [];
|
8871 | let t = this;
|
8872 | function visit(children) {
|
8873 | if (children)
|
8874 | results.push(visitAll$1(t, children, context));
|
8875 | }
|
8876 | cb(visit);
|
8877 | return Array.prototype.concat.apply([], results);
|
8878 | }
|
8879 | }
|
8880 |
|
8881 | /**
|
8882 | * @license
|
8883 | * Copyright Google LLC All Rights Reserved.
|
8884 | *
|
8885 | * Use of this source code is governed by an MIT-style license that can be
|
8886 | * found in the LICENSE file at https://angular.io/license
|
8887 | */
|
8888 | var TokenType;
|
8889 | (function (TokenType) {
|
8890 | TokenType[TokenType["TAG_OPEN_START"] = 0] = "TAG_OPEN_START";
|
8891 | TokenType[TokenType["TAG_OPEN_END"] = 1] = "TAG_OPEN_END";
|
8892 | TokenType[TokenType["TAG_OPEN_END_VOID"] = 2] = "TAG_OPEN_END_VOID";
|
8893 | TokenType[TokenType["TAG_CLOSE"] = 3] = "TAG_CLOSE";
|
8894 | TokenType[TokenType["INCOMPLETE_TAG_OPEN"] = 4] = "INCOMPLETE_TAG_OPEN";
|
8895 | TokenType[TokenType["TEXT"] = 5] = "TEXT";
|
8896 | TokenType[TokenType["ESCAPABLE_RAW_TEXT"] = 6] = "ESCAPABLE_RAW_TEXT";
|
8897 | TokenType[TokenType["RAW_TEXT"] = 7] = "RAW_TEXT";
|
8898 | TokenType[TokenType["COMMENT_START"] = 8] = "COMMENT_START";
|
8899 | TokenType[TokenType["COMMENT_END"] = 9] = "COMMENT_END";
|
8900 | TokenType[TokenType["CDATA_START"] = 10] = "CDATA_START";
|
8901 | TokenType[TokenType["CDATA_END"] = 11] = "CDATA_END";
|
8902 | TokenType[TokenType["ATTR_NAME"] = 12] = "ATTR_NAME";
|
8903 | TokenType[TokenType["ATTR_QUOTE"] = 13] = "ATTR_QUOTE";
|
8904 | TokenType[TokenType["ATTR_VALUE"] = 14] = "ATTR_VALUE";
|
8905 | TokenType[TokenType["DOC_TYPE"] = 15] = "DOC_TYPE";
|
8906 | TokenType[TokenType["EXPANSION_FORM_START"] = 16] = "EXPANSION_FORM_START";
|
8907 | TokenType[TokenType["EXPANSION_CASE_VALUE"] = 17] = "EXPANSION_CASE_VALUE";
|
8908 | TokenType[TokenType["EXPANSION_CASE_EXP_START"] = 18] = "EXPANSION_CASE_EXP_START";
|
8909 | TokenType[TokenType["EXPANSION_CASE_EXP_END"] = 19] = "EXPANSION_CASE_EXP_END";
|
8910 | TokenType[TokenType["EXPANSION_FORM_END"] = 20] = "EXPANSION_FORM_END";
|
8911 | TokenType[TokenType["EOF"] = 21] = "EOF";
|
8912 | })(TokenType || (TokenType = {}));
|
8913 | class Token {
|
8914 | constructor(type, parts, sourceSpan) {
|
8915 | this.type = type;
|
8916 | this.parts = parts;
|
8917 | this.sourceSpan = sourceSpan;
|
8918 | }
|
8919 | }
|
8920 | class TokenError extends ParseError {
|
8921 | constructor(errorMsg, tokenType, span) {
|
8922 | super(span, errorMsg);
|
8923 | this.tokenType = tokenType;
|
8924 | }
|
8925 | }
|
8926 | class TokenizeResult {
|
8927 | constructor(tokens, errors, nonNormalizedIcuExpressions) {
|
8928 | this.tokens = tokens;
|
8929 | this.errors = errors;
|
8930 | this.nonNormalizedIcuExpressions = nonNormalizedIcuExpressions;
|
8931 | }
|
8932 | }
|
8933 | function tokenize(source, url, getTagDefinition, options = {}) {
|
8934 | const tokenizer = new _Tokenizer(new ParseSourceFile(source, url), getTagDefinition, options);
|
8935 | tokenizer.tokenize();
|
8936 | return new TokenizeResult(mergeTextTokens(tokenizer.tokens), tokenizer.errors, tokenizer.nonNormalizedIcuExpressions);
|
8937 | }
|
8938 | const _CR_OR_CRLF_REGEXP = /\r\n?/g;
|
8939 | function _unexpectedCharacterErrorMsg(charCode) {
|
8940 | const char = charCode === $EOF ? 'EOF' : String.fromCharCode(charCode);
|
8941 | return `Unexpected character "${char}"`;
|
8942 | }
|
8943 | function _unknownEntityErrorMsg(entitySrc) {
|
8944 | return `Unknown entity "${entitySrc}" - use the "&#<decimal>;" or "&#x<hex>;" syntax`;
|
8945 | }
|
8946 | function _unparsableEntityErrorMsg(type, entityStr) {
|
8947 | return `Unable to parse entity "${entityStr}" - ${type} character reference entities must end with ";"`;
|
8948 | }
|
8949 | var CharacterReferenceType;
|
8950 | (function (CharacterReferenceType) {
|
8951 | CharacterReferenceType["HEX"] = "hexadecimal";
|
8952 | CharacterReferenceType["DEC"] = "decimal";
|
8953 | })(CharacterReferenceType || (CharacterReferenceType = {}));
|
8954 | class _ControlFlowError {
|
8955 | constructor(error) {
|
8956 | this.error = error;
|
8957 | }
|
8958 | }
|
8959 | // See https://www.w3.org/TR/html51/syntax.html#writing-html-documents
|
8960 | class _Tokenizer {
|
8961 | /**
|
8962 | * @param _file The html source file being tokenized.
|
8963 | * @param _getTagDefinition A function that will retrieve a tag definition for a given tag name.
|
8964 | * @param options Configuration of the tokenization.
|
8965 | */
|
8966 | constructor(_file, _getTagDefinition, options) {
|
8967 | this._getTagDefinition = _getTagDefinition;
|
8968 | this._currentTokenStart = null;
|
8969 | this._currentTokenType = null;
|
8970 | this._expansionCaseStack = [];
|
8971 | this._inInterpolation = false;
|
8972 | this.tokens = [];
|
8973 | this.errors = [];
|
8974 | this.nonNormalizedIcuExpressions = [];
|
8975 | this._tokenizeIcu = options.tokenizeExpansionForms || false;
|
8976 | this._interpolationConfig = options.interpolationConfig || DEFAULT_INTERPOLATION_CONFIG;
|
8977 | this._leadingTriviaCodePoints =
|
8978 | options.leadingTriviaChars && options.leadingTriviaChars.map(c => c.codePointAt(0) || 0);
|
8979 | const range = options.range || { endPos: _file.content.length, startPos: 0, startLine: 0, startCol: 0 };
|
8980 | this._cursor = options.escapedString ? new EscapedCharacterCursor(_file, range) :
|
8981 | new PlainCharacterCursor(_file, range);
|
8982 | this._preserveLineEndings = options.preserveLineEndings || false;
|
8983 | this._escapedString = options.escapedString || false;
|
8984 | this._i18nNormalizeLineEndingsInICUs = options.i18nNormalizeLineEndingsInICUs || false;
|
8985 | try {
|
8986 | this._cursor.init();
|
8987 | }
|
8988 | catch (e) {
|
8989 | this.handleError(e);
|
8990 | }
|
8991 | }
|
8992 | _processCarriageReturns(content) {
|
8993 | if (this._preserveLineEndings) {
|
8994 | return content;
|
8995 | }
|
8996 | // https://www.w3.org/TR/html51/syntax.html#preprocessing-the-input-stream
|
8997 | // In order to keep the original position in the source, we can not
|
8998 | // pre-process it.
|
8999 | // Instead CRs are processed right before instantiating the tokens.
|
9000 | return content.replace(_CR_OR_CRLF_REGEXP, '\n');
|
9001 | }
|
9002 | tokenize() {
|
9003 | while (this._cursor.peek() !== $EOF) {
|
9004 | const start = this._cursor.clone();
|
9005 | try {
|
9006 | if (this._attemptCharCode($LT)) {
|
9007 | if (this._attemptCharCode($BANG)) {
|
9008 | if (this._attemptCharCode($LBRACKET)) {
|
9009 | this._consumeCdata(start);
|
9010 | }
|
9011 | else if (this._attemptCharCode($MINUS)) {
|
9012 | this._consumeComment(start);
|
9013 | }
|
9014 | else {
|
9015 | this._consumeDocType(start);
|
9016 | }
|
9017 | }
|
9018 | else if (this._attemptCharCode($SLASH)) {
|
9019 | this._consumeTagClose(start);
|
9020 | }
|
9021 | else {
|
9022 | this._consumeTagOpen(start);
|
9023 | }
|
9024 | }
|
9025 | else if (!(this._tokenizeIcu && this._tokenizeExpansionForm())) {
|
9026 | this._consumeText();
|
9027 | }
|
9028 | }
|
9029 | catch (e) {
|
9030 | this.handleError(e);
|
9031 | }
|
9032 | }
|
9033 | this._beginToken(TokenType.EOF);
|
9034 | this._endToken([]);
|
9035 | }
|
9036 | /**
|
9037 | * @returns whether an ICU token has been created
|
9038 | * @internal
|
9039 | */
|
9040 | _tokenizeExpansionForm() {
|
9041 | if (this.isExpansionFormStart()) {
|
9042 | this._consumeExpansionFormStart();
|
9043 | return true;
|
9044 | }
|
9045 | if (isExpansionCaseStart(this._cursor.peek()) && this._isInExpansionForm()) {
|
9046 | this._consumeExpansionCaseStart();
|
9047 | return true;
|
9048 | }
|
9049 | if (this._cursor.peek() === $RBRACE) {
|
9050 | if (this._isInExpansionCase()) {
|
9051 | this._consumeExpansionCaseEnd();
|
9052 | return true;
|
9053 | }
|
9054 | if (this._isInExpansionForm()) {
|
9055 | this._consumeExpansionFormEnd();
|
9056 | return true;
|
9057 | }
|
9058 | }
|
9059 | return false;
|
9060 | }
|
9061 | _beginToken(type, start = this._cursor.clone()) {
|
9062 | this._currentTokenStart = start;
|
9063 | this._currentTokenType = type;
|
9064 | }
|
9065 | _endToken(parts, end) {
|
9066 | if (this._currentTokenStart === null) {
|
9067 | throw new TokenError('Programming error - attempted to end a token when there was no start to the token', this._currentTokenType, this._cursor.getSpan(end));
|
9068 | }
|
9069 | if (this._currentTokenType === null) {
|
9070 | throw new TokenError('Programming error - attempted to end a token which has no token type', null, this._cursor.getSpan(this._currentTokenStart));
|
9071 | }
|
9072 | const token = new Token(this._currentTokenType, parts, this._cursor.getSpan(this._currentTokenStart, this._leadingTriviaCodePoints));
|
9073 | this.tokens.push(token);
|
9074 | this._currentTokenStart = null;
|
9075 | this._currentTokenType = null;
|
9076 | return token;
|
9077 | }
|
9078 | _createError(msg, span) {
|
9079 | if (this._isInExpansionForm()) {
|
9080 | msg += ` (Do you have an unescaped "{" in your template? Use "{{ '{' }}") to escape it.)`;
|
9081 | }
|
9082 | const error = new TokenError(msg, this._currentTokenType, span);
|
9083 | this._currentTokenStart = null;
|
9084 | this._currentTokenType = null;
|
9085 | return new _ControlFlowError(error);
|
9086 | }
|
9087 | handleError(e) {
|
9088 | if (e instanceof CursorError) {
|
9089 | e = this._createError(e.msg, this._cursor.getSpan(e.cursor));
|
9090 | }
|
9091 | if (e instanceof _ControlFlowError) {
|
9092 | this.errors.push(e.error);
|
9093 | }
|
9094 | else {
|
9095 | throw e;
|
9096 | }
|
9097 | }
|
9098 | _attemptCharCode(charCode) {
|
9099 | if (this._cursor.peek() === charCode) {
|
9100 | this._cursor.advance();
|
9101 | return true;
|
9102 | }
|
9103 | return false;
|
9104 | }
|
9105 | _attemptCharCodeCaseInsensitive(charCode) {
|
9106 | if (compareCharCodeCaseInsensitive(this._cursor.peek(), charCode)) {
|
9107 | this._cursor.advance();
|
9108 | return true;
|
9109 | }
|
9110 | return false;
|
9111 | }
|
9112 | _requireCharCode(charCode) {
|
9113 | const location = this._cursor.clone();
|
9114 | if (!this._attemptCharCode(charCode)) {
|
9115 | throw this._createError(_unexpectedCharacterErrorMsg(this._cursor.peek()), this._cursor.getSpan(location));
|
9116 | }
|
9117 | }
|
9118 | _attemptStr(chars) {
|
9119 | const len = chars.length;
|
9120 | if (this._cursor.charsLeft() < len) {
|
9121 | return false;
|
9122 | }
|
9123 | const initialPosition = this._cursor.clone();
|
9124 | for (let i = 0; i < len; i++) {
|
9125 | if (!this._attemptCharCode(chars.charCodeAt(i))) {
|
9126 | // If attempting to parse the string fails, we want to reset the parser
|
9127 | // to where it was before the attempt
|
9128 | this._cursor = initialPosition;
|
9129 | return false;
|
9130 | }
|
9131 | }
|
9132 | return true;
|
9133 | }
|
9134 | _attemptStrCaseInsensitive(chars) {
|
9135 | for (let i = 0; i < chars.length; i++) {
|
9136 | if (!this._attemptCharCodeCaseInsensitive(chars.charCodeAt(i))) {
|
9137 | return false;
|
9138 | }
|
9139 | }
|
9140 | return true;
|
9141 | }
|
9142 | _requireStr(chars) {
|
9143 | const location = this._cursor.clone();
|
9144 | if (!this._attemptStr(chars)) {
|
9145 | throw this._createError(_unexpectedCharacterErrorMsg(this._cursor.peek()), this._cursor.getSpan(location));
|
9146 | }
|
9147 | }
|
9148 | _attemptCharCodeUntilFn(predicate) {
|
9149 | while (!predicate(this._cursor.peek())) {
|
9150 | this._cursor.advance();
|
9151 | }
|
9152 | }
|
9153 | _requireCharCodeUntilFn(predicate, len) {
|
9154 | const start = this._cursor.clone();
|
9155 | this._attemptCharCodeUntilFn(predicate);
|
9156 | if (this._cursor.diff(start) < len) {
|
9157 | throw this._createError(_unexpectedCharacterErrorMsg(this._cursor.peek()), this._cursor.getSpan(start));
|
9158 | }
|
9159 | }
|
9160 | _attemptUntilChar(char) {
|
9161 | while (this._cursor.peek() !== char) {
|
9162 | this._cursor.advance();
|
9163 | }
|
9164 | }
|
9165 | _readChar(decodeEntities) {
|
9166 | if (decodeEntities && this._cursor.peek() === $AMPERSAND) {
|
9167 | return this._decodeEntity();
|
9168 | }
|
9169 | else {
|
9170 | // Don't rely upon reading directly from `_input` as the actual char value
|
9171 | // may have been generated from an escape sequence.
|
9172 | const char = String.fromCodePoint(this._cursor.peek());
|
9173 | this._cursor.advance();
|
9174 | return char;
|
9175 | }
|
9176 | }
|
9177 | _decodeEntity() {
|
9178 | const start = this._cursor.clone();
|
9179 | this._cursor.advance();
|
9180 | if (this._attemptCharCode($HASH)) {
|
9181 | const isHex = this._attemptCharCode($x) || this._attemptCharCode($X);
|
9182 | const codeStart = this._cursor.clone();
|
9183 | this._attemptCharCodeUntilFn(isDigitEntityEnd);
|
9184 | if (this._cursor.peek() != $SEMICOLON) {
|
9185 | // Advance cursor to include the peeked character in the string provided to the error
|
9186 | // message.
|
9187 | this._cursor.advance();
|
9188 | const entityType = isHex ? CharacterReferenceType.HEX : CharacterReferenceType.DEC;
|
9189 | throw this._createError(_unparsableEntityErrorMsg(entityType, this._cursor.getChars(start)), this._cursor.getSpan());
|
9190 | }
|
9191 | const strNum = this._cursor.getChars(codeStart);
|
9192 | this._cursor.advance();
|
9193 | try {
|
9194 | const charCode = parseInt(strNum, isHex ? 16 : 10);
|
9195 | return String.fromCharCode(charCode);
|
9196 | }
|
9197 | catch (_a) {
|
9198 | throw this._createError(_unknownEntityErrorMsg(this._cursor.getChars(start)), this._cursor.getSpan());
|
9199 | }
|
9200 | }
|
9201 | else {
|
9202 | const nameStart = this._cursor.clone();
|
9203 | this._attemptCharCodeUntilFn(isNamedEntityEnd);
|
9204 | if (this._cursor.peek() != $SEMICOLON) {
|
9205 | this._cursor = nameStart;
|
9206 | return '&';
|
9207 | }
|
9208 | const name = this._cursor.getChars(nameStart);
|
9209 | this._cursor.advance();
|
9210 | const char = NAMED_ENTITIES[name];
|
9211 | if (!char) {
|
9212 | throw this._createError(_unknownEntityErrorMsg(name), this._cursor.getSpan(start));
|
9213 | }
|
9214 | return char;
|
9215 | }
|
9216 | }
|
9217 | _consumeRawText(decodeEntities, endMarkerPredicate) {
|
9218 | this._beginToken(decodeEntities ? TokenType.ESCAPABLE_RAW_TEXT : TokenType.RAW_TEXT);
|
9219 | const parts = [];
|
9220 | while (true) {
|
9221 | const tagCloseStart = this._cursor.clone();
|
9222 | const foundEndMarker = endMarkerPredicate();
|
9223 | this._cursor = tagCloseStart;
|
9224 | if (foundEndMarker) {
|
9225 | break;
|
9226 | }
|
9227 | parts.push(this._readChar(decodeEntities));
|
9228 | }
|
9229 | return this._endToken([this._processCarriageReturns(parts.join(''))]);
|
9230 | }
|
9231 | _consumeComment(start) {
|
9232 | this._beginToken(TokenType.COMMENT_START, start);
|
9233 | this._requireCharCode($MINUS);
|
9234 | this._endToken([]);
|
9235 | this._consumeRawText(false, () => this._attemptStr('-->'));
|
9236 | this._beginToken(TokenType.COMMENT_END);
|
9237 | this._requireStr('-->');
|
9238 | this._endToken([]);
|
9239 | }
|
9240 | _consumeCdata(start) {
|
9241 | this._beginToken(TokenType.CDATA_START, start);
|
9242 | this._requireStr('CDATA[');
|
9243 | this._endToken([]);
|
9244 | this._consumeRawText(false, () => this._attemptStr(']]>'));
|
9245 | this._beginToken(TokenType.CDATA_END);
|
9246 | this._requireStr(']]>');
|
9247 | this._endToken([]);
|
9248 | }
|
9249 | _consumeDocType(start) {
|
9250 | this._beginToken(TokenType.DOC_TYPE, start);
|
9251 | const contentStart = this._cursor.clone();
|
9252 | this._attemptUntilChar($GT);
|
9253 | const content = this._cursor.getChars(contentStart);
|
9254 | this._cursor.advance();
|
9255 | this._endToken([content]);
|
9256 | }
|
9257 | _consumePrefixAndName() {
|
9258 | const nameOrPrefixStart = this._cursor.clone();
|
9259 | let prefix = '';
|
9260 | while (this._cursor.peek() !== $COLON && !isPrefixEnd(this._cursor.peek())) {
|
9261 | this._cursor.advance();
|
9262 | }
|
9263 | let nameStart;
|
9264 | if (this._cursor.peek() === $COLON) {
|
9265 | prefix = this._cursor.getChars(nameOrPrefixStart);
|
9266 | this._cursor.advance();
|
9267 | nameStart = this._cursor.clone();
|
9268 | }
|
9269 | else {
|
9270 | nameStart = nameOrPrefixStart;
|
9271 | }
|
9272 | this._requireCharCodeUntilFn(isNameEnd, prefix === '' ? 0 : 1);
|
9273 | const name = this._cursor.getChars(nameStart);
|
9274 | return [prefix, name];
|
9275 | }
|
9276 | _consumeTagOpen(start) {
|
9277 | let tagName;
|
9278 | let prefix;
|
9279 | let openTagToken;
|
9280 | try {
|
9281 | if (!isAsciiLetter(this._cursor.peek())) {
|
9282 | throw this._createError(_unexpectedCharacterErrorMsg(this._cursor.peek()), this._cursor.getSpan(start));
|
9283 | }
|
9284 | openTagToken = this._consumeTagOpenStart(start);
|
9285 | prefix = openTagToken.parts[0];
|
9286 | tagName = openTagToken.parts[1];
|
9287 | this._attemptCharCodeUntilFn(isNotWhitespace);
|
9288 | while (this._cursor.peek() !== $SLASH && this._cursor.peek() !== $GT &&
|
9289 | this._cursor.peek() !== $LT && this._cursor.peek() !== $EOF) {
|
9290 | this._consumeAttributeName();
|
9291 | this._attemptCharCodeUntilFn(isNotWhitespace);
|
9292 | if (this._attemptCharCode($EQ)) {
|
9293 | this._attemptCharCodeUntilFn(isNotWhitespace);
|
9294 | this._consumeAttributeValue();
|
9295 | }
|
9296 | this._attemptCharCodeUntilFn(isNotWhitespace);
|
9297 | }
|
9298 | this._consumeTagOpenEnd();
|
9299 | }
|
9300 | catch (e) {
|
9301 | if (e instanceof _ControlFlowError) {
|
9302 | if (openTagToken) {
|
9303 | // We errored before we could close the opening tag, so it is incomplete.
|
9304 | openTagToken.type = TokenType.INCOMPLETE_TAG_OPEN;
|
9305 | }
|
9306 | else {
|
9307 | // When the start tag is invalid, assume we want a "<" as text.
|
9308 | // Back to back text tokens are merged at the end.
|
9309 | this._beginToken(TokenType.TEXT, start);
|
9310 | this._endToken(['<']);
|
9311 | }
|
9312 | return;
|
9313 | }
|
9314 | throw e;
|
9315 | }
|
9316 | const contentTokenType = this._getTagDefinition(tagName).getContentType(prefix);
|
9317 | if (contentTokenType === TagContentType.RAW_TEXT) {
|
9318 | this._consumeRawTextWithTagClose(prefix, tagName, false);
|
9319 | }
|
9320 | else if (contentTokenType === TagContentType.ESCAPABLE_RAW_TEXT) {
|
9321 | this._consumeRawTextWithTagClose(prefix, tagName, true);
|
9322 | }
|
9323 | }
|
9324 | _consumeRawTextWithTagClose(prefix, tagName, decodeEntities) {
|
9325 | this._consumeRawText(decodeEntities, () => {
|
9326 | if (!this._attemptCharCode($LT))
|
9327 | return false;
|
9328 | if (!this._attemptCharCode($SLASH))
|
9329 | return false;
|
9330 | this._attemptCharCodeUntilFn(isNotWhitespace);
|
9331 | if (!this._attemptStrCaseInsensitive(tagName))
|
9332 | return false;
|
9333 | this._attemptCharCodeUntilFn(isNotWhitespace);
|
9334 | return this._attemptCharCode($GT);
|
9335 | });
|
9336 | this._beginToken(TokenType.TAG_CLOSE);
|
9337 | this._requireCharCodeUntilFn(code => code === $GT, 3);
|
9338 | this._cursor.advance(); // Consume the `>`
|
9339 | this._endToken([prefix, tagName]);
|
9340 | }
|
9341 | _consumeTagOpenStart(start) {
|
9342 | this._beginToken(TokenType.TAG_OPEN_START, start);
|
9343 | const parts = this._consumePrefixAndName();
|
9344 | return this._endToken(parts);
|
9345 | }
|
9346 | _consumeAttributeName() {
|
9347 | const attrNameStart = this._cursor.peek();
|
9348 | if (attrNameStart === $SQ || attrNameStart === $DQ) {
|
9349 | throw this._createError(_unexpectedCharacterErrorMsg(attrNameStart), this._cursor.getSpan());
|
9350 | }
|
9351 | this._beginToken(TokenType.ATTR_NAME);
|
9352 | const prefixAndName = this._consumePrefixAndName();
|
9353 | this._endToken(prefixAndName);
|
9354 | }
|
9355 | _consumeAttributeValue() {
|
9356 | let value;
|
9357 | if (this._cursor.peek() === $SQ || this._cursor.peek() === $DQ) {
|
9358 | this._beginToken(TokenType.ATTR_QUOTE);
|
9359 | const quoteChar = this._cursor.peek();
|
9360 | this._cursor.advance();
|
9361 | this._endToken([String.fromCodePoint(quoteChar)]);
|
9362 | this._beginToken(TokenType.ATTR_VALUE);
|
9363 | const parts = [];
|
9364 | while (this._cursor.peek() !== quoteChar) {
|
9365 | parts.push(this._readChar(true));
|
9366 | }
|
9367 | value = parts.join('');
|
9368 | this._endToken([this._processCarriageReturns(value)]);
|
9369 | this._beginToken(TokenType.ATTR_QUOTE);
|
9370 | this._cursor.advance();
|
9371 | this._endToken([String.fromCodePoint(quoteChar)]);
|
9372 | }
|
9373 | else {
|
9374 | this._beginToken(TokenType.ATTR_VALUE);
|
9375 | const valueStart = this._cursor.clone();
|
9376 | this._requireCharCodeUntilFn(isNameEnd, 1);
|
9377 | value = this._cursor.getChars(valueStart);
|
9378 | this._endToken([this._processCarriageReturns(value)]);
|
9379 | }
|
9380 | }
|
9381 | _consumeTagOpenEnd() {
|
9382 | const tokenType = this._attemptCharCode($SLASH) ? TokenType.TAG_OPEN_END_VOID : TokenType.TAG_OPEN_END;
|
9383 | this._beginToken(tokenType);
|
9384 | this._requireCharCode($GT);
|
9385 | this._endToken([]);
|
9386 | }
|
9387 | _consumeTagClose(start) {
|
9388 | this._beginToken(TokenType.TAG_CLOSE, start);
|
9389 | this._attemptCharCodeUntilFn(isNotWhitespace);
|
9390 | const prefixAndName = this._consumePrefixAndName();
|
9391 | this._attemptCharCodeUntilFn(isNotWhitespace);
|
9392 | this._requireCharCode($GT);
|
9393 | this._endToken(prefixAndName);
|
9394 | }
|
9395 | _consumeExpansionFormStart() {
|
9396 | this._beginToken(TokenType.EXPANSION_FORM_START);
|
9397 | this._requireCharCode($LBRACE);
|
9398 | this._endToken([]);
|
9399 | this._expansionCaseStack.push(TokenType.EXPANSION_FORM_START);
|
9400 | this._beginToken(TokenType.RAW_TEXT);
|
9401 | const condition = this._readUntil($COMMA);
|
9402 | const normalizedCondition = this._processCarriageReturns(condition);
|
9403 | if (this._i18nNormalizeLineEndingsInICUs) {
|
9404 | // We explicitly want to normalize line endings for this text.
|
9405 | this._endToken([normalizedCondition]);
|
9406 | }
|
9407 | else {
|
9408 | // We are not normalizing line endings.
|
9409 | const conditionToken = this._endToken([condition]);
|
9410 | if (normalizedCondition !== condition) {
|
9411 | this.nonNormalizedIcuExpressions.push(conditionToken);
|
9412 | }
|
9413 | }
|
9414 | this._requireCharCode($COMMA);
|
9415 | this._attemptCharCodeUntilFn(isNotWhitespace);
|
9416 | this._beginToken(TokenType.RAW_TEXT);
|
9417 | const type = this._readUntil($COMMA);
|
9418 | this._endToken([type]);
|
9419 | this._requireCharCode($COMMA);
|
9420 | this._attemptCharCodeUntilFn(isNotWhitespace);
|
9421 | }
|
9422 | _consumeExpansionCaseStart() {
|
9423 | this._beginToken(TokenType.EXPANSION_CASE_VALUE);
|
9424 | const value = this._readUntil($LBRACE).trim();
|
9425 | this._endToken([value]);
|
9426 | this._attemptCharCodeUntilFn(isNotWhitespace);
|
9427 | this._beginToken(TokenType.EXPANSION_CASE_EXP_START);
|
9428 | this._requireCharCode($LBRACE);
|
9429 | this._endToken([]);
|
9430 | this._attemptCharCodeUntilFn(isNotWhitespace);
|
9431 | this._expansionCaseStack.push(TokenType.EXPANSION_CASE_EXP_START);
|
9432 | }
|
9433 | _consumeExpansionCaseEnd() {
|
9434 | this._beginToken(TokenType.EXPANSION_CASE_EXP_END);
|
9435 | this._requireCharCode($RBRACE);
|
9436 | this._endToken([]);
|
9437 | this._attemptCharCodeUntilFn(isNotWhitespace);
|
9438 | this._expansionCaseStack.pop();
|
9439 | }
|
9440 | _consumeExpansionFormEnd() {
|
9441 | this._beginToken(TokenType.EXPANSION_FORM_END);
|
9442 | this._requireCharCode($RBRACE);
|
9443 | this._endToken([]);
|
9444 | this._expansionCaseStack.pop();
|
9445 | }
|
9446 | _consumeText() {
|
9447 | const start = this._cursor.clone();
|
9448 | this._beginToken(TokenType.TEXT, start);
|
9449 | const parts = [];
|
9450 | do {
|
9451 | if (this._interpolationConfig && this._attemptStr(this._interpolationConfig.start)) {
|
9452 | parts.push(this._interpolationConfig.start);
|
9453 | this._inInterpolation = true;
|
9454 | }
|
9455 | else if (this._interpolationConfig && this._inInterpolation &&
|
9456 | this._attemptStr(this._interpolationConfig.end)) {
|
9457 | parts.push(this._interpolationConfig.end);
|
9458 | this._inInterpolation = false;
|
9459 | }
|
9460 | else {
|
9461 | parts.push(this._readChar(true));
|
9462 | }
|
9463 | } while (!this._isTextEnd());
|
9464 | this._endToken([this._processCarriageReturns(parts.join(''))]);
|
9465 | }
|
9466 | _isTextEnd() {
|
9467 | if (this._cursor.peek() === $LT || this._cursor.peek() === $EOF) {
|
9468 | return true;
|
9469 | }
|
9470 | if (this._tokenizeIcu && !this._inInterpolation) {
|
9471 | if (this.isExpansionFormStart()) {
|
9472 | // start of an expansion form
|
9473 | return true;
|
9474 | }
|
9475 | if (this._cursor.peek() === $RBRACE && this._isInExpansionCase()) {
|
9476 | // end of and expansion case
|
9477 | return true;
|
9478 | }
|
9479 | }
|
9480 | return false;
|
9481 | }
|
9482 | _readUntil(char) {
|
9483 | const start = this._cursor.clone();
|
9484 | this._attemptUntilChar(char);
|
9485 | return this._cursor.getChars(start);
|
9486 | }
|
9487 | _isInExpansionCase() {
|
9488 | return this._expansionCaseStack.length > 0 &&
|
9489 | this._expansionCaseStack[this._expansionCaseStack.length - 1] ===
|
9490 | TokenType.EXPANSION_CASE_EXP_START;
|
9491 | }
|
9492 | _isInExpansionForm() {
|
9493 | return this._expansionCaseStack.length > 0 &&
|
9494 | this._expansionCaseStack[this._expansionCaseStack.length - 1] ===
|
9495 | TokenType.EXPANSION_FORM_START;
|
9496 | }
|
9497 | isExpansionFormStart() {
|
9498 | if (this._cursor.peek() !== $LBRACE) {
|
9499 | return false;
|
9500 | }
|
9501 | if (this._interpolationConfig) {
|
9502 | const start = this._cursor.clone();
|
9503 | const isInterpolation = this._attemptStr(this._interpolationConfig.start);
|
9504 | this._cursor = start;
|
9505 | return !isInterpolation;
|
9506 | }
|
9507 | return true;
|
9508 | }
|
9509 | }
|
9510 | function isNotWhitespace(code) {
|
9511 | return !isWhitespace(code) || code === $EOF;
|
9512 | }
|
9513 | function isNameEnd(code) {
|
9514 | return isWhitespace(code) || code === $GT || code === $LT ||
|
9515 | code === $SLASH || code === $SQ || code === $DQ || code === $EQ ||
|
9516 | code === $EOF;
|
9517 | }
|
9518 | function isPrefixEnd(code) {
|
9519 | return (code < $a || $z < code) && (code < $A || $Z < code) &&
|
9520 | (code < $0 || code > $9);
|
9521 | }
|
9522 | function isDigitEntityEnd(code) {
|
9523 | return code == $SEMICOLON || code == $EOF || !isAsciiHexDigit(code);
|
9524 | }
|
9525 | function isNamedEntityEnd(code) {
|
9526 | return code == $SEMICOLON || code == $EOF || !isAsciiLetter(code);
|
9527 | }
|
9528 | function isExpansionCaseStart(peek) {
|
9529 | return peek !== $RBRACE;
|
9530 | }
|
9531 | function compareCharCodeCaseInsensitive(code1, code2) {
|
9532 | return toUpperCaseCharCode(code1) == toUpperCaseCharCode(code2);
|
9533 | }
|
9534 | function toUpperCaseCharCode(code) {
|
9535 | return code >= $a && code <= $z ? code - $a + $A : code;
|
9536 | }
|
9537 | function mergeTextTokens(srcTokens) {
|
9538 | const dstTokens = [];
|
9539 | let lastDstToken = undefined;
|
9540 | for (let i = 0; i < srcTokens.length; i++) {
|
9541 | const token = srcTokens[i];
|
9542 | if (lastDstToken && lastDstToken.type == TokenType.TEXT && token.type == TokenType.TEXT) {
|
9543 | lastDstToken.parts[0] += token.parts[0];
|
9544 | lastDstToken.sourceSpan.end = token.sourceSpan.end;
|
9545 | }
|
9546 | else {
|
9547 | lastDstToken = token;
|
9548 | dstTokens.push(lastDstToken);
|
9549 | }
|
9550 | }
|
9551 | return dstTokens;
|
9552 | }
|
9553 | class PlainCharacterCursor {
|
9554 | constructor(fileOrCursor, range) {
|
9555 | if (fileOrCursor instanceof PlainCharacterCursor) {
|
9556 | this.file = fileOrCursor.file;
|
9557 | this.input = fileOrCursor.input;
|
9558 | this.end = fileOrCursor.end;
|
9559 | const state = fileOrCursor.state;
|
9560 | // Note: avoid using `{...fileOrCursor.state}` here as that has a severe performance penalty.
|
9561 | // In ES5 bundles the object spread operator is translated into the `__assign` helper, which
|
9562 | // is not optimized by VMs as efficiently as a raw object literal. Since this constructor is
|
9563 | // called in tight loops, this difference matters.
|
9564 | this.state = {
|
9565 | peek: state.peek,
|
9566 | offset: state.offset,
|
9567 | line: state.line,
|
9568 | column: state.column,
|
9569 | };
|
9570 | }
|
9571 | else {
|
9572 | if (!range) {
|
9573 | throw new Error('Programming error: the range argument must be provided with a file argument.');
|
9574 | }
|
9575 | this.file = fileOrCursor;
|
9576 | this.input = fileOrCursor.content;
|
9577 | this.end = range.endPos;
|
9578 | this.state = {
|
9579 | peek: -1,
|
9580 | offset: range.startPos,
|
9581 | line: range.startLine,
|
9582 | column: range.startCol,
|
9583 | };
|
9584 | }
|
9585 | }
|
9586 | clone() {
|
9587 | return new PlainCharacterCursor(this);
|
9588 | }
|
9589 | peek() {
|
9590 | return this.state.peek;
|
9591 | }
|
9592 | charsLeft() {
|
9593 | return this.end - this.state.offset;
|
9594 | }
|
9595 | diff(other) {
|
9596 | return this.state.offset - other.state.offset;
|
9597 | }
|
9598 | advance() {
|
9599 | this.advanceState(this.state);
|
9600 | }
|
9601 | init() {
|
9602 | this.updatePeek(this.state);
|
9603 | }
|
9604 | getSpan(start, leadingTriviaCodePoints) {
|
9605 | start = start || this;
|
9606 | let fullStart = start;
|
9607 | if (leadingTriviaCodePoints) {
|
9608 | while (this.diff(start) > 0 && leadingTriviaCodePoints.indexOf(start.peek()) !== -1) {
|
9609 | if (fullStart === start) {
|
9610 | start = start.clone();
|
9611 | }
|
9612 | start.advance();
|
9613 | }
|
9614 | }
|
9615 | const startLocation = this.locationFromCursor(start);
|
9616 | const endLocation = this.locationFromCursor(this);
|
9617 | const fullStartLocation = fullStart !== start ? this.locationFromCursor(fullStart) : startLocation;
|
9618 | return new ParseSourceSpan(startLocation, endLocation, fullStartLocation);
|
9619 | }
|
9620 | getChars(start) {
|
9621 | return this.input.substring(start.state.offset, this.state.offset);
|
9622 | }
|
9623 | charAt(pos) {
|
9624 | return this.input.charCodeAt(pos);
|
9625 | }
|
9626 | advanceState(state) {
|
9627 | if (state.offset >= this.end) {
|
9628 | this.state = state;
|
9629 | throw new CursorError('Unexpected character "EOF"', this);
|
9630 | }
|
9631 | const currentChar = this.charAt(state.offset);
|
9632 | if (currentChar === $LF) {
|
9633 | state.line++;
|
9634 | state.column = 0;
|
9635 | }
|
9636 | else if (!isNewLine(currentChar)) {
|
9637 | state.column++;
|
9638 | }
|
9639 | state.offset++;
|
9640 | this.updatePeek(state);
|
9641 | }
|
9642 | updatePeek(state) {
|
9643 | state.peek = state.offset >= this.end ? $EOF : this.charAt(state.offset);
|
9644 | }
|
9645 | locationFromCursor(cursor) {
|
9646 | return new ParseLocation(cursor.file, cursor.state.offset, cursor.state.line, cursor.state.column);
|
9647 | }
|
9648 | }
|
9649 | class EscapedCharacterCursor extends PlainCharacterCursor {
|
9650 | constructor(fileOrCursor, range) {
|
9651 | if (fileOrCursor instanceof EscapedCharacterCursor) {
|
9652 | super(fileOrCursor);
|
9653 | this.internalState = Object.assign({}, fileOrCursor.internalState);
|
9654 | }
|
9655 | else {
|
9656 | super(fileOrCursor, range);
|
9657 | this.internalState = this.state;
|
9658 | }
|
9659 | }
|
9660 | advance() {
|
9661 | this.state = this.internalState;
|
9662 | super.advance();
|
9663 | this.processEscapeSequence();
|
9664 | }
|
9665 | init() {
|
9666 | super.init();
|
9667 | this.processEscapeSequence();
|
9668 | }
|
9669 | clone() {
|
9670 | return new EscapedCharacterCursor(this);
|
9671 | }
|
9672 | getChars(start) {
|
9673 | const cursor = start.clone();
|
9674 | let chars = '';
|
9675 | while (cursor.internalState.offset < this.internalState.offset) {
|
9676 | chars += String.fromCodePoint(cursor.peek());
|
9677 | cursor.advance();
|
9678 | }
|
9679 | return chars;
|
9680 | }
|
9681 | /**
|
9682 | * Process the escape sequence that starts at the current position in the text.
|
9683 | *
|
9684 | * This method is called to ensure that `peek` has the unescaped value of escape sequences.
|
9685 | */
|
9686 | processEscapeSequence() {
|
9687 | const peek = () => this.internalState.peek;
|
9688 | if (peek() === $BACKSLASH) {
|
9689 | // We have hit an escape sequence so we need the internal state to become independent
|
9690 | // of the external state.
|
9691 | this.internalState = Object.assign({}, this.state);
|
9692 | // Move past the backslash
|
9693 | this.advanceState(this.internalState);
|
9694 | // First check for standard control char sequences
|
9695 | if (peek() === $n) {
|
9696 | this.state.peek = $LF;
|
9697 | }
|
9698 | else if (peek() === $r) {
|
9699 | this.state.peek = $CR;
|
9700 | }
|
9701 | else if (peek() === $v) {
|
9702 | this.state.peek = $VTAB;
|
9703 | }
|
9704 | else if (peek() === $t) {
|
9705 | this.state.peek = $TAB;
|
9706 | }
|
9707 | else if (peek() === $b) {
|
9708 | this.state.peek = $BSPACE;
|
9709 | }
|
9710 | else if (peek() === $f) {
|
9711 | this.state.peek = $FF;
|
9712 | }
|
9713 | // Now consider more complex sequences
|
9714 | else if (peek() === $u) {
|
9715 | // Unicode code-point sequence
|
9716 | this.advanceState(this.internalState); // advance past the `u` char
|
9717 | if (peek() === $LBRACE) {
|
9718 | // Variable length Unicode, e.g. `\x{123}`
|
9719 | this.advanceState(this.internalState); // advance past the `{` char
|
9720 | // Advance past the variable number of hex digits until we hit a `}` char
|
9721 | const digitStart = this.clone();
|
9722 | let length = 0;
|
9723 | while (peek() !== $RBRACE) {
|
9724 | this.advanceState(this.internalState);
|
9725 | length++;
|
9726 | }
|
9727 | this.state.peek = this.decodeHexDigits(digitStart, length);
|
9728 | }
|
9729 | else {
|
9730 | // Fixed length Unicode, e.g. `\u1234`
|
9731 | const digitStart = this.clone();
|
9732 | this.advanceState(this.internalState);
|
9733 | this.advanceState(this.internalState);
|
9734 | this.advanceState(this.internalState);
|
9735 | this.state.peek = this.decodeHexDigits(digitStart, 4);
|
9736 | }
|
9737 | }
|
9738 | else if (peek() === $x) {
|
9739 | // Hex char code, e.g. `\x2F`
|
9740 | this.advanceState(this.internalState); // advance past the `x` char
|
9741 | const digitStart = this.clone();
|
9742 | this.advanceState(this.internalState);
|
9743 | this.state.peek = this.decodeHexDigits(digitStart, 2);
|
9744 | }
|
9745 | else if (isOctalDigit(peek())) {
|
9746 | // Octal char code, e.g. `\012`,
|
9747 | let octal = '';
|
9748 | let length = 0;
|
9749 | let previous = this.clone();
|
9750 | while (isOctalDigit(peek()) && length < 3) {
|
9751 | previous = this.clone();
|
9752 | octal += String.fromCodePoint(peek());
|
9753 | this.advanceState(this.internalState);
|
9754 | length++;
|
9755 | }
|
9756 | this.state.peek = parseInt(octal, 8);
|
9757 | // Backup one char
|
9758 | this.internalState = previous.internalState;
|
9759 | }
|
9760 | else if (isNewLine(this.internalState.peek)) {
|
9761 | // Line continuation `\` followed by a new line
|
9762 | this.advanceState(this.internalState); // advance over the newline
|
9763 | this.state = this.internalState;
|
9764 | }
|
9765 | else {
|
9766 | // If none of the `if` blocks were executed then we just have an escaped normal character.
|
9767 | // In that case we just, effectively, skip the backslash from the character.
|
9768 | this.state.peek = this.internalState.peek;
|
9769 | }
|
9770 | }
|
9771 | }
|
9772 | decodeHexDigits(start, length) {
|
9773 | const hex = this.input.substr(start.internalState.offset, length);
|
9774 | const charCode = parseInt(hex, 16);
|
9775 | if (!isNaN(charCode)) {
|
9776 | return charCode;
|
9777 | }
|
9778 | else {
|
9779 | start.state = start.internalState;
|
9780 | throw new CursorError('Invalid hexadecimal escape sequence', start);
|
9781 | }
|
9782 | }
|
9783 | }
|
9784 | class CursorError {
|
9785 | constructor(msg, cursor) {
|
9786 | this.msg = msg;
|
9787 | this.cursor = cursor;
|
9788 | }
|
9789 | }
|
9790 |
|
9791 | /**
|
9792 | * @license
|
9793 | * Copyright Google LLC All Rights Reserved.
|
9794 | *
|
9795 | * Use of this source code is governed by an MIT-style license that can be
|
9796 | * found in the LICENSE file at https://angular.io/license
|
9797 | */
|
9798 | class TreeError extends ParseError {
|
9799 | constructor(elementName, span, msg) {
|
9800 | super(span, msg);
|
9801 | this.elementName = elementName;
|
9802 | }
|
9803 | static create(elementName, span, msg) {
|
9804 | return new TreeError(elementName, span, msg);
|
9805 | }
|
9806 | }
|
9807 | class ParseTreeResult {
|
9808 | constructor(rootNodes, errors) {
|
9809 | this.rootNodes = rootNodes;
|
9810 | this.errors = errors;
|
9811 | }
|
9812 | }
|
9813 | class Parser {
|
9814 | constructor(getTagDefinition) {
|
9815 | this.getTagDefinition = getTagDefinition;
|
9816 | }
|
9817 | parse(source, url, options) {
|
9818 | const tokenizeResult = tokenize(source, url, this.getTagDefinition, options);
|
9819 | const parser = new _TreeBuilder(tokenizeResult.tokens, this.getTagDefinition);
|
9820 | parser.build();
|
9821 | return new ParseTreeResult(parser.rootNodes, tokenizeResult.errors.concat(parser.errors));
|
9822 | }
|
9823 | }
|
9824 | class _TreeBuilder {
|
9825 | constructor(tokens, getTagDefinition) {
|
9826 | this.tokens = tokens;
|
9827 | this.getTagDefinition = getTagDefinition;
|
9828 | this._index = -1;
|
9829 | this._elementStack = [];
|
9830 | this.rootNodes = [];
|
9831 | this.errors = [];
|
9832 | this._advance();
|
9833 | }
|
9834 | build() {
|
9835 | while (this._peek.type !== TokenType.EOF) {
|
9836 | if (this._peek.type === TokenType.TAG_OPEN_START ||
|
9837 | this._peek.type === TokenType.INCOMPLETE_TAG_OPEN) {
|
9838 | this._consumeStartTag(this._advance());
|
9839 | }
|
9840 | else if (this._peek.type === TokenType.TAG_CLOSE) {
|
9841 | this._consumeEndTag(this._advance());
|
9842 | }
|
9843 | else if (this._peek.type === TokenType.CDATA_START) {
|
9844 | this._closeVoidElement();
|
9845 | this._consumeCdata(this._advance());
|
9846 | }
|
9847 | else if (this._peek.type === TokenType.COMMENT_START) {
|
9848 | this._closeVoidElement();
|
9849 | this._consumeComment(this._advance());
|
9850 | }
|
9851 | else if (this._peek.type === TokenType.TEXT || this._peek.type === TokenType.RAW_TEXT ||
|
9852 | this._peek.type === TokenType.ESCAPABLE_RAW_TEXT) {
|
9853 | this._closeVoidElement();
|
9854 | this._consumeText(this._advance());
|
9855 | }
|
9856 | else if (this._peek.type === TokenType.EXPANSION_FORM_START) {
|
9857 | this._consumeExpansion(this._advance());
|
9858 | }
|
9859 | else {
|
9860 | // Skip all other tokens...
|
9861 | this._advance();
|
9862 | }
|
9863 | }
|
9864 | }
|
9865 | _advance() {
|
9866 | const prev = this._peek;
|
9867 | if (this._index < this.tokens.length - 1) {
|
9868 | // Note: there is always an EOF token at the end
|
9869 | this._index++;
|
9870 | }
|
9871 | this._peek = this.tokens[this._index];
|
9872 | return prev;
|
9873 | }
|
9874 | _advanceIf(type) {
|
9875 | if (this._peek.type === type) {
|
9876 | return this._advance();
|
9877 | }
|
9878 | return null;
|
9879 | }
|
9880 | _consumeCdata(_startToken) {
|
9881 | this._consumeText(this._advance());
|
9882 | this._advanceIf(TokenType.CDATA_END);
|
9883 | }
|
9884 | _consumeComment(token) {
|
9885 | const text = this._advanceIf(TokenType.RAW_TEXT);
|
9886 | this._advanceIf(TokenType.COMMENT_END);
|
9887 | const value = text != null ? text.parts[0].trim() : null;
|
9888 | this._addToParent(new Comment(value, token.sourceSpan));
|
9889 | }
|
9890 | _consumeExpansion(token) {
|
9891 | const switchValue = this._advance();
|
9892 | const type = this._advance();
|
9893 | const cases = [];
|
9894 | // read =
|
9895 | while (this._peek.type === TokenType.EXPANSION_CASE_VALUE) {
|
9896 | const expCase = this._parseExpansionCase();
|
9897 | if (!expCase)
|
9898 | return; // error
|
9899 | cases.push(expCase);
|
9900 | }
|
9901 | // read the final }
|
9902 | if (this._peek.type !== TokenType.EXPANSION_FORM_END) {
|
9903 | this.errors.push(TreeError.create(null, this._peek.sourceSpan, `Invalid ICU message. Missing '}'.`));
|
9904 | return;
|
9905 | }
|
9906 | const sourceSpan = new ParseSourceSpan(token.sourceSpan.start, this._peek.sourceSpan.end, token.sourceSpan.fullStart);
|
9907 | this._addToParent(new Expansion(switchValue.parts[0], type.parts[0], cases, sourceSpan, switchValue.sourceSpan));
|
9908 | this._advance();
|
9909 | }
|
9910 | _parseExpansionCase() {
|
9911 | const value = this._advance();
|
9912 | // read {
|
9913 | if (this._peek.type !== TokenType.EXPANSION_CASE_EXP_START) {
|
9914 | this.errors.push(TreeError.create(null, this._peek.sourceSpan, `Invalid ICU message. Missing '{'.`));
|
9915 | return null;
|
9916 | }
|
9917 | // read until }
|
9918 | const start = this._advance();
|
9919 | const exp = this._collectExpansionExpTokens(start);
|
9920 | if (!exp)
|
9921 | return null;
|
9922 | const end = this._advance();
|
9923 | exp.push(new Token(TokenType.EOF, [], end.sourceSpan));
|
9924 | // parse everything in between { and }
|
9925 | const expansionCaseParser = new _TreeBuilder(exp, this.getTagDefinition);
|
9926 | expansionCaseParser.build();
|
9927 | if (expansionCaseParser.errors.length > 0) {
|
9928 | this.errors = this.errors.concat(expansionCaseParser.errors);
|
9929 | return null;
|
9930 | }
|
9931 | const sourceSpan = new ParseSourceSpan(value.sourceSpan.start, end.sourceSpan.end, value.sourceSpan.fullStart);
|
9932 | const expSourceSpan = new ParseSourceSpan(start.sourceSpan.start, end.sourceSpan.end, start.sourceSpan.fullStart);
|
9933 | return new ExpansionCase(value.parts[0], expansionCaseParser.rootNodes, sourceSpan, value.sourceSpan, expSourceSpan);
|
9934 | }
|
9935 | _collectExpansionExpTokens(start) {
|
9936 | const exp = [];
|
9937 | const expansionFormStack = [TokenType.EXPANSION_CASE_EXP_START];
|
9938 | while (true) {
|
9939 | if (this._peek.type === TokenType.EXPANSION_FORM_START ||
|
9940 | this._peek.type === TokenType.EXPANSION_CASE_EXP_START) {
|
9941 | expansionFormStack.push(this._peek.type);
|
9942 | }
|
9943 | if (this._peek.type === TokenType.EXPANSION_CASE_EXP_END) {
|
9944 | if (lastOnStack(expansionFormStack, TokenType.EXPANSION_CASE_EXP_START)) {
|
9945 | expansionFormStack.pop();
|
9946 | if (expansionFormStack.length == 0)
|
9947 | return exp;
|
9948 | }
|
9949 | else {
|
9950 | this.errors.push(TreeError.create(null, start.sourceSpan, `Invalid ICU message. Missing '}'.`));
|
9951 | return null;
|
9952 | }
|
9953 | }
|
9954 | if (this._peek.type === TokenType.EXPANSION_FORM_END) {
|
9955 | if (lastOnStack(expansionFormStack, TokenType.EXPANSION_FORM_START)) {
|
9956 | expansionFormStack.pop();
|
9957 | }
|
9958 | else {
|
9959 | this.errors.push(TreeError.create(null, start.sourceSpan, `Invalid ICU message. Missing '}'.`));
|
9960 | return null;
|
9961 | }
|
9962 | }
|
9963 | if (this._peek.type === TokenType.EOF) {
|
9964 | this.errors.push(TreeError.create(null, start.sourceSpan, `Invalid ICU message. Missing '}'.`));
|
9965 | return null;
|
9966 | }
|
9967 | exp.push(this._advance());
|
9968 | }
|
9969 | }
|
9970 | _consumeText(token) {
|
9971 | let text = token.parts[0];
|
9972 | if (text.length > 0 && text[0] == '\n') {
|
9973 | const parent = this._getParentElement();
|
9974 | if (parent != null && parent.children.length == 0 &&
|
9975 | this.getTagDefinition(parent.name).ignoreFirstLf) {
|
9976 | text = text.substring(1);
|
9977 | }
|
9978 | }
|
9979 | if (text.length > 0) {
|
9980 | this._addToParent(new Text$2(text, token.sourceSpan));
|
9981 | }
|
9982 | }
|
9983 | _closeVoidElement() {
|
9984 | const el = this._getParentElement();
|
9985 | if (el && this.getTagDefinition(el.name).isVoid) {
|
9986 | this._elementStack.pop();
|
9987 | }
|
9988 | }
|
9989 | _consumeStartTag(startTagToken) {
|
9990 | const [prefix, name] = startTagToken.parts;
|
9991 | const attrs = [];
|
9992 | while (this._peek.type === TokenType.ATTR_NAME) {
|
9993 | attrs.push(this._consumeAttr(this._advance()));
|
9994 | }
|
9995 | const fullName = this._getElementFullName(prefix, name, this._getParentElement());
|
9996 | let selfClosing = false;
|
9997 | // Note: There could have been a tokenizer error
|
9998 | // so that we don't get a token for the end tag...
|
9999 | if (this._peek.type === TokenType.TAG_OPEN_END_VOID) {
|
10000 | this._advance();
|
10001 | selfClosing = true;
|
10002 | const tagDef = this.getTagDefinition(fullName);
|
10003 | if (!(tagDef.canSelfClose || getNsPrefix(fullName) !== null || tagDef.isVoid)) {
|
10004 | this.errors.push(TreeError.create(fullName, startTagToken.sourceSpan, `Only void and foreign elements can be self closed "${startTagToken.parts[1]}"`));
|
10005 | }
|
10006 | }
|
10007 | else if (this._peek.type === TokenType.TAG_OPEN_END) {
|
10008 | this._advance();
|
10009 | selfClosing = false;
|
10010 | }
|
10011 | const end = this._peek.sourceSpan.fullStart;
|
10012 | const span = new ParseSourceSpan(startTagToken.sourceSpan.start, end, startTagToken.sourceSpan.fullStart);
|
10013 | // Create a separate `startSpan` because `span` will be modified when there is an `end` span.
|
10014 | const startSpan = new ParseSourceSpan(startTagToken.sourceSpan.start, end, startTagToken.sourceSpan.fullStart);
|
10015 | const el = new Element$1(fullName, attrs, [], span, startSpan, undefined);
|
10016 | this._pushElement(el);
|
10017 | if (selfClosing) {
|
10018 | // Elements that are self-closed have their `endSourceSpan` set to the full span, as the
|
10019 | // element start tag also represents the end tag.
|
10020 | this._popElement(fullName, span);
|
10021 | }
|
10022 | else if (startTagToken.type === TokenType.INCOMPLETE_TAG_OPEN) {
|
10023 | // We already know the opening tag is not complete, so it is unlikely it has a corresponding
|
10024 | // close tag. Let's optimistically parse it as a full element and emit an error.
|
10025 | this._popElement(fullName, null);
|
10026 | this.errors.push(TreeError.create(fullName, span, `Opening tag "${fullName}" not terminated.`));
|
10027 | }
|
10028 | }
|
10029 | _pushElement(el) {
|
10030 | const parentEl = this._getParentElement();
|
10031 | if (parentEl && this.getTagDefinition(parentEl.name).isClosedByChild(el.name)) {
|
10032 | this._elementStack.pop();
|
10033 | }
|
10034 | this._addToParent(el);
|
10035 | this._elementStack.push(el);
|
10036 | }
|
10037 | _consumeEndTag(endTagToken) {
|
10038 | const fullName = this._getElementFullName(endTagToken.parts[0], endTagToken.parts[1], this._getParentElement());
|
10039 | if (this.getTagDefinition(fullName).isVoid) {
|
10040 | this.errors.push(TreeError.create(fullName, endTagToken.sourceSpan, `Void elements do not have end tags "${endTagToken.parts[1]}"`));
|
10041 | }
|
10042 | else if (!this._popElement(fullName, endTagToken.sourceSpan)) {
|
10043 | 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`;
|
10044 | this.errors.push(TreeError.create(fullName, endTagToken.sourceSpan, errMsg));
|
10045 | }
|
10046 | }
|
10047 | /**
|
10048 | * Closes the nearest element with the tag name `fullName` in the parse tree.
|
10049 | * `endSourceSpan` is the span of the closing tag, or null if the element does
|
10050 | * not have a closing tag (for example, this happens when an incomplete
|
10051 | * opening tag is recovered).
|
10052 | */
|
10053 | _popElement(fullName, endSourceSpan) {
|
10054 | for (let stackIndex = this._elementStack.length - 1; stackIndex >= 0; stackIndex--) {
|
10055 | const el = this._elementStack[stackIndex];
|
10056 | if (el.name == fullName) {
|
10057 | // Record the parse span with the element that is being closed. Any elements that are
|
10058 | // removed from the element stack at this point are closed implicitly, so they won't get
|
10059 | // an end source span (as there is no explicit closing element).
|
10060 | el.endSourceSpan = endSourceSpan;
|
10061 | el.sourceSpan.end = endSourceSpan !== null ? endSourceSpan.end : el.sourceSpan.end;
|
10062 | this._elementStack.splice(stackIndex, this._elementStack.length - stackIndex);
|
10063 | return true;
|
10064 | }
|
10065 | if (!this.getTagDefinition(el.name).closedByParent) {
|
10066 | return false;
|
10067 | }
|
10068 | }
|
10069 | return false;
|
10070 | }
|
10071 | _consumeAttr(attrName) {
|
10072 | const fullName = mergeNsAndName(attrName.parts[0], attrName.parts[1]);
|
10073 | let end = attrName.sourceSpan.end;
|
10074 | let value = '';
|
10075 | let valueSpan = undefined;
|
10076 | if (this._peek.type === TokenType.ATTR_QUOTE) {
|
10077 | this._advance();
|
10078 | }
|
10079 | if (this._peek.type === TokenType.ATTR_VALUE) {
|
10080 | const valueToken = this._advance();
|
10081 | value = valueToken.parts[0];
|
10082 | end = valueToken.sourceSpan.end;
|
10083 | valueSpan = valueToken.sourceSpan;
|
10084 | }
|
10085 | if (this._peek.type === TokenType.ATTR_QUOTE) {
|
10086 | const quoteToken = this._advance();
|
10087 | end = quoteToken.sourceSpan.end;
|
10088 | }
|
10089 | const keySpan = new ParseSourceSpan(attrName.sourceSpan.start, attrName.sourceSpan.end);
|
10090 | return new Attribute(fullName, value, new ParseSourceSpan(attrName.sourceSpan.start, end, attrName.sourceSpan.fullStart), keySpan, valueSpan);
|
10091 | }
|
10092 | _getParentElement() {
|
10093 | return this._elementStack.length > 0 ? this._elementStack[this._elementStack.length - 1] : null;
|
10094 | }
|
10095 | _addToParent(node) {
|
10096 | const parent = this._getParentElement();
|
10097 | if (parent != null) {
|
10098 | parent.children.push(node);
|
10099 | }
|
10100 | else {
|
10101 | this.rootNodes.push(node);
|
10102 | }
|
10103 | }
|
10104 | _getElementFullName(prefix, localName, parentElement) {
|
10105 | if (prefix === '') {
|
10106 | prefix = this.getTagDefinition(localName).implicitNamespacePrefix || '';
|
10107 | if (prefix === '' && parentElement != null) {
|
10108 | const parentTagName = splitNsName(parentElement.name)[1];
|
10109 | const parentTagDefinition = this.getTagDefinition(parentTagName);
|
10110 | if (!parentTagDefinition.preventNamespaceInheritance) {
|
10111 | prefix = getNsPrefix(parentElement.name);
|
10112 | }
|
10113 | }
|
10114 | }
|
10115 | return mergeNsAndName(prefix, localName);
|
10116 | }
|
10117 | }
|
10118 | function lastOnStack(stack, element) {
|
10119 | return stack.length > 0 && stack[stack.length - 1] === element;
|
10120 | }
|
10121 |
|
10122 | /**
|
10123 | * @license
|
10124 | * Copyright Google LLC All Rights Reserved.
|
10125 | *
|
10126 | * Use of this source code is governed by an MIT-style license that can be
|
10127 | * found in the LICENSE file at https://angular.io/license
|
10128 | */
|
10129 | class HtmlParser extends Parser {
|
10130 | constructor() {
|
10131 | super(getHtmlTagDefinition);
|
10132 | }
|
10133 | parse(source, url, options) {
|
10134 | return super.parse(source, url, options);
|
10135 | }
|
10136 | }
|
10137 |
|
10138 | /**
|
10139 | * @license
|
10140 | * Copyright Google LLC All Rights Reserved.
|
10141 | *
|
10142 | * Use of this source code is governed by an MIT-style license that can be
|
10143 | * found in the LICENSE file at https://angular.io/license
|
10144 | */
|
10145 | const PRESERVE_WS_ATTR_NAME = 'ngPreserveWhitespaces';
|
10146 | const SKIP_WS_TRIM_TAGS = new Set(['pre', 'template', 'textarea', 'script', 'style']);
|
10147 | // Equivalent to \s with \u00a0 (non-breaking space) excluded.
|
10148 | // Based on https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp
|
10149 | const WS_CHARS = ' \f\n\r\t\v\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff';
|
10150 | const NO_WS_REGEXP = new RegExp(`[^${WS_CHARS}]`);
|
10151 | const WS_REPLACE_REGEXP = new RegExp(`[${WS_CHARS}]{2,}`, 'g');
|
10152 | function hasPreserveWhitespacesAttr(attrs) {
|
10153 | return attrs.some((attr) => attr.name === PRESERVE_WS_ATTR_NAME);
|
10154 | }
|
10155 | /**
|
10156 | * Angular Dart introduced &ngsp; as a placeholder for non-removable space, see:
|
10157 | * https://github.com/dart-lang/angular/blob/0bb611387d29d65b5af7f9d2515ab571fd3fbee4/_tests/test/compiler/preserve_whitespace_test.dart#L25-L32
|
10158 | * In Angular Dart &ngsp; is converted to the 0xE500 PUA (Private Use Areas) unicode character
|
10159 | * and later on replaced by a space. We are re-implementing the same idea here.
|
10160 | */
|
10161 | function replaceNgsp(value) {
|
10162 | // lexer is replacing the &ngsp; pseudo-entity with NGSP_UNICODE
|
10163 | return value.replace(new RegExp(NGSP_UNICODE, 'g'), ' ');
|
10164 | }
|
10165 | /**
|
10166 | * This visitor can walk HTML parse tree and remove / trim text nodes using the following rules:
|
10167 | * - consider spaces, tabs and new lines as whitespace characters;
|
10168 | * - drop text nodes consisting of whitespace characters only;
|
10169 | * - for all other text nodes replace consecutive whitespace characters with one space;
|
10170 | * - convert &ngsp; pseudo-entity to a single space;
|
10171 | *
|
10172 | * Removal and trimming of whitespaces have positive performance impact (less code to generate
|
10173 | * while compiling templates, faster view creation). At the same time it can be "destructive"
|
10174 | * in some cases (whitespaces can influence layout). Because of the potential of breaking layout
|
10175 | * this visitor is not activated by default in Angular 5 and people need to explicitly opt-in for
|
10176 | * whitespace removal. The default option for whitespace removal will be revisited in Angular 6
|
10177 | * and might be changed to "on" by default.
|
10178 | */
|
10179 | class WhitespaceVisitor {
|
10180 | visitElement(element, context) {
|
10181 | if (SKIP_WS_TRIM_TAGS.has(element.name) || hasPreserveWhitespacesAttr(element.attrs)) {
|
10182 | // don't descent into elements where we need to preserve whitespaces
|
10183 | // but still visit all attributes to eliminate one used as a market to preserve WS
|
10184 | return new Element$1(element.name, visitAll$1(this, element.attrs), element.children, element.sourceSpan, element.startSourceSpan, element.endSourceSpan, element.i18n);
|
10185 | }
|
10186 | return new Element$1(element.name, element.attrs, visitAllWithSiblings(this, element.children), element.sourceSpan, element.startSourceSpan, element.endSourceSpan, element.i18n);
|
10187 | }
|
10188 | visitAttribute(attribute, context) {
|
10189 | return attribute.name !== PRESERVE_WS_ATTR_NAME ? attribute : null;
|
10190 | }
|
10191 | visitText(text, context) {
|
10192 | const isNotBlank = text.value.match(NO_WS_REGEXP);
|
10193 | const hasExpansionSibling = context &&
|
10194 | (context.prev instanceof Expansion || context.next instanceof Expansion);
|
10195 | if (isNotBlank || hasExpansionSibling) {
|
10196 | return new Text$2(replaceNgsp(text.value).replace(WS_REPLACE_REGEXP, ' '), text.sourceSpan, text.i18n);
|
10197 | }
|
10198 | return null;
|
10199 | }
|
10200 | visitComment(comment, context) {
|
10201 | return comment;
|
10202 | }
|
10203 | visitExpansion(expansion, context) {
|
10204 | return expansion;
|
10205 | }
|
10206 | visitExpansionCase(expansionCase, context) {
|
10207 | return expansionCase;
|
10208 | }
|
10209 | }
|
10210 | function removeWhitespaces(htmlAstWithErrors) {
|
10211 | return new ParseTreeResult(visitAll$1(new WhitespaceVisitor(), htmlAstWithErrors.rootNodes), htmlAstWithErrors.errors);
|
10212 | }
|
10213 | function visitAllWithSiblings(visitor, nodes) {
|
10214 | const result = [];
|
10215 | nodes.forEach((ast, i) => {
|
10216 | const context = { prev: nodes[i - 1], next: nodes[i + 1] };
|
10217 | const astResult = ast.visit(visitor, context);
|
10218 | if (astResult) {
|
10219 | result.push(astResult);
|
10220 | }
|
10221 | });
|
10222 | return result;
|
10223 | }
|
10224 |
|
10225 | /**
|
10226 | * @license
|
10227 | * Copyright Google LLC All Rights Reserved.
|
10228 | *
|
10229 | * Use of this source code is governed by an MIT-style license that can be
|
10230 | * found in the LICENSE file at https://angular.io/license
|
10231 | */
|
10232 | // http://cldr.unicode.org/index/cldr-spec/plural-rules
|
10233 | const PLURAL_CASES = ['zero', 'one', 'two', 'few', 'many', 'other'];
|
10234 | /**
|
10235 | * Expands special forms into elements.
|
10236 | *
|
10237 | * For example,
|
10238 | *
|
10239 | * ```
|
10240 | * { messages.length, plural,
|
10241 | * =0 {zero}
|
10242 | * =1 {one}
|
10243 | * other {more than one}
|
10244 | * }
|
10245 | * ```
|
10246 | *
|
10247 | * will be expanded into
|
10248 | *
|
10249 | * ```
|
10250 | * <ng-container [ngPlural]="messages.length">
|
10251 | * <ng-template ngPluralCase="=0">zero</ng-template>
|
10252 | * <ng-template ngPluralCase="=1">one</ng-template>
|
10253 | * <ng-template ngPluralCase="other">more than one</ng-template>
|
10254 | * </ng-container>
|
10255 | * ```
|
10256 | */
|
10257 | function expandNodes(nodes) {
|
10258 | const expander = new _Expander();
|
10259 | return new ExpansionResult(visitAll$1(expander, nodes), expander.isExpanded, expander.errors);
|
10260 | }
|
10261 | class ExpansionResult {
|
10262 | constructor(nodes, expanded, errors) {
|
10263 | this.nodes = nodes;
|
10264 | this.expanded = expanded;
|
10265 | this.errors = errors;
|
10266 | }
|
10267 | }
|
10268 | class ExpansionError extends ParseError {
|
10269 | constructor(span, errorMsg) {
|
10270 | super(span, errorMsg);
|
10271 | }
|
10272 | }
|
10273 | /**
|
10274 | * Expand expansion forms (plural, select) to directives
|
10275 | *
|
10276 | * @internal
|
10277 | */
|
10278 | class _Expander {
|
10279 | constructor() {
|
10280 | this.isExpanded = false;
|
10281 | this.errors = [];
|
10282 | }
|
10283 | visitElement(element, context) {
|
10284 | return new Element$1(element.name, element.attrs, visitAll$1(this, element.children), element.sourceSpan, element.startSourceSpan, element.endSourceSpan);
|
10285 | }
|
10286 | visitAttribute(attribute, context) {
|
10287 | return attribute;
|
10288 | }
|
10289 | visitText(text, context) {
|
10290 | return text;
|
10291 | }
|
10292 | visitComment(comment, context) {
|
10293 | return comment;
|
10294 | }
|
10295 | visitExpansion(icu, context) {
|
10296 | this.isExpanded = true;
|
10297 | return icu.type == 'plural' ? _expandPluralForm(icu, this.errors) :
|
10298 | _expandDefaultForm(icu, this.errors);
|
10299 | }
|
10300 | visitExpansionCase(icuCase, context) {
|
10301 | throw new Error('Should not be reached');
|
10302 | }
|
10303 | }
|
10304 | // Plural forms are expanded to `NgPlural` and `NgPluralCase`s
|
10305 | function _expandPluralForm(ast, errors) {
|
10306 | const children = ast.cases.map(c => {
|
10307 | if (PLURAL_CASES.indexOf(c.value) == -1 && !c.value.match(/^=\d+$/)) {
|
10308 | errors.push(new ExpansionError(c.valueSourceSpan, `Plural cases should be "=<number>" or one of ${PLURAL_CASES.join(', ')}`));
|
10309 | }
|
10310 | const expansionResult = expandNodes(c.expression);
|
10311 | errors.push(...expansionResult.errors);
|
10312 | return new Element$1(`ng-template`, [new Attribute('ngPluralCase', `${c.value}`, c.valueSourceSpan, undefined /* keySpan */, undefined /* valueSpan */, undefined /* i18n */)], expansionResult.nodes, c.sourceSpan, c.sourceSpan, c.sourceSpan);
|
10313 | });
|
10314 | const switchAttr = new Attribute('[ngPlural]', ast.switchValue, ast.switchValueSourceSpan, undefined /* keySpan */, undefined /* valueSpan */, undefined /* i18n */);
|
10315 | return new Element$1('ng-container', [switchAttr], children, ast.sourceSpan, ast.sourceSpan, ast.sourceSpan);
|
10316 | }
|
10317 | // ICU messages (excluding plural form) are expanded to `NgSwitch` and `NgSwitchCase`s
|
10318 | function _expandDefaultForm(ast, errors) {
|
10319 | const children = ast.cases.map(c => {
|
10320 | const expansionResult = expandNodes(c.expression);
|
10321 | errors.push(...expansionResult.errors);
|
10322 | if (c.value === 'other') {
|
10323 | // other is the default case when no values match
|
10324 | return new Element$1(`ng-template`, [new Attribute('ngSwitchDefault', '', c.valueSourceSpan, undefined /* keySpan */, undefined /* valueSpan */, undefined /* i18n */)], expansionResult.nodes, c.sourceSpan, c.sourceSpan, c.sourceSpan);
|
10325 | }
|
10326 | return new Element$1(`ng-template`, [new Attribute('ngSwitchCase', `${c.value}`, c.valueSourceSpan, undefined /* keySpan */, undefined /* valueSpan */, undefined /* i18n */)], expansionResult.nodes, c.sourceSpan, c.sourceSpan, c.sourceSpan);
|
10327 | });
|
10328 | const switchAttr = new Attribute('[ngSwitch]', ast.switchValue, ast.switchValueSourceSpan, undefined /* keySpan */, undefined /* valueSpan */, undefined /* i18n */);
|
10329 | return new Element$1('ng-container', [switchAttr], children, ast.sourceSpan, ast.sourceSpan, ast.sourceSpan);
|
10330 | }
|
10331 |
|
10332 | /**
|
10333 | * @license
|
10334 | * Copyright Google LLC All Rights Reserved.
|
10335 | *
|
10336 | * Use of this source code is governed by an MIT-style license that can be
|
10337 | * found in the LICENSE file at https://angular.io/license
|
10338 | */
|
10339 | /**
|
10340 | * A segment of text within the template.
|
10341 | */
|
10342 | class TextAst {
|
10343 | constructor(value, ngContentIndex, sourceSpan) {
|
10344 | this.value = value;
|
10345 | this.ngContentIndex = ngContentIndex;
|
10346 | this.sourceSpan = sourceSpan;
|
10347 | }
|
10348 | visit(visitor, context) {
|
10349 | return visitor.visitText(this, context);
|
10350 | }
|
10351 | }
|
10352 | /**
|
10353 | * A bound expression within the text of a template.
|
10354 | */
|
10355 | class BoundTextAst {
|
10356 | constructor(value, ngContentIndex, sourceSpan) {
|
10357 | this.value = value;
|
10358 | this.ngContentIndex = ngContentIndex;
|
10359 | this.sourceSpan = sourceSpan;
|
10360 | }
|
10361 | visit(visitor, context) {
|
10362 | return visitor.visitBoundText(this, context);
|
10363 | }
|
10364 | }
|
10365 | /**
|
10366 | * A plain attribute on an element.
|
10367 | */
|
10368 | class AttrAst {
|
10369 | constructor(name, value, sourceSpan) {
|
10370 | this.name = name;
|
10371 | this.value = value;
|
10372 | this.sourceSpan = sourceSpan;
|
10373 | }
|
10374 | visit(visitor, context) {
|
10375 | return visitor.visitAttr(this, context);
|
10376 | }
|
10377 | }
|
10378 | const BoundPropertyMapping = {
|
10379 | [4 /* Animation */]: 4 /* Animation */,
|
10380 | [1 /* Attribute */]: 1 /* Attribute */,
|
10381 | [2 /* Class */]: 2 /* Class */,
|
10382 | [0 /* Property */]: 0 /* Property */,
|
10383 | [3 /* Style */]: 3 /* Style */,
|
10384 | };
|
10385 | /**
|
10386 | * A binding for an element property (e.g. `[property]="expression"`) or an animation trigger (e.g.
|
10387 | * `[@trigger]="stateExp"`)
|
10388 | */
|
10389 | class BoundElementPropertyAst {
|
10390 | constructor(name, type, securityContext, value, unit, sourceSpan) {
|
10391 | this.name = name;
|
10392 | this.type = type;
|
10393 | this.securityContext = securityContext;
|
10394 | this.value = value;
|
10395 | this.unit = unit;
|
10396 | this.sourceSpan = sourceSpan;
|
10397 | this.isAnimation = this.type === 4 /* Animation */;
|
10398 | }
|
10399 | static fromBoundProperty(prop) {
|
10400 | const type = BoundPropertyMapping[prop.type];
|
10401 | return new BoundElementPropertyAst(prop.name, type, prop.securityContext, prop.value, prop.unit, prop.sourceSpan);
|
10402 | }
|
10403 | visit(visitor, context) {
|
10404 | return visitor.visitElementProperty(this, context);
|
10405 | }
|
10406 | }
|
10407 | /**
|
10408 | * A binding for an element event (e.g. `(event)="handler()"`) or an animation trigger event (e.g.
|
10409 | * `(@trigger.phase)="callback($event)"`).
|
10410 | */
|
10411 | class BoundEventAst {
|
10412 | constructor(name, target, phase, handler, sourceSpan, handlerSpan) {
|
10413 | this.name = name;
|
10414 | this.target = target;
|
10415 | this.phase = phase;
|
10416 | this.handler = handler;
|
10417 | this.sourceSpan = sourceSpan;
|
10418 | this.handlerSpan = handlerSpan;
|
10419 | this.fullName = BoundEventAst.calcFullName(this.name, this.target, this.phase);
|
10420 | this.isAnimation = !!this.phase;
|
10421 | }
|
10422 | static calcFullName(name, target, phase) {
|
10423 | if (target) {
|
10424 | return `${target}:${name}`;
|
10425 | }
|
10426 | if (phase) {
|
10427 | return `@${name}.${phase}`;
|
10428 | }
|
10429 | return name;
|
10430 | }
|
10431 | static fromParsedEvent(event) {
|
10432 | const target = event.type === 0 /* Regular */ ? event.targetOrPhase : null;
|
10433 | const phase = event.type === 1 /* Animation */ ? event.targetOrPhase : null;
|
10434 | return new BoundEventAst(event.name, target, phase, event.handler, event.sourceSpan, event.handlerSpan);
|
10435 | }
|
10436 | visit(visitor, context) {
|
10437 | return visitor.visitEvent(this, context);
|
10438 | }
|
10439 | }
|
10440 | /**
|
10441 | * A reference declaration on an element (e.g. `let someName="expression"`).
|
10442 | */
|
10443 | class ReferenceAst {
|
10444 | constructor(name, value, originalValue, sourceSpan) {
|
10445 | this.name = name;
|
10446 | this.value = value;
|
10447 | this.originalValue = originalValue;
|
10448 | this.sourceSpan = sourceSpan;
|
10449 | }
|
10450 | visit(visitor, context) {
|
10451 | return visitor.visitReference(this, context);
|
10452 | }
|
10453 | }
|
10454 | /**
|
10455 | * A variable declaration on a <ng-template> (e.g. `var-someName="someLocalName"`).
|
10456 | */
|
10457 | class VariableAst {
|
10458 | constructor(name, value, sourceSpan, valueSpan) {
|
10459 | this.name = name;
|
10460 | this.value = value;
|
10461 | this.sourceSpan = sourceSpan;
|
10462 | this.valueSpan = valueSpan;
|
10463 | }
|
10464 | static fromParsedVariable(v) {
|
10465 | return new VariableAst(v.name, v.value, v.sourceSpan, v.valueSpan);
|
10466 | }
|
10467 | visit(visitor, context) {
|
10468 | return visitor.visitVariable(this, context);
|
10469 | }
|
10470 | }
|
10471 | /**
|
10472 | * An element declaration in a template.
|
10473 | */
|
10474 | class ElementAst {
|
10475 | constructor(name, attrs, inputs, outputs, references, directives, providers, hasViewContainer, queryMatches, children, ngContentIndex, sourceSpan, endSourceSpan) {
|
10476 | this.name = name;
|
10477 | this.attrs = attrs;
|
10478 | this.inputs = inputs;
|
10479 | this.outputs = outputs;
|
10480 | this.references = references;
|
10481 | this.directives = directives;
|
10482 | this.providers = providers;
|
10483 | this.hasViewContainer = hasViewContainer;
|
10484 | this.queryMatches = queryMatches;
|
10485 | this.children = children;
|
10486 | this.ngContentIndex = ngContentIndex;
|
10487 | this.sourceSpan = sourceSpan;
|
10488 | this.endSourceSpan = endSourceSpan;
|
10489 | }
|
10490 | visit(visitor, context) {
|
10491 | return visitor.visitElement(this, context);
|
10492 | }
|
10493 | }
|
10494 | /**
|
10495 | * A `<ng-template>` element included in an Angular template.
|
10496 | */
|
10497 | class EmbeddedTemplateAst {
|
10498 | constructor(attrs, outputs, references, variables, directives, providers, hasViewContainer, queryMatches, children, ngContentIndex, sourceSpan) {
|
10499 | this.attrs = attrs;
|
10500 | this.outputs = outputs;
|
10501 | this.references = references;
|
10502 | this.variables = variables;
|
10503 | this.directives = directives;
|
10504 | this.providers = providers;
|
10505 | this.hasViewContainer = hasViewContainer;
|
10506 | this.queryMatches = queryMatches;
|
10507 | this.children = children;
|
10508 | this.ngContentIndex = ngContentIndex;
|
10509 | this.sourceSpan = sourceSpan;
|
10510 | }
|
10511 | visit(visitor, context) {
|
10512 | return visitor.visitEmbeddedTemplate(this, context);
|
10513 | }
|
10514 | }
|
10515 | /**
|
10516 | * A directive property with a bound value (e.g. `*ngIf="condition").
|
10517 | */
|
10518 | class BoundDirectivePropertyAst {
|
10519 | constructor(directiveName, templateName, value, sourceSpan) {
|
10520 | this.directiveName = directiveName;
|
10521 | this.templateName = templateName;
|
10522 | this.value = value;
|
10523 | this.sourceSpan = sourceSpan;
|
10524 | }
|
10525 | visit(visitor, context) {
|
10526 | return visitor.visitDirectiveProperty(this, context);
|
10527 | }
|
10528 | }
|
10529 | /**
|
10530 | * A directive declared on an element.
|
10531 | */
|
10532 | class DirectiveAst {
|
10533 | constructor(directive, inputs, hostProperties, hostEvents, contentQueryStartId, sourceSpan) {
|
10534 | this.directive = directive;
|
10535 | this.inputs = inputs;
|
10536 | this.hostProperties = hostProperties;
|
10537 | this.hostEvents = hostEvents;
|
10538 | this.contentQueryStartId = contentQueryStartId;
|
10539 | this.sourceSpan = sourceSpan;
|
10540 | }
|
10541 | visit(visitor, context) {
|
10542 | return visitor.visitDirective(this, context);
|
10543 | }
|
10544 | }
|
10545 | /**
|
10546 | * A provider declared on an element
|
10547 | */
|
10548 | class ProviderAst {
|
10549 | constructor(token, multiProvider, eager, providers, providerType, lifecycleHooks, sourceSpan, isModule) {
|
10550 | this.token = token;
|
10551 | this.multiProvider = multiProvider;
|
10552 | this.eager = eager;
|
10553 | this.providers = providers;
|
10554 | this.providerType = providerType;
|
10555 | this.lifecycleHooks = lifecycleHooks;
|
10556 | this.sourceSpan = sourceSpan;
|
10557 | this.isModule = isModule;
|
10558 | }
|
10559 | visit(visitor, context) {
|
10560 | // No visit method in the visitor for now...
|
10561 | return null;
|
10562 | }
|
10563 | }
|
10564 | var ProviderAstType;
|
10565 | (function (ProviderAstType) {
|
10566 | ProviderAstType[ProviderAstType["PublicService"] = 0] = "PublicService";
|
10567 | ProviderAstType[ProviderAstType["PrivateService"] = 1] = "PrivateService";
|
10568 | ProviderAstType[ProviderAstType["Component"] = 2] = "Component";
|
10569 | ProviderAstType[ProviderAstType["Directive"] = 3] = "Directive";
|
10570 | ProviderAstType[ProviderAstType["Builtin"] = 4] = "Builtin";
|
10571 | })(ProviderAstType || (ProviderAstType = {}));
|
10572 | /**
|
10573 | * Position where content is to be projected (instance of `<ng-content>` in a template).
|
10574 | */
|
10575 | class NgContentAst {
|
10576 | constructor(index, ngContentIndex, sourceSpan) {
|
10577 | this.index = index;
|
10578 | this.ngContentIndex = ngContentIndex;
|
10579 | this.sourceSpan = sourceSpan;
|
10580 | }
|
10581 | visit(visitor, context) {
|
10582 | return visitor.visitNgContent(this, context);
|
10583 | }
|
10584 | }
|
10585 | /**
|
10586 | * A visitor that accepts each node but doesn't do anything. It is intended to be used
|
10587 | * as the base class for a visitor that is only interested in a subset of the node types.
|
10588 | */
|
10589 | class NullTemplateVisitor {
|
10590 | visitNgContent(ast, context) { }
|
10591 | visitEmbeddedTemplate(ast, context) { }
|
10592 | visitElement(ast, context) { }
|
10593 | visitReference(ast, context) { }
|
10594 | visitVariable(ast, context) { }
|
10595 | visitEvent(ast, context) { }
|
10596 | visitElementProperty(ast, context) { }
|
10597 | visitAttr(ast, context) { }
|
10598 | visitBoundText(ast, context) { }
|
10599 | visitText(ast, context) { }
|
10600 | visitDirective(ast, context) { }
|
10601 | visitDirectiveProperty(ast, context) { }
|
10602 | }
|
10603 | /**
|
10604 | * Base class that can be used to build a visitor that visits each node
|
10605 | * in an template ast recursively.
|
10606 | */
|
10607 | class RecursiveTemplateAstVisitor extends NullTemplateVisitor {
|
10608 | constructor() {
|
10609 | super();
|
10610 | }
|
10611 | // Nodes with children
|
10612 | visitEmbeddedTemplate(ast, context) {
|
10613 | return this.visitChildren(context, visit => {
|
10614 | visit(ast.attrs);
|
10615 | visit(ast.references);
|
10616 | visit(ast.variables);
|
10617 | visit(ast.directives);
|
10618 | visit(ast.providers);
|
10619 | visit(ast.children);
|
10620 | });
|
10621 | }
|
10622 | visitElement(ast, context) {
|
10623 | return this.visitChildren(context, visit => {
|
10624 | visit(ast.attrs);
|
10625 | visit(ast.inputs);
|
10626 | visit(ast.outputs);
|
10627 | visit(ast.references);
|
10628 | visit(ast.directives);
|
10629 | visit(ast.providers);
|
10630 | visit(ast.children);
|
10631 | });
|
10632 | }
|
10633 | visitDirective(ast, context) {
|
10634 | return this.visitChildren(context, visit => {
|
10635 | visit(ast.inputs);
|
10636 | visit(ast.hostProperties);
|
10637 | visit(ast.hostEvents);
|
10638 | });
|
10639 | }
|
10640 | visitChildren(context, cb) {
|
10641 | let results = [];
|
10642 | let t = this;
|
10643 | function visit(children) {
|
10644 | if (children && children.length)
|
10645 | results.push(templateVisitAll(t, children, context));
|
10646 | }
|
10647 | cb(visit);
|
10648 | return Array.prototype.concat.apply([], results);
|
10649 | }
|
10650 | }
|
10651 | /**
|
10652 | * Visit every node in a list of {@link TemplateAst}s with the given {@link TemplateAstVisitor}.
|
10653 | */
|
10654 | function templateVisitAll(visitor, asts, context = null) {
|
10655 | const result = [];
|
10656 | const visit = visitor.visit ?
|
10657 | (ast) => visitor.visit(ast, context) || ast.visit(visitor, context) :
|
10658 | (ast) => ast.visit(visitor, context);
|
10659 | asts.forEach(ast => {
|
10660 | const astResult = visit(ast);
|
10661 | if (astResult) {
|
10662 | result.push(astResult);
|
10663 | }
|
10664 | });
|
10665 | return result;
|
10666 | }
|
10667 |
|
10668 | /**
|
10669 | * @license
|
10670 | * Copyright Google LLC All Rights Reserved.
|
10671 | *
|
10672 | * Use of this source code is governed by an MIT-style license that can be
|
10673 | * found in the LICENSE file at https://angular.io/license
|
10674 | */
|
10675 | class ProviderError extends ParseError {
|
10676 | constructor(message, span) {
|
10677 | super(span, message);
|
10678 | }
|
10679 | }
|
10680 | class ProviderViewContext {
|
10681 | constructor(reflector, component) {
|
10682 | this.reflector = reflector;
|
10683 | this.component = component;
|
10684 | this.errors = [];
|
10685 | this.viewQueries = _getViewQueries(component);
|
10686 | this.viewProviders = new Map();
|
10687 | component.viewProviders.forEach((provider) => {
|
10688 | if (this.viewProviders.get(tokenReference(provider.token)) == null) {
|
10689 | this.viewProviders.set(tokenReference(provider.token), true);
|
10690 | }
|
10691 | });
|
10692 | }
|
10693 | }
|
10694 | class ProviderElementContext {
|
10695 | constructor(viewContext, _parent, _isViewRoot, _directiveAsts, attrs, refs, isTemplate, contentQueryStartId, _sourceSpan) {
|
10696 | this.viewContext = viewContext;
|
10697 | this._parent = _parent;
|
10698 | this._isViewRoot = _isViewRoot;
|
10699 | this._directiveAsts = _directiveAsts;
|
10700 | this._sourceSpan = _sourceSpan;
|
10701 | this._transformedProviders = new Map();
|
10702 | this._seenProviders = new Map();
|
10703 | this._queriedTokens = new Map();
|
10704 | this.transformedHasViewContainer = false;
|
10705 | this._attrs = {};
|
10706 | attrs.forEach((attrAst) => this._attrs[attrAst.name] = attrAst.value);
|
10707 | const directivesMeta = _directiveAsts.map(directiveAst => directiveAst.directive);
|
10708 | this._allProviders =
|
10709 | _resolveProvidersFromDirectives(directivesMeta, _sourceSpan, viewContext.errors);
|
10710 | this._contentQueries = _getContentQueries(contentQueryStartId, directivesMeta);
|
10711 | Array.from(this._allProviders.values()).forEach((provider) => {
|
10712 | this._addQueryReadsTo(provider.token, provider.token, this._queriedTokens);
|
10713 | });
|
10714 | if (isTemplate) {
|
10715 | const templateRefId = createTokenForExternalReference(this.viewContext.reflector, Identifiers.TemplateRef);
|
10716 | this._addQueryReadsTo(templateRefId, templateRefId, this._queriedTokens);
|
10717 | }
|
10718 | refs.forEach((refAst) => {
|
10719 | let defaultQueryValue = refAst.value ||
|
10720 | createTokenForExternalReference(this.viewContext.reflector, Identifiers.ElementRef);
|
10721 | this._addQueryReadsTo({ value: refAst.name }, defaultQueryValue, this._queriedTokens);
|
10722 | });
|
10723 | if (this._queriedTokens.get(this.viewContext.reflector.resolveExternalReference(Identifiers.ViewContainerRef))) {
|
10724 | this.transformedHasViewContainer = true;
|
10725 | }
|
10726 | // create the providers that we know are eager first
|
10727 | Array.from(this._allProviders.values()).forEach((provider) => {
|
10728 | const eager = provider.eager || this._queriedTokens.get(tokenReference(provider.token));
|
10729 | if (eager) {
|
10730 | this._getOrCreateLocalProvider(provider.providerType, provider.token, true);
|
10731 | }
|
10732 | });
|
10733 | }
|
10734 | afterElement() {
|
10735 | // collect lazy providers
|
10736 | Array.from(this._allProviders.values()).forEach((provider) => {
|
10737 | this._getOrCreateLocalProvider(provider.providerType, provider.token, false);
|
10738 | });
|
10739 | }
|
10740 | get transformProviders() {
|
10741 | // Note: Maps keep their insertion order.
|
10742 | const lazyProviders = [];
|
10743 | const eagerProviders = [];
|
10744 | this._transformedProviders.forEach(provider => {
|
10745 | if (provider.eager) {
|
10746 | eagerProviders.push(provider);
|
10747 | }
|
10748 | else {
|
10749 | lazyProviders.push(provider);
|
10750 | }
|
10751 | });
|
10752 | return lazyProviders.concat(eagerProviders);
|
10753 | }
|
10754 | get transformedDirectiveAsts() {
|
10755 | const sortedProviderTypes = this.transformProviders.map(provider => provider.token.identifier);
|
10756 | const sortedDirectives = this._directiveAsts.slice();
|
10757 | sortedDirectives.sort((dir1, dir2) => sortedProviderTypes.indexOf(dir1.directive.type) -
|
10758 | sortedProviderTypes.indexOf(dir2.directive.type));
|
10759 | return sortedDirectives;
|
10760 | }
|
10761 | get queryMatches() {
|
10762 | const allMatches = [];
|
10763 | this._queriedTokens.forEach((matches) => {
|
10764 | allMatches.push(...matches);
|
10765 | });
|
10766 | return allMatches;
|
10767 | }
|
10768 | _addQueryReadsTo(token, defaultValue, queryReadTokens) {
|
10769 | this._getQueriesFor(token).forEach((query) => {
|
10770 | const queryValue = query.meta.read || defaultValue;
|
10771 | const tokenRef = tokenReference(queryValue);
|
10772 | let queryMatches = queryReadTokens.get(tokenRef);
|
10773 | if (!queryMatches) {
|
10774 | queryMatches = [];
|
10775 | queryReadTokens.set(tokenRef, queryMatches);
|
10776 | }
|
10777 | queryMatches.push({ queryId: query.queryId, value: queryValue });
|
10778 | });
|
10779 | }
|
10780 | _getQueriesFor(token) {
|
10781 | const result = [];
|
10782 | let currentEl = this;
|
10783 | let distance = 0;
|
10784 | let queries;
|
10785 | while (currentEl !== null) {
|
10786 | queries = currentEl._contentQueries.get(tokenReference(token));
|
10787 | if (queries) {
|
10788 | result.push(...queries.filter((query) => query.meta.descendants || distance <= 1));
|
10789 | }
|
10790 | if (currentEl._directiveAsts.length > 0) {
|
10791 | distance++;
|
10792 | }
|
10793 | currentEl = currentEl._parent;
|
10794 | }
|
10795 | queries = this.viewContext.viewQueries.get(tokenReference(token));
|
10796 | if (queries) {
|
10797 | result.push(...queries);
|
10798 | }
|
10799 | return result;
|
10800 | }
|
10801 | _getOrCreateLocalProvider(requestingProviderType, token, eager) {
|
10802 | const resolvedProvider = this._allProviders.get(tokenReference(token));
|
10803 | if (!resolvedProvider ||
|
10804 | ((requestingProviderType === ProviderAstType.Directive ||
|
10805 | requestingProviderType === ProviderAstType.PublicService) &&
|
10806 | resolvedProvider.providerType === ProviderAstType.PrivateService) ||
|
10807 | ((requestingProviderType === ProviderAstType.PrivateService ||
|
10808 | requestingProviderType === ProviderAstType.PublicService) &&
|
10809 | resolvedProvider.providerType === ProviderAstType.Builtin)) {
|
10810 | return null;
|
10811 | }
|
10812 | let transformedProviderAst = this._transformedProviders.get(tokenReference(token));
|
10813 | if (transformedProviderAst) {
|
10814 | return transformedProviderAst;
|
10815 | }
|
10816 | if (this._seenProviders.get(tokenReference(token)) != null) {
|
10817 | this.viewContext.errors.push(new ProviderError(`Cannot instantiate cyclic dependency! ${tokenName(token)}`, this._sourceSpan));
|
10818 | return null;
|
10819 | }
|
10820 | this._seenProviders.set(tokenReference(token), true);
|
10821 | const transformedProviders = resolvedProvider.providers.map((provider) => {
|
10822 | let transformedUseValue = provider.useValue;
|
10823 | let transformedUseExisting = provider.useExisting;
|
10824 | let transformedDeps = undefined;
|
10825 | if (provider.useExisting != null) {
|
10826 | const existingDiDep = this._getDependency(resolvedProvider.providerType, { token: provider.useExisting }, eager);
|
10827 | if (existingDiDep.token != null) {
|
10828 | transformedUseExisting = existingDiDep.token;
|
10829 | }
|
10830 | else {
|
10831 | transformedUseExisting = null;
|
10832 | transformedUseValue = existingDiDep.value;
|
10833 | }
|
10834 | }
|
10835 | else if (provider.useFactory) {
|
10836 | const deps = provider.deps || provider.useFactory.diDeps;
|
10837 | transformedDeps =
|
10838 | deps.map((dep) => this._getDependency(resolvedProvider.providerType, dep, eager));
|
10839 | }
|
10840 | else if (provider.useClass) {
|
10841 | const deps = provider.deps || provider.useClass.diDeps;
|
10842 | transformedDeps =
|
10843 | deps.map((dep) => this._getDependency(resolvedProvider.providerType, dep, eager));
|
10844 | }
|
10845 | return _transformProvider(provider, {
|
10846 | useExisting: transformedUseExisting,
|
10847 | useValue: transformedUseValue,
|
10848 | deps: transformedDeps
|
10849 | });
|
10850 | });
|
10851 | transformedProviderAst =
|
10852 | _transformProviderAst(resolvedProvider, { eager: eager, providers: transformedProviders });
|
10853 | this._transformedProviders.set(tokenReference(token), transformedProviderAst);
|
10854 | return transformedProviderAst;
|
10855 | }
|
10856 | _getLocalDependency(requestingProviderType, dep, eager = false) {
|
10857 | if (dep.isAttribute) {
|
10858 | const attrValue = this._attrs[dep.token.value];
|
10859 | return { isValue: true, value: attrValue == null ? null : attrValue };
|
10860 | }
|
10861 | if (dep.token != null) {
|
10862 | // access builtints
|
10863 | if ((requestingProviderType === ProviderAstType.Directive ||
|
10864 | requestingProviderType === ProviderAstType.Component)) {
|
10865 | if (tokenReference(dep.token) ===
|
10866 | this.viewContext.reflector.resolveExternalReference(Identifiers.Renderer) ||
|
10867 | tokenReference(dep.token) ===
|
10868 | this.viewContext.reflector.resolveExternalReference(Identifiers.ElementRef) ||
|
10869 | tokenReference(dep.token) ===
|
10870 | this.viewContext.reflector.resolveExternalReference(Identifiers.ChangeDetectorRef) ||
|
10871 | tokenReference(dep.token) ===
|
10872 | this.viewContext.reflector.resolveExternalReference(Identifiers.TemplateRef)) {
|
10873 | return dep;
|
10874 | }
|
10875 | if (tokenReference(dep.token) ===
|
10876 | this.viewContext.reflector.resolveExternalReference(Identifiers.ViewContainerRef)) {
|
10877 | this.transformedHasViewContainer = true;
|
10878 | }
|
10879 | }
|
10880 | // access the injector
|
10881 | if (tokenReference(dep.token) ===
|
10882 | this.viewContext.reflector.resolveExternalReference(Identifiers.Injector)) {
|
10883 | return dep;
|
10884 | }
|
10885 | // access providers
|
10886 | if (this._getOrCreateLocalProvider(requestingProviderType, dep.token, eager) != null) {
|
10887 | return dep;
|
10888 | }
|
10889 | }
|
10890 | return null;
|
10891 | }
|
10892 | _getDependency(requestingProviderType, dep, eager = false) {
|
10893 | let currElement = this;
|
10894 | let currEager = eager;
|
10895 | let result = null;
|
10896 | if (!dep.isSkipSelf) {
|
10897 | result = this._getLocalDependency(requestingProviderType, dep, eager);
|
10898 | }
|
10899 | if (dep.isSelf) {
|
10900 | if (!result && dep.isOptional) {
|
10901 | result = { isValue: true, value: null };
|
10902 | }
|
10903 | }
|
10904 | else {
|
10905 | // check parent elements
|
10906 | while (!result && currElement._parent) {
|
10907 | const prevElement = currElement;
|
10908 | currElement = currElement._parent;
|
10909 | if (prevElement._isViewRoot) {
|
10910 | currEager = false;
|
10911 | }
|
10912 | result = currElement._getLocalDependency(ProviderAstType.PublicService, dep, currEager);
|
10913 | }
|
10914 | // check @Host restriction
|
10915 | if (!result) {
|
10916 | if (!dep.isHost || this.viewContext.component.isHost ||
|
10917 | this.viewContext.component.type.reference === tokenReference(dep.token) ||
|
10918 | this.viewContext.viewProviders.get(tokenReference(dep.token)) != null) {
|
10919 | result = dep;
|
10920 | }
|
10921 | else {
|
10922 | result = dep.isOptional ? { isValue: true, value: null } : null;
|
10923 | }
|
10924 | }
|
10925 | }
|
10926 | if (!result) {
|
10927 | this.viewContext.errors.push(new ProviderError(`No provider for ${tokenName(dep.token)}`, this._sourceSpan));
|
10928 | }
|
10929 | return result;
|
10930 | }
|
10931 | }
|
10932 | function _transformProvider(provider, { useExisting, useValue, deps }) {
|
10933 | return {
|
10934 | token: provider.token,
|
10935 | useClass: provider.useClass,
|
10936 | useExisting: useExisting,
|
10937 | useFactory: provider.useFactory,
|
10938 | useValue: useValue,
|
10939 | deps: deps,
|
10940 | multi: provider.multi
|
10941 | };
|
10942 | }
|
10943 | function _transformProviderAst(provider, { eager, providers }) {
|
10944 | return new ProviderAst(provider.token, provider.multiProvider, provider.eager || eager, providers, provider.providerType, provider.lifecycleHooks, provider.sourceSpan, provider.isModule);
|
10945 | }
|
10946 | function _resolveProvidersFromDirectives(directives, sourceSpan, targetErrors) {
|
10947 | const providersByToken = new Map();
|
10948 | directives.forEach((directive) => {
|
10949 | const dirProvider = { token: { identifier: directive.type }, useClass: directive.type };
|
10950 | _resolveProviders([dirProvider], directive.isComponent ? ProviderAstType.Component : ProviderAstType.Directive, true, sourceSpan, targetErrors, providersByToken, /* isModule */ false);
|
10951 | });
|
10952 | // Note: directives need to be able to overwrite providers of a component!
|
10953 | const directivesWithComponentFirst = directives.filter(dir => dir.isComponent).concat(directives.filter(dir => !dir.isComponent));
|
10954 | directivesWithComponentFirst.forEach((directive) => {
|
10955 | _resolveProviders(directive.providers, ProviderAstType.PublicService, false, sourceSpan, targetErrors, providersByToken, /* isModule */ false);
|
10956 | _resolveProviders(directive.viewProviders, ProviderAstType.PrivateService, false, sourceSpan, targetErrors, providersByToken, /* isModule */ false);
|
10957 | });
|
10958 | return providersByToken;
|
10959 | }
|
10960 | function _resolveProviders(providers, providerType, eager, sourceSpan, targetErrors, targetProvidersByToken, isModule) {
|
10961 | providers.forEach((provider) => {
|
10962 | let resolvedProvider = targetProvidersByToken.get(tokenReference(provider.token));
|
10963 | if (resolvedProvider != null && !!resolvedProvider.multiProvider !== !!provider.multi) {
|
10964 | targetErrors.push(new ProviderError(`Mixing multi and non multi provider is not possible for token ${tokenName(resolvedProvider.token)}`, sourceSpan));
|
10965 | }
|
10966 | if (!resolvedProvider) {
|
10967 | const lifecycleHooks = provider.token.identifier &&
|
10968 | provider.token.identifier.lifecycleHooks ?
|
10969 | provider.token.identifier.lifecycleHooks :
|
10970 | [];
|
10971 | const isUseValue = !(provider.useClass || provider.useExisting || provider.useFactory);
|
10972 | resolvedProvider = new ProviderAst(provider.token, !!provider.multi, eager || isUseValue, [provider], providerType, lifecycleHooks, sourceSpan, isModule);
|
10973 | targetProvidersByToken.set(tokenReference(provider.token), resolvedProvider);
|
10974 | }
|
10975 | else {
|
10976 | if (!provider.multi) {
|
10977 | resolvedProvider.providers.length = 0;
|
10978 | }
|
10979 | resolvedProvider.providers.push(provider);
|
10980 | }
|
10981 | });
|
10982 | }
|
10983 | function _getViewQueries(component) {
|
10984 | // Note: queries start with id 1 so we can use the number in a Bloom filter!
|
10985 | let viewQueryId = 1;
|
10986 | const viewQueries = new Map();
|
10987 | if (component.viewQueries) {
|
10988 | component.viewQueries.forEach((query) => _addQueryToTokenMap(viewQueries, { meta: query, queryId: viewQueryId++ }));
|
10989 | }
|
10990 | return viewQueries;
|
10991 | }
|
10992 | function _getContentQueries(contentQueryStartId, directives) {
|
10993 | let contentQueryId = contentQueryStartId;
|
10994 | const contentQueries = new Map();
|
10995 | directives.forEach((directive, directiveIndex) => {
|
10996 | if (directive.queries) {
|
10997 | directive.queries.forEach((query) => _addQueryToTokenMap(contentQueries, { meta: query, queryId: contentQueryId++ }));
|
10998 | }
|
10999 | });
|
11000 | return contentQueries;
|
11001 | }
|
11002 | function _addQueryToTokenMap(map, query) {
|
11003 | query.meta.selectors.forEach((token) => {
|
11004 | let entry = map.get(tokenReference(token));
|
11005 | if (!entry) {
|
11006 | entry = [];
|
11007 | map.set(tokenReference(token), entry);
|
11008 | }
|
11009 | entry.push(query);
|
11010 | });
|
11011 | }
|
11012 |
|
11013 | /**
|
11014 | * @license
|
11015 | * Copyright Google LLC All Rights Reserved.
|
11016 | *
|
11017 | * Use of this source code is governed by an MIT-style license that can be
|
11018 | * found in the LICENSE file at https://angular.io/license
|
11019 | */
|
11020 | class StyleWithImports {
|
11021 | constructor(style, styleUrls) {
|
11022 | this.style = style;
|
11023 | this.styleUrls = styleUrls;
|
11024 | }
|
11025 | }
|
11026 | function isStyleUrlResolvable(url) {
|
11027 | if (url == null || url.length === 0 || url[0] == '/')
|
11028 | return false;
|
11029 | const schemeMatch = url.match(URL_WITH_SCHEMA_REGEXP);
|
11030 | return schemeMatch === null || schemeMatch[1] == 'package' || schemeMatch[1] == 'asset';
|
11031 | }
|
11032 | /**
|
11033 | * Rewrites stylesheets by resolving and removing the @import urls that
|
11034 | * are either relative or don't have a `package:` scheme
|
11035 | */
|
11036 | function extractStyleUrls(resolver, baseUrl, cssText) {
|
11037 | const foundUrls = [];
|
11038 | const modifiedCssText = cssText.replace(CSS_STRIPPABLE_COMMENT_REGEXP, '')
|
11039 | .replace(CSS_IMPORT_REGEXP, (...m) => {
|
11040 | const url = m[1] || m[2];
|
11041 | if (!isStyleUrlResolvable(url)) {
|
11042 | // Do not attempt to resolve non-package absolute URLs with URI
|
11043 | // scheme
|
11044 | return m[0];
|
11045 | }
|
11046 | foundUrls.push(resolver.resolve(baseUrl, url));
|
11047 | return '';
|
11048 | });
|
11049 | return new StyleWithImports(modifiedCssText, foundUrls);
|
11050 | }
|
11051 | const CSS_IMPORT_REGEXP = /@import\s+(?:url\()?\s*(?:(?:['"]([^'"]*))|([^;\)\s]*))[^;]*;?/g;
|
11052 | const CSS_STRIPPABLE_COMMENT_REGEXP = /\/\*(?!#\s*(?:sourceURL|sourceMappingURL)=)[\s\S]+?\*\//g;
|
11053 | const URL_WITH_SCHEMA_REGEXP = /^([^:/?#]+):/;
|
11054 |
|
11055 | /**
|
11056 | * @license
|
11057 | * Copyright Google LLC All Rights Reserved.
|
11058 | *
|
11059 | * Use of this source code is governed by an MIT-style license that can be
|
11060 | * found in the LICENSE file at https://angular.io/license
|
11061 | */
|
11062 | const PROPERTY_PARTS_SEPARATOR = '.';
|
11063 | const ATTRIBUTE_PREFIX = 'attr';
|
11064 | const CLASS_PREFIX = 'class';
|
11065 | const STYLE_PREFIX = 'style';
|
11066 | const TEMPLATE_ATTR_PREFIX = '*';
|
11067 | const ANIMATE_PROP_PREFIX = 'animate-';
|
11068 | /**
|
11069 | * Parses bindings in templates and in the directive host area.
|
11070 | */
|
11071 | class BindingParser {
|
11072 | constructor(_exprParser, _interpolationConfig, _schemaRegistry, pipes, errors) {
|
11073 | this._exprParser = _exprParser;
|
11074 | this._interpolationConfig = _interpolationConfig;
|
11075 | this._schemaRegistry = _schemaRegistry;
|
11076 | this.errors = errors;
|
11077 | this.pipesByName = null;
|
11078 | this._usedPipes = new Map();
|
11079 | // When the `pipes` parameter is `null`, do not check for used pipes
|
11080 | // This is used in IVY when we might not know the available pipes at compile time
|
11081 | if (pipes) {
|
11082 | const pipesByName = new Map();
|
11083 | pipes.forEach(pipe => pipesByName.set(pipe.name, pipe));
|
11084 | this.pipesByName = pipesByName;
|
11085 | }
|
11086 | }
|
11087 | get interpolationConfig() {
|
11088 | return this._interpolationConfig;
|
11089 | }
|
11090 | getUsedPipes() {
|
11091 | return Array.from(this._usedPipes.values());
|
11092 | }
|
11093 | createBoundHostProperties(dirMeta, sourceSpan) {
|
11094 | if (dirMeta.hostProperties) {
|
11095 | const boundProps = [];
|
11096 | Object.keys(dirMeta.hostProperties).forEach(propName => {
|
11097 | const expression = dirMeta.hostProperties[propName];
|
11098 | if (typeof expression === 'string') {
|
11099 | this.parsePropertyBinding(propName, expression, true, sourceSpan, sourceSpan.start.offset, undefined, [],
|
11100 | // Use the `sourceSpan` for `keySpan`. This isn't really accurate, but neither is the
|
11101 | // sourceSpan, as it represents the sourceSpan of the host itself rather than the
|
11102 | // source of the host binding (which doesn't exist in the template). Regardless,
|
11103 | // neither of these values are used in Ivy but are only here to satisfy the function
|
11104 | // signature. This should likely be refactored in the future so that `sourceSpan`
|
11105 | // isn't being used inaccurately.
|
11106 | boundProps, sourceSpan);
|
11107 | }
|
11108 | else {
|
11109 | this._reportError(`Value of the host property binding "${propName}" needs to be a string representing an expression but got "${expression}" (${typeof expression})`, sourceSpan);
|
11110 | }
|
11111 | });
|
11112 | return boundProps;
|
11113 | }
|
11114 | return null;
|
11115 | }
|
11116 | createDirectiveHostPropertyAsts(dirMeta, elementSelector, sourceSpan) {
|
11117 | const boundProps = this.createBoundHostProperties(dirMeta, sourceSpan);
|
11118 | return boundProps &&
|
11119 | boundProps.map((prop) => this.createBoundElementProperty(elementSelector, prop));
|
11120 | }
|
11121 | createDirectiveHostEventAsts(dirMeta, sourceSpan) {
|
11122 | if (dirMeta.hostListeners) {
|
11123 | const targetEvents = [];
|
11124 | Object.keys(dirMeta.hostListeners).forEach(propName => {
|
11125 | const expression = dirMeta.hostListeners[propName];
|
11126 | if (typeof expression === 'string') {
|
11127 | // Use the `sourceSpan` for `keySpan` and `handlerSpan`. This isn't really accurate, but
|
11128 | // neither is the `sourceSpan`, as it represents the `sourceSpan` of the host itself
|
11129 | // rather than the source of the host binding (which doesn't exist in the template).
|
11130 | // Regardless, neither of these values are used in Ivy but are only here to satisfy the
|
11131 | // function signature. This should likely be refactored in the future so that `sourceSpan`
|
11132 | // isn't being used inaccurately.
|
11133 | this.parseEvent(propName, expression, sourceSpan, sourceSpan, [], targetEvents, sourceSpan);
|
11134 | }
|
11135 | else {
|
11136 | this._reportError(`Value of the host listener "${propName}" needs to be a string representing an expression but got "${expression}" (${typeof expression})`, sourceSpan);
|
11137 | }
|
11138 | });
|
11139 | return targetEvents;
|
11140 | }
|
11141 | return null;
|
11142 | }
|
11143 | parseInterpolation(value, sourceSpan) {
|
11144 | const sourceInfo = sourceSpan.start.toString();
|
11145 | const absoluteOffset = sourceSpan.fullStart.offset;
|
11146 | try {
|
11147 | const ast = this._exprParser.parseInterpolation(value, sourceInfo, absoluteOffset, this._interpolationConfig);
|
11148 | if (ast)
|
11149 | this._reportExpressionParserErrors(ast.errors, sourceSpan);
|
11150 | this._checkPipes(ast, sourceSpan);
|
11151 | return ast;
|
11152 | }
|
11153 | catch (e) {
|
11154 | this._reportError(`${e}`, sourceSpan);
|
11155 | return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo, absoluteOffset);
|
11156 | }
|
11157 | }
|
11158 | /**
|
11159 | * Similar to `parseInterpolation`, but treats the provided string as a single expression
|
11160 | * element that would normally appear within the interpolation prefix and suffix (`{{` and `}}`).
|
11161 | * This is used for parsing the switch expression in ICUs.
|
11162 | */
|
11163 | parseInterpolationExpression(expression, sourceSpan) {
|
11164 | const sourceInfo = sourceSpan.start.toString();
|
11165 | const absoluteOffset = sourceSpan.start.offset;
|
11166 | try {
|
11167 | const ast = this._exprParser.parseInterpolationExpression(expression, sourceInfo, absoluteOffset);
|
11168 | if (ast)
|
11169 | this._reportExpressionParserErrors(ast.errors, sourceSpan);
|
11170 | this._checkPipes(ast, sourceSpan);
|
11171 | return ast;
|
11172 | }
|
11173 | catch (e) {
|
11174 | this._reportError(`${e}`, sourceSpan);
|
11175 | return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo, absoluteOffset);
|
11176 | }
|
11177 | }
|
11178 | /**
|
11179 | * Parses the bindings in a microsyntax expression, and converts them to
|
11180 | * `ParsedProperty` or `ParsedVariable`.
|
11181 | *
|
11182 | * @param tplKey template binding name
|
11183 | * @param tplValue template binding value
|
11184 | * @param sourceSpan span of template binding relative to entire the template
|
11185 | * @param absoluteValueOffset start of the tplValue relative to the entire template
|
11186 | * @param targetMatchableAttrs potential attributes to match in the template
|
11187 | * @param targetProps target property bindings in the template
|
11188 | * @param targetVars target variables in the template
|
11189 | */
|
11190 | parseInlineTemplateBinding(tplKey, tplValue, sourceSpan, absoluteValueOffset, targetMatchableAttrs, targetProps, targetVars, isIvyAst) {
|
11191 | const absoluteKeyOffset = sourceSpan.start.offset + TEMPLATE_ATTR_PREFIX.length;
|
11192 | const bindings = this._parseTemplateBindings(tplKey, tplValue, sourceSpan, absoluteKeyOffset, absoluteValueOffset);
|
11193 | for (const binding of bindings) {
|
11194 | // sourceSpan is for the entire HTML attribute. bindingSpan is for a particular
|
11195 | // binding within the microsyntax expression so it's more narrow than sourceSpan.
|
11196 | const bindingSpan = moveParseSourceSpan(sourceSpan, binding.sourceSpan);
|
11197 | const key = binding.key.source;
|
11198 | const keySpan = moveParseSourceSpan(sourceSpan, binding.key.span);
|
11199 | if (binding instanceof VariableBinding) {
|
11200 | const value = binding.value ? binding.value.source : '$implicit';
|
11201 | const valueSpan = binding.value ? moveParseSourceSpan(sourceSpan, binding.value.span) : undefined;
|
11202 | targetVars.push(new ParsedVariable(key, value, bindingSpan, keySpan, valueSpan));
|
11203 | }
|
11204 | else if (binding.value) {
|
11205 | const srcSpan = isIvyAst ? bindingSpan : sourceSpan;
|
11206 | const valueSpan = moveParseSourceSpan(sourceSpan, binding.value.ast.sourceSpan);
|
11207 | this._parsePropertyAst(key, binding.value, srcSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps);
|
11208 | }
|
11209 | else {
|
11210 | targetMatchableAttrs.push([key, '' /* value */]);
|
11211 | // Since this is a literal attribute with no RHS, source span should be
|
11212 | // just the key span.
|
11213 | this.parseLiteralAttr(key, null /* value */, keySpan, absoluteValueOffset, undefined /* valueSpan */, targetMatchableAttrs, targetProps, keySpan);
|
11214 | }
|
11215 | }
|
11216 | }
|
11217 | /**
|
11218 | * Parses the bindings in a microsyntax expression, e.g.
|
11219 | * ```
|
11220 | * <tag *tplKey="let value1 = prop; let value2 = localVar">
|
11221 | * ```
|
11222 | *
|
11223 | * @param tplKey template binding name
|
11224 | * @param tplValue template binding value
|
11225 | * @param sourceSpan span of template binding relative to entire the template
|
11226 | * @param absoluteKeyOffset start of the `tplKey`
|
11227 | * @param absoluteValueOffset start of the `tplValue`
|
11228 | */
|
11229 | _parseTemplateBindings(tplKey, tplValue, sourceSpan, absoluteKeyOffset, absoluteValueOffset) {
|
11230 | const sourceInfo = sourceSpan.start.toString();
|
11231 | try {
|
11232 | const bindingsResult = this._exprParser.parseTemplateBindings(tplKey, tplValue, sourceInfo, absoluteKeyOffset, absoluteValueOffset);
|
11233 | this._reportExpressionParserErrors(bindingsResult.errors, sourceSpan);
|
11234 | bindingsResult.templateBindings.forEach((binding) => {
|
11235 | if (binding.value instanceof ASTWithSource) {
|
11236 | this._checkPipes(binding.value, sourceSpan);
|
11237 | }
|
11238 | });
|
11239 | bindingsResult.warnings.forEach((warning) => {
|
11240 | this._reportError(warning, sourceSpan, ParseErrorLevel.WARNING);
|
11241 | });
|
11242 | return bindingsResult.templateBindings;
|
11243 | }
|
11244 | catch (e) {
|
11245 | this._reportError(`${e}`, sourceSpan);
|
11246 | return [];
|
11247 | }
|
11248 | }
|
11249 | parseLiteralAttr(name, value, sourceSpan, absoluteOffset, valueSpan, targetMatchableAttrs,
|
11250 | // TODO(atscott): keySpan is only optional here so VE template parser implementation does not
|
11251 | // have to change This should be required when VE is removed.
|
11252 | targetProps, keySpan) {
|
11253 | if (isAnimationLabel(name)) {
|
11254 | name = name.substring(1);
|
11255 | if (keySpan !== undefined) {
|
11256 | keySpan = moveParseSourceSpan(keySpan, new AbsoluteSourceSpan(keySpan.start.offset + 1, keySpan.end.offset));
|
11257 | }
|
11258 | if (value) {
|
11259 | this._reportError(`Assigning animation triggers via @prop="exp" attributes with an expression is invalid.` +
|
11260 | ` Use property bindings (e.g. [@prop]="exp") or use an attribute without a value (e.g. @prop) instead.`, sourceSpan, ParseErrorLevel.ERROR);
|
11261 | }
|
11262 | this._parseAnimation(name, value, sourceSpan, absoluteOffset, keySpan, valueSpan, targetMatchableAttrs, targetProps);
|
11263 | }
|
11264 | else {
|
11265 | targetProps.push(new ParsedProperty(name, this._exprParser.wrapLiteralPrimitive(value, '', absoluteOffset), ParsedPropertyType.LITERAL_ATTR, sourceSpan, keySpan, valueSpan));
|
11266 | }
|
11267 | }
|
11268 | parsePropertyBinding(name, expression, isHost, sourceSpan, absoluteOffset, valueSpan,
|
11269 | // TODO(atscott): keySpan is only optional here so VE template parser implementation does not
|
11270 | // have to change This should be required when VE is removed.
|
11271 | targetMatchableAttrs, targetProps, keySpan) {
|
11272 | if (name.length === 0) {
|
11273 | this._reportError(`Property name is missing in binding`, sourceSpan);
|
11274 | }
|
11275 | let isAnimationProp = false;
|
11276 | if (name.startsWith(ANIMATE_PROP_PREFIX)) {
|
11277 | isAnimationProp = true;
|
11278 | name = name.substring(ANIMATE_PROP_PREFIX.length);
|
11279 | if (keySpan !== undefined) {
|
11280 | keySpan = moveParseSourceSpan(keySpan, new AbsoluteSourceSpan(keySpan.start.offset + ANIMATE_PROP_PREFIX.length, keySpan.end.offset));
|
11281 | }
|
11282 | }
|
11283 | else if (isAnimationLabel(name)) {
|
11284 | isAnimationProp = true;
|
11285 | name = name.substring(1);
|
11286 | if (keySpan !== undefined) {
|
11287 | keySpan = moveParseSourceSpan(keySpan, new AbsoluteSourceSpan(keySpan.start.offset + 1, keySpan.end.offset));
|
11288 | }
|
11289 | }
|
11290 | if (isAnimationProp) {
|
11291 | this._parseAnimation(name, expression, sourceSpan, absoluteOffset, keySpan, valueSpan, targetMatchableAttrs, targetProps);
|
11292 | }
|
11293 | else {
|
11294 | this._parsePropertyAst(name, this._parseBinding(expression, isHost, valueSpan || sourceSpan, absoluteOffset), sourceSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps);
|
11295 | }
|
11296 | }
|
11297 | parsePropertyInterpolation(name, value, sourceSpan, valueSpan, targetMatchableAttrs,
|
11298 | // TODO(atscott): keySpan is only optional here so VE template parser implementation does not
|
11299 | // have to change This should be required when VE is removed.
|
11300 | targetProps, keySpan) {
|
11301 | const expr = this.parseInterpolation(value, valueSpan || sourceSpan);
|
11302 | if (expr) {
|
11303 | this._parsePropertyAst(name, expr, sourceSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps);
|
11304 | return true;
|
11305 | }
|
11306 | return false;
|
11307 | }
|
11308 | _parsePropertyAst(name, ast, sourceSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps) {
|
11309 | targetMatchableAttrs.push([name, ast.source]);
|
11310 | targetProps.push(new ParsedProperty(name, ast, ParsedPropertyType.DEFAULT, sourceSpan, keySpan, valueSpan));
|
11311 | }
|
11312 | _parseAnimation(name, expression, sourceSpan, absoluteOffset, keySpan, valueSpan, targetMatchableAttrs, targetProps) {
|
11313 | if (name.length === 0) {
|
11314 | this._reportError('Animation trigger is missing', sourceSpan);
|
11315 | }
|
11316 | // This will occur when a @trigger is not paired with an expression.
|
11317 | // For animations it is valid to not have an expression since */void
|
11318 | // states will be applied by angular when the element is attached/detached
|
11319 | const ast = this._parseBinding(expression || 'undefined', false, valueSpan || sourceSpan, absoluteOffset);
|
11320 | targetMatchableAttrs.push([name, ast.source]);
|
11321 | targetProps.push(new ParsedProperty(name, ast, ParsedPropertyType.ANIMATION, sourceSpan, keySpan, valueSpan));
|
11322 | }
|
11323 | _parseBinding(value, isHostBinding, sourceSpan, absoluteOffset) {
|
11324 | const sourceInfo = (sourceSpan && sourceSpan.start || '(unknown)').toString();
|
11325 | try {
|
11326 | const ast = isHostBinding ?
|
11327 | this._exprParser.parseSimpleBinding(value, sourceInfo, absoluteOffset, this._interpolationConfig) :
|
11328 | this._exprParser.parseBinding(value, sourceInfo, absoluteOffset, this._interpolationConfig);
|
11329 | if (ast)
|
11330 | this._reportExpressionParserErrors(ast.errors, sourceSpan);
|
11331 | this._checkPipes(ast, sourceSpan);
|
11332 | return ast;
|
11333 | }
|
11334 | catch (e) {
|
11335 | this._reportError(`${e}`, sourceSpan);
|
11336 | return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo, absoluteOffset);
|
11337 | }
|
11338 | }
|
11339 | createBoundElementProperty(elementSelector, boundProp, skipValidation = false, mapPropertyName = true) {
|
11340 | if (boundProp.isAnimation) {
|
11341 | return new BoundElementProperty(boundProp.name, 4 /* Animation */, SecurityContext.NONE, boundProp.expression, null, boundProp.sourceSpan, boundProp.keySpan, boundProp.valueSpan);
|
11342 | }
|
11343 | let unit = null;
|
11344 | let bindingType = undefined;
|
11345 | let boundPropertyName = null;
|
11346 | const parts = boundProp.name.split(PROPERTY_PARTS_SEPARATOR);
|
11347 | let securityContexts = undefined;
|
11348 | // Check for special cases (prefix style, attr, class)
|
11349 | if (parts.length > 1) {
|
11350 | if (parts[0] == ATTRIBUTE_PREFIX) {
|
11351 | boundPropertyName = parts.slice(1).join(PROPERTY_PARTS_SEPARATOR);
|
11352 | if (!skipValidation) {
|
11353 | this._validatePropertyOrAttributeName(boundPropertyName, boundProp.sourceSpan, true);
|
11354 | }
|
11355 | securityContexts = calcPossibleSecurityContexts(this._schemaRegistry, elementSelector, boundPropertyName, true);
|
11356 | const nsSeparatorIdx = boundPropertyName.indexOf(':');
|
11357 | if (nsSeparatorIdx > -1) {
|
11358 | const ns = boundPropertyName.substring(0, nsSeparatorIdx);
|
11359 | const name = boundPropertyName.substring(nsSeparatorIdx + 1);
|
11360 | boundPropertyName = mergeNsAndName(ns, name);
|
11361 | }
|
11362 | bindingType = 1 /* Attribute */;
|
11363 | }
|
11364 | else if (parts[0] == CLASS_PREFIX) {
|
11365 | boundPropertyName = parts[1];
|
11366 | bindingType = 2 /* Class */;
|
11367 | securityContexts = [SecurityContext.NONE];
|
11368 | }
|
11369 | else if (parts[0] == STYLE_PREFIX) {
|
11370 | unit = parts.length > 2 ? parts[2] : null;
|
11371 | boundPropertyName = parts[1];
|
11372 | bindingType = 3 /* Style */;
|
11373 | securityContexts = [SecurityContext.STYLE];
|
11374 | }
|
11375 | }
|
11376 | // If not a special case, use the full property name
|
11377 | if (boundPropertyName === null) {
|
11378 | const mappedPropName = this._schemaRegistry.getMappedPropName(boundProp.name);
|
11379 | boundPropertyName = mapPropertyName ? mappedPropName : boundProp.name;
|
11380 | securityContexts = calcPossibleSecurityContexts(this._schemaRegistry, elementSelector, mappedPropName, false);
|
11381 | bindingType = 0 /* Property */;
|
11382 | if (!skipValidation) {
|
11383 | this._validatePropertyOrAttributeName(mappedPropName, boundProp.sourceSpan, false);
|
11384 | }
|
11385 | }
|
11386 | return new BoundElementProperty(boundPropertyName, bindingType, securityContexts[0], boundProp.expression, unit, boundProp.sourceSpan, boundProp.keySpan, boundProp.valueSpan);
|
11387 | }
|
11388 | // TODO: keySpan should be required but was made optional to avoid changing VE parser.
|
11389 | parseEvent(name, expression, sourceSpan, handlerSpan, targetMatchableAttrs, targetEvents, keySpan) {
|
11390 | if (name.length === 0) {
|
11391 | this._reportError(`Event name is missing in binding`, sourceSpan);
|
11392 | }
|
11393 | if (isAnimationLabel(name)) {
|
11394 | name = name.substr(1);
|
11395 | if (keySpan !== undefined) {
|
11396 | keySpan = moveParseSourceSpan(keySpan, new AbsoluteSourceSpan(keySpan.start.offset + 1, keySpan.end.offset));
|
11397 | }
|
11398 | this._parseAnimationEvent(name, expression, sourceSpan, handlerSpan, targetEvents, keySpan);
|
11399 | }
|
11400 | else {
|
11401 | this._parseRegularEvent(name, expression, sourceSpan, handlerSpan, targetMatchableAttrs, targetEvents, keySpan);
|
11402 | }
|
11403 | }
|
11404 | calcPossibleSecurityContexts(selector, propName, isAttribute) {
|
11405 | const prop = this._schemaRegistry.getMappedPropName(propName);
|
11406 | return calcPossibleSecurityContexts(this._schemaRegistry, selector, prop, isAttribute);
|
11407 | }
|
11408 | _parseAnimationEvent(name, expression, sourceSpan, handlerSpan, targetEvents, keySpan) {
|
11409 | const matches = splitAtPeriod(name, [name, '']);
|
11410 | const eventName = matches[0];
|
11411 | const phase = matches[1].toLowerCase();
|
11412 | const ast = this._parseAction(expression, handlerSpan);
|
11413 | targetEvents.push(new ParsedEvent(eventName, phase, 1 /* Animation */, ast, sourceSpan, handlerSpan, keySpan));
|
11414 | if (eventName.length === 0) {
|
11415 | this._reportError(`Animation event name is missing in binding`, sourceSpan);
|
11416 | }
|
11417 | if (phase) {
|
11418 | if (phase !== 'start' && phase !== 'done') {
|
11419 | this._reportError(`The provided animation output phase value "${phase}" for "@${eventName}" is not supported (use start or done)`, sourceSpan);
|
11420 | }
|
11421 | }
|
11422 | else {
|
11423 | this._reportError(`The animation trigger output event (@${eventName}) is missing its phase value name (start or done are currently supported)`, sourceSpan);
|
11424 | }
|
11425 | }
|
11426 | _parseRegularEvent(name, expression, sourceSpan, handlerSpan, targetMatchableAttrs, targetEvents, keySpan) {
|
11427 | // long format: 'target: eventName'
|
11428 | const [target, eventName] = splitAtColon(name, [null, name]);
|
11429 | const ast = this._parseAction(expression, handlerSpan);
|
11430 | targetMatchableAttrs.push([name, ast.source]);
|
11431 | targetEvents.push(new ParsedEvent(eventName, target, 0 /* Regular */, ast, sourceSpan, handlerSpan, keySpan));
|
11432 | // Don't detect directives for event names for now,
|
11433 | // so don't add the event name to the matchableAttrs
|
11434 | }
|
11435 | _parseAction(value, sourceSpan) {
|
11436 | const sourceInfo = (sourceSpan && sourceSpan.start || '(unknown').toString();
|
11437 | const absoluteOffset = (sourceSpan && sourceSpan.start) ? sourceSpan.start.offset : 0;
|
11438 | try {
|
11439 | const ast = this._exprParser.parseAction(value, sourceInfo, absoluteOffset, this._interpolationConfig);
|
11440 | if (ast) {
|
11441 | this._reportExpressionParserErrors(ast.errors, sourceSpan);
|
11442 | }
|
11443 | if (!ast || ast.ast instanceof EmptyExpr) {
|
11444 | this._reportError(`Empty expressions are not allowed`, sourceSpan);
|
11445 | return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo, absoluteOffset);
|
11446 | }
|
11447 | this._checkPipes(ast, sourceSpan);
|
11448 | return ast;
|
11449 | }
|
11450 | catch (e) {
|
11451 | this._reportError(`${e}`, sourceSpan);
|
11452 | return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo, absoluteOffset);
|
11453 | }
|
11454 | }
|
11455 | _reportError(message, sourceSpan, level = ParseErrorLevel.ERROR) {
|
11456 | this.errors.push(new ParseError(sourceSpan, message, level));
|
11457 | }
|
11458 | _reportExpressionParserErrors(errors, sourceSpan) {
|
11459 | for (const error of errors) {
|
11460 | this._reportError(error.message, sourceSpan);
|
11461 | }
|
11462 | }
|
11463 | // Make sure all the used pipes are known in `this.pipesByName`
|
11464 | _checkPipes(ast, sourceSpan) {
|
11465 | if (ast && this.pipesByName) {
|
11466 | const collector = new PipeCollector();
|
11467 | ast.visit(collector);
|
11468 | collector.pipes.forEach((ast, pipeName) => {
|
11469 | const pipeMeta = this.pipesByName.get(pipeName);
|
11470 | if (!pipeMeta) {
|
11471 | this._reportError(`The pipe '${pipeName}' could not be found`, new ParseSourceSpan(sourceSpan.start.moveBy(ast.span.start), sourceSpan.start.moveBy(ast.span.end)));
|
11472 | }
|
11473 | else {
|
11474 | this._usedPipes.set(pipeName, pipeMeta);
|
11475 | }
|
11476 | });
|
11477 | }
|
11478 | }
|
11479 | /**
|
11480 | * @param propName the name of the property / attribute
|
11481 | * @param sourceSpan
|
11482 | * @param isAttr true when binding to an attribute
|
11483 | */
|
11484 | _validatePropertyOrAttributeName(propName, sourceSpan, isAttr) {
|
11485 | const report = isAttr ? this._schemaRegistry.validateAttribute(propName) :
|
11486 | this._schemaRegistry.validateProperty(propName);
|
11487 | if (report.error) {
|
11488 | this._reportError(report.msg, sourceSpan, ParseErrorLevel.ERROR);
|
11489 | }
|
11490 | }
|
11491 | }
|
11492 | class PipeCollector extends RecursiveAstVisitor {
|
11493 | constructor() {
|
11494 | super(...arguments);
|
11495 | this.pipes = new Map();
|
11496 | }
|
11497 | visitPipe(ast, context) {
|
11498 | this.pipes.set(ast.name, ast);
|
11499 | ast.exp.visit(this);
|
11500 | this.visitAll(ast.args, context);
|
11501 | return null;
|
11502 | }
|
11503 | }
|
11504 | function isAnimationLabel(name) {
|
11505 | return name[0] == '@';
|
11506 | }
|
11507 | function calcPossibleSecurityContexts(registry, selector, propName, isAttribute) {
|
11508 | const ctxs = [];
|
11509 | CssSelector.parse(selector).forEach((selector) => {
|
11510 | const elementNames = selector.element ? [selector.element] : registry.allKnownElementNames();
|
11511 | const notElementNames = new Set(selector.notSelectors.filter(selector => selector.isElementSelector())
|
11512 | .map((selector) => selector.element));
|
11513 | const possibleElementNames = elementNames.filter(elementName => !notElementNames.has(elementName));
|
11514 | ctxs.push(...possibleElementNames.map(elementName => registry.securityContext(elementName, propName, isAttribute)));
|
11515 | });
|
11516 | return ctxs.length === 0 ? [SecurityContext.NONE] : Array.from(new Set(ctxs)).sort();
|
11517 | }
|
11518 | /**
|
11519 | * Compute a new ParseSourceSpan based off an original `sourceSpan` by using
|
11520 | * absolute offsets from the specified `absoluteSpan`.
|
11521 | *
|
11522 | * @param sourceSpan original source span
|
11523 | * @param absoluteSpan absolute source span to move to
|
11524 | */
|
11525 | function moveParseSourceSpan(sourceSpan, absoluteSpan) {
|
11526 | // The difference of two absolute offsets provide the relative offset
|
11527 | const startDiff = absoluteSpan.start - sourceSpan.start.offset;
|
11528 | const endDiff = absoluteSpan.end - sourceSpan.end.offset;
|
11529 | return new ParseSourceSpan(sourceSpan.start.moveBy(startDiff), sourceSpan.end.moveBy(endDiff), sourceSpan.fullStart.moveBy(startDiff), sourceSpan.details);
|
11530 | }
|
11531 |
|
11532 | /**
|
11533 | * @license
|
11534 | * Copyright Google LLC All Rights Reserved.
|
11535 | *
|
11536 | * Use of this source code is governed by an MIT-style license that can be
|
11537 | * found in the LICENSE file at https://angular.io/license
|
11538 | */
|
11539 | const NG_CONTENT_SELECT_ATTR = 'select';
|
11540 | const LINK_ELEMENT = 'link';
|
11541 | const LINK_STYLE_REL_ATTR = 'rel';
|
11542 | const LINK_STYLE_HREF_ATTR = 'href';
|
11543 | const LINK_STYLE_REL_VALUE = 'stylesheet';
|
11544 | const STYLE_ELEMENT = 'style';
|
11545 | const SCRIPT_ELEMENT = 'script';
|
11546 | const NG_NON_BINDABLE_ATTR = 'ngNonBindable';
|
11547 | const NG_PROJECT_AS = 'ngProjectAs';
|
11548 | function preparseElement(ast) {
|
11549 | let selectAttr = null;
|
11550 | let hrefAttr = null;
|
11551 | let relAttr = null;
|
11552 | let nonBindable = false;
|
11553 | let projectAs = '';
|
11554 | ast.attrs.forEach(attr => {
|
11555 | const lcAttrName = attr.name.toLowerCase();
|
11556 | if (lcAttrName == NG_CONTENT_SELECT_ATTR) {
|
11557 | selectAttr = attr.value;
|
11558 | }
|
11559 | else if (lcAttrName == LINK_STYLE_HREF_ATTR) {
|
11560 | hrefAttr = attr.value;
|
11561 | }
|
11562 | else if (lcAttrName == LINK_STYLE_REL_ATTR) {
|
11563 | relAttr = attr.value;
|
11564 | }
|
11565 | else if (attr.name == NG_NON_BINDABLE_ATTR) {
|
11566 | nonBindable = true;
|
11567 | }
|
11568 | else if (attr.name == NG_PROJECT_AS) {
|
11569 | if (attr.value.length > 0) {
|
11570 | projectAs = attr.value;
|
11571 | }
|
11572 | }
|
11573 | });
|
11574 | selectAttr = normalizeNgContentSelect(selectAttr);
|
11575 | const nodeName = ast.name.toLowerCase();
|
11576 | let type = PreparsedElementType.OTHER;
|
11577 | if (isNgContent(nodeName)) {
|
11578 | type = PreparsedElementType.NG_CONTENT;
|
11579 | }
|
11580 | else if (nodeName == STYLE_ELEMENT) {
|
11581 | type = PreparsedElementType.STYLE;
|
11582 | }
|
11583 | else if (nodeName == SCRIPT_ELEMENT) {
|
11584 | type = PreparsedElementType.SCRIPT;
|
11585 | }
|
11586 | else if (nodeName == LINK_ELEMENT && relAttr == LINK_STYLE_REL_VALUE) {
|
11587 | type = PreparsedElementType.STYLESHEET;
|
11588 | }
|
11589 | return new PreparsedElement(type, selectAttr, hrefAttr, nonBindable, projectAs);
|
11590 | }
|
11591 | var PreparsedElementType;
|
11592 | (function (PreparsedElementType) {
|
11593 | PreparsedElementType[PreparsedElementType["NG_CONTENT"] = 0] = "NG_CONTENT";
|
11594 | PreparsedElementType[PreparsedElementType["STYLE"] = 1] = "STYLE";
|
11595 | PreparsedElementType[PreparsedElementType["STYLESHEET"] = 2] = "STYLESHEET";
|
11596 | PreparsedElementType[PreparsedElementType["SCRIPT"] = 3] = "SCRIPT";
|
11597 | PreparsedElementType[PreparsedElementType["OTHER"] = 4] = "OTHER";
|
11598 | })(PreparsedElementType || (PreparsedElementType = {}));
|
11599 | class PreparsedElement {
|
11600 | constructor(type, selectAttr, hrefAttr, nonBindable, projectAs) {
|
11601 | this.type = type;
|
11602 | this.selectAttr = selectAttr;
|
11603 | this.hrefAttr = hrefAttr;
|
11604 | this.nonBindable = nonBindable;
|
11605 | this.projectAs = projectAs;
|
11606 | }
|
11607 | }
|
11608 | function normalizeNgContentSelect(selectAttr) {
|
11609 | if (selectAttr === null || selectAttr.length === 0) {
|
11610 | return '*';
|
11611 | }
|
11612 | return selectAttr;
|
11613 | }
|
11614 |
|
11615 | /**
|
11616 | * @license
|
11617 | * Copyright Google LLC All Rights Reserved.
|
11618 | *
|
11619 | * Use of this source code is governed by an MIT-style license that can be
|
11620 | * found in the LICENSE file at https://angular.io/license
|
11621 | */
|
11622 | const BIND_NAME_REGEXP = /^(?:(?:(?:(bind-)|(let-)|(ref-|#)|(on-)|(bindon-)|(@))(.*))|\[\(([^\)]+)\)\]|\[([^\]]+)\]|\(([^\)]+)\))$/;
|
11623 | // Group 1 = "bind-"
|
11624 | const KW_BIND_IDX = 1;
|
11625 | // Group 2 = "let-"
|
11626 | const KW_LET_IDX = 2;
|
11627 | // Group 3 = "ref-/#"
|
11628 | const KW_REF_IDX = 3;
|
11629 | // Group 4 = "on-"
|
11630 | const KW_ON_IDX = 4;
|
11631 | // Group 5 = "bindon-"
|
11632 | const KW_BINDON_IDX = 5;
|
11633 | // Group 6 = "@"
|
11634 | const KW_AT_IDX = 6;
|
11635 | // Group 7 = the identifier after "bind-", "let-", "ref-/#", "on-", "bindon-" or "@"
|
11636 | const IDENT_KW_IDX = 7;
|
11637 | // Group 8 = identifier inside [()]
|
11638 | const IDENT_BANANA_BOX_IDX = 8;
|
11639 | // Group 9 = identifier inside []
|
11640 | const IDENT_PROPERTY_IDX = 9;
|
11641 | // Group 10 = identifier inside ()
|
11642 | const IDENT_EVENT_IDX = 10;
|
11643 | const TEMPLATE_ATTR_PREFIX$1 = '*';
|
11644 | const CLASS_ATTR = 'class';
|
11645 | let _TEXT_CSS_SELECTOR;
|
11646 | function TEXT_CSS_SELECTOR() {
|
11647 | if (!_TEXT_CSS_SELECTOR) {
|
11648 | _TEXT_CSS_SELECTOR = CssSelector.parse('*')[0];
|
11649 | }
|
11650 | return _TEXT_CSS_SELECTOR;
|
11651 | }
|
11652 | class TemplateParseError extends ParseError {
|
11653 | constructor(message, span, level) {
|
11654 | super(span, message, level);
|
11655 | }
|
11656 | }
|
11657 | class TemplateParseResult {
|
11658 | constructor(templateAst, usedPipes, errors) {
|
11659 | this.templateAst = templateAst;
|
11660 | this.usedPipes = usedPipes;
|
11661 | this.errors = errors;
|
11662 | }
|
11663 | }
|
11664 | class TemplateParser {
|
11665 | constructor(_config, _reflector, _exprParser, _schemaRegistry, _htmlParser, _console, transforms) {
|
11666 | this._config = _config;
|
11667 | this._reflector = _reflector;
|
11668 | this._exprParser = _exprParser;
|
11669 | this._schemaRegistry = _schemaRegistry;
|
11670 | this._htmlParser = _htmlParser;
|
11671 | this._console = _console;
|
11672 | this.transforms = transforms;
|
11673 | }
|
11674 | get expressionParser() {
|
11675 | return this._exprParser;
|
11676 | }
|
11677 | parse(component, template, directives, pipes, schemas, templateUrl, preserveWhitespaces) {
|
11678 | var _a;
|
11679 | const result = this.tryParse(component, template, directives, pipes, schemas, templateUrl, preserveWhitespaces);
|
11680 | const warnings = result.errors.filter(error => error.level === ParseErrorLevel.WARNING);
|
11681 | const errors = result.errors.filter(error => error.level === ParseErrorLevel.ERROR);
|
11682 | if (warnings.length > 0) {
|
11683 | (_a = this._console) === null || _a === void 0 ? void 0 : _a.warn(`Template parse warnings:\n${warnings.join('\n')}`);
|
11684 | }
|
11685 | if (errors.length > 0) {
|
11686 | const errorString = errors.join('\n');
|
11687 | throw syntaxError(`Template parse errors:\n${errorString}`, errors);
|
11688 | }
|
11689 | return { template: result.templateAst, pipes: result.usedPipes };
|
11690 | }
|
11691 | tryParse(component, template, directives, pipes, schemas, templateUrl, preserveWhitespaces) {
|
11692 | let htmlParseResult = typeof template === 'string' ?
|
11693 | this._htmlParser.parse(template, templateUrl, {
|
11694 | tokenizeExpansionForms: true,
|
11695 | interpolationConfig: this.getInterpolationConfig(component)
|
11696 | }) :
|
11697 | template;
|
11698 | if (!preserveWhitespaces) {
|
11699 | htmlParseResult = removeWhitespaces(htmlParseResult);
|
11700 | }
|
11701 | return this.tryParseHtml(this.expandHtml(htmlParseResult), component, directives, pipes, schemas);
|
11702 | }
|
11703 | tryParseHtml(htmlAstWithErrors, component, directives, pipes, schemas) {
|
11704 | let result;
|
11705 | const errors = htmlAstWithErrors.errors;
|
11706 | const usedPipes = [];
|
11707 | if (htmlAstWithErrors.rootNodes.length > 0) {
|
11708 | const uniqDirectives = removeSummaryDuplicates(directives);
|
11709 | const uniqPipes = removeSummaryDuplicates(pipes);
|
11710 | const providerViewContext = new ProviderViewContext(this._reflector, component);
|
11711 | let interpolationConfig = undefined;
|
11712 | if (component.template && component.template.interpolation) {
|
11713 | interpolationConfig = {
|
11714 | start: component.template.interpolation[0],
|
11715 | end: component.template.interpolation[1]
|
11716 | };
|
11717 | }
|
11718 | const bindingParser = new BindingParser(this._exprParser, interpolationConfig, this._schemaRegistry, uniqPipes, errors);
|
11719 | const parseVisitor = new TemplateParseVisitor(this._reflector, this._config, providerViewContext, uniqDirectives, bindingParser, this._schemaRegistry, schemas, errors);
|
11720 | result = visitAll$1(parseVisitor, htmlAstWithErrors.rootNodes, EMPTY_ELEMENT_CONTEXT);
|
11721 | errors.push(...providerViewContext.errors);
|
11722 | usedPipes.push(...bindingParser.getUsedPipes());
|
11723 | }
|
11724 | else {
|
11725 | result = [];
|
11726 | }
|
11727 | this._assertNoReferenceDuplicationOnTemplate(result, errors);
|
11728 | if (errors.length > 0) {
|
11729 | return new TemplateParseResult(result, usedPipes, errors);
|
11730 | }
|
11731 | if (this.transforms) {
|
11732 | this.transforms.forEach((transform) => {
|
11733 | result = templateVisitAll(transform, result);
|
11734 | });
|
11735 | }
|
11736 | return new TemplateParseResult(result, usedPipes, errors);
|
11737 | }
|
11738 | expandHtml(htmlAstWithErrors, forced = false) {
|
11739 | const errors = htmlAstWithErrors.errors;
|
11740 | if (errors.length == 0 || forced) {
|
11741 | // Transform ICU messages to angular directives
|
11742 | const expandedHtmlAst = expandNodes(htmlAstWithErrors.rootNodes);
|
11743 | errors.push(...expandedHtmlAst.errors);
|
11744 | htmlAstWithErrors = new ParseTreeResult(expandedHtmlAst.nodes, errors);
|
11745 | }
|
11746 | return htmlAstWithErrors;
|
11747 | }
|
11748 | getInterpolationConfig(component) {
|
11749 | if (component.template) {
|
11750 | return InterpolationConfig.fromArray(component.template.interpolation);
|
11751 | }
|
11752 | return undefined;
|
11753 | }
|
11754 | /** @internal */
|
11755 | _assertNoReferenceDuplicationOnTemplate(result, errors) {
|
11756 | const existingReferences = [];
|
11757 | result.filter(element => !!element.references)
|
11758 | .forEach(element => element.references.forEach((reference) => {
|
11759 | const name = reference.name;
|
11760 | if (existingReferences.indexOf(name) < 0) {
|
11761 | existingReferences.push(name);
|
11762 | }
|
11763 | else {
|
11764 | const error = new TemplateParseError(`Reference "#${name}" is defined several times`, reference.sourceSpan, ParseErrorLevel.ERROR);
|
11765 | errors.push(error);
|
11766 | }
|
11767 | }));
|
11768 | }
|
11769 | }
|
11770 | class TemplateParseVisitor {
|
11771 | constructor(reflector, config, providerViewContext, directives, _bindingParser, _schemaRegistry, _schemas, _targetErrors) {
|
11772 | this.reflector = reflector;
|
11773 | this.config = config;
|
11774 | this.providerViewContext = providerViewContext;
|
11775 | this._bindingParser = _bindingParser;
|
11776 | this._schemaRegistry = _schemaRegistry;
|
11777 | this._schemas = _schemas;
|
11778 | this._targetErrors = _targetErrors;
|
11779 | this.selectorMatcher = new SelectorMatcher();
|
11780 | this.directivesIndex = new Map();
|
11781 | this.ngContentCount = 0;
|
11782 | // Note: queries start with id 1 so we can use the number in a Bloom filter!
|
11783 | this.contentQueryStartId = providerViewContext.component.viewQueries.length + 1;
|
11784 | directives.forEach((directive, index) => {
|
11785 | const selector = CssSelector.parse(directive.selector);
|
11786 | this.selectorMatcher.addSelectables(selector, directive);
|
11787 | this.directivesIndex.set(directive, index);
|
11788 | });
|
11789 | }
|
11790 | visitExpansion(expansion, context) {
|
11791 | return null;
|
11792 | }
|
11793 | visitExpansionCase(expansionCase, context) {
|
11794 | return null;
|
11795 | }
|
11796 | visitText(text, parent) {
|
11797 | const ngContentIndex = parent.findNgContentIndex(TEXT_CSS_SELECTOR());
|
11798 | const valueNoNgsp = replaceNgsp(text.value);
|
11799 | const expr = this._bindingParser.parseInterpolation(valueNoNgsp, text.sourceSpan);
|
11800 | return expr ? new BoundTextAst(expr, ngContentIndex, text.sourceSpan) :
|
11801 | new TextAst(valueNoNgsp, ngContentIndex, text.sourceSpan);
|
11802 | }
|
11803 | visitAttribute(attribute, context) {
|
11804 | return new AttrAst(attribute.name, attribute.value, attribute.sourceSpan);
|
11805 | }
|
11806 | visitComment(comment, context) {
|
11807 | return null;
|
11808 | }
|
11809 | visitElement(element, parent) {
|
11810 | const queryStartIndex = this.contentQueryStartId;
|
11811 | const elName = element.name;
|
11812 | const preparsedElement = preparseElement(element);
|
11813 | if (preparsedElement.type === PreparsedElementType.SCRIPT ||
|
11814 | preparsedElement.type === PreparsedElementType.STYLE) {
|
11815 | // Skipping <script> for security reasons
|
11816 | // Skipping <style> as we already processed them
|
11817 | // in the StyleCompiler
|
11818 | return null;
|
11819 | }
|
11820 | if (preparsedElement.type === PreparsedElementType.STYLESHEET &&
|
11821 | isStyleUrlResolvable(preparsedElement.hrefAttr)) {
|
11822 | // Skipping stylesheets with either relative urls or package scheme as we already processed
|
11823 | // them in the StyleCompiler
|
11824 | return null;
|
11825 | }
|
11826 | const matchableAttrs = [];
|
11827 | const elementOrDirectiveProps = [];
|
11828 | const elementOrDirectiveRefs = [];
|
11829 | const elementVars = [];
|
11830 | const events = [];
|
11831 | const templateElementOrDirectiveProps = [];
|
11832 | const templateMatchableAttrs = [];
|
11833 | const templateElementVars = [];
|
11834 | let hasInlineTemplates = false;
|
11835 | const attrs = [];
|
11836 | const isTemplateElement = isNgTemplate(element.name);
|
11837 | element.attrs.forEach(attr => {
|
11838 | const parsedVariables = [];
|
11839 | const hasBinding = this._parseAttr(isTemplateElement, attr, matchableAttrs, elementOrDirectiveProps, events, elementOrDirectiveRefs, elementVars);
|
11840 | elementVars.push(...parsedVariables.map(v => VariableAst.fromParsedVariable(v)));
|
11841 | let templateValue;
|
11842 | let templateKey;
|
11843 | const normalizedName = this._normalizeAttributeName(attr.name);
|
11844 | if (normalizedName.startsWith(TEMPLATE_ATTR_PREFIX$1)) {
|
11845 | templateValue = attr.value;
|
11846 | templateKey = normalizedName.substring(TEMPLATE_ATTR_PREFIX$1.length);
|
11847 | }
|
11848 | const hasTemplateBinding = templateValue != null;
|
11849 | if (hasTemplateBinding) {
|
11850 | if (hasInlineTemplates) {
|
11851 | this._reportError(`Can't have multiple template bindings on one element. Use only one attribute prefixed with *`, attr.sourceSpan);
|
11852 | }
|
11853 | hasInlineTemplates = true;
|
11854 | const parsedVariables = [];
|
11855 | const absoluteOffset = (attr.valueSpan || attr.sourceSpan).start.offset;
|
11856 | this._bindingParser.parseInlineTemplateBinding(templateKey, templateValue, attr.sourceSpan, absoluteOffset, templateMatchableAttrs, templateElementOrDirectiveProps, parsedVariables, false /* isIvyAst */);
|
11857 | templateElementVars.push(...parsedVariables.map(v => VariableAst.fromParsedVariable(v)));
|
11858 | }
|
11859 | if (!hasBinding && !hasTemplateBinding) {
|
11860 | // don't include the bindings as attributes as well in the AST
|
11861 | attrs.push(this.visitAttribute(attr, null));
|
11862 | matchableAttrs.push([attr.name, attr.value]);
|
11863 | }
|
11864 | });
|
11865 | const elementCssSelector = createElementCssSelector(elName, matchableAttrs);
|
11866 | const { directives: directiveMetas, matchElement } = this._parseDirectives(this.selectorMatcher, elementCssSelector);
|
11867 | const references = [];
|
11868 | const boundDirectivePropNames = new Set();
|
11869 | const directiveAsts = this._createDirectiveAsts(isTemplateElement, element.name, directiveMetas, elementOrDirectiveProps, elementOrDirectiveRefs, element.sourceSpan, references, boundDirectivePropNames);
|
11870 | const elementProps = this._createElementPropertyAsts(element.name, elementOrDirectiveProps, boundDirectivePropNames);
|
11871 | const isViewRoot = parent.isTemplateElement || hasInlineTemplates;
|
11872 | const providerContext = new ProviderElementContext(this.providerViewContext, parent.providerContext, isViewRoot, directiveAsts, attrs, references, isTemplateElement, queryStartIndex, element.sourceSpan);
|
11873 | const children = visitAll$1(preparsedElement.nonBindable ? NON_BINDABLE_VISITOR : this, element.children, ElementContext.create(isTemplateElement, directiveAsts, isTemplateElement ? parent.providerContext : providerContext));
|
11874 | providerContext.afterElement();
|
11875 | // Override the actual selector when the `ngProjectAs` attribute is provided
|
11876 | const projectionSelector = preparsedElement.projectAs != '' ?
|
11877 | CssSelector.parse(preparsedElement.projectAs)[0] :
|
11878 | elementCssSelector;
|
11879 | const ngContentIndex = parent.findNgContentIndex(projectionSelector);
|
11880 | let parsedElement;
|
11881 | if (preparsedElement.type === PreparsedElementType.NG_CONTENT) {
|
11882 | // `<ng-content>` element
|
11883 | if (element.children && !element.children.every(_isEmptyTextNode)) {
|
11884 | this._reportError(`<ng-content> element cannot have content.`, element.sourceSpan);
|
11885 | }
|
11886 | parsedElement = new NgContentAst(this.ngContentCount++, hasInlineTemplates ? null : ngContentIndex, element.sourceSpan);
|
11887 | }
|
11888 | else if (isTemplateElement) {
|
11889 | // `<ng-template>` element
|
11890 | this._assertAllEventsPublishedByDirectives(directiveAsts, events);
|
11891 | this._assertNoComponentsNorElementBindingsOnTemplate(directiveAsts, elementProps, element.sourceSpan);
|
11892 | parsedElement = new EmbeddedTemplateAst(attrs, events, references, elementVars, providerContext.transformedDirectiveAsts, providerContext.transformProviders, providerContext.transformedHasViewContainer, providerContext.queryMatches, children, hasInlineTemplates ? null : ngContentIndex, element.sourceSpan);
|
11893 | }
|
11894 | else {
|
11895 | // element other than `<ng-content>` and `<ng-template>`
|
11896 | this._assertElementExists(matchElement, element);
|
11897 | this._assertOnlyOneComponent(directiveAsts, element.sourceSpan);
|
11898 | const ngContentIndex = hasInlineTemplates ? null : parent.findNgContentIndex(projectionSelector);
|
11899 | parsedElement = new ElementAst(elName, attrs, elementProps, events, references, providerContext.transformedDirectiveAsts, providerContext.transformProviders, providerContext.transformedHasViewContainer, providerContext.queryMatches, children, hasInlineTemplates ? null : ngContentIndex, element.sourceSpan, element.endSourceSpan || null);
|
11900 | }
|
11901 | if (hasInlineTemplates) {
|
11902 | // The element as a *-attribute
|
11903 | const templateQueryStartIndex = this.contentQueryStartId;
|
11904 | const templateSelector = createElementCssSelector('ng-template', templateMatchableAttrs);
|
11905 | const { directives } = this._parseDirectives(this.selectorMatcher, templateSelector);
|
11906 | const templateBoundDirectivePropNames = new Set();
|
11907 | const templateDirectiveAsts = this._createDirectiveAsts(true, elName, directives, templateElementOrDirectiveProps, [], element.sourceSpan, [], templateBoundDirectivePropNames);
|
11908 | const templateElementProps = this._createElementPropertyAsts(elName, templateElementOrDirectiveProps, templateBoundDirectivePropNames);
|
11909 | this._assertNoComponentsNorElementBindingsOnTemplate(templateDirectiveAsts, templateElementProps, element.sourceSpan);
|
11910 | const templateProviderContext = new ProviderElementContext(this.providerViewContext, parent.providerContext, parent.isTemplateElement, templateDirectiveAsts, [], [], true, templateQueryStartIndex, element.sourceSpan);
|
11911 | templateProviderContext.afterElement();
|
11912 | parsedElement = new EmbeddedTemplateAst([], [], [], templateElementVars, templateProviderContext.transformedDirectiveAsts, templateProviderContext.transformProviders, templateProviderContext.transformedHasViewContainer, templateProviderContext.queryMatches, [parsedElement], ngContentIndex, element.sourceSpan);
|
11913 | }
|
11914 | return parsedElement;
|
11915 | }
|
11916 | _parseAttr(isTemplateElement, attr, targetMatchableAttrs, targetProps, targetEvents, targetRefs, targetVars) {
|
11917 | const name = this._normalizeAttributeName(attr.name);
|
11918 | const value = attr.value;
|
11919 | const srcSpan = attr.sourceSpan;
|
11920 | const absoluteOffset = attr.valueSpan ? attr.valueSpan.start.offset : srcSpan.start.offset;
|
11921 | const boundEvents = [];
|
11922 | const bindParts = name.match(BIND_NAME_REGEXP);
|
11923 | let hasBinding = false;
|
11924 | if (bindParts !== null) {
|
11925 | hasBinding = true;
|
11926 | if (bindParts[KW_BIND_IDX] != null) {
|
11927 | this._bindingParser.parsePropertyBinding(bindParts[IDENT_KW_IDX], value, false, srcSpan, absoluteOffset, attr.valueSpan, targetMatchableAttrs, targetProps);
|
11928 | }
|
11929 | else if (bindParts[KW_LET_IDX]) {
|
11930 | if (isTemplateElement) {
|
11931 | const identifier = bindParts[IDENT_KW_IDX];
|
11932 | this._parseVariable(identifier, value, srcSpan, targetVars);
|
11933 | }
|
11934 | else {
|
11935 | this._reportError(`"let-" is only supported on ng-template elements.`, srcSpan);
|
11936 | }
|
11937 | }
|
11938 | else if (bindParts[KW_REF_IDX]) {
|
11939 | const identifier = bindParts[IDENT_KW_IDX];
|
11940 | this._parseReference(identifier, value, srcSpan, targetRefs);
|
11941 | }
|
11942 | else if (bindParts[KW_ON_IDX]) {
|
11943 | this._bindingParser.parseEvent(bindParts[IDENT_KW_IDX], value, srcSpan, attr.valueSpan || srcSpan, targetMatchableAttrs, boundEvents);
|
11944 | }
|
11945 | else if (bindParts[KW_BINDON_IDX]) {
|
11946 | this._bindingParser.parsePropertyBinding(bindParts[IDENT_KW_IDX], value, false, srcSpan, absoluteOffset, attr.valueSpan, targetMatchableAttrs, targetProps);
|
11947 | this._parseAssignmentEvent(bindParts[IDENT_KW_IDX], value, srcSpan, attr.valueSpan || srcSpan, targetMatchableAttrs, boundEvents);
|
11948 | }
|
11949 | else if (bindParts[KW_AT_IDX]) {
|
11950 | this._bindingParser.parseLiteralAttr(name, value, srcSpan, absoluteOffset, attr.valueSpan, targetMatchableAttrs, targetProps);
|
11951 | }
|
11952 | else if (bindParts[IDENT_BANANA_BOX_IDX]) {
|
11953 | this._bindingParser.parsePropertyBinding(bindParts[IDENT_BANANA_BOX_IDX], value, false, srcSpan, absoluteOffset, attr.valueSpan, targetMatchableAttrs, targetProps);
|
11954 | this._parseAssignmentEvent(bindParts[IDENT_BANANA_BOX_IDX], value, srcSpan, attr.valueSpan || srcSpan, targetMatchableAttrs, boundEvents);
|
11955 | }
|
11956 | else if (bindParts[IDENT_PROPERTY_IDX]) {
|
11957 | this._bindingParser.parsePropertyBinding(bindParts[IDENT_PROPERTY_IDX], value, false, srcSpan, absoluteOffset, attr.valueSpan, targetMatchableAttrs, targetProps);
|
11958 | }
|
11959 | else if (bindParts[IDENT_EVENT_IDX]) {
|
11960 | this._bindingParser.parseEvent(bindParts[IDENT_EVENT_IDX], value, srcSpan, attr.valueSpan || srcSpan, targetMatchableAttrs, boundEvents);
|
11961 | }
|
11962 | }
|
11963 | else {
|
11964 | hasBinding = this._bindingParser.parsePropertyInterpolation(name, value, srcSpan, attr.valueSpan, targetMatchableAttrs, targetProps);
|
11965 | }
|
11966 | if (!hasBinding) {
|
11967 | this._bindingParser.parseLiteralAttr(name, value, srcSpan, absoluteOffset, attr.valueSpan, targetMatchableAttrs, targetProps);
|
11968 | }
|
11969 | targetEvents.push(...boundEvents.map(e => BoundEventAst.fromParsedEvent(e)));
|
11970 | return hasBinding;
|
11971 | }
|
11972 | _normalizeAttributeName(attrName) {
|
11973 | return /^data-/i.test(attrName) ? attrName.substring(5) : attrName;
|
11974 | }
|
11975 | _parseVariable(identifier, value, sourceSpan, targetVars) {
|
11976 | if (identifier.indexOf('-') > -1) {
|
11977 | this._reportError(`"-" is not allowed in variable names`, sourceSpan);
|
11978 | }
|
11979 | else if (identifier.length === 0) {
|
11980 | this._reportError(`Variable does not have a name`, sourceSpan);
|
11981 | }
|
11982 | targetVars.push(new VariableAst(identifier, value, sourceSpan));
|
11983 | }
|
11984 | _parseReference(identifier, value, sourceSpan, targetRefs) {
|
11985 | if (identifier.indexOf('-') > -1) {
|
11986 | this._reportError(`"-" is not allowed in reference names`, sourceSpan);
|
11987 | }
|
11988 | else if (identifier.length === 0) {
|
11989 | this._reportError(`Reference does not have a name`, sourceSpan);
|
11990 | }
|
11991 | targetRefs.push(new ElementOrDirectiveRef(identifier, value, sourceSpan));
|
11992 | }
|
11993 | _parseAssignmentEvent(name, expression, sourceSpan, valueSpan, targetMatchableAttrs, targetEvents) {
|
11994 | this._bindingParser.parseEvent(`${name}Change`, `${expression}=$event`, sourceSpan, valueSpan, targetMatchableAttrs, targetEvents);
|
11995 | }
|
11996 | _parseDirectives(selectorMatcher, elementCssSelector) {
|
11997 | // Need to sort the directives so that we get consistent results throughout,
|
11998 | // as selectorMatcher uses Maps inside.
|
11999 | // Also deduplicate directives as they might match more than one time!
|
12000 | const directives = newArray(this.directivesIndex.size);
|
12001 | // Whether any directive selector matches on the element name
|
12002 | let matchElement = false;
|
12003 | selectorMatcher.match(elementCssSelector, (selector, directive) => {
|
12004 | directives[this.directivesIndex.get(directive)] = directive;
|
12005 | matchElement = matchElement || selector.hasElementSelector();
|
12006 | });
|
12007 | return {
|
12008 | directives: directives.filter(dir => !!dir),
|
12009 | matchElement,
|
12010 | };
|
12011 | }
|
12012 | _createDirectiveAsts(isTemplateElement, elementName, directives, props, elementOrDirectiveRefs, elementSourceSpan, targetReferences, targetBoundDirectivePropNames) {
|
12013 | const matchedReferences = new Set();
|
12014 | let component = null;
|
12015 | const directiveAsts = directives.map((directive) => {
|
12016 | const sourceSpan = new ParseSourceSpan(elementSourceSpan.start, elementSourceSpan.end, elementSourceSpan.fullStart, `Directive ${identifierName(directive.type)}`);
|
12017 | if (directive.isComponent) {
|
12018 | component = directive;
|
12019 | }
|
12020 | const directiveProperties = [];
|
12021 | const boundProperties = this._bindingParser.createDirectiveHostPropertyAsts(directive, elementName, sourceSpan);
|
12022 | let hostProperties = boundProperties.map(prop => BoundElementPropertyAst.fromBoundProperty(prop));
|
12023 | // Note: We need to check the host properties here as well,
|
12024 | // as we don't know the element name in the DirectiveWrapperCompiler yet.
|
12025 | hostProperties = this._checkPropertiesInSchema(elementName, hostProperties);
|
12026 | const parsedEvents = this._bindingParser.createDirectiveHostEventAsts(directive, sourceSpan);
|
12027 | this._createDirectivePropertyAsts(directive.inputs, props, directiveProperties, targetBoundDirectivePropNames);
|
12028 | elementOrDirectiveRefs.forEach((elOrDirRef) => {
|
12029 | if ((elOrDirRef.value.length === 0 && directive.isComponent) ||
|
12030 | (elOrDirRef.isReferenceToDirective(directive))) {
|
12031 | targetReferences.push(new ReferenceAst(elOrDirRef.name, createTokenForReference(directive.type.reference), elOrDirRef.value, elOrDirRef.sourceSpan));
|
12032 | matchedReferences.add(elOrDirRef.name);
|
12033 | }
|
12034 | });
|
12035 | const hostEvents = parsedEvents.map(e => BoundEventAst.fromParsedEvent(e));
|
12036 | const contentQueryStartId = this.contentQueryStartId;
|
12037 | this.contentQueryStartId += directive.queries.length;
|
12038 | return new DirectiveAst(directive, directiveProperties, hostProperties, hostEvents, contentQueryStartId, sourceSpan);
|
12039 | });
|
12040 | elementOrDirectiveRefs.forEach((elOrDirRef) => {
|
12041 | if (elOrDirRef.value.length > 0) {
|
12042 | if (!matchedReferences.has(elOrDirRef.name)) {
|
12043 | this._reportError(`There is no directive with "exportAs" set to "${elOrDirRef.value}"`, elOrDirRef.sourceSpan);
|
12044 | }
|
12045 | }
|
12046 | else if (!component) {
|
12047 | let refToken = null;
|
12048 | if (isTemplateElement) {
|
12049 | refToken = createTokenForExternalReference(this.reflector, Identifiers.TemplateRef);
|
12050 | }
|
12051 | targetReferences.push(new ReferenceAst(elOrDirRef.name, refToken, elOrDirRef.value, elOrDirRef.sourceSpan));
|
12052 | }
|
12053 | });
|
12054 | return directiveAsts;
|
12055 | }
|
12056 | _createDirectivePropertyAsts(directiveProperties, boundProps, targetBoundDirectiveProps, targetBoundDirectivePropNames) {
|
12057 | if (directiveProperties) {
|
12058 | const boundPropsByName = new Map();
|
12059 | boundProps.forEach(boundProp => {
|
12060 | const prevValue = boundPropsByName.get(boundProp.name);
|
12061 | if (!prevValue || prevValue.isLiteral) {
|
12062 | // give [a]="b" a higher precedence than a="b" on the same element
|
12063 | boundPropsByName.set(boundProp.name, boundProp);
|
12064 | }
|
12065 | });
|
12066 | Object.keys(directiveProperties).forEach(dirProp => {
|
12067 | const elProp = directiveProperties[dirProp];
|
12068 | const boundProp = boundPropsByName.get(elProp);
|
12069 | // Bindings are optional, so this binding only needs to be set up if an expression is given.
|
12070 | if (boundProp) {
|
12071 | targetBoundDirectivePropNames.add(boundProp.name);
|
12072 | if (!isEmptyExpression(boundProp.expression)) {
|
12073 | targetBoundDirectiveProps.push(new BoundDirectivePropertyAst(dirProp, boundProp.name, boundProp.expression, boundProp.sourceSpan));
|
12074 | }
|
12075 | }
|
12076 | });
|
12077 | }
|
12078 | }
|
12079 | _createElementPropertyAsts(elementName, props, boundDirectivePropNames) {
|
12080 | const boundElementProps = [];
|
12081 | props.forEach((prop) => {
|
12082 | if (!prop.isLiteral && !boundDirectivePropNames.has(prop.name)) {
|
12083 | const boundProp = this._bindingParser.createBoundElementProperty(elementName, prop);
|
12084 | boundElementProps.push(BoundElementPropertyAst.fromBoundProperty(boundProp));
|
12085 | }
|
12086 | });
|
12087 | return this._checkPropertiesInSchema(elementName, boundElementProps);
|
12088 | }
|
12089 | _findComponentDirectives(directives) {
|
12090 | return directives.filter(directive => directive.directive.isComponent);
|
12091 | }
|
12092 | _findComponentDirectiveNames(directives) {
|
12093 | return this._findComponentDirectives(directives)
|
12094 | .map(directive => identifierName(directive.directive.type));
|
12095 | }
|
12096 | _assertOnlyOneComponent(directives, sourceSpan) {
|
12097 | const componentTypeNames = this._findComponentDirectiveNames(directives);
|
12098 | if (componentTypeNames.length > 1) {
|
12099 | this._reportError(`More than one component matched on this element.\n` +
|
12100 | `Make sure that only one component's selector can match a given element.\n` +
|
12101 | `Conflicting components: ${componentTypeNames.join(',')}`, sourceSpan);
|
12102 | }
|
12103 | }
|
12104 | /**
|
12105 | * Make sure that non-angular tags conform to the schemas.
|
12106 | *
|
12107 | * Note: An element is considered an angular tag when at least one directive selector matches the
|
12108 | * tag name.
|
12109 | *
|
12110 | * @param matchElement Whether any directive has matched on the tag name
|
12111 | * @param element the html element
|
12112 | */
|
12113 | _assertElementExists(matchElement, element) {
|
12114 | const elName = element.name.replace(/^:xhtml:/, '');
|
12115 | if (!matchElement && !this._schemaRegistry.hasElement(elName, this._schemas)) {
|
12116 | let errorMsg = `'${elName}' is not a known element:\n`;
|
12117 | errorMsg += `1. If '${elName}' is an Angular component, then verify that it is part of this module.\n`;
|
12118 | if (elName.indexOf('-') > -1) {
|
12119 | errorMsg += `2. If '${elName}' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message.`;
|
12120 | }
|
12121 | else {
|
12122 | errorMsg +=
|
12123 | `2. To allow any element add 'NO_ERRORS_SCHEMA' to the '@NgModule.schemas' of this component.`;
|
12124 | }
|
12125 | this._reportError(errorMsg, element.sourceSpan);
|
12126 | }
|
12127 | }
|
12128 | _assertNoComponentsNorElementBindingsOnTemplate(directives, elementProps, sourceSpan) {
|
12129 | const componentTypeNames = this._findComponentDirectiveNames(directives);
|
12130 | if (componentTypeNames.length > 0) {
|
12131 | this._reportError(`Components on an embedded template: ${componentTypeNames.join(',')}`, sourceSpan);
|
12132 | }
|
12133 | elementProps.forEach(prop => {
|
12134 | this._reportError(`Property binding ${prop.name} not used by any directive on an embedded template. Make sure that the property name is spelled correctly and all directives are listed in the "@NgModule.declarations".`, sourceSpan);
|
12135 | });
|
12136 | }
|
12137 | _assertAllEventsPublishedByDirectives(directives, events) {
|
12138 | const allDirectiveEvents = new Set();
|
12139 | directives.forEach(directive => {
|
12140 | Object.keys(directive.directive.outputs).forEach(k => {
|
12141 | const eventName = directive.directive.outputs[k];
|
12142 | allDirectiveEvents.add(eventName);
|
12143 | });
|
12144 | });
|
12145 | events.forEach(event => {
|
12146 | if (event.target != null || !allDirectiveEvents.has(event.name)) {
|
12147 | this._reportError(`Event binding ${event
|
12148 | .fullName} not emitted by any directive on an embedded template. Make sure that the event name is spelled correctly and all directives are listed in the "@NgModule.declarations".`, event.sourceSpan);
|
12149 | }
|
12150 | });
|
12151 | }
|
12152 | _checkPropertiesInSchema(elementName, boundProps) {
|
12153 | // Note: We can't filter out empty expressions before this method,
|
12154 | // as we still want to validate them!
|
12155 | return boundProps.filter((boundProp) => {
|
12156 | if (boundProp.type === 0 /* Property */ &&
|
12157 | !this._schemaRegistry.hasProperty(elementName, boundProp.name, this._schemas)) {
|
12158 | let errorMsg = `Can't bind to '${boundProp.name}' since it isn't a known property of '${elementName}'.`;
|
12159 | if (elementName.startsWith('ng-')) {
|
12160 | errorMsg +=
|
12161 | `\n1. If '${boundProp
|
12162 | .name}' is an Angular directive, then add 'CommonModule' to the '@NgModule.imports' of this component.` +
|
12163 | `\n2. To allow any property add 'NO_ERRORS_SCHEMA' to the '@NgModule.schemas' of this component.`;
|
12164 | }
|
12165 | else if (elementName.indexOf('-') > -1) {
|
12166 | errorMsg +=
|
12167 | `\n1. If '${elementName}' is an Angular component and it has '${boundProp.name}' input, then verify that it is part of this module.` +
|
12168 | `\n2. If '${elementName}' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message.` +
|
12169 | `\n3. To allow any property add 'NO_ERRORS_SCHEMA' to the '@NgModule.schemas' of this component.`;
|
12170 | }
|
12171 | this._reportError(errorMsg, boundProp.sourceSpan);
|
12172 | }
|
12173 | return !isEmptyExpression(boundProp.value);
|
12174 | });
|
12175 | }
|
12176 | _reportError(message, sourceSpan, level = ParseErrorLevel.ERROR) {
|
12177 | this._targetErrors.push(new ParseError(sourceSpan, message, level));
|
12178 | }
|
12179 | }
|
12180 | class NonBindableVisitor {
|
12181 | visitElement(ast, parent) {
|
12182 | const preparsedElement = preparseElement(ast);
|
12183 | if (preparsedElement.type === PreparsedElementType.SCRIPT ||
|
12184 | preparsedElement.type === PreparsedElementType.STYLE ||
|
12185 | preparsedElement.type === PreparsedElementType.STYLESHEET) {
|
12186 | // Skipping <script> for security reasons
|
12187 | // Skipping <style> and stylesheets as we already processed them
|
12188 | // in the StyleCompiler
|
12189 | return null;
|
12190 | }
|
12191 | const attrNameAndValues = ast.attrs.map((attr) => [attr.name, attr.value]);
|
12192 | const selector = createElementCssSelector(ast.name, attrNameAndValues);
|
12193 | const ngContentIndex = parent.findNgContentIndex(selector);
|
12194 | const children = visitAll$1(this, ast.children, EMPTY_ELEMENT_CONTEXT);
|
12195 | return new ElementAst(ast.name, visitAll$1(this, ast.attrs), [], [], [], [], [], false, [], children, ngContentIndex, ast.sourceSpan, ast.endSourceSpan);
|
12196 | }
|
12197 | visitComment(comment, context) {
|
12198 | return null;
|
12199 | }
|
12200 | visitAttribute(attribute, context) {
|
12201 | return new AttrAst(attribute.name, attribute.value, attribute.sourceSpan);
|
12202 | }
|
12203 | visitText(text, parent) {
|
12204 | const ngContentIndex = parent.findNgContentIndex(TEXT_CSS_SELECTOR());
|
12205 | return new TextAst(text.value, ngContentIndex, text.sourceSpan);
|
12206 | }
|
12207 | visitExpansion(expansion, context) {
|
12208 | return expansion;
|
12209 | }
|
12210 | visitExpansionCase(expansionCase, context) {
|
12211 | return expansionCase;
|
12212 | }
|
12213 | }
|
12214 | /**
|
12215 | * A reference to an element or directive in a template. E.g., the reference in this template:
|
12216 | *
|
12217 | * <div #myMenu="coolMenu">
|
12218 | *
|
12219 | * would be {name: 'myMenu', value: 'coolMenu', sourceSpan: ...}
|
12220 | */
|
12221 | class ElementOrDirectiveRef {
|
12222 | constructor(name, value, sourceSpan) {
|
12223 | this.name = name;
|
12224 | this.value = value;
|
12225 | this.sourceSpan = sourceSpan;
|
12226 | }
|
12227 | /** Gets whether this is a reference to the given directive. */
|
12228 | isReferenceToDirective(directive) {
|
12229 | return splitExportAs(directive.exportAs).indexOf(this.value) !== -1;
|
12230 | }
|
12231 | }
|
12232 | /** Splits a raw, potentially comma-delimited `exportAs` value into an array of names. */
|
12233 | function splitExportAs(exportAs) {
|
12234 | return exportAs ? exportAs.split(',').map(e => e.trim()) : [];
|
12235 | }
|
12236 | function splitClasses(classAttrValue) {
|
12237 | return classAttrValue.trim().split(/\s+/g);
|
12238 | }
|
12239 | class ElementContext {
|
12240 | constructor(isTemplateElement, _ngContentIndexMatcher, _wildcardNgContentIndex, providerContext) {
|
12241 | this.isTemplateElement = isTemplateElement;
|
12242 | this._ngContentIndexMatcher = _ngContentIndexMatcher;
|
12243 | this._wildcardNgContentIndex = _wildcardNgContentIndex;
|
12244 | this.providerContext = providerContext;
|
12245 | }
|
12246 | static create(isTemplateElement, directives, providerContext) {
|
12247 | const matcher = new SelectorMatcher();
|
12248 | let wildcardNgContentIndex = null;
|
12249 | const component = directives.find(directive => directive.directive.isComponent);
|
12250 | if (component) {
|
12251 | const ngContentSelectors = component.directive.template.ngContentSelectors;
|
12252 | for (let i = 0; i < ngContentSelectors.length; i++) {
|
12253 | const selector = ngContentSelectors[i];
|
12254 | if (selector === '*') {
|
12255 | wildcardNgContentIndex = i;
|
12256 | }
|
12257 | else {
|
12258 | matcher.addSelectables(CssSelector.parse(ngContentSelectors[i]), i);
|
12259 | }
|
12260 | }
|
12261 | }
|
12262 | return new ElementContext(isTemplateElement, matcher, wildcardNgContentIndex, providerContext);
|
12263 | }
|
12264 | findNgContentIndex(selector) {
|
12265 | const ngContentIndices = [];
|
12266 | this._ngContentIndexMatcher.match(selector, (selector, ngContentIndex) => {
|
12267 | ngContentIndices.push(ngContentIndex);
|
12268 | });
|
12269 | ngContentIndices.sort();
|
12270 | if (this._wildcardNgContentIndex != null) {
|
12271 | ngContentIndices.push(this._wildcardNgContentIndex);
|
12272 | }
|
12273 | return ngContentIndices.length > 0 ? ngContentIndices[0] : null;
|
12274 | }
|
12275 | }
|
12276 | function createElementCssSelector(elementName, attributes) {
|
12277 | const cssSelector = new CssSelector();
|
12278 | const elNameNoNs = splitNsName(elementName)[1];
|
12279 | cssSelector.setElement(elNameNoNs);
|
12280 | for (let i = 0; i < attributes.length; i++) {
|
12281 | const attrName = attributes[i][0];
|
12282 | const attrNameNoNs = splitNsName(attrName)[1];
|
12283 | const attrValue = attributes[i][1];
|
12284 | cssSelector.addAttribute(attrNameNoNs, attrValue);
|
12285 | if (attrName.toLowerCase() == CLASS_ATTR) {
|
12286 | const classes = splitClasses(attrValue);
|
12287 | classes.forEach(className => cssSelector.addClassName(className));
|
12288 | }
|
12289 | }
|
12290 | return cssSelector;
|
12291 | }
|
12292 | const EMPTY_ELEMENT_CONTEXT = new ElementContext(true, new SelectorMatcher(), null, null);
|
12293 | const NON_BINDABLE_VISITOR = new NonBindableVisitor();
|
12294 | function _isEmptyTextNode(node) {
|
12295 | return node instanceof Text$2 && node.value.trim().length == 0;
|
12296 | }
|
12297 | function removeSummaryDuplicates(items) {
|
12298 | const map = new Map();
|
12299 | items.forEach((item) => {
|
12300 | if (!map.get(item.type.reference)) {
|
12301 | map.set(item.type.reference, item);
|
12302 | }
|
12303 | });
|
12304 | return Array.from(map.values());
|
12305 | }
|
12306 | function isEmptyExpression(ast) {
|
12307 | if (ast instanceof ASTWithSource) {
|
12308 | ast = ast.ast;
|
12309 | }
|
12310 | return ast instanceof EmptyExpr;
|
12311 | }
|
12312 |
|
12313 | /**
|
12314 | * @license
|
12315 | * Copyright Google LLC All Rights Reserved.
|
12316 | *
|
12317 | * Use of this source code is governed by an MIT-style license that can be
|
12318 | * found in the LICENSE file at https://angular.io/license
|
12319 | */
|
12320 | /**
|
12321 | * Parses string representation of a style and converts it into object literal.
|
12322 | *
|
12323 | * @param value string representation of style as used in the `style` attribute in HTML.
|
12324 | * Example: `color: red; height: auto`.
|
12325 | * @returns An array of style property name and value pairs, e.g. `['color', 'red', 'height',
|
12326 | * 'auto']`
|
12327 | */
|
12328 | function parse(value) {
|
12329 | // we use a string array here instead of a string map
|
12330 | // because a string-map is not guaranteed to retain the
|
12331 | // order of the entries whereas a string array can be
|
12332 | // constructed in a [key, value, key, value] format.
|
12333 | const styles = [];
|
12334 | let i = 0;
|
12335 | let parenDepth = 0;
|
12336 | let quote = 0 /* QuoteNone */;
|
12337 | let valueStart = 0;
|
12338 | let propStart = 0;
|
12339 | let currentProp = null;
|
12340 | let valueHasQuotes = false;
|
12341 | while (i < value.length) {
|
12342 | const token = value.charCodeAt(i++);
|
12343 | switch (token) {
|
12344 | case 40 /* OpenParen */:
|
12345 | parenDepth++;
|
12346 | break;
|
12347 | case 41 /* CloseParen */:
|
12348 | parenDepth--;
|
12349 | break;
|
12350 | case 39 /* QuoteSingle */:
|
12351 | // valueStart needs to be there since prop values don't
|
12352 | // have quotes in CSS
|
12353 | valueHasQuotes = valueHasQuotes || valueStart > 0;
|
12354 | if (quote === 0 /* QuoteNone */) {
|
12355 | quote = 39 /* QuoteSingle */;
|
12356 | }
|
12357 | else if (quote === 39 /* QuoteSingle */ && value.charCodeAt(i - 1) !== 92 /* BackSlash */) {
|
12358 | quote = 0 /* QuoteNone */;
|
12359 | }
|
12360 | break;
|
12361 | case 34 /* QuoteDouble */:
|
12362 | // same logic as above
|
12363 | valueHasQuotes = valueHasQuotes || valueStart > 0;
|
12364 | if (quote === 0 /* QuoteNone */) {
|
12365 | quote = 34 /* QuoteDouble */;
|
12366 | }
|
12367 | else if (quote === 34 /* QuoteDouble */ && value.charCodeAt(i - 1) !== 92 /* BackSlash */) {
|
12368 | quote = 0 /* QuoteNone */;
|
12369 | }
|
12370 | break;
|
12371 | case 58 /* Colon */:
|
12372 | if (!currentProp && parenDepth === 0 && quote === 0 /* QuoteNone */) {
|
12373 | currentProp = hyphenate(value.substring(propStart, i - 1).trim());
|
12374 | valueStart = i;
|
12375 | }
|
12376 | break;
|
12377 | case 59 /* Semicolon */:
|
12378 | if (currentProp && valueStart > 0 && parenDepth === 0 && quote === 0 /* QuoteNone */) {
|
12379 | const styleVal = value.substring(valueStart, i - 1).trim();
|
12380 | styles.push(currentProp, valueHasQuotes ? stripUnnecessaryQuotes(styleVal) : styleVal);
|
12381 | propStart = i;
|
12382 | valueStart = 0;
|
12383 | currentProp = null;
|
12384 | valueHasQuotes = false;
|
12385 | }
|
12386 | break;
|
12387 | }
|
12388 | }
|
12389 | if (currentProp && valueStart) {
|
12390 | const styleVal = value.substr(valueStart).trim();
|
12391 | styles.push(currentProp, valueHasQuotes ? stripUnnecessaryQuotes(styleVal) : styleVal);
|
12392 | }
|
12393 | return styles;
|
12394 | }
|
12395 | function stripUnnecessaryQuotes(value) {
|
12396 | const qS = value.charCodeAt(0);
|
12397 | const qE = value.charCodeAt(value.length - 1);
|
12398 | if (qS == qE && (qS == 39 /* QuoteSingle */ || qS == 34 /* QuoteDouble */)) {
|
12399 | const tempValue = value.substring(1, value.length - 1);
|
12400 | // special case to avoid using a multi-quoted string that was just chomped
|
12401 | // (e.g. `font-family: "Verdana", "sans-serif"`)
|
12402 | if (tempValue.indexOf('\'') == -1 && tempValue.indexOf('"') == -1) {
|
12403 | value = tempValue;
|
12404 | }
|
12405 | }
|
12406 | return value;
|
12407 | }
|
12408 | function hyphenate(value) {
|
12409 | return value
|
12410 | .replace(/[a-z][A-Z]/g, v => {
|
12411 | return v.charAt(0) + '-' + v.charAt(1);
|
12412 | })
|
12413 | .toLowerCase();
|
12414 | }
|
12415 |
|
12416 | const IMPORTANT_FLAG = '!important';
|
12417 | /**
|
12418 | * Minimum amount of binding slots required in the runtime for style/class bindings.
|
12419 | *
|
12420 | * Styling in Angular uses up two slots in the runtime LView/TData data structures to
|
12421 | * record binding data, property information and metadata.
|
12422 | *
|
12423 | * When a binding is registered it will place the following information in the `LView`:
|
12424 | *
|
12425 | * slot 1) binding value
|
12426 | * slot 2) cached value (all other values collected before it in string form)
|
12427 | *
|
12428 | * When a binding is registered it will place the following information in the `TData`:
|
12429 | *
|
12430 | * slot 1) prop name
|
12431 | * slot 2) binding index that points to the previous style/class binding (and some extra config
|
12432 | * values)
|
12433 | *
|
12434 | * Let's imagine we have a binding that looks like so:
|
12435 | *
|
12436 | * ```
|
12437 | * <div [style.width]="x" [style.height]="y">
|
12438 | * ```
|
12439 | *
|
12440 | * Our `LView` and `TData` data-structures look like so:
|
12441 | *
|
12442 | * ```typescript
|
12443 | * LView = [
|
12444 | * // ...
|
12445 | * x, // value of x
|
12446 | * "width: x",
|
12447 | *
|
12448 | * y, // value of y
|
12449 | * "width: x; height: y",
|
12450 | * // ...
|
12451 | * ];
|
12452 | *
|
12453 | * TData = [
|
12454 | * // ...
|
12455 | * "width", // binding slot 20
|
12456 | * 0,
|
12457 | *
|
12458 | * "height",
|
12459 | * 20,
|
12460 | * // ...
|
12461 | * ];
|
12462 | * ```
|
12463 | *
|
12464 | * */
|
12465 | const MIN_STYLING_BINDING_SLOTS_REQUIRED = 2;
|
12466 | /**
|
12467 | * Produces creation/update instructions for all styling bindings (class and style)
|
12468 | *
|
12469 | * It also produces the creation instruction to register all initial styling values
|
12470 | * (which are all the static class="..." and style="..." attribute values that exist
|
12471 | * on an element within a template).
|
12472 | *
|
12473 | * The builder class below handles producing instructions for the following cases:
|
12474 | *
|
12475 | * - Static style/class attributes (style="..." and class="...")
|
12476 | * - Dynamic style/class map bindings ([style]="map" and [class]="map|string")
|
12477 | * - Dynamic style/class property bindings ([style.prop]="exp" and [class.name]="exp")
|
12478 | *
|
12479 | * Due to the complex relationship of all of these cases, the instructions generated
|
12480 | * for these attributes/properties/bindings must be done so in the correct order. The
|
12481 | * order which these must be generated is as follows:
|
12482 | *
|
12483 | * if (createMode) {
|
12484 | * styling(...)
|
12485 | * }
|
12486 | * if (updateMode) {
|
12487 | * styleMap(...)
|
12488 | * classMap(...)
|
12489 | * styleProp(...)
|
12490 | * classProp(...)
|
12491 | * }
|
12492 | *
|
12493 | * The creation/update methods within the builder class produce these instructions.
|
12494 | */
|
12495 | class StylingBuilder {
|
12496 | constructor(_directiveExpr) {
|
12497 | this._directiveExpr = _directiveExpr;
|
12498 | /** Whether or not there are any static styling values present */
|
12499 | this._hasInitialValues = false;
|
12500 | /**
|
12501 | * Whether or not there are any styling bindings present
|
12502 | * (i.e. `[style]`, `[class]`, `[style.prop]` or `[class.name]`)
|
12503 | */
|
12504 | this.hasBindings = false;
|
12505 | this.hasBindingsWithPipes = false;
|
12506 | /** the input for [class] (if it exists) */
|
12507 | this._classMapInput = null;
|
12508 | /** the input for [style] (if it exists) */
|
12509 | this._styleMapInput = null;
|
12510 | /** an array of each [style.prop] input */
|
12511 | this._singleStyleInputs = null;
|
12512 | /** an array of each [class.name] input */
|
12513 | this._singleClassInputs = null;
|
12514 | this._lastStylingInput = null;
|
12515 | this._firstStylingInput = null;
|
12516 | // maps are used instead of hash maps because a Map will
|
12517 | // retain the ordering of the keys
|
12518 | /**
|
12519 | * Represents the location of each style binding in the template
|
12520 | * (e.g. `<div [style.width]="w" [style.height]="h">` implies
|
12521 | * that `width=0` and `height=1`)
|
12522 | */
|
12523 | this._stylesIndex = new Map();
|
12524 | /**
|
12525 | * Represents the location of each class binding in the template
|
12526 | * (e.g. `<div [class.big]="b" [class.hidden]="h">` implies
|
12527 | * that `big=0` and `hidden=1`)
|
12528 | */
|
12529 | this._classesIndex = new Map();
|
12530 | this._initialStyleValues = [];
|
12531 | this._initialClassValues = [];
|
12532 | }
|
12533 | /**
|
12534 | * Registers a given input to the styling builder to be later used when producing AOT code.
|
12535 | *
|
12536 | * The code below will only accept the input if it is somehow tied to styling (whether it be
|
12537 | * style/class bindings or static style/class attributes).
|
12538 | */
|
12539 | registerBoundInput(input) {
|
12540 | // [attr.style] or [attr.class] are skipped in the code below,
|
12541 | // they should not be treated as styling-based bindings since
|
12542 | // they are intended to be written directly to the attr and
|
12543 | // will therefore skip all style/class resolution that is present
|
12544 | // with style="", [style]="" and [style.prop]="", class="",
|
12545 | // [class.prop]="". [class]="" assignments
|
12546 | let binding = null;
|
12547 | let name = input.name;
|
12548 | switch (input.type) {
|
12549 | case 0 /* Property */:
|
12550 | binding = this.registerInputBasedOnName(name, input.value, input.sourceSpan);
|
12551 | break;
|
12552 | case 3 /* Style */:
|
12553 | binding = this.registerStyleInput(name, false, input.value, input.sourceSpan, input.unit);
|
12554 | break;
|
12555 | case 2 /* Class */:
|
12556 | binding = this.registerClassInput(name, false, input.value, input.sourceSpan);
|
12557 | break;
|
12558 | }
|
12559 | return binding ? true : false;
|
12560 | }
|
12561 | registerInputBasedOnName(name, expression, sourceSpan) {
|
12562 | let binding = null;
|
12563 | const prefix = name.substring(0, 6);
|
12564 | const isStyle = name === 'style' || prefix === 'style.' || prefix === 'style!';
|
12565 | const isClass = !isStyle && (name === 'class' || prefix === 'class.' || prefix === 'class!');
|
12566 | if (isStyle || isClass) {
|
12567 | const isMapBased = name.charAt(5) !== '.'; // style.prop or class.prop makes this a no
|
12568 | const property = name.substr(isMapBased ? 5 : 6); // the dot explains why there's a +1
|
12569 | if (isStyle) {
|
12570 | binding = this.registerStyleInput(property, isMapBased, expression, sourceSpan);
|
12571 | }
|
12572 | else {
|
12573 | binding = this.registerClassInput(property, isMapBased, expression, sourceSpan);
|
12574 | }
|
12575 | }
|
12576 | return binding;
|
12577 | }
|
12578 | registerStyleInput(name, isMapBased, value, sourceSpan, suffix) {
|
12579 | if (isEmptyExpression(value)) {
|
12580 | return null;
|
12581 | }
|
12582 | name = normalizePropName(name);
|
12583 | const { property, hasOverrideFlag, suffix: bindingSuffix } = parseProperty(name);
|
12584 | suffix = typeof suffix === 'string' && suffix.length !== 0 ? suffix : bindingSuffix;
|
12585 | const entry = { name: property, suffix: suffix, value, sourceSpan, hasOverrideFlag };
|
12586 | if (isMapBased) {
|
12587 | this._styleMapInput = entry;
|
12588 | }
|
12589 | else {
|
12590 | (this._singleStyleInputs = this._singleStyleInputs || []).push(entry);
|
12591 | registerIntoMap(this._stylesIndex, property);
|
12592 | }
|
12593 | this._lastStylingInput = entry;
|
12594 | this._firstStylingInput = this._firstStylingInput || entry;
|
12595 | this._checkForPipes(value);
|
12596 | this.hasBindings = true;
|
12597 | return entry;
|
12598 | }
|
12599 | registerClassInput(name, isMapBased, value, sourceSpan) {
|
12600 | if (isEmptyExpression(value)) {
|
12601 | return null;
|
12602 | }
|
12603 | const { property, hasOverrideFlag } = parseProperty(name);
|
12604 | const entry = { name: property, value, sourceSpan, hasOverrideFlag, suffix: null };
|
12605 | if (isMapBased) {
|
12606 | if (this._classMapInput) {
|
12607 | throw new Error('[class] and [className] bindings cannot be used on the same element simultaneously');
|
12608 | }
|
12609 | this._classMapInput = entry;
|
12610 | }
|
12611 | else {
|
12612 | (this._singleClassInputs = this._singleClassInputs || []).push(entry);
|
12613 | registerIntoMap(this._classesIndex, property);
|
12614 | }
|
12615 | this._lastStylingInput = entry;
|
12616 | this._firstStylingInput = this._firstStylingInput || entry;
|
12617 | this._checkForPipes(value);
|
12618 | this.hasBindings = true;
|
12619 | return entry;
|
12620 | }
|
12621 | _checkForPipes(value) {
|
12622 | if ((value instanceof ASTWithSource) && (value.ast instanceof BindingPipe)) {
|
12623 | this.hasBindingsWithPipes = true;
|
12624 | }
|
12625 | }
|
12626 | /**
|
12627 | * Registers the element's static style string value to the builder.
|
12628 | *
|
12629 | * @param value the style string (e.g. `width:100px; height:200px;`)
|
12630 | */
|
12631 | registerStyleAttr(value) {
|
12632 | this._initialStyleValues = parse(value);
|
12633 | this._hasInitialValues = true;
|
12634 | }
|
12635 | /**
|
12636 | * Registers the element's static class string value to the builder.
|
12637 | *
|
12638 | * @param value the className string (e.g. `disabled gold zoom`)
|
12639 | */
|
12640 | registerClassAttr(value) {
|
12641 | this._initialClassValues = value.trim().split(/\s+/g);
|
12642 | this._hasInitialValues = true;
|
12643 | }
|
12644 | /**
|
12645 | * Appends all styling-related expressions to the provided attrs array.
|
12646 | *
|
12647 | * @param attrs an existing array where each of the styling expressions
|
12648 | * will be inserted into.
|
12649 | */
|
12650 | populateInitialStylingAttrs(attrs) {
|
12651 | // [CLASS_MARKER, 'foo', 'bar', 'baz' ...]
|
12652 | if (this._initialClassValues.length) {
|
12653 | attrs.push(literal(1 /* Classes */));
|
12654 | for (let i = 0; i < this._initialClassValues.length; i++) {
|
12655 | attrs.push(literal(this._initialClassValues[i]));
|
12656 | }
|
12657 | }
|
12658 | // [STYLE_MARKER, 'width', '200px', 'height', '100px', ...]
|
12659 | if (this._initialStyleValues.length) {
|
12660 | attrs.push(literal(2 /* Styles */));
|
12661 | for (let i = 0; i < this._initialStyleValues.length; i += 2) {
|
12662 | attrs.push(literal(this._initialStyleValues[i]), literal(this._initialStyleValues[i + 1]));
|
12663 | }
|
12664 | }
|
12665 | }
|
12666 | /**
|
12667 | * Builds an instruction with all the expressions and parameters for `elementHostAttrs`.
|
12668 | *
|
12669 | * The instruction generation code below is used for producing the AOT statement code which is
|
12670 | * responsible for registering initial styles (within a directive hostBindings' creation block),
|
12671 | * as well as any of the provided attribute values, to the directive host element.
|
12672 | */
|
12673 | assignHostAttrs(attrs, definitionMap) {
|
12674 | if (this._directiveExpr && (attrs.length || this._hasInitialValues)) {
|
12675 | this.populateInitialStylingAttrs(attrs);
|
12676 | definitionMap.set('hostAttrs', literalArr(attrs));
|
12677 | }
|
12678 | }
|
12679 | /**
|
12680 | * Builds an instruction with all the expressions and parameters for `classMap`.
|
12681 | *
|
12682 | * The instruction data will contain all expressions for `classMap` to function
|
12683 | * which includes the `[class]` expression params.
|
12684 | */
|
12685 | buildClassMapInstruction(valueConverter) {
|
12686 | if (this._classMapInput) {
|
12687 | return this._buildMapBasedInstruction(valueConverter, true, this._classMapInput);
|
12688 | }
|
12689 | return null;
|
12690 | }
|
12691 | /**
|
12692 | * Builds an instruction with all the expressions and parameters for `styleMap`.
|
12693 | *
|
12694 | * The instruction data will contain all expressions for `styleMap` to function
|
12695 | * which includes the `[style]` expression params.
|
12696 | */
|
12697 | buildStyleMapInstruction(valueConverter) {
|
12698 | if (this._styleMapInput) {
|
12699 | return this._buildMapBasedInstruction(valueConverter, false, this._styleMapInput);
|
12700 | }
|
12701 | return null;
|
12702 | }
|
12703 | _buildMapBasedInstruction(valueConverter, isClassBased, stylingInput) {
|
12704 | // each styling binding value is stored in the LView
|
12705 | // map-based bindings allocate two slots: one for the
|
12706 | // previous binding value and another for the previous
|
12707 | // className or style attribute value.
|
12708 | let totalBindingSlotsRequired = MIN_STYLING_BINDING_SLOTS_REQUIRED;
|
12709 | // these values must be outside of the update block so that they can
|
12710 | // be evaluated (the AST visit call) during creation time so that any
|
12711 | // pipes can be picked up in time before the template is built
|
12712 | const mapValue = stylingInput.value.visit(valueConverter);
|
12713 | let reference;
|
12714 | if (mapValue instanceof Interpolation) {
|
12715 | totalBindingSlotsRequired += mapValue.expressions.length;
|
12716 | reference = isClassBased ? getClassMapInterpolationExpression(mapValue) :
|
12717 | getStyleMapInterpolationExpression(mapValue);
|
12718 | }
|
12719 | else {
|
12720 | reference = isClassBased ? Identifiers$1.classMap : Identifiers$1.styleMap;
|
12721 | }
|
12722 | return {
|
12723 | reference,
|
12724 | calls: [{
|
12725 | supportsInterpolation: true,
|
12726 | sourceSpan: stylingInput.sourceSpan,
|
12727 | allocateBindingSlots: totalBindingSlotsRequired,
|
12728 | params: (convertFn) => {
|
12729 | const convertResult = convertFn(mapValue);
|
12730 | const params = Array.isArray(convertResult) ? convertResult : [convertResult];
|
12731 | return params;
|
12732 | }
|
12733 | }]
|
12734 | };
|
12735 | }
|
12736 | _buildSingleInputs(reference, inputs, valueConverter, getInterpolationExpressionFn, isClassBased) {
|
12737 | const instructions = [];
|
12738 | inputs.forEach(input => {
|
12739 | const previousInstruction = instructions[instructions.length - 1];
|
12740 | const value = input.value.visit(valueConverter);
|
12741 | let referenceForCall = reference;
|
12742 | // each styling binding value is stored in the LView
|
12743 | // but there are two values stored for each binding:
|
12744 | // 1) the value itself
|
12745 | // 2) an intermediate value (concatenation of style up to this point).
|
12746 | // We need to store the intermediate value so that we don't allocate
|
12747 | // the strings on each CD.
|
12748 | let totalBindingSlotsRequired = MIN_STYLING_BINDING_SLOTS_REQUIRED;
|
12749 | if (value instanceof Interpolation) {
|
12750 | totalBindingSlotsRequired += value.expressions.length;
|
12751 | if (getInterpolationExpressionFn) {
|
12752 | referenceForCall = getInterpolationExpressionFn(value);
|
12753 | }
|
12754 | }
|
12755 | const call = {
|
12756 | sourceSpan: input.sourceSpan,
|
12757 | allocateBindingSlots: totalBindingSlotsRequired,
|
12758 | supportsInterpolation: !!getInterpolationExpressionFn,
|
12759 | params: (convertFn) => {
|
12760 | // params => stylingProp(propName, value, suffix)
|
12761 | const params = [];
|
12762 | params.push(literal(input.name));
|
12763 | const convertResult = convertFn(value);
|
12764 | if (Array.isArray(convertResult)) {
|
12765 | params.push(...convertResult);
|
12766 | }
|
12767 | else {
|
12768 | params.push(convertResult);
|
12769 | }
|
12770 | // [style.prop] bindings may use suffix values (e.g. px, em, etc...), therefore,
|
12771 | // if that is detected then we need to pass that in as an optional param.
|
12772 | if (!isClassBased && input.suffix !== null) {
|
12773 | params.push(literal(input.suffix));
|
12774 | }
|
12775 | return params;
|
12776 | }
|
12777 | };
|
12778 | // If we ended up generating a call to the same instruction as the previous styling property
|
12779 | // we can chain the calls together safely to save some bytes, otherwise we have to generate
|
12780 | // a separate instruction call. This is primarily a concern with interpolation instructions
|
12781 | // where we may start off with one `reference`, but end up using another based on the
|
12782 | // number of interpolations.
|
12783 | if (previousInstruction && previousInstruction.reference === referenceForCall) {
|
12784 | previousInstruction.calls.push(call);
|
12785 | }
|
12786 | else {
|
12787 | instructions.push({ reference: referenceForCall, calls: [call] });
|
12788 | }
|
12789 | });
|
12790 | return instructions;
|
12791 | }
|
12792 | _buildClassInputs(valueConverter) {
|
12793 | if (this._singleClassInputs) {
|
12794 | return this._buildSingleInputs(Identifiers$1.classProp, this._singleClassInputs, valueConverter, null, true);
|
12795 | }
|
12796 | return [];
|
12797 | }
|
12798 | _buildStyleInputs(valueConverter) {
|
12799 | if (this._singleStyleInputs) {
|
12800 | return this._buildSingleInputs(Identifiers$1.styleProp, this._singleStyleInputs, valueConverter, getStylePropInterpolationExpression, false);
|
12801 | }
|
12802 | return [];
|
12803 | }
|
12804 | /**
|
12805 | * Constructs all instructions which contain the expressions that will be placed
|
12806 | * into the update block of a template function or a directive hostBindings function.
|
12807 | */
|
12808 | buildUpdateLevelInstructions(valueConverter) {
|
12809 | const instructions = [];
|
12810 | if (this.hasBindings) {
|
12811 | const styleMapInstruction = this.buildStyleMapInstruction(valueConverter);
|
12812 | if (styleMapInstruction) {
|
12813 | instructions.push(styleMapInstruction);
|
12814 | }
|
12815 | const classMapInstruction = this.buildClassMapInstruction(valueConverter);
|
12816 | if (classMapInstruction) {
|
12817 | instructions.push(classMapInstruction);
|
12818 | }
|
12819 | instructions.push(...this._buildStyleInputs(valueConverter));
|
12820 | instructions.push(...this._buildClassInputs(valueConverter));
|
12821 | }
|
12822 | return instructions;
|
12823 | }
|
12824 | }
|
12825 | function registerIntoMap(map, key) {
|
12826 | if (!map.has(key)) {
|
12827 | map.set(key, map.size);
|
12828 | }
|
12829 | }
|
12830 | function parseProperty(name) {
|
12831 | let hasOverrideFlag = false;
|
12832 | const overrideIndex = name.indexOf(IMPORTANT_FLAG);
|
12833 | if (overrideIndex !== -1) {
|
12834 | name = overrideIndex > 0 ? name.substring(0, overrideIndex) : '';
|
12835 | hasOverrideFlag = true;
|
12836 | }
|
12837 | let suffix = null;
|
12838 | let property = name;
|
12839 | const unitIndex = name.lastIndexOf('.');
|
12840 | if (unitIndex > 0) {
|
12841 | suffix = name.substr(unitIndex + 1);
|
12842 | property = name.substring(0, unitIndex);
|
12843 | }
|
12844 | return { property, suffix, hasOverrideFlag };
|
12845 | }
|
12846 | /**
|
12847 | * Gets the instruction to generate for an interpolated class map.
|
12848 | * @param interpolation An Interpolation AST
|
12849 | */
|
12850 | function getClassMapInterpolationExpression(interpolation) {
|
12851 | switch (getInterpolationArgsLength(interpolation)) {
|
12852 | case 1:
|
12853 | return Identifiers$1.classMap;
|
12854 | case 3:
|
12855 | return Identifiers$1.classMapInterpolate1;
|
12856 | case 5:
|
12857 | return Identifiers$1.classMapInterpolate2;
|
12858 | case 7:
|
12859 | return Identifiers$1.classMapInterpolate3;
|
12860 | case 9:
|
12861 | return Identifiers$1.classMapInterpolate4;
|
12862 | case 11:
|
12863 | return Identifiers$1.classMapInterpolate5;
|
12864 | case 13:
|
12865 | return Identifiers$1.classMapInterpolate6;
|
12866 | case 15:
|
12867 | return Identifiers$1.classMapInterpolate7;
|
12868 | case 17:
|
12869 | return Identifiers$1.classMapInterpolate8;
|
12870 | default:
|
12871 | return Identifiers$1.classMapInterpolateV;
|
12872 | }
|
12873 | }
|
12874 | /**
|
12875 | * Gets the instruction to generate for an interpolated style map.
|
12876 | * @param interpolation An Interpolation AST
|
12877 | */
|
12878 | function getStyleMapInterpolationExpression(interpolation) {
|
12879 | switch (getInterpolationArgsLength(interpolation)) {
|
12880 | case 1:
|
12881 | return Identifiers$1.styleMap;
|
12882 | case 3:
|
12883 | return Identifiers$1.styleMapInterpolate1;
|
12884 | case 5:
|
12885 | return Identifiers$1.styleMapInterpolate2;
|
12886 | case 7:
|
12887 | return Identifiers$1.styleMapInterpolate3;
|
12888 | case 9:
|
12889 | return Identifiers$1.styleMapInterpolate4;
|
12890 | case 11:
|
12891 | return Identifiers$1.styleMapInterpolate5;
|
12892 | case 13:
|
12893 | return Identifiers$1.styleMapInterpolate6;
|
12894 | case 15:
|
12895 | return Identifiers$1.styleMapInterpolate7;
|
12896 | case 17:
|
12897 | return Identifiers$1.styleMapInterpolate8;
|
12898 | default:
|
12899 | return Identifiers$1.styleMapInterpolateV;
|
12900 | }
|
12901 | }
|
12902 | /**
|
12903 | * Gets the instruction to generate for an interpolated style prop.
|
12904 | * @param interpolation An Interpolation AST
|
12905 | */
|
12906 | function getStylePropInterpolationExpression(interpolation) {
|
12907 | switch (getInterpolationArgsLength(interpolation)) {
|
12908 | case 1:
|
12909 | return Identifiers$1.styleProp;
|
12910 | case 3:
|
12911 | return Identifiers$1.stylePropInterpolate1;
|
12912 | case 5:
|
12913 | return Identifiers$1.stylePropInterpolate2;
|
12914 | case 7:
|
12915 | return Identifiers$1.stylePropInterpolate3;
|
12916 | case 9:
|
12917 | return Identifiers$1.stylePropInterpolate4;
|
12918 | case 11:
|
12919 | return Identifiers$1.stylePropInterpolate5;
|
12920 | case 13:
|
12921 | return Identifiers$1.stylePropInterpolate6;
|
12922 | case 15:
|
12923 | return Identifiers$1.stylePropInterpolate7;
|
12924 | case 17:
|
12925 | return Identifiers$1.stylePropInterpolate8;
|
12926 | default:
|
12927 | return Identifiers$1.stylePropInterpolateV;
|
12928 | }
|
12929 | }
|
12930 | function normalizePropName(prop) {
|
12931 | return hyphenate(prop);
|
12932 | }
|
12933 |
|
12934 | /**
|
12935 | * @license
|
12936 | * Copyright Google LLC All Rights Reserved.
|
12937 | *
|
12938 | * Use of this source code is governed by an MIT-style license that can be
|
12939 | * found in the LICENSE file at https://angular.io/license
|
12940 | */
|
12941 | var TokenType$1;
|
12942 | (function (TokenType) {
|
12943 | TokenType[TokenType["Character"] = 0] = "Character";
|
12944 | TokenType[TokenType["Identifier"] = 1] = "Identifier";
|
12945 | TokenType[TokenType["Keyword"] = 2] = "Keyword";
|
12946 | TokenType[TokenType["String"] = 3] = "String";
|
12947 | TokenType[TokenType["Operator"] = 4] = "Operator";
|
12948 | TokenType[TokenType["Number"] = 5] = "Number";
|
12949 | TokenType[TokenType["Error"] = 6] = "Error";
|
12950 | })(TokenType$1 || (TokenType$1 = {}));
|
12951 | const KEYWORDS = ['var', 'let', 'as', 'null', 'undefined', 'true', 'false', 'if', 'else', 'this'];
|
12952 | class Lexer {
|
12953 | tokenize(text) {
|
12954 | const scanner = new _Scanner(text);
|
12955 | const tokens = [];
|
12956 | let token = scanner.scanToken();
|
12957 | while (token != null) {
|
12958 | tokens.push(token);
|
12959 | token = scanner.scanToken();
|
12960 | }
|
12961 | return tokens;
|
12962 | }
|
12963 | }
|
12964 | class Token$1 {
|
12965 | constructor(index, end, type, numValue, strValue) {
|
12966 | this.index = index;
|
12967 | this.end = end;
|
12968 | this.type = type;
|
12969 | this.numValue = numValue;
|
12970 | this.strValue = strValue;
|
12971 | }
|
12972 | isCharacter(code) {
|
12973 | return this.type == TokenType$1.Character && this.numValue == code;
|
12974 | }
|
12975 | isNumber() {
|
12976 | return this.type == TokenType$1.Number;
|
12977 | }
|
12978 | isString() {
|
12979 | return this.type == TokenType$1.String;
|
12980 | }
|
12981 | isOperator(operator) {
|
12982 | return this.type == TokenType$1.Operator && this.strValue == operator;
|
12983 | }
|
12984 | isIdentifier() {
|
12985 | return this.type == TokenType$1.Identifier;
|
12986 | }
|
12987 | isKeyword() {
|
12988 | return this.type == TokenType$1.Keyword;
|
12989 | }
|
12990 | isKeywordLet() {
|
12991 | return this.type == TokenType$1.Keyword && this.strValue == 'let';
|
12992 | }
|
12993 | isKeywordAs() {
|
12994 | return this.type == TokenType$1.Keyword && this.strValue == 'as';
|
12995 | }
|
12996 | isKeywordNull() {
|
12997 | return this.type == TokenType$1.Keyword && this.strValue == 'null';
|
12998 | }
|
12999 | isKeywordUndefined() {
|
13000 | return this.type == TokenType$1.Keyword && this.strValue == 'undefined';
|
13001 | }
|
13002 | isKeywordTrue() {
|
13003 | return this.type == TokenType$1.Keyword && this.strValue == 'true';
|
13004 | }
|
13005 | isKeywordFalse() {
|
13006 | return this.type == TokenType$1.Keyword && this.strValue == 'false';
|
13007 | }
|
13008 | isKeywordThis() {
|
13009 | return this.type == TokenType$1.Keyword && this.strValue == 'this';
|
13010 | }
|
13011 | isError() {
|
13012 | return this.type == TokenType$1.Error;
|
13013 | }
|
13014 | toNumber() {
|
13015 | return this.type == TokenType$1.Number ? this.numValue : -1;
|
13016 | }
|
13017 | toString() {
|
13018 | switch (this.type) {
|
13019 | case TokenType$1.Character:
|
13020 | case TokenType$1.Identifier:
|
13021 | case TokenType$1.Keyword:
|
13022 | case TokenType$1.Operator:
|
13023 | case TokenType$1.String:
|
13024 | case TokenType$1.Error:
|
13025 | return this.strValue;
|
13026 | case TokenType$1.Number:
|
13027 | return this.numValue.toString();
|
13028 | default:
|
13029 | return null;
|
13030 | }
|
13031 | }
|
13032 | }
|
13033 | function newCharacterToken(index, end, code) {
|
13034 | return new Token$1(index, end, TokenType$1.Character, code, String.fromCharCode(code));
|
13035 | }
|
13036 | function newIdentifierToken(index, end, text) {
|
13037 | return new Token$1(index, end, TokenType$1.Identifier, 0, text);
|
13038 | }
|
13039 | function newKeywordToken(index, end, text) {
|
13040 | return new Token$1(index, end, TokenType$1.Keyword, 0, text);
|
13041 | }
|
13042 | function newOperatorToken(index, end, text) {
|
13043 | return new Token$1(index, end, TokenType$1.Operator, 0, text);
|
13044 | }
|
13045 | function newStringToken(index, end, text) {
|
13046 | return new Token$1(index, end, TokenType$1.String, 0, text);
|
13047 | }
|
13048 | function newNumberToken(index, end, n) {
|
13049 | return new Token$1(index, end, TokenType$1.Number, n, '');
|
13050 | }
|
13051 | function newErrorToken(index, end, message) {
|
13052 | return new Token$1(index, end, TokenType$1.Error, 0, message);
|
13053 | }
|
13054 | const EOF = new Token$1(-1, -1, TokenType$1.Character, 0, '');
|
13055 | class _Scanner {
|
13056 | constructor(input) {
|
13057 | this.input = input;
|
13058 | this.peek = 0;
|
13059 | this.index = -1;
|
13060 | this.length = input.length;
|
13061 | this.advance();
|
13062 | }
|
13063 | advance() {
|
13064 | this.peek = ++this.index >= this.length ? $EOF : this.input.charCodeAt(this.index);
|
13065 | }
|
13066 | scanToken() {
|
13067 | const input = this.input, length = this.length;
|
13068 | let peek = this.peek, index = this.index;
|
13069 | // Skip whitespace.
|
13070 | while (peek <= $SPACE) {
|
13071 | if (++index >= length) {
|
13072 | peek = $EOF;
|
13073 | break;
|
13074 | }
|
13075 | else {
|
13076 | peek = input.charCodeAt(index);
|
13077 | }
|
13078 | }
|
13079 | this.peek = peek;
|
13080 | this.index = index;
|
13081 | if (index >= length) {
|
13082 | return null;
|
13083 | }
|
13084 | // Handle identifiers and numbers.
|
13085 | if (isIdentifierStart(peek))
|
13086 | return this.scanIdentifier();
|
13087 | if (isDigit(peek))
|
13088 | return this.scanNumber(index);
|
13089 | const start = index;
|
13090 | switch (peek) {
|
13091 | case $PERIOD:
|
13092 | this.advance();
|
13093 | return isDigit(this.peek) ? this.scanNumber(start) :
|
13094 | newCharacterToken(start, this.index, $PERIOD);
|
13095 | case $LPAREN:
|
13096 | case $RPAREN:
|
13097 | case $LBRACE:
|
13098 | case $RBRACE:
|
13099 | case $LBRACKET:
|
13100 | case $RBRACKET:
|
13101 | case $COMMA:
|
13102 | case $COLON:
|
13103 | case $SEMICOLON:
|
13104 | return this.scanCharacter(start, peek);
|
13105 | case $SQ:
|
13106 | case $DQ:
|
13107 | return this.scanString();
|
13108 | case $HASH:
|
13109 | case $PLUS:
|
13110 | case $MINUS:
|
13111 | case $STAR:
|
13112 | case $SLASH:
|
13113 | case $PERCENT:
|
13114 | case $CARET:
|
13115 | return this.scanOperator(start, String.fromCharCode(peek));
|
13116 | case $QUESTION:
|
13117 | return this.scanComplexOperator(start, '?', $PERIOD, '.');
|
13118 | case $LT:
|
13119 | case $GT:
|
13120 | return this.scanComplexOperator(start, String.fromCharCode(peek), $EQ, '=');
|
13121 | case $BANG:
|
13122 | case $EQ:
|
13123 | return this.scanComplexOperator(start, String.fromCharCode(peek), $EQ, '=', $EQ, '=');
|
13124 | case $AMPERSAND:
|
13125 | return this.scanComplexOperator(start, '&', $AMPERSAND, '&');
|
13126 | case $BAR:
|
13127 | return this.scanComplexOperator(start, '|', $BAR, '|');
|
13128 | case $NBSP:
|
13129 | while (isWhitespace(this.peek))
|
13130 | this.advance();
|
13131 | return this.scanToken();
|
13132 | }
|
13133 | this.advance();
|
13134 | return this.error(`Unexpected character [${String.fromCharCode(peek)}]`, 0);
|
13135 | }
|
13136 | scanCharacter(start, code) {
|
13137 | this.advance();
|
13138 | return newCharacterToken(start, this.index, code);
|
13139 | }
|
13140 | scanOperator(start, str) {
|
13141 | this.advance();
|
13142 | return newOperatorToken(start, this.index, str);
|
13143 | }
|
13144 | /**
|
13145 | * Tokenize a 2/3 char long operator
|
13146 | *
|
13147 | * @param start start index in the expression
|
13148 | * @param one first symbol (always part of the operator)
|
13149 | * @param twoCode code point for the second symbol
|
13150 | * @param two second symbol (part of the operator when the second code point matches)
|
13151 | * @param threeCode code point for the third symbol
|
13152 | * @param three third symbol (part of the operator when provided and matches source expression)
|
13153 | */
|
13154 | scanComplexOperator(start, one, twoCode, two, threeCode, three) {
|
13155 | this.advance();
|
13156 | let str = one;
|
13157 | if (this.peek == twoCode) {
|
13158 | this.advance();
|
13159 | str += two;
|
13160 | }
|
13161 | if (threeCode != null && this.peek == threeCode) {
|
13162 | this.advance();
|
13163 | str += three;
|
13164 | }
|
13165 | return newOperatorToken(start, this.index, str);
|
13166 | }
|
13167 | scanIdentifier() {
|
13168 | const start = this.index;
|
13169 | this.advance();
|
13170 | while (isIdentifierPart(this.peek))
|
13171 | this.advance();
|
13172 | const str = this.input.substring(start, this.index);
|
13173 | return KEYWORDS.indexOf(str) > -1 ? newKeywordToken(start, this.index, str) :
|
13174 | newIdentifierToken(start, this.index, str);
|
13175 | }
|
13176 | scanNumber(start) {
|
13177 | let simple = (this.index === start);
|
13178 | this.advance(); // Skip initial digit.
|
13179 | while (true) {
|
13180 | if (isDigit(this.peek)) ;
|
13181 | else if (this.peek == $PERIOD) {
|
13182 | simple = false;
|
13183 | }
|
13184 | else if (isExponentStart(this.peek)) {
|
13185 | this.advance();
|
13186 | if (isExponentSign(this.peek))
|
13187 | this.advance();
|
13188 | if (!isDigit(this.peek))
|
13189 | return this.error('Invalid exponent', -1);
|
13190 | simple = false;
|
13191 | }
|
13192 | else {
|
13193 | break;
|
13194 | }
|
13195 | this.advance();
|
13196 | }
|
13197 | const str = this.input.substring(start, this.index);
|
13198 | const value = simple ? parseIntAutoRadix(str) : parseFloat(str);
|
13199 | return newNumberToken(start, this.index, value);
|
13200 | }
|
13201 | scanString() {
|
13202 | const start = this.index;
|
13203 | const quote = this.peek;
|
13204 | this.advance(); // Skip initial quote.
|
13205 | let buffer = '';
|
13206 | let marker = this.index;
|
13207 | const input = this.input;
|
13208 | while (this.peek != quote) {
|
13209 | if (this.peek == $BACKSLASH) {
|
13210 | buffer += input.substring(marker, this.index);
|
13211 | this.advance();
|
13212 | let unescapedCode;
|
13213 | // Workaround for TS2.1-introduced type strictness
|
13214 | this.peek = this.peek;
|
13215 | if (this.peek == $u) {
|
13216 | // 4 character hex code for unicode character.
|
13217 | const hex = input.substring(this.index + 1, this.index + 5);
|
13218 | if (/^[0-9a-f]+$/i.test(hex)) {
|
13219 | unescapedCode = parseInt(hex, 16);
|
13220 | }
|
13221 | else {
|
13222 | return this.error(`Invalid unicode escape [\\u${hex}]`, 0);
|
13223 | }
|
13224 | for (let i = 0; i < 5; i++) {
|
13225 | this.advance();
|
13226 | }
|
13227 | }
|
13228 | else {
|
13229 | unescapedCode = unescape(this.peek);
|
13230 | this.advance();
|
13231 | }
|
13232 | buffer += String.fromCharCode(unescapedCode);
|
13233 | marker = this.index;
|
13234 | }
|
13235 | else if (this.peek == $EOF) {
|
13236 | return this.error('Unterminated quote', 0);
|
13237 | }
|
13238 | else {
|
13239 | this.advance();
|
13240 | }
|
13241 | }
|
13242 | const last = input.substring(marker, this.index);
|
13243 | this.advance(); // Skip terminating quote.
|
13244 | return newStringToken(start, this.index, buffer + last);
|
13245 | }
|
13246 | error(message, offset) {
|
13247 | const position = this.index + offset;
|
13248 | return newErrorToken(position, this.index, `Lexer Error: ${message} at column ${position} in expression [${this.input}]`);
|
13249 | }
|
13250 | }
|
13251 | function isIdentifierStart(code) {
|
13252 | return ($a <= code && code <= $z) || ($A <= code && code <= $Z) ||
|
13253 | (code == $_) || (code == $$);
|
13254 | }
|
13255 | function isIdentifier(input) {
|
13256 | if (input.length == 0)
|
13257 | return false;
|
13258 | const scanner = new _Scanner(input);
|
13259 | if (!isIdentifierStart(scanner.peek))
|
13260 | return false;
|
13261 | scanner.advance();
|
13262 | while (scanner.peek !== $EOF) {
|
13263 | if (!isIdentifierPart(scanner.peek))
|
13264 | return false;
|
13265 | scanner.advance();
|
13266 | }
|
13267 | return true;
|
13268 | }
|
13269 | function isIdentifierPart(code) {
|
13270 | return isAsciiLetter(code) || isDigit(code) || (code == $_) ||
|
13271 | (code == $$);
|
13272 | }
|
13273 | function isExponentStart(code) {
|
13274 | return code == $e || code == $E;
|
13275 | }
|
13276 | function isExponentSign(code) {
|
13277 | return code == $MINUS || code == $PLUS;
|
13278 | }
|
13279 | function isQuote(code) {
|
13280 | return code === $SQ || code === $DQ || code === $BT;
|
13281 | }
|
13282 | function unescape(code) {
|
13283 | switch (code) {
|
13284 | case $n:
|
13285 | return $LF;
|
13286 | case $f:
|
13287 | return $FF;
|
13288 | case $r:
|
13289 | return $CR;
|
13290 | case $t:
|
13291 | return $TAB;
|
13292 | case $v:
|
13293 | return $VTAB;
|
13294 | default:
|
13295 | return code;
|
13296 | }
|
13297 | }
|
13298 | function parseIntAutoRadix(text) {
|
13299 | const result = parseInt(text);
|
13300 | if (isNaN(result)) {
|
13301 | throw new Error('Invalid integer literal when parsing ' + text);
|
13302 | }
|
13303 | return result;
|
13304 | }
|
13305 |
|
13306 | /**
|
13307 | * @license
|
13308 | * Copyright Google LLC All Rights Reserved.
|
13309 | *
|
13310 | * Use of this source code is governed by an MIT-style license that can be
|
13311 | * found in the LICENSE file at https://angular.io/license
|
13312 | */
|
13313 | class SplitInterpolation {
|
13314 | constructor(strings, expressions, offsets) {
|
13315 | this.strings = strings;
|
13316 | this.expressions = expressions;
|
13317 | this.offsets = offsets;
|
13318 | }
|
13319 | }
|
13320 | class TemplateBindingParseResult {
|
13321 | constructor(templateBindings, warnings, errors) {
|
13322 | this.templateBindings = templateBindings;
|
13323 | this.warnings = warnings;
|
13324 | this.errors = errors;
|
13325 | }
|
13326 | }
|
13327 | class Parser$1 {
|
13328 | constructor(_lexer) {
|
13329 | this._lexer = _lexer;
|
13330 | this.errors = [];
|
13331 | this.simpleExpressionChecker = SimpleExpressionChecker;
|
13332 | }
|
13333 | parseAction(input, location, absoluteOffset, interpolationConfig = DEFAULT_INTERPOLATION_CONFIG) {
|
13334 | this._checkNoInterpolation(input, location, interpolationConfig);
|
13335 | const sourceToLex = this._stripComments(input);
|
13336 | const tokens = this._lexer.tokenize(this._stripComments(input));
|
13337 | const ast = new _ParseAST(input, location, absoluteOffset, tokens, sourceToLex.length, true, this.errors, input.length - sourceToLex.length)
|
13338 | .parseChain();
|
13339 | return new ASTWithSource(ast, input, location, absoluteOffset, this.errors);
|
13340 | }
|
13341 | parseBinding(input, location, absoluteOffset, interpolationConfig = DEFAULT_INTERPOLATION_CONFIG) {
|
13342 | const ast = this._parseBindingAst(input, location, absoluteOffset, interpolationConfig);
|
13343 | return new ASTWithSource(ast, input, location, absoluteOffset, this.errors);
|
13344 | }
|
13345 | checkSimpleExpression(ast) {
|
13346 | const checker = new this.simpleExpressionChecker();
|
13347 | ast.visit(checker);
|
13348 | return checker.errors;
|
13349 | }
|
13350 | parseSimpleBinding(input, location, absoluteOffset, interpolationConfig = DEFAULT_INTERPOLATION_CONFIG) {
|
13351 | const ast = this._parseBindingAst(input, location, absoluteOffset, interpolationConfig);
|
13352 | const errors = this.checkSimpleExpression(ast);
|
13353 | if (errors.length > 0) {
|
13354 | this._reportError(`Host binding expression cannot contain ${errors.join(' ')}`, input, location);
|
13355 | }
|
13356 | return new ASTWithSource(ast, input, location, absoluteOffset, this.errors);
|
13357 | }
|
13358 | _reportError(message, input, errLocation, ctxLocation) {
|
13359 | this.errors.push(new ParserError(message, input, errLocation, ctxLocation));
|
13360 | }
|
13361 | _parseBindingAst(input, location, absoluteOffset, interpolationConfig) {
|
13362 | // Quotes expressions use 3rd-party expression language. We don't want to use
|
13363 | // our lexer or parser for that, so we check for that ahead of time.
|
13364 | const quote = this._parseQuote(input, location, absoluteOffset);
|
13365 | if (quote != null) {
|
13366 | return quote;
|
13367 | }
|
13368 | this._checkNoInterpolation(input, location, interpolationConfig);
|
13369 | const sourceToLex = this._stripComments(input);
|
13370 | const tokens = this._lexer.tokenize(sourceToLex);
|
13371 | return new _ParseAST(input, location, absoluteOffset, tokens, sourceToLex.length, false, this.errors, input.length - sourceToLex.length)
|
13372 | .parseChain();
|
13373 | }
|
13374 | _parseQuote(input, location, absoluteOffset) {
|
13375 | if (input == null)
|
13376 | return null;
|
13377 | const prefixSeparatorIndex = input.indexOf(':');
|
13378 | if (prefixSeparatorIndex == -1)
|
13379 | return null;
|
13380 | const prefix = input.substring(0, prefixSeparatorIndex).trim();
|
13381 | if (!isIdentifier(prefix))
|
13382 | return null;
|
13383 | const uninterpretedExpression = input.substring(prefixSeparatorIndex + 1);
|
13384 | const span = new ParseSpan(0, input.length);
|
13385 | return new Quote(span, span.toAbsolute(absoluteOffset), prefix, uninterpretedExpression, location);
|
13386 | }
|
13387 | /**
|
13388 | * Parse microsyntax template expression and return a list of bindings or
|
13389 | * parsing errors in case the given expression is invalid.
|
13390 | *
|
13391 | * For example,
|
13392 | * ```
|
13393 | * <div *ngFor="let item of items">
|
13394 | * ^ ^ absoluteValueOffset for `templateValue`
|
13395 | * absoluteKeyOffset for `templateKey`
|
13396 | * ```
|
13397 | * contains three bindings:
|
13398 | * 1. ngFor -> null
|
13399 | * 2. item -> NgForOfContext.$implicit
|
13400 | * 3. ngForOf -> items
|
13401 | *
|
13402 | * This is apparent from the de-sugared template:
|
13403 | * ```
|
13404 | * <ng-template ngFor let-item [ngForOf]="items">
|
13405 | * ```
|
13406 | *
|
13407 | * @param templateKey name of directive, without the * prefix. For example: ngIf, ngFor
|
13408 | * @param templateValue RHS of the microsyntax attribute
|
13409 | * @param templateUrl template filename if it's external, component filename if it's inline
|
13410 | * @param absoluteKeyOffset start of the `templateKey`
|
13411 | * @param absoluteValueOffset start of the `templateValue`
|
13412 | */
|
13413 | parseTemplateBindings(templateKey, templateValue, templateUrl, absoluteKeyOffset, absoluteValueOffset) {
|
13414 | const tokens = this._lexer.tokenize(templateValue);
|
13415 | const parser = new _ParseAST(templateValue, templateUrl, absoluteValueOffset, tokens, templateValue.length, false /* parseAction */, this.errors, 0 /* relative offset */);
|
13416 | return parser.parseTemplateBindings({
|
13417 | source: templateKey,
|
13418 | span: new AbsoluteSourceSpan(absoluteKeyOffset, absoluteKeyOffset + templateKey.length),
|
13419 | });
|
13420 | }
|
13421 | parseInterpolation(input, location, absoluteOffset, interpolationConfig = DEFAULT_INTERPOLATION_CONFIG) {
|
13422 | const { strings, expressions, offsets } = this.splitInterpolation(input, location, interpolationConfig);
|
13423 | if (expressions.length === 0)
|
13424 | return null;
|
13425 | const expressionNodes = [];
|
13426 | for (let i = 0; i < expressions.length; ++i) {
|
13427 | const expressionText = expressions[i].text;
|
13428 | const sourceToLex = this._stripComments(expressionText);
|
13429 | const tokens = this._lexer.tokenize(sourceToLex);
|
13430 | const ast = new _ParseAST(input, location, absoluteOffset, tokens, sourceToLex.length, false, this.errors, offsets[i] + (expressionText.length - sourceToLex.length))
|
13431 | .parseChain();
|
13432 | expressionNodes.push(ast);
|
13433 | }
|
13434 | return this.createInterpolationAst(strings.map(s => s.text), expressionNodes, input, location, absoluteOffset);
|
13435 | }
|
13436 | /**
|
13437 | * Similar to `parseInterpolation`, but treats the provided string as a single expression
|
13438 | * element that would normally appear within the interpolation prefix and suffix (`{{` and `}}`).
|
13439 | * This is used for parsing the switch expression in ICUs.
|
13440 | */
|
13441 | parseInterpolationExpression(expression, location, absoluteOffset) {
|
13442 | const sourceToLex = this._stripComments(expression);
|
13443 | const tokens = this._lexer.tokenize(sourceToLex);
|
13444 | const ast = new _ParseAST(expression, location, absoluteOffset, tokens, sourceToLex.length,
|
13445 | /* parseAction */ false, this.errors, 0)
|
13446 | .parseChain();
|
13447 | const strings = ['', '']; // The prefix and suffix strings are both empty
|
13448 | return this.createInterpolationAst(strings, [ast], expression, location, absoluteOffset);
|
13449 | }
|
13450 | createInterpolationAst(strings, expressions, input, location, absoluteOffset) {
|
13451 | const span = new ParseSpan(0, input.length);
|
13452 | const interpolation = new Interpolation(span, span.toAbsolute(absoluteOffset), strings, expressions);
|
13453 | return new ASTWithSource(interpolation, input, location, absoluteOffset, this.errors);
|
13454 | }
|
13455 | /**
|
13456 | * Splits a string of text into "raw" text segments and expressions present in interpolations in
|
13457 | * the string.
|
13458 | * Returns `null` if there are no interpolations, otherwise a
|
13459 | * `SplitInterpolation` with splits that look like
|
13460 | * <raw text> <expression> <raw text> ... <raw text> <expression> <raw text>
|
13461 | */
|
13462 | splitInterpolation(input, location, interpolationConfig = DEFAULT_INTERPOLATION_CONFIG) {
|
13463 | const strings = [];
|
13464 | const expressions = [];
|
13465 | const offsets = [];
|
13466 | let i = 0;
|
13467 | let atInterpolation = false;
|
13468 | let extendLastString = false;
|
13469 | let { start: interpStart, end: interpEnd } = interpolationConfig;
|
13470 | while (i < input.length) {
|
13471 | if (!atInterpolation) {
|
13472 | // parse until starting {{
|
13473 | const start = i;
|
13474 | i = input.indexOf(interpStart, i);
|
13475 | if (i === -1) {
|
13476 | i = input.length;
|
13477 | }
|
13478 | const text = input.substring(start, i);
|
13479 | strings.push({ text, start, end: i });
|
13480 | atInterpolation = true;
|
13481 | }
|
13482 | else {
|
13483 | // parse from starting {{ to ending }} while ignoring content inside quotes.
|
13484 | const fullStart = i;
|
13485 | const exprStart = fullStart + interpStart.length;
|
13486 | const exprEnd = this._getInterpolationEndIndex(input, interpEnd, exprStart);
|
13487 | if (exprEnd === -1) {
|
13488 | // Could not find the end of the interpolation; do not parse an expression.
|
13489 | // Instead we should extend the content on the last raw string.
|
13490 | atInterpolation = false;
|
13491 | extendLastString = true;
|
13492 | break;
|
13493 | }
|
13494 | const fullEnd = exprEnd + interpEnd.length;
|
13495 | const text = input.substring(exprStart, exprEnd);
|
13496 | if (text.trim().length === 0) {
|
13497 | this._reportError('Blank expressions are not allowed in interpolated strings', input, `at column ${i} in`, location);
|
13498 | }
|
13499 | expressions.push({ text, start: fullStart, end: fullEnd });
|
13500 | offsets.push(exprStart);
|
13501 | i = fullEnd;
|
13502 | atInterpolation = false;
|
13503 | }
|
13504 | }
|
13505 | if (!atInterpolation) {
|
13506 | // If we are now at a text section, add the remaining content as a raw string.
|
13507 | if (extendLastString) {
|
13508 | const piece = strings[strings.length - 1];
|
13509 | piece.text += input.substring(i);
|
13510 | piece.end = input.length;
|
13511 | }
|
13512 | else {
|
13513 | strings.push({ text: input.substring(i), start: i, end: input.length });
|
13514 | }
|
13515 | }
|
13516 | return new SplitInterpolation(strings, expressions, offsets);
|
13517 | }
|
13518 | wrapLiteralPrimitive(input, location, absoluteOffset) {
|
13519 | const span = new ParseSpan(0, input == null ? 0 : input.length);
|
13520 | return new ASTWithSource(new LiteralPrimitive(span, span.toAbsolute(absoluteOffset), input), input, location, absoluteOffset, this.errors);
|
13521 | }
|
13522 | _stripComments(input) {
|
13523 | const i = this._commentStart(input);
|
13524 | return i != null ? input.substring(0, i).trim() : input;
|
13525 | }
|
13526 | _commentStart(input) {
|
13527 | let outerQuote = null;
|
13528 | for (let i = 0; i < input.length - 1; i++) {
|
13529 | const char = input.charCodeAt(i);
|
13530 | const nextChar = input.charCodeAt(i + 1);
|
13531 | if (char === $SLASH && nextChar == $SLASH && outerQuote == null)
|
13532 | return i;
|
13533 | if (outerQuote === char) {
|
13534 | outerQuote = null;
|
13535 | }
|
13536 | else if (outerQuote == null && isQuote(char)) {
|
13537 | outerQuote = char;
|
13538 | }
|
13539 | }
|
13540 | return null;
|
13541 | }
|
13542 | _checkNoInterpolation(input, location, { start, end }) {
|
13543 | let startIndex = -1;
|
13544 | let endIndex = -1;
|
13545 | for (const charIndex of this._forEachUnquotedChar(input, 0)) {
|
13546 | if (startIndex === -1) {
|
13547 | if (input.startsWith(start)) {
|
13548 | startIndex = charIndex;
|
13549 | }
|
13550 | }
|
13551 | else {
|
13552 | endIndex = this._getInterpolationEndIndex(input, end, charIndex);
|
13553 | if (endIndex > -1) {
|
13554 | break;
|
13555 | }
|
13556 | }
|
13557 | }
|
13558 | if (startIndex > -1 && endIndex > -1) {
|
13559 | this._reportError(`Got interpolation (${start}${end}) where expression was expected`, input, `at column ${startIndex} in`, location);
|
13560 | }
|
13561 | }
|
13562 | /**
|
13563 | * Finds the index of the end of an interpolation expression
|
13564 | * while ignoring comments and quoted content.
|
13565 | */
|
13566 | _getInterpolationEndIndex(input, expressionEnd, start) {
|
13567 | for (const charIndex of this._forEachUnquotedChar(input, start)) {
|
13568 | if (input.startsWith(expressionEnd, charIndex)) {
|
13569 | return charIndex;
|
13570 | }
|
13571 | // Nothing else in the expression matters after we've
|
13572 | // hit a comment so look directly for the end token.
|
13573 | if (input.startsWith('//', charIndex)) {
|
13574 | return input.indexOf(expressionEnd, charIndex);
|
13575 | }
|
13576 | }
|
13577 | return -1;
|
13578 | }
|
13579 | /**
|
13580 | * Generator used to iterate over the character indexes of a string that are outside of quotes.
|
13581 | * @param input String to loop through.
|
13582 | * @param start Index within the string at which to start.
|
13583 | */
|
13584 | *_forEachUnquotedChar(input, start) {
|
13585 | let currentQuote = null;
|
13586 | let escapeCount = 0;
|
13587 | for (let i = start; i < input.length; i++) {
|
13588 | const char = input[i];
|
13589 | // Skip the characters inside quotes. Note that we only care about the outer-most
|
13590 | // quotes matching up and we need to account for escape characters.
|
13591 | if (isQuote(input.charCodeAt(i)) && (currentQuote === null || currentQuote === char) &&
|
13592 | escapeCount % 2 === 0) {
|
13593 | currentQuote = currentQuote === null ? char : null;
|
13594 | }
|
13595 | else if (currentQuote === null) {
|
13596 | yield i;
|
13597 | }
|
13598 | escapeCount = char === '\\' ? escapeCount + 1 : 0;
|
13599 | }
|
13600 | }
|
13601 | }
|
13602 | class IvyParser extends Parser$1 {
|
13603 | constructor() {
|
13604 | super(...arguments);
|
13605 | this.simpleExpressionChecker = IvySimpleExpressionChecker;
|
13606 | }
|
13607 | }
|
13608 | /** Describes a stateful context an expression parser is in. */
|
13609 | var ParseContextFlags;
|
13610 | (function (ParseContextFlags) {
|
13611 | ParseContextFlags[ParseContextFlags["None"] = 0] = "None";
|
13612 | /**
|
13613 | * A Writable context is one in which a value may be written to an lvalue.
|
13614 | * For example, after we see a property access, we may expect a write to the
|
13615 | * property via the "=" operator.
|
13616 | * prop
|
13617 | * ^ possible "=" after
|
13618 | */
|
13619 | ParseContextFlags[ParseContextFlags["Writable"] = 1] = "Writable";
|
13620 | })(ParseContextFlags || (ParseContextFlags = {}));
|
13621 | class _ParseAST {
|
13622 | constructor(input, location, absoluteOffset, tokens, inputLength, parseAction, errors, offset) {
|
13623 | this.input = input;
|
13624 | this.location = location;
|
13625 | this.absoluteOffset = absoluteOffset;
|
13626 | this.tokens = tokens;
|
13627 | this.inputLength = inputLength;
|
13628 | this.parseAction = parseAction;
|
13629 | this.errors = errors;
|
13630 | this.offset = offset;
|
13631 | this.rparensExpected = 0;
|
13632 | this.rbracketsExpected = 0;
|
13633 | this.rbracesExpected = 0;
|
13634 | this.context = ParseContextFlags.None;
|
13635 | // Cache of expression start and input indeces to the absolute source span they map to, used to
|
13636 | // prevent creating superfluous source spans in `sourceSpan`.
|
13637 | // A serial of the expression start and input index is used for mapping because both are stateful
|
13638 | // and may change for subsequent expressions visited by the parser.
|
13639 | this.sourceSpanCache = new Map();
|
13640 | this.index = 0;
|
13641 | }
|
13642 | peek(offset) {
|
13643 | const i = this.index + offset;
|
13644 | return i < this.tokens.length ? this.tokens[i] : EOF;
|
13645 | }
|
13646 | get next() {
|
13647 | return this.peek(0);
|
13648 | }
|
13649 | /** Whether all the parser input has been processed. */
|
13650 | get atEOF() {
|
13651 | return this.index >= this.tokens.length;
|
13652 | }
|
13653 | /**
|
13654 | * Index of the next token to be processed, or the end of the last token if all have been
|
13655 | * processed.
|
13656 | */
|
13657 | get inputIndex() {
|
13658 | return this.atEOF ? this.currentEndIndex : this.next.index + this.offset;
|
13659 | }
|
13660 | /**
|
13661 | * End index of the last processed token, or the start of the first token if none have been
|
13662 | * processed.
|
13663 | */
|
13664 | get currentEndIndex() {
|
13665 | if (this.index > 0) {
|
13666 | const curToken = this.peek(-1);
|
13667 | return curToken.end + this.offset;
|
13668 | }
|
13669 | // No tokens have been processed yet; return the next token's start or the length of the input
|
13670 | // if there is no token.
|
13671 | if (this.tokens.length === 0) {
|
13672 | return this.inputLength + this.offset;
|
13673 | }
|
13674 | return this.next.index + this.offset;
|
13675 | }
|
13676 | /**
|
13677 | * Returns the absolute offset of the start of the current token.
|
13678 | */
|
13679 | get currentAbsoluteOffset() {
|
13680 | return this.absoluteOffset + this.inputIndex;
|
13681 | }
|
13682 | /**
|
13683 | * Retrieve a `ParseSpan` from `start` to the current position (or to `artificialEndIndex` if
|
13684 | * provided).
|
13685 | *
|
13686 | * @param start Position from which the `ParseSpan` will start.
|
13687 | * @param artificialEndIndex Optional ending index to be used if provided (and if greater than the
|
13688 | * natural ending index)
|
13689 | */
|
13690 | span(start, artificialEndIndex) {
|
13691 | let endIndex = this.currentEndIndex;
|
13692 | if (artificialEndIndex !== undefined && artificialEndIndex > this.currentEndIndex) {
|
13693 | endIndex = artificialEndIndex;
|
13694 | }
|
13695 | return new ParseSpan(start, endIndex);
|
13696 | }
|
13697 | sourceSpan(start, artificialEndIndex) {
|
13698 | const serial = `${start}@${this.inputIndex}:${artificialEndIndex}`;
|
13699 | if (!this.sourceSpanCache.has(serial)) {
|
13700 | this.sourceSpanCache.set(serial, this.span(start, artificialEndIndex).toAbsolute(this.absoluteOffset));
|
13701 | }
|
13702 | return this.sourceSpanCache.get(serial);
|
13703 | }
|
13704 | advance() {
|
13705 | this.index++;
|
13706 | }
|
13707 | /**
|
13708 | * Executes a callback in the provided context.
|
13709 | */
|
13710 | withContext(context, cb) {
|
13711 | this.context |= context;
|
13712 | const ret = cb();
|
13713 | this.context ^= context;
|
13714 | return ret;
|
13715 | }
|
13716 | consumeOptionalCharacter(code) {
|
13717 | if (this.next.isCharacter(code)) {
|
13718 | this.advance();
|
13719 | return true;
|
13720 | }
|
13721 | else {
|
13722 | return false;
|
13723 | }
|
13724 | }
|
13725 | peekKeywordLet() {
|
13726 | return this.next.isKeywordLet();
|
13727 | }
|
13728 | peekKeywordAs() {
|
13729 | return this.next.isKeywordAs();
|
13730 | }
|
13731 | /**
|
13732 | * Consumes an expected character, otherwise emits an error about the missing expected character
|
13733 | * and skips over the token stream until reaching a recoverable point.
|
13734 | *
|
13735 | * See `this.error` and `this.skip` for more details.
|
13736 | */
|
13737 | expectCharacter(code) {
|
13738 | if (this.consumeOptionalCharacter(code))
|
13739 | return;
|
13740 | this.error(`Missing expected ${String.fromCharCode(code)}`);
|
13741 | }
|
13742 | consumeOptionalOperator(op) {
|
13743 | if (this.next.isOperator(op)) {
|
13744 | this.advance();
|
13745 | return true;
|
13746 | }
|
13747 | else {
|
13748 | return false;
|
13749 | }
|
13750 | }
|
13751 | expectOperator(operator) {
|
13752 | if (this.consumeOptionalOperator(operator))
|
13753 | return;
|
13754 | this.error(`Missing expected operator ${operator}`);
|
13755 | }
|
13756 | prettyPrintToken(tok) {
|
13757 | return tok === EOF ? 'end of input' : `token ${tok}`;
|
13758 | }
|
13759 | expectIdentifierOrKeyword() {
|
13760 | const n = this.next;
|
13761 | if (!n.isIdentifier() && !n.isKeyword()) {
|
13762 | this.error(`Unexpected ${this.prettyPrintToken(n)}, expected identifier or keyword`);
|
13763 | return null;
|
13764 | }
|
13765 | this.advance();
|
13766 | return n.toString();
|
13767 | }
|
13768 | expectIdentifierOrKeywordOrString() {
|
13769 | const n = this.next;
|
13770 | if (!n.isIdentifier() && !n.isKeyword() && !n.isString()) {
|
13771 | this.error(`Unexpected ${this.prettyPrintToken(n)}, expected identifier, keyword, or string`);
|
13772 | return '';
|
13773 | }
|
13774 | this.advance();
|
13775 | return n.toString();
|
13776 | }
|
13777 | parseChain() {
|
13778 | const exprs = [];
|
13779 | const start = this.inputIndex;
|
13780 | while (this.index < this.tokens.length) {
|
13781 | const expr = this.parsePipe();
|
13782 | exprs.push(expr);
|
13783 | if (this.consumeOptionalCharacter($SEMICOLON)) {
|
13784 | if (!this.parseAction) {
|
13785 | this.error('Binding expression cannot contain chained expression');
|
13786 | }
|
13787 | while (this.consumeOptionalCharacter($SEMICOLON)) {
|
13788 | } // read all semicolons
|
13789 | }
|
13790 | else if (this.index < this.tokens.length) {
|
13791 | this.error(`Unexpected token '${this.next}'`);
|
13792 | }
|
13793 | }
|
13794 | if (exprs.length == 0) {
|
13795 | // We have no expressions so create an empty expression that spans the entire input length
|
13796 | const artificialStart = this.offset;
|
13797 | const artificialEnd = this.offset + this.inputLength;
|
13798 | return new EmptyExpr(this.span(artificialStart, artificialEnd), this.sourceSpan(artificialStart, artificialEnd));
|
13799 | }
|
13800 | if (exprs.length == 1)
|
13801 | return exprs[0];
|
13802 | return new Chain(this.span(start), this.sourceSpan(start), exprs);
|
13803 | }
|
13804 | parsePipe() {
|
13805 | const start = this.inputIndex;
|
13806 | let result = this.parseExpression();
|
13807 | if (this.consumeOptionalOperator('|')) {
|
13808 | if (this.parseAction) {
|
13809 | this.error('Cannot have a pipe in an action expression');
|
13810 | }
|
13811 | do {
|
13812 | const nameStart = this.inputIndex;
|
13813 | let nameId = this.expectIdentifierOrKeyword();
|
13814 | let nameSpan;
|
13815 | let fullSpanEnd = undefined;
|
13816 | if (nameId !== null) {
|
13817 | nameSpan = this.sourceSpan(nameStart);
|
13818 | }
|
13819 | else {
|
13820 | // No valid identifier was found, so we'll assume an empty pipe name ('').
|
13821 | nameId = '';
|
13822 | // However, there may have been whitespace present between the pipe character and the next
|
13823 | // token in the sequence (or the end of input). We want to track this whitespace so that
|
13824 | // the `BindingPipe` we produce covers not just the pipe character, but any trailing
|
13825 | // whitespace beyond it. Another way of thinking about this is that the zero-length name
|
13826 | // is assumed to be at the end of any whitespace beyond the pipe character.
|
13827 | //
|
13828 | // Therefore, we push the end of the `ParseSpan` for this pipe all the way up to the
|
13829 | // beginning of the next token, or until the end of input if the next token is EOF.
|
13830 | fullSpanEnd = this.next.index !== -1 ? this.next.index : this.inputLength + this.offset;
|
13831 | // The `nameSpan` for an empty pipe name is zero-length at the end of any whitespace
|
13832 | // beyond the pipe character.
|
13833 | nameSpan = new ParseSpan(fullSpanEnd, fullSpanEnd).toAbsolute(this.absoluteOffset);
|
13834 | }
|
13835 | const args = [];
|
13836 | while (this.consumeOptionalCharacter($COLON)) {
|
13837 | args.push(this.parseExpression());
|
13838 | // If there are additional expressions beyond the name, then the artificial end for the
|
13839 | // name is no longer relevant.
|
13840 | }
|
13841 | result = new BindingPipe(this.span(start), this.sourceSpan(start, fullSpanEnd), result, nameId, args, nameSpan);
|
13842 | } while (this.consumeOptionalOperator('|'));
|
13843 | }
|
13844 | return result;
|
13845 | }
|
13846 | parseExpression() {
|
13847 | return this.parseConditional();
|
13848 | }
|
13849 | parseConditional() {
|
13850 | const start = this.inputIndex;
|
13851 | const result = this.parseLogicalOr();
|
13852 | if (this.consumeOptionalOperator('?')) {
|
13853 | const yes = this.parsePipe();
|
13854 | let no;
|
13855 | if (!this.consumeOptionalCharacter($COLON)) {
|
13856 | const end = this.inputIndex;
|
13857 | const expression = this.input.substring(start, end);
|
13858 | this.error(`Conditional expression ${expression} requires all 3 expressions`);
|
13859 | no = new EmptyExpr(this.span(start), this.sourceSpan(start));
|
13860 | }
|
13861 | else {
|
13862 | no = this.parsePipe();
|
13863 | }
|
13864 | return new Conditional(this.span(start), this.sourceSpan(start), result, yes, no);
|
13865 | }
|
13866 | else {
|
13867 | return result;
|
13868 | }
|
13869 | }
|
13870 | parseLogicalOr() {
|
13871 | // '||'
|
13872 | const start = this.inputIndex;
|
13873 | let result = this.parseLogicalAnd();
|
13874 | while (this.consumeOptionalOperator('||')) {
|
13875 | const right = this.parseLogicalAnd();
|
13876 | result = new Binary(this.span(start), this.sourceSpan(start), '||', result, right);
|
13877 | }
|
13878 | return result;
|
13879 | }
|
13880 | parseLogicalAnd() {
|
13881 | // '&&'
|
13882 | const start = this.inputIndex;
|
13883 | let result = this.parseEquality();
|
13884 | while (this.consumeOptionalOperator('&&')) {
|
13885 | const right = this.parseEquality();
|
13886 | result = new Binary(this.span(start), this.sourceSpan(start), '&&', result, right);
|
13887 | }
|
13888 | return result;
|
13889 | }
|
13890 | parseEquality() {
|
13891 | // '==','!=','===','!=='
|
13892 | const start = this.inputIndex;
|
13893 | let result = this.parseRelational();
|
13894 | while (this.next.type == TokenType$1.Operator) {
|
13895 | const operator = this.next.strValue;
|
13896 | switch (operator) {
|
13897 | case '==':
|
13898 | case '===':
|
13899 | case '!=':
|
13900 | case '!==':
|
13901 | this.advance();
|
13902 | const right = this.parseRelational();
|
13903 | result = new Binary(this.span(start), this.sourceSpan(start), operator, result, right);
|
13904 | continue;
|
13905 | }
|
13906 | break;
|
13907 | }
|
13908 | return result;
|
13909 | }
|
13910 | parseRelational() {
|
13911 | // '<', '>', '<=', '>='
|
13912 | const start = this.inputIndex;
|
13913 | let result = this.parseAdditive();
|
13914 | while (this.next.type == TokenType$1.Operator) {
|
13915 | const operator = this.next.strValue;
|
13916 | switch (operator) {
|
13917 | case '<':
|
13918 | case '>':
|
13919 | case '<=':
|
13920 | case '>=':
|
13921 | this.advance();
|
13922 | const right = this.parseAdditive();
|
13923 | result = new Binary(this.span(start), this.sourceSpan(start), operator, result, right);
|
13924 | continue;
|
13925 | }
|
13926 | break;
|
13927 | }
|
13928 | return result;
|
13929 | }
|
13930 | parseAdditive() {
|
13931 | // '+', '-'
|
13932 | const start = this.inputIndex;
|
13933 | let result = this.parseMultiplicative();
|
13934 | while (this.next.type == TokenType$1.Operator) {
|
13935 | const operator = this.next.strValue;
|
13936 | switch (operator) {
|
13937 | case '+':
|
13938 | case '-':
|
13939 | this.advance();
|
13940 | let right = this.parseMultiplicative();
|
13941 | result = new Binary(this.span(start), this.sourceSpan(start), operator, result, right);
|
13942 | continue;
|
13943 | }
|
13944 | break;
|
13945 | }
|
13946 | return result;
|
13947 | }
|
13948 | parseMultiplicative() {
|
13949 | // '*', '%', '/'
|
13950 | const start = this.inputIndex;
|
13951 | let result = this.parsePrefix();
|
13952 | while (this.next.type == TokenType$1.Operator) {
|
13953 | const operator = this.next.strValue;
|
13954 | switch (operator) {
|
13955 | case '*':
|
13956 | case '%':
|
13957 | case '/':
|
13958 | this.advance();
|
13959 | let right = this.parsePrefix();
|
13960 | result = new Binary(this.span(start), this.sourceSpan(start), operator, result, right);
|
13961 | continue;
|
13962 | }
|
13963 | break;
|
13964 | }
|
13965 | return result;
|
13966 | }
|
13967 | parsePrefix() {
|
13968 | if (this.next.type == TokenType$1.Operator) {
|
13969 | const start = this.inputIndex;
|
13970 | const operator = this.next.strValue;
|
13971 | let result;
|
13972 | switch (operator) {
|
13973 | case '+':
|
13974 | this.advance();
|
13975 | result = this.parsePrefix();
|
13976 | return Unary.createPlus(this.span(start), this.sourceSpan(start), result);
|
13977 | case '-':
|
13978 | this.advance();
|
13979 | result = this.parsePrefix();
|
13980 | return Unary.createMinus(this.span(start), this.sourceSpan(start), result);
|
13981 | case '!':
|
13982 | this.advance();
|
13983 | result = this.parsePrefix();
|
13984 | return new PrefixNot(this.span(start), this.sourceSpan(start), result);
|
13985 | }
|
13986 | }
|
13987 | return this.parseCallChain();
|
13988 | }
|
13989 | parseCallChain() {
|
13990 | const start = this.inputIndex;
|
13991 | let result = this.parsePrimary();
|
13992 | while (true) {
|
13993 | if (this.consumeOptionalCharacter($PERIOD)) {
|
13994 | result = this.parseAccessMemberOrMethodCall(result, start, false);
|
13995 | }
|
13996 | else if (this.consumeOptionalOperator('?.')) {
|
13997 | result = this.parseAccessMemberOrMethodCall(result, start, true);
|
13998 | }
|
13999 | else if (this.consumeOptionalCharacter($LBRACKET)) {
|
14000 | this.withContext(ParseContextFlags.Writable, () => {
|
14001 | this.rbracketsExpected++;
|
14002 | const key = this.parsePipe();
|
14003 | if (key instanceof EmptyExpr) {
|
14004 | this.error(`Key access cannot be empty`);
|
14005 | }
|
14006 | this.rbracketsExpected--;
|
14007 | this.expectCharacter($RBRACKET);
|
14008 | if (this.consumeOptionalOperator('=')) {
|
14009 | const value = this.parseConditional();
|
14010 | result = new KeyedWrite(this.span(start), this.sourceSpan(start), result, key, value);
|
14011 | }
|
14012 | else {
|
14013 | result = new KeyedRead(this.span(start), this.sourceSpan(start), result, key);
|
14014 | }
|
14015 | });
|
14016 | }
|
14017 | else if (this.consumeOptionalCharacter($LPAREN)) {
|
14018 | this.rparensExpected++;
|
14019 | const args = this.parseCallArguments();
|
14020 | this.rparensExpected--;
|
14021 | this.expectCharacter($RPAREN);
|
14022 | result = new FunctionCall(this.span(start), this.sourceSpan(start), result, args);
|
14023 | }
|
14024 | else if (this.consumeOptionalOperator('!')) {
|
14025 | result = new NonNullAssert(this.span(start), this.sourceSpan(start), result);
|
14026 | }
|
14027 | else {
|
14028 | return result;
|
14029 | }
|
14030 | }
|
14031 | }
|
14032 | parsePrimary() {
|
14033 | const start = this.inputIndex;
|
14034 | if (this.consumeOptionalCharacter($LPAREN)) {
|
14035 | this.rparensExpected++;
|
14036 | const result = this.parsePipe();
|
14037 | this.rparensExpected--;
|
14038 | this.expectCharacter($RPAREN);
|
14039 | return result;
|
14040 | }
|
14041 | else if (this.next.isKeywordNull()) {
|
14042 | this.advance();
|
14043 | return new LiteralPrimitive(this.span(start), this.sourceSpan(start), null);
|
14044 | }
|
14045 | else if (this.next.isKeywordUndefined()) {
|
14046 | this.advance();
|
14047 | return new LiteralPrimitive(this.span(start), this.sourceSpan(start), void 0);
|
14048 | }
|
14049 | else if (this.next.isKeywordTrue()) {
|
14050 | this.advance();
|
14051 | return new LiteralPrimitive(this.span(start), this.sourceSpan(start), true);
|
14052 | }
|
14053 | else if (this.next.isKeywordFalse()) {
|
14054 | this.advance();
|
14055 | return new LiteralPrimitive(this.span(start), this.sourceSpan(start), false);
|
14056 | }
|
14057 | else if (this.next.isKeywordThis()) {
|
14058 | this.advance();
|
14059 | return new ThisReceiver(this.span(start), this.sourceSpan(start));
|
14060 | }
|
14061 | else if (this.consumeOptionalCharacter($LBRACKET)) {
|
14062 | this.rbracketsExpected++;
|
14063 | const elements = this.parseExpressionList($RBRACKET);
|
14064 | this.rbracketsExpected--;
|
14065 | this.expectCharacter($RBRACKET);
|
14066 | return new LiteralArray(this.span(start), this.sourceSpan(start), elements);
|
14067 | }
|
14068 | else if (this.next.isCharacter($LBRACE)) {
|
14069 | return this.parseLiteralMap();
|
14070 | }
|
14071 | else if (this.next.isIdentifier()) {
|
14072 | return this.parseAccessMemberOrMethodCall(new ImplicitReceiver(this.span(start), this.sourceSpan(start)), start, false);
|
14073 | }
|
14074 | else if (this.next.isNumber()) {
|
14075 | const value = this.next.toNumber();
|
14076 | this.advance();
|
14077 | return new LiteralPrimitive(this.span(start), this.sourceSpan(start), value);
|
14078 | }
|
14079 | else if (this.next.isString()) {
|
14080 | const literalValue = this.next.toString();
|
14081 | this.advance();
|
14082 | return new LiteralPrimitive(this.span(start), this.sourceSpan(start), literalValue);
|
14083 | }
|
14084 | else if (this.index >= this.tokens.length) {
|
14085 | this.error(`Unexpected end of expression: ${this.input}`);
|
14086 | return new EmptyExpr(this.span(start), this.sourceSpan(start));
|
14087 | }
|
14088 | else {
|
14089 | this.error(`Unexpected token ${this.next}`);
|
14090 | return new EmptyExpr(this.span(start), this.sourceSpan(start));
|
14091 | }
|
14092 | }
|
14093 | parseExpressionList(terminator) {
|
14094 | const result = [];
|
14095 | do {
|
14096 | if (!this.next.isCharacter(terminator)) {
|
14097 | result.push(this.parsePipe());
|
14098 | }
|
14099 | else {
|
14100 | break;
|
14101 | }
|
14102 | } while (this.consumeOptionalCharacter($COMMA));
|
14103 | return result;
|
14104 | }
|
14105 | parseLiteralMap() {
|
14106 | const keys = [];
|
14107 | const values = [];
|
14108 | const start = this.inputIndex;
|
14109 | this.expectCharacter($LBRACE);
|
14110 | if (!this.consumeOptionalCharacter($RBRACE)) {
|
14111 | this.rbracesExpected++;
|
14112 | do {
|
14113 | const quoted = this.next.isString();
|
14114 | const key = this.expectIdentifierOrKeywordOrString();
|
14115 | keys.push({ key, quoted });
|
14116 | this.expectCharacter($COLON);
|
14117 | values.push(this.parsePipe());
|
14118 | } while (this.consumeOptionalCharacter($COMMA));
|
14119 | this.rbracesExpected--;
|
14120 | this.expectCharacter($RBRACE);
|
14121 | }
|
14122 | return new LiteralMap(this.span(start), this.sourceSpan(start), keys, values);
|
14123 | }
|
14124 | parseAccessMemberOrMethodCall(receiver, start, isSafe = false) {
|
14125 | const nameStart = this.inputIndex;
|
14126 | const id = this.withContext(ParseContextFlags.Writable, () => {
|
14127 | var _a;
|
14128 | const id = (_a = this.expectIdentifierOrKeyword()) !== null && _a !== void 0 ? _a : '';
|
14129 | if (id.length === 0) {
|
14130 | this.error(`Expected identifier for property access`, receiver.span.end);
|
14131 | }
|
14132 | return id;
|
14133 | });
|
14134 | const nameSpan = this.sourceSpan(nameStart);
|
14135 | if (this.consumeOptionalCharacter($LPAREN)) {
|
14136 | this.rparensExpected++;
|
14137 | const args = this.parseCallArguments();
|
14138 | this.expectCharacter($RPAREN);
|
14139 | this.rparensExpected--;
|
14140 | const span = this.span(start);
|
14141 | const sourceSpan = this.sourceSpan(start);
|
14142 | return isSafe ? new SafeMethodCall(span, sourceSpan, nameSpan, receiver, id, args) :
|
14143 | new MethodCall(span, sourceSpan, nameSpan, receiver, id, args);
|
14144 | }
|
14145 | else {
|
14146 | if (isSafe) {
|
14147 | if (this.consumeOptionalOperator('=')) {
|
14148 | this.error('The \'?.\' operator cannot be used in the assignment');
|
14149 | return new EmptyExpr(this.span(start), this.sourceSpan(start));
|
14150 | }
|
14151 | else {
|
14152 | return new SafePropertyRead(this.span(start), this.sourceSpan(start), nameSpan, receiver, id);
|
14153 | }
|
14154 | }
|
14155 | else {
|
14156 | if (this.consumeOptionalOperator('=')) {
|
14157 | if (!this.parseAction) {
|
14158 | this.error('Bindings cannot contain assignments');
|
14159 | return new EmptyExpr(this.span(start), this.sourceSpan(start));
|
14160 | }
|
14161 | const value = this.parseConditional();
|
14162 | return new PropertyWrite(this.span(start), this.sourceSpan(start), nameSpan, receiver, id, value);
|
14163 | }
|
14164 | else {
|
14165 | return new PropertyRead(this.span(start), this.sourceSpan(start), nameSpan, receiver, id);
|
14166 | }
|
14167 | }
|
14168 | }
|
14169 | }
|
14170 | parseCallArguments() {
|
14171 | if (this.next.isCharacter($RPAREN))
|
14172 | return [];
|
14173 | const positionals = [];
|
14174 | do {
|
14175 | positionals.push(this.parsePipe());
|
14176 | } while (this.consumeOptionalCharacter($COMMA));
|
14177 | return positionals;
|
14178 | }
|
14179 | /**
|
14180 | * Parses an identifier, a keyword, a string with an optional `-` in between,
|
14181 | * and returns the string along with its absolute source span.
|
14182 | */
|
14183 | expectTemplateBindingKey() {
|
14184 | let result = '';
|
14185 | let operatorFound = false;
|
14186 | const start = this.currentAbsoluteOffset;
|
14187 | do {
|
14188 | result += this.expectIdentifierOrKeywordOrString();
|
14189 | operatorFound = this.consumeOptionalOperator('-');
|
14190 | if (operatorFound) {
|
14191 | result += '-';
|
14192 | }
|
14193 | } while (operatorFound);
|
14194 | return {
|
14195 | source: result,
|
14196 | span: new AbsoluteSourceSpan(start, start + result.length),
|
14197 | };
|
14198 | }
|
14199 | /**
|
14200 | * Parse microsyntax template expression and return a list of bindings or
|
14201 | * parsing errors in case the given expression is invalid.
|
14202 | *
|
14203 | * For example,
|
14204 | * ```
|
14205 | * <div *ngFor="let item of items; index as i; trackBy: func">
|
14206 | * ```
|
14207 | * contains five bindings:
|
14208 | * 1. ngFor -> null
|
14209 | * 2. item -> NgForOfContext.$implicit
|
14210 | * 3. ngForOf -> items
|
14211 | * 4. i -> NgForOfContext.index
|
14212 | * 5. ngForTrackBy -> func
|
14213 | *
|
14214 | * For a full description of the microsyntax grammar, see
|
14215 | * https://gist.github.com/mhevery/d3530294cff2e4a1b3fe15ff75d08855
|
14216 | *
|
14217 | * @param templateKey name of the microsyntax directive, like ngIf, ngFor,
|
14218 | * without the *, along with its absolute span.
|
14219 | */
|
14220 | parseTemplateBindings(templateKey) {
|
14221 | const bindings = [];
|
14222 | // The first binding is for the template key itself
|
14223 | // In *ngFor="let item of items", key = "ngFor", value = null
|
14224 | // In *ngIf="cond | pipe", key = "ngIf", value = "cond | pipe"
|
14225 | bindings.push(...this.parseDirectiveKeywordBindings(templateKey));
|
14226 | while (this.index < this.tokens.length) {
|
14227 | // If it starts with 'let', then this must be variable declaration
|
14228 | const letBinding = this.parseLetBinding();
|
14229 | if (letBinding) {
|
14230 | bindings.push(letBinding);
|
14231 | }
|
14232 | else {
|
14233 | // Two possible cases here, either `value "as" key` or
|
14234 | // "directive-keyword expression". We don't know which case, but both
|
14235 | // "value" and "directive-keyword" are template binding key, so consume
|
14236 | // the key first.
|
14237 | const key = this.expectTemplateBindingKey();
|
14238 | // Peek at the next token, if it is "as" then this must be variable
|
14239 | // declaration.
|
14240 | const binding = this.parseAsBinding(key);
|
14241 | if (binding) {
|
14242 | bindings.push(binding);
|
14243 | }
|
14244 | else {
|
14245 | // Otherwise the key must be a directive keyword, like "of". Transform
|
14246 | // the key to actual key. Eg. of -> ngForOf, trackBy -> ngForTrackBy
|
14247 | key.source =
|
14248 | templateKey.source + key.source.charAt(0).toUpperCase() + key.source.substring(1);
|
14249 | bindings.push(...this.parseDirectiveKeywordBindings(key));
|
14250 | }
|
14251 | }
|
14252 | this.consumeStatementTerminator();
|
14253 | }
|
14254 | return new TemplateBindingParseResult(bindings, [] /* warnings */, this.errors);
|
14255 | }
|
14256 | /**
|
14257 | * Parse a directive keyword, followed by a mandatory expression.
|
14258 | * For example, "of items", "trackBy: func".
|
14259 | * The bindings are: ngForOf -> items, ngForTrackBy -> func
|
14260 | * There could be an optional "as" binding that follows the expression.
|
14261 | * For example,
|
14262 | * ```
|
14263 | * *ngFor="let item of items | slice:0:1 as collection".
|
14264 | * ^^ ^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^
|
14265 | * keyword bound target optional 'as' binding
|
14266 | * ```
|
14267 | *
|
14268 | * @param key binding key, for example, ngFor, ngIf, ngForOf, along with its
|
14269 | * absolute span.
|
14270 | */
|
14271 | parseDirectiveKeywordBindings(key) {
|
14272 | const bindings = [];
|
14273 | this.consumeOptionalCharacter($COLON); // trackBy: trackByFunction
|
14274 | const value = this.getDirectiveBoundTarget();
|
14275 | let spanEnd = this.currentAbsoluteOffset;
|
14276 | // The binding could optionally be followed by "as". For example,
|
14277 | // *ngIf="cond | pipe as x". In this case, the key in the "as" binding
|
14278 | // is "x" and the value is the template key itself ("ngIf"). Note that the
|
14279 | // 'key' in the current context now becomes the "value" in the next binding.
|
14280 | const asBinding = this.parseAsBinding(key);
|
14281 | if (!asBinding) {
|
14282 | this.consumeStatementTerminator();
|
14283 | spanEnd = this.currentAbsoluteOffset;
|
14284 | }
|
14285 | const sourceSpan = new AbsoluteSourceSpan(key.span.start, spanEnd);
|
14286 | bindings.push(new ExpressionBinding(sourceSpan, key, value));
|
14287 | if (asBinding) {
|
14288 | bindings.push(asBinding);
|
14289 | }
|
14290 | return bindings;
|
14291 | }
|
14292 | /**
|
14293 | * Return the expression AST for the bound target of a directive keyword
|
14294 | * binding. For example,
|
14295 | * ```
|
14296 | * *ngIf="condition | pipe"
|
14297 | * ^^^^^^^^^^^^^^^^ bound target for "ngIf"
|
14298 | * *ngFor="let item of items"
|
14299 | * ^^^^^ bound target for "ngForOf"
|
14300 | * ```
|
14301 | */
|
14302 | getDirectiveBoundTarget() {
|
14303 | if (this.next === EOF || this.peekKeywordAs() || this.peekKeywordLet()) {
|
14304 | return null;
|
14305 | }
|
14306 | const ast = this.parsePipe(); // example: "condition | async"
|
14307 | const { start, end } = ast.span;
|
14308 | const value = this.input.substring(start, end);
|
14309 | return new ASTWithSource(ast, value, this.location, this.absoluteOffset + start, this.errors);
|
14310 | }
|
14311 | /**
|
14312 | * Return the binding for a variable declared using `as`. Note that the order
|
14313 | * of the key-value pair in this declaration is reversed. For example,
|
14314 | * ```
|
14315 | * *ngFor="let item of items; index as i"
|
14316 | * ^^^^^ ^
|
14317 | * value key
|
14318 | * ```
|
14319 | *
|
14320 | * @param value name of the value in the declaration, "ngIf" in the example
|
14321 | * above, along with its absolute span.
|
14322 | */
|
14323 | parseAsBinding(value) {
|
14324 | if (!this.peekKeywordAs()) {
|
14325 | return null;
|
14326 | }
|
14327 | this.advance(); // consume the 'as' keyword
|
14328 | const key = this.expectTemplateBindingKey();
|
14329 | this.consumeStatementTerminator();
|
14330 | const sourceSpan = new AbsoluteSourceSpan(value.span.start, this.currentAbsoluteOffset);
|
14331 | return new VariableBinding(sourceSpan, key, value);
|
14332 | }
|
14333 | /**
|
14334 | * Return the binding for a variable declared using `let`. For example,
|
14335 | * ```
|
14336 | * *ngFor="let item of items; let i=index;"
|
14337 | * ^^^^^^^^ ^^^^^^^^^^^
|
14338 | * ```
|
14339 | * In the first binding, `item` is bound to `NgForOfContext.$implicit`.
|
14340 | * In the second binding, `i` is bound to `NgForOfContext.index`.
|
14341 | */
|
14342 | parseLetBinding() {
|
14343 | if (!this.peekKeywordLet()) {
|
14344 | return null;
|
14345 | }
|
14346 | const spanStart = this.currentAbsoluteOffset;
|
14347 | this.advance(); // consume the 'let' keyword
|
14348 | const key = this.expectTemplateBindingKey();
|
14349 | let value = null;
|
14350 | if (this.consumeOptionalOperator('=')) {
|
14351 | value = this.expectTemplateBindingKey();
|
14352 | }
|
14353 | this.consumeStatementTerminator();
|
14354 | const sourceSpan = new AbsoluteSourceSpan(spanStart, this.currentAbsoluteOffset);
|
14355 | return new VariableBinding(sourceSpan, key, value);
|
14356 | }
|
14357 | /**
|
14358 | * Consume the optional statement terminator: semicolon or comma.
|
14359 | */
|
14360 | consumeStatementTerminator() {
|
14361 | this.consumeOptionalCharacter($SEMICOLON) || this.consumeOptionalCharacter($COMMA);
|
14362 | }
|
14363 | /**
|
14364 | * Records an error and skips over the token stream until reaching a recoverable point. See
|
14365 | * `this.skip` for more details on token skipping.
|
14366 | */
|
14367 | error(message, index = null) {
|
14368 | this.errors.push(new ParserError(message, this.input, this.locationText(index), this.location));
|
14369 | this.skip();
|
14370 | }
|
14371 | locationText(index = null) {
|
14372 | if (index == null)
|
14373 | index = this.index;
|
14374 | return (index < this.tokens.length) ? `at column ${this.tokens[index].index + 1} in` :
|
14375 | `at the end of the expression`;
|
14376 | }
|
14377 | /**
|
14378 | * Error recovery should skip tokens until it encounters a recovery point.
|
14379 | *
|
14380 | * The following are treated as unconditional recovery points:
|
14381 | * - end of input
|
14382 | * - ';' (parseChain() is always the root production, and it expects a ';')
|
14383 | * - '|' (since pipes may be chained and each pipe expression may be treated independently)
|
14384 | *
|
14385 | * The following are conditional recovery points:
|
14386 | * - ')', '}', ']' if one of calling productions is expecting one of these symbols
|
14387 | * - This allows skip() to recover from errors such as '(a.) + 1' allowing more of the AST to
|
14388 | * be retained (it doesn't skip any tokens as the ')' is retained because of the '(' begins
|
14389 | * an '(' <expr> ')' production).
|
14390 | * The recovery points of grouping symbols must be conditional as they must be skipped if
|
14391 | * none of the calling productions are not expecting the closing token else we will never
|
14392 | * make progress in the case of an extraneous group closing symbol (such as a stray ')').
|
14393 | * That is, we skip a closing symbol if we are not in a grouping production.
|
14394 | * - '=' in a `Writable` context
|
14395 | * - In this context, we are able to recover after seeing the `=` operator, which
|
14396 | * signals the presence of an independent rvalue expression following the `=` operator.
|
14397 | *
|
14398 | * If a production expects one of these token it increments the corresponding nesting count,
|
14399 | * and then decrements it just prior to checking if the token is in the input.
|
14400 | */
|
14401 | skip() {
|
14402 | let n = this.next;
|
14403 | while (this.index < this.tokens.length && !n.isCharacter($SEMICOLON) &&
|
14404 | !n.isOperator('|') && (this.rparensExpected <= 0 || !n.isCharacter($RPAREN)) &&
|
14405 | (this.rbracesExpected <= 0 || !n.isCharacter($RBRACE)) &&
|
14406 | (this.rbracketsExpected <= 0 || !n.isCharacter($RBRACKET)) &&
|
14407 | (!(this.context & ParseContextFlags.Writable) || !n.isOperator('='))) {
|
14408 | if (this.next.isError()) {
|
14409 | this.errors.push(new ParserError(this.next.toString(), this.input, this.locationText(), this.location));
|
14410 | }
|
14411 | this.advance();
|
14412 | n = this.next;
|
14413 | }
|
14414 | }
|
14415 | }
|
14416 | class SimpleExpressionChecker {
|
14417 | constructor() {
|
14418 | this.errors = [];
|
14419 | }
|
14420 | visitImplicitReceiver(ast, context) { }
|
14421 | visitThisReceiver(ast, context) { }
|
14422 | visitInterpolation(ast, context) { }
|
14423 | visitLiteralPrimitive(ast, context) { }
|
14424 | visitPropertyRead(ast, context) { }
|
14425 | visitPropertyWrite(ast, context) { }
|
14426 | visitSafePropertyRead(ast, context) { }
|
14427 | visitMethodCall(ast, context) { }
|
14428 | visitSafeMethodCall(ast, context) { }
|
14429 | visitFunctionCall(ast, context) { }
|
14430 | visitLiteralArray(ast, context) {
|
14431 | this.visitAll(ast.expressions, context);
|
14432 | }
|
14433 | visitLiteralMap(ast, context) {
|
14434 | this.visitAll(ast.values, context);
|
14435 | }
|
14436 | visitUnary(ast, context) { }
|
14437 | visitBinary(ast, context) { }
|
14438 | visitPrefixNot(ast, context) { }
|
14439 | visitNonNullAssert(ast, context) { }
|
14440 | visitConditional(ast, context) { }
|
14441 | visitPipe(ast, context) {
|
14442 | this.errors.push('pipes');
|
14443 | }
|
14444 | visitKeyedRead(ast, context) { }
|
14445 | visitKeyedWrite(ast, context) { }
|
14446 | visitAll(asts, context) {
|
14447 | return asts.map(node => node.visit(this, context));
|
14448 | }
|
14449 | visitChain(ast, context) { }
|
14450 | visitQuote(ast, context) { }
|
14451 | }
|
14452 | /**
|
14453 | * This class implements SimpleExpressionChecker used in View Engine and performs more strict checks
|
14454 | * to make sure host bindings do not contain pipes. In View Engine, having pipes in host bindings is
|
14455 | * not supported as well, but in some cases (like `!(value | async)`) the error is not triggered at
|
14456 | * compile time. In order to preserve View Engine behavior, more strict checks are introduced for
|
14457 | * Ivy mode only.
|
14458 | */
|
14459 | class IvySimpleExpressionChecker extends RecursiveAstVisitor {
|
14460 | constructor() {
|
14461 | super(...arguments);
|
14462 | this.errors = [];
|
14463 | }
|
14464 | visitPipe() {
|
14465 | this.errors.push('pipes');
|
14466 | }
|
14467 | }
|
14468 |
|
14469 | /**
|
14470 | * @license
|
14471 | * Copyright Google LLC All Rights Reserved.
|
14472 | *
|
14473 | * Use of this source code is governed by an MIT-style license that can be
|
14474 | * found in the LICENSE file at https://angular.io/license
|
14475 | */
|
14476 | // =================================================================================================
|
14477 | // =================================================================================================
|
14478 | // =========== S T O P - S T O P - S T O P - S T O P - S T O P - S T O P ===========
|
14479 | // =================================================================================================
|
14480 | // =================================================================================================
|
14481 | //
|
14482 | // DO NOT EDIT THIS LIST OF SECURITY SENSITIVE PROPERTIES WITHOUT A SECURITY REVIEW!
|
14483 | // Reach out to mprobst for details.
|
14484 | //
|
14485 | // =================================================================================================
|
14486 | /** Map from tagName|propertyName to SecurityContext. Properties applying to all tags use '*'. */
|
14487 | let _SECURITY_SCHEMA;
|
14488 | function SECURITY_SCHEMA() {
|
14489 | if (!_SECURITY_SCHEMA) {
|
14490 | _SECURITY_SCHEMA = {};
|
14491 | // Case is insignificant below, all element and attribute names are lower-cased for lookup.
|
14492 | registerContext(SecurityContext.HTML, [
|
14493 | 'iframe|srcdoc',
|
14494 | '*|innerHTML',
|
14495 | '*|outerHTML',
|
14496 | ]);
|
14497 | registerContext(SecurityContext.STYLE, ['*|style']);
|
14498 | // NB: no SCRIPT contexts here, they are never allowed due to the parser stripping them.
|
14499 | registerContext(SecurityContext.URL, [
|
14500 | '*|formAction', 'area|href', 'area|ping', 'audio|src', 'a|href',
|
14501 | 'a|ping', 'blockquote|cite', 'body|background', 'del|cite', 'form|action',
|
14502 | 'img|src', 'img|srcset', 'input|src', 'ins|cite', 'q|cite',
|
14503 | 'source|src', 'source|srcset', 'track|src', 'video|poster', 'video|src',
|
14504 | ]);
|
14505 | registerContext(SecurityContext.RESOURCE_URL, [
|
14506 | 'applet|code',
|
14507 | 'applet|codebase',
|
14508 | 'base|href',
|
14509 | 'embed|src',
|
14510 | 'frame|src',
|
14511 | 'head|profile',
|
14512 | 'html|manifest',
|
14513 | 'iframe|src',
|
14514 | 'link|href',
|
14515 | 'media|src',
|
14516 | 'object|codebase',
|
14517 | 'object|data',
|
14518 | 'script|src',
|
14519 | ]);
|
14520 | }
|
14521 | return _SECURITY_SCHEMA;
|
14522 | }
|
14523 | function registerContext(ctx, specs) {
|
14524 | for (const spec of specs)
|
14525 | _SECURITY_SCHEMA[spec.toLowerCase()] = ctx;
|
14526 | }
|
14527 |
|
14528 | /**
|
14529 | * @license
|
14530 | * Copyright Google LLC All Rights Reserved.
|
14531 | *
|
14532 | * Use of this source code is governed by an MIT-style license that can be
|
14533 | * found in the LICENSE file at https://angular.io/license
|
14534 | */
|
14535 | class ElementSchemaRegistry {
|
14536 | }
|
14537 |
|
14538 | /**
|
14539 | * @license
|
14540 | * Copyright Google LLC All Rights Reserved.
|
14541 | *
|
14542 | * Use of this source code is governed by an MIT-style license that can be
|
14543 | * found in the LICENSE file at https://angular.io/license
|
14544 | */
|
14545 | const BOOLEAN = 'boolean';
|
14546 | const NUMBER = 'number';
|
14547 | const STRING = 'string';
|
14548 | const OBJECT = 'object';
|
14549 | /**
|
14550 | * This array represents the DOM schema. It encodes inheritance, properties, and events.
|
14551 | *
|
14552 | * ## Overview
|
14553 | *
|
14554 | * Each line represents one kind of element. The `element_inheritance` and properties are joined
|
14555 | * using `element_inheritance|properties` syntax.
|
14556 | *
|
14557 | * ## Element Inheritance
|
14558 | *
|
14559 | * The `element_inheritance` can be further subdivided as `element1,element2,...^parentElement`.
|
14560 | * Here the individual elements are separated by `,` (commas). Every element in the list
|
14561 | * has identical properties.
|
14562 | *
|
14563 | * An `element` may inherit additional properties from `parentElement` If no `^parentElement` is
|
14564 | * specified then `""` (blank) element is assumed.
|
14565 | *
|
14566 | * NOTE: The blank element inherits from root `[Element]` element, the super element of all
|
14567 | * elements.
|
14568 | *
|
14569 | * NOTE an element prefix such as `:svg:` has no special meaning to the schema.
|
14570 | *
|
14571 | * ## Properties
|
14572 | *
|
14573 | * Each element has a set of properties separated by `,` (commas). Each property can be prefixed
|
14574 | * by a special character designating its type:
|
14575 | *
|
14576 | * - (no prefix): property is a string.
|
14577 | * - `*`: property represents an event.
|
14578 | * - `!`: property is a boolean.
|
14579 | * - `#`: property is a number.
|
14580 | * - `%`: property is an object.
|
14581 | *
|
14582 | * ## Query
|
14583 | *
|
14584 | * The class creates an internal squas representation which allows to easily answer the query of
|
14585 | * if a given property exist on a given element.
|
14586 | *
|
14587 | * NOTE: We don't yet support querying for types or events.
|
14588 | * NOTE: This schema is auto extracted from `schema_extractor.ts` located in the test folder,
|
14589 | * see dom_element_schema_registry_spec.ts
|
14590 | */
|
14591 | // =================================================================================================
|
14592 | // =================================================================================================
|
14593 | // =========== S T O P - S T O P - S T O P - S T O P - S T O P - S T O P ===========
|
14594 | // =================================================================================================
|
14595 | // =================================================================================================
|
14596 | //
|
14597 | // DO NOT EDIT THIS DOM SCHEMA WITHOUT A SECURITY REVIEW!
|
14598 | //
|
14599 | // Newly added properties must be security reviewed and assigned an appropriate SecurityContext in
|
14600 | // dom_security_schema.ts. Reach out to mprobst & rjamet for details.
|
14601 | //
|
14602 | // =================================================================================================
|
14603 | const SCHEMA = [
|
14604 | '[Element]|textContent,%classList,className,id,innerHTML,*beforecopy,*beforecut,*beforepaste,*copy,*cut,*paste,*search,*selectstart,*webkitfullscreenchange,*webkitfullscreenerror,*wheel,outerHTML,#scrollLeft,#scrollTop,slot' +
|
14605 | /* added manually to avoid breaking changes */
|
14606 | ',*message,*mozfullscreenchange,*mozfullscreenerror,*mozpointerlockchange,*mozpointerlockerror,*webglcontextcreationerror,*webglcontextlost,*webglcontextrestored',
|
14607 | '[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',
|
14608 | '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',
|
14609 | 'media^[HTMLElement]|!autoplay,!controls,%controlsList,%crossOrigin,#currentTime,!defaultMuted,#defaultPlaybackRate,!disableRemotePlayback,!loop,!muted,*encrypted,*waitingforkey,#playbackRate,preload,src,%srcObject,#volume',
|
14610 | ':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',
|
14611 | ':svg:graphics^:svg:|',
|
14612 | ':svg:animation^:svg:|*begin,*end,*repeat',
|
14613 | ':svg:geometry^:svg:|',
|
14614 | ':svg:componentTransferFunction^:svg:|',
|
14615 | ':svg:gradient^:svg:|',
|
14616 | ':svg:textContent^:svg:graphics|',
|
14617 | ':svg:textPositioning^:svg:textContent|',
|
14618 | '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',
|
14619 | 'area^[HTMLElement]|alt,coords,download,hash,host,hostname,href,!noHref,password,pathname,ping,port,protocol,referrerPolicy,rel,search,shape,target,username',
|
14620 | 'audio^media|',
|
14621 | 'br^[HTMLElement]|clear',
|
14622 | 'base^[HTMLElement]|href,target',
|
14623 | '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',
|
14624 | 'button^[HTMLElement]|!autofocus,!disabled,formAction,formEnctype,formMethod,!formNoValidate,formTarget,name,type,value',
|
14625 | 'canvas^[HTMLElement]|#height,#width',
|
14626 | 'content^[HTMLElement]|select',
|
14627 | 'dl^[HTMLElement]|!compact',
|
14628 | 'datalist^[HTMLElement]|',
|
14629 | 'details^[HTMLElement]|!open',
|
14630 | 'dialog^[HTMLElement]|!open,returnValue',
|
14631 | 'dir^[HTMLElement]|!compact',
|
14632 | 'div^[HTMLElement]|align',
|
14633 | 'embed^[HTMLElement]|align,height,name,src,type,width',
|
14634 | 'fieldset^[HTMLElement]|!disabled,name',
|
14635 | 'font^[HTMLElement]|color,face,size',
|
14636 | 'form^[HTMLElement]|acceptCharset,action,autocomplete,encoding,enctype,method,name,!noValidate,target',
|
14637 | 'frame^[HTMLElement]|frameBorder,longDesc,marginHeight,marginWidth,name,!noResize,scrolling,src',
|
14638 | 'frameset^[HTMLElement]|cols,*beforeunload,*blur,*error,*focus,*hashchange,*languagechange,*load,*message,*offline,*online,*pagehide,*pageshow,*popstate,*rejectionhandled,*resize,*scroll,*storage,*unhandledrejection,*unload,rows',
|
14639 | 'hr^[HTMLElement]|align,color,!noShade,size,width',
|
14640 | 'head^[HTMLElement]|',
|
14641 | 'h1,h2,h3,h4,h5,h6^[HTMLElement]|align',
|
14642 | 'html^[HTMLElement]|version',
|
14643 | 'iframe^[HTMLElement]|align,!allowFullscreen,frameBorder,height,longDesc,marginHeight,marginWidth,name,referrerPolicy,%sandbox,scrolling,src,srcdoc,width',
|
14644 | 'img^[HTMLElement]|align,alt,border,%crossOrigin,#height,#hspace,!isMap,longDesc,lowsrc,name,referrerPolicy,sizes,src,srcset,useMap,#vspace,#width',
|
14645 | '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',
|
14646 | 'li^[HTMLElement]|type,#value',
|
14647 | 'label^[HTMLElement]|htmlFor',
|
14648 | 'legend^[HTMLElement]|align',
|
14649 | 'link^[HTMLElement]|as,charset,%crossOrigin,!disabled,href,hreflang,integrity,media,referrerPolicy,rel,%relList,rev,%sizes,target,type',
|
14650 | 'map^[HTMLElement]|name',
|
14651 | 'marquee^[HTMLElement]|behavior,bgColor,direction,height,#hspace,#loop,#scrollAmount,#scrollDelay,!trueSpeed,#vspace,width',
|
14652 | 'menu^[HTMLElement]|!compact',
|
14653 | 'meta^[HTMLElement]|content,httpEquiv,name,scheme',
|
14654 | 'meter^[HTMLElement]|#high,#low,#max,#min,#optimum,#value',
|
14655 | 'ins,del^[HTMLElement]|cite,dateTime',
|
14656 | 'ol^[HTMLElement]|!compact,!reversed,#start,type',
|
14657 | 'object^[HTMLElement]|align,archive,border,code,codeBase,codeType,data,!declare,height,#hspace,name,standby,type,useMap,#vspace,width',
|
14658 | 'optgroup^[HTMLElement]|!disabled,label',
|
14659 | 'option^[HTMLElement]|!defaultSelected,!disabled,label,!selected,text,value',
|
14660 | 'output^[HTMLElement]|defaultValue,%htmlFor,name,value',
|
14661 | 'p^[HTMLElement]|align',
|
14662 | 'param^[HTMLElement]|name,type,value,valueType',
|
14663 | 'picture^[HTMLElement]|',
|
14664 | 'pre^[HTMLElement]|#width',
|
14665 | 'progress^[HTMLElement]|#max,#value',
|
14666 | 'q,blockquote,cite^[HTMLElement]|',
|
14667 | 'script^[HTMLElement]|!async,charset,%crossOrigin,!defer,event,htmlFor,integrity,src,text,type',
|
14668 | 'select^[HTMLElement]|autocomplete,!autofocus,!disabled,#length,!multiple,name,!required,#selectedIndex,#size,value',
|
14669 | 'shadow^[HTMLElement]|',
|
14670 | 'slot^[HTMLElement]|name',
|
14671 | 'source^[HTMLElement]|media,sizes,src,srcset,type',
|
14672 | 'span^[HTMLElement]|',
|
14673 | 'style^[HTMLElement]|!disabled,media,type',
|
14674 | 'caption^[HTMLElement]|align',
|
14675 | 'th,td^[HTMLElement]|abbr,align,axis,bgColor,ch,chOff,#colSpan,headers,height,!noWrap,#rowSpan,scope,vAlign,width',
|
14676 | 'col,colgroup^[HTMLElement]|align,ch,chOff,#span,vAlign,width',
|
14677 | 'table^[HTMLElement]|align,bgColor,border,%caption,cellPadding,cellSpacing,frame,rules,summary,%tFoot,%tHead,width',
|
14678 | 'tr^[HTMLElement]|align,bgColor,ch,chOff,vAlign',
|
14679 | 'tfoot,thead,tbody^[HTMLElement]|align,ch,chOff,vAlign',
|
14680 | 'template^[HTMLElement]|',
|
14681 | 'textarea^[HTMLElement]|autocapitalize,autocomplete,!autofocus,#cols,defaultValue,dirName,!disabled,#maxLength,#minLength,name,placeholder,!readOnly,!required,#rows,selectionDirection,#selectionEnd,#selectionStart,value,wrap',
|
14682 | 'title^[HTMLElement]|text',
|
14683 | 'track^[HTMLElement]|!default,kind,label,src,srclang',
|
14684 | 'ul^[HTMLElement]|!compact,type',
|
14685 | 'unknown^[HTMLElement]|',
|
14686 | 'video^media|#height,poster,#width',
|
14687 | ':svg:a^:svg:graphics|',
|
14688 | ':svg:animate^:svg:animation|',
|
14689 | ':svg:animateMotion^:svg:animation|',
|
14690 | ':svg:animateTransform^:svg:animation|',
|
14691 | ':svg:circle^:svg:geometry|',
|
14692 | ':svg:clipPath^:svg:graphics|',
|
14693 | ':svg:defs^:svg:graphics|',
|
14694 | ':svg:desc^:svg:|',
|
14695 | ':svg:discard^:svg:|',
|
14696 | ':svg:ellipse^:svg:geometry|',
|
14697 | ':svg:feBlend^:svg:|',
|
14698 | ':svg:feColorMatrix^:svg:|',
|
14699 | ':svg:feComponentTransfer^:svg:|',
|
14700 | ':svg:feComposite^:svg:|',
|
14701 | ':svg:feConvolveMatrix^:svg:|',
|
14702 | ':svg:feDiffuseLighting^:svg:|',
|
14703 | ':svg:feDisplacementMap^:svg:|',
|
14704 | ':svg:feDistantLight^:svg:|',
|
14705 | ':svg:feDropShadow^:svg:|',
|
14706 | ':svg:feFlood^:svg:|',
|
14707 | ':svg:feFuncA^:svg:componentTransferFunction|',
|
14708 | ':svg:feFuncB^:svg:componentTransferFunction|',
|
14709 | ':svg:feFuncG^:svg:componentTransferFunction|',
|
14710 | ':svg:feFuncR^:svg:componentTransferFunction|',
|
14711 | ':svg:feGaussianBlur^:svg:|',
|
14712 | ':svg:feImage^:svg:|',
|
14713 | ':svg:feMerge^:svg:|',
|
14714 | ':svg:feMergeNode^:svg:|',
|
14715 | ':svg:feMorphology^:svg:|',
|
14716 | ':svg:feOffset^:svg:|',
|
14717 | ':svg:fePointLight^:svg:|',
|
14718 | ':svg:feSpecularLighting^:svg:|',
|
14719 | ':svg:feSpotLight^:svg:|',
|
14720 | ':svg:feTile^:svg:|',
|
14721 | ':svg:feTurbulence^:svg:|',
|
14722 | ':svg:filter^:svg:|',
|
14723 | ':svg:foreignObject^:svg:graphics|',
|
14724 | ':svg:g^:svg:graphics|',
|
14725 | ':svg:image^:svg:graphics|',
|
14726 | ':svg:line^:svg:geometry|',
|
14727 | ':svg:linearGradient^:svg:gradient|',
|
14728 | ':svg:mpath^:svg:|',
|
14729 | ':svg:marker^:svg:|',
|
14730 | ':svg:mask^:svg:|',
|
14731 | ':svg:metadata^:svg:|',
|
14732 | ':svg:path^:svg:geometry|',
|
14733 | ':svg:pattern^:svg:|',
|
14734 | ':svg:polygon^:svg:geometry|',
|
14735 | ':svg:polyline^:svg:geometry|',
|
14736 | ':svg:radialGradient^:svg:gradient|',
|
14737 | ':svg:rect^:svg:geometry|',
|
14738 | ':svg:svg^:svg:graphics|#currentScale,#zoomAndPan',
|
14739 | ':svg:script^:svg:|type',
|
14740 | ':svg:set^:svg:animation|',
|
14741 | ':svg:stop^:svg:|',
|
14742 | ':svg:style^:svg:|!disabled,media,title,type',
|
14743 | ':svg:switch^:svg:graphics|',
|
14744 | ':svg:symbol^:svg:|',
|
14745 | ':svg:tspan^:svg:textPositioning|',
|
14746 | ':svg:text^:svg:textPositioning|',
|
14747 | ':svg:textPath^:svg:textContent|',
|
14748 | ':svg:title^:svg:|',
|
14749 | ':svg:use^:svg:graphics|',
|
14750 | ':svg:view^:svg:|#zoomAndPan',
|
14751 | 'data^[HTMLElement]|value',
|
14752 | 'keygen^[HTMLElement]|!autofocus,challenge,!disabled,form,keytype,name',
|
14753 | 'menuitem^[HTMLElement]|type,label,icon,!disabled,!checked,radiogroup,!default',
|
14754 | 'summary^[HTMLElement]|',
|
14755 | 'time^[HTMLElement]|dateTime',
|
14756 | ':svg:cursor^:svg:|',
|
14757 | ];
|
14758 | const _ATTR_TO_PROP = {
|
14759 | 'class': 'className',
|
14760 | 'for': 'htmlFor',
|
14761 | 'formaction': 'formAction',
|
14762 | 'innerHtml': 'innerHTML',
|
14763 | 'readonly': 'readOnly',
|
14764 | 'tabindex': 'tabIndex',
|
14765 | };
|
14766 | // Invert _ATTR_TO_PROP.
|
14767 | const _PROP_TO_ATTR = Object.keys(_ATTR_TO_PROP).reduce((inverted, attr) => {
|
14768 | inverted[_ATTR_TO_PROP[attr]] = attr;
|
14769 | return inverted;
|
14770 | }, {});
|
14771 | class DomElementSchemaRegistry extends ElementSchemaRegistry {
|
14772 | constructor() {
|
14773 | super();
|
14774 | this._schema = {};
|
14775 | SCHEMA.forEach(encodedType => {
|
14776 | const type = {};
|
14777 | const [strType, strProperties] = encodedType.split('|');
|
14778 | const properties = strProperties.split(',');
|
14779 | const [typeNames, superName] = strType.split('^');
|
14780 | typeNames.split(',').forEach(tag => this._schema[tag.toLowerCase()] = type);
|
14781 | const superType = superName && this._schema[superName.toLowerCase()];
|
14782 | if (superType) {
|
14783 | Object.keys(superType).forEach((prop) => {
|
14784 | type[prop] = superType[prop];
|
14785 | });
|
14786 | }
|
14787 | properties.forEach((property) => {
|
14788 | if (property.length > 0) {
|
14789 | switch (property[0]) {
|
14790 | case '*':
|
14791 | // We don't yet support events.
|
14792 | // If ever allowing to bind to events, GO THROUGH A SECURITY REVIEW, allowing events
|
14793 | // will
|
14794 | // almost certainly introduce bad XSS vulnerabilities.
|
14795 | // type[property.substring(1)] = EVENT;
|
14796 | break;
|
14797 | case '!':
|
14798 | type[property.substring(1)] = BOOLEAN;
|
14799 | break;
|
14800 | case '#':
|
14801 | type[property.substring(1)] = NUMBER;
|
14802 | break;
|
14803 | case '%':
|
14804 | type[property.substring(1)] = OBJECT;
|
14805 | break;
|
14806 | default:
|
14807 | type[property] = STRING;
|
14808 | }
|
14809 | }
|
14810 | });
|
14811 | });
|
14812 | }
|
14813 | hasProperty(tagName, propName, schemaMetas) {
|
14814 | if (schemaMetas.some((schema) => schema.name === NO_ERRORS_SCHEMA.name)) {
|
14815 | return true;
|
14816 | }
|
14817 | if (tagName.indexOf('-') > -1) {
|
14818 | if (isNgContainer(tagName) || isNgContent(tagName)) {
|
14819 | return false;
|
14820 | }
|
14821 | if (schemaMetas.some((schema) => schema.name === CUSTOM_ELEMENTS_SCHEMA.name)) {
|
14822 | // Can't tell now as we don't know which properties a custom element will get
|
14823 | // once it is instantiated
|
14824 | return true;
|
14825 | }
|
14826 | }
|
14827 | const elementProperties = this._schema[tagName.toLowerCase()] || this._schema['unknown'];
|
14828 | return !!elementProperties[propName];
|
14829 | }
|
14830 | hasElement(tagName, schemaMetas) {
|
14831 | if (schemaMetas.some((schema) => schema.name === NO_ERRORS_SCHEMA.name)) {
|
14832 | return true;
|
14833 | }
|
14834 | if (tagName.indexOf('-') > -1) {
|
14835 | if (isNgContainer(tagName) || isNgContent(tagName)) {
|
14836 | return true;
|
14837 | }
|
14838 | if (schemaMetas.some((schema) => schema.name === CUSTOM_ELEMENTS_SCHEMA.name)) {
|
14839 | // Allow any custom elements
|
14840 | return true;
|
14841 | }
|
14842 | }
|
14843 | return !!this._schema[tagName.toLowerCase()];
|
14844 | }
|
14845 | /**
|
14846 | * securityContext returns the security context for the given property on the given DOM tag.
|
14847 | *
|
14848 | * Tag and property name are statically known and cannot change at runtime, i.e. it is not
|
14849 | * possible to bind a value into a changing attribute or tag name.
|
14850 | *
|
14851 | * The filtering is based on a list of allowed tags|attributes. All attributes in the schema
|
14852 | * above are assumed to have the 'NONE' security context, i.e. that they are safe inert
|
14853 | * string values. Only specific well known attack vectors are assigned their appropriate context.
|
14854 | */
|
14855 | securityContext(tagName, propName, isAttribute) {
|
14856 | if (isAttribute) {
|
14857 | // NB: For security purposes, use the mapped property name, not the attribute name.
|
14858 | propName = this.getMappedPropName(propName);
|
14859 | }
|
14860 | // Make sure comparisons are case insensitive, so that case differences between attribute and
|
14861 | // property names do not have a security impact.
|
14862 | tagName = tagName.toLowerCase();
|
14863 | propName = propName.toLowerCase();
|
14864 | let ctx = SECURITY_SCHEMA()[tagName + '|' + propName];
|
14865 | if (ctx) {
|
14866 | return ctx;
|
14867 | }
|
14868 | ctx = SECURITY_SCHEMA()['*|' + propName];
|
14869 | return ctx ? ctx : SecurityContext.NONE;
|
14870 | }
|
14871 | getMappedPropName(propName) {
|
14872 | return _ATTR_TO_PROP[propName] || propName;
|
14873 | }
|
14874 | getDefaultComponentElementName() {
|
14875 | return 'ng-component';
|
14876 | }
|
14877 | validateProperty(name) {
|
14878 | if (name.toLowerCase().startsWith('on')) {
|
14879 | const msg = `Binding to event property '${name}' is disallowed for security reasons, ` +
|
14880 | `please use (${name.slice(2)})=...` +
|
14881 | `\nIf '${name}' is a directive input, make sure the directive is imported by the` +
|
14882 | ` current module.`;
|
14883 | return { error: true, msg: msg };
|
14884 | }
|
14885 | else {
|
14886 | return { error: false };
|
14887 | }
|
14888 | }
|
14889 | validateAttribute(name) {
|
14890 | if (name.toLowerCase().startsWith('on')) {
|
14891 | const msg = `Binding to event attribute '${name}' is disallowed for security reasons, ` +
|
14892 | `please use (${name.slice(2)})=...`;
|
14893 | return { error: true, msg: msg };
|
14894 | }
|
14895 | else {
|
14896 | return { error: false };
|
14897 | }
|
14898 | }
|
14899 | allKnownElementNames() {
|
14900 | return Object.keys(this._schema);
|
14901 | }
|
14902 | allKnownAttributesOfElement(tagName) {
|
14903 | const elementProperties = this._schema[tagName.toLowerCase()] || this._schema['unknown'];
|
14904 | // Convert properties to attributes.
|
14905 | return Object.keys(elementProperties).map(prop => { var _a; return (_a = _PROP_TO_ATTR[prop]) !== null && _a !== void 0 ? _a : prop; });
|
14906 | }
|
14907 | normalizeAnimationStyleProperty(propName) {
|
14908 | return dashCaseToCamelCase(propName);
|
14909 | }
|
14910 | normalizeAnimationStyleValue(camelCaseProp, userProvidedProp, val) {
|
14911 | let unit = '';
|
14912 | const strVal = val.toString().trim();
|
14913 | let errorMsg = null;
|
14914 | if (_isPixelDimensionStyle(camelCaseProp) && val !== 0 && val !== '0') {
|
14915 | if (typeof val === 'number') {
|
14916 | unit = 'px';
|
14917 | }
|
14918 | else {
|
14919 | const valAndSuffixMatch = val.match(/^[+-]?[\d\.]+([a-z]*)$/);
|
14920 | if (valAndSuffixMatch && valAndSuffixMatch[1].length == 0) {
|
14921 | errorMsg = `Please provide a CSS unit value for ${userProvidedProp}:${val}`;
|
14922 | }
|
14923 | }
|
14924 | }
|
14925 | return { error: errorMsg, value: strVal + unit };
|
14926 | }
|
14927 | }
|
14928 | function _isPixelDimensionStyle(prop) {
|
14929 | switch (prop) {
|
14930 | case 'width':
|
14931 | case 'height':
|
14932 | case 'minWidth':
|
14933 | case 'minHeight':
|
14934 | case 'maxWidth':
|
14935 | case 'maxHeight':
|
14936 | case 'left':
|
14937 | case 'top':
|
14938 | case 'bottom':
|
14939 | case 'right':
|
14940 | case 'fontSize':
|
14941 | case 'outlineWidth':
|
14942 | case 'outlineOffset':
|
14943 | case 'paddingTop':
|
14944 | case 'paddingLeft':
|
14945 | case 'paddingBottom':
|
14946 | case 'paddingRight':
|
14947 | case 'marginTop':
|
14948 | case 'marginLeft':
|
14949 | case 'marginBottom':
|
14950 | case 'marginRight':
|
14951 | case 'borderRadius':
|
14952 | case 'borderWidth':
|
14953 | case 'borderTopWidth':
|
14954 | case 'borderLeftWidth':
|
14955 | case 'borderRightWidth':
|
14956 | case 'borderBottomWidth':
|
14957 | case 'textIndent':
|
14958 | return true;
|
14959 | default:
|
14960 | return false;
|
14961 | }
|
14962 | }
|
14963 |
|
14964 | /**
|
14965 | * @license
|
14966 | * Copyright Google LLC All Rights Reserved.
|
14967 | *
|
14968 | * Use of this source code is governed by an MIT-style license that can be
|
14969 | * found in the LICENSE file at https://angular.io/license
|
14970 | */
|
14971 | /**
|
14972 | * Set of tagName|propertyName corresponding to Trusted Types sinks. Properties applying to all
|
14973 | * tags use '*'.
|
14974 | *
|
14975 | * Extracted from, and should be kept in sync with
|
14976 | * https://w3c.github.io/webappsec-trusted-types/dist/spec/#integrations
|
14977 | */
|
14978 | const TRUSTED_TYPES_SINKS = new Set([
|
14979 | // NOTE: All strings in this set *must* be lowercase!
|
14980 | // TrustedHTML
|
14981 | 'iframe|srcdoc',
|
14982 | '*|innerhtml',
|
14983 | '*|outerhtml',
|
14984 | // NB: no TrustedScript here, as the corresponding tags are stripped by the compiler.
|
14985 | // TrustedScriptURL
|
14986 | 'embed|src',
|
14987 | 'object|codebase',
|
14988 | 'object|data',
|
14989 | ]);
|
14990 | /**
|
14991 | * isTrustedTypesSink returns true if the given property on the given DOM tag is a Trusted Types
|
14992 | * sink. In that case, use `ElementSchemaRegistry.securityContext` to determine which particular
|
14993 | * Trusted Type is required for values passed to the sink:
|
14994 | * - SecurityContext.HTML corresponds to TrustedHTML
|
14995 | * - SecurityContext.RESOURCE_URL corresponds to TrustedScriptURL
|
14996 | */
|
14997 | function isTrustedTypesSink(tagName, propName) {
|
14998 | // Make sure comparisons are case insensitive, so that case differences between attribute and
|
14999 | // property names do not have a security impact.
|
15000 | tagName = tagName.toLowerCase();
|
15001 | propName = propName.toLowerCase();
|
15002 | return TRUSTED_TYPES_SINKS.has(tagName + '|' + propName) ||
|
15003 | TRUSTED_TYPES_SINKS.has('*|' + propName);
|
15004 | }
|
15005 |
|
15006 | /**
|
15007 | * @license
|
15008 | * Copyright Google LLC All Rights Reserved.
|
15009 | *
|
15010 | * Use of this source code is governed by an MIT-style license that can be
|
15011 | * found in the LICENSE file at https://angular.io/license
|
15012 | */
|
15013 | const BIND_NAME_REGEXP$1 = /^(?:(bind-)|(let-)|(ref-|#)|(on-)|(bindon-)|(@))(.*)$/;
|
15014 | // Group 1 = "bind-"
|
15015 | const KW_BIND_IDX$1 = 1;
|
15016 | // Group 2 = "let-"
|
15017 | const KW_LET_IDX$1 = 2;
|
15018 | // Group 3 = "ref-/#"
|
15019 | const KW_REF_IDX$1 = 3;
|
15020 | // Group 4 = "on-"
|
15021 | const KW_ON_IDX$1 = 4;
|
15022 | // Group 5 = "bindon-"
|
15023 | const KW_BINDON_IDX$1 = 5;
|
15024 | // Group 6 = "@"
|
15025 | const KW_AT_IDX$1 = 6;
|
15026 | // Group 7 = the identifier after "bind-", "let-", "ref-/#", "on-", "bindon-" or "@"
|
15027 | const IDENT_KW_IDX$1 = 7;
|
15028 | const BINDING_DELIMS = {
|
15029 | BANANA_BOX: { start: '[(', end: ')]' },
|
15030 | PROPERTY: { start: '[', end: ']' },
|
15031 | EVENT: { start: '(', end: ')' },
|
15032 | };
|
15033 | const TEMPLATE_ATTR_PREFIX$2 = '*';
|
15034 | function htmlAstToRender3Ast(htmlNodes, bindingParser) {
|
15035 | const transformer = new HtmlAstToIvyAst(bindingParser);
|
15036 | const ivyNodes = visitAll$1(transformer, htmlNodes);
|
15037 | // Errors might originate in either the binding parser or the html to ivy transformer
|
15038 | const allErrors = bindingParser.errors.concat(transformer.errors);
|
15039 | return {
|
15040 | nodes: ivyNodes,
|
15041 | errors: allErrors,
|
15042 | styleUrls: transformer.styleUrls,
|
15043 | styles: transformer.styles,
|
15044 | ngContentSelectors: transformer.ngContentSelectors,
|
15045 | };
|
15046 | }
|
15047 | class HtmlAstToIvyAst {
|
15048 | constructor(bindingParser) {
|
15049 | this.bindingParser = bindingParser;
|
15050 | this.errors = [];
|
15051 | this.styles = [];
|
15052 | this.styleUrls = [];
|
15053 | this.ngContentSelectors = [];
|
15054 | this.inI18nBlock = false;
|
15055 | }
|
15056 | // HTML visitor
|
15057 | visitElement(element) {
|
15058 | const isI18nRootElement = isI18nRootNode(element.i18n);
|
15059 | if (isI18nRootElement) {
|
15060 | if (this.inI18nBlock) {
|
15061 | this.reportError('Cannot mark an element as translatable inside of a translatable section. Please remove the nested i18n marker.', element.sourceSpan);
|
15062 | }
|
15063 | this.inI18nBlock = true;
|
15064 | }
|
15065 | const preparsedElement = preparseElement(element);
|
15066 | if (preparsedElement.type === PreparsedElementType.SCRIPT) {
|
15067 | return null;
|
15068 | }
|
15069 | else if (preparsedElement.type === PreparsedElementType.STYLE) {
|
15070 | const contents = textContents(element);
|
15071 | if (contents !== null) {
|
15072 | this.styles.push(contents);
|
15073 | }
|
15074 | return null;
|
15075 | }
|
15076 | else if (preparsedElement.type === PreparsedElementType.STYLESHEET &&
|
15077 | isStyleUrlResolvable(preparsedElement.hrefAttr)) {
|
15078 | this.styleUrls.push(preparsedElement.hrefAttr);
|
15079 | return null;
|
15080 | }
|
15081 | // Whether the element is a `<ng-template>`
|
15082 | const isTemplateElement = isNgTemplate(element.name);
|
15083 | const parsedProperties = [];
|
15084 | const boundEvents = [];
|
15085 | const variables = [];
|
15086 | const references = [];
|
15087 | const attributes = [];
|
15088 | const i18nAttrsMeta = {};
|
15089 | const templateParsedProperties = [];
|
15090 | const templateVariables = [];
|
15091 | // Whether the element has any *-attribute
|
15092 | let elementHasInlineTemplate = false;
|
15093 | for (const attribute of element.attrs) {
|
15094 | let hasBinding = false;
|
15095 | const normalizedName = normalizeAttributeName(attribute.name);
|
15096 | // `*attr` defines template bindings
|
15097 | let isTemplateBinding = false;
|
15098 | if (attribute.i18n) {
|
15099 | i18nAttrsMeta[attribute.name] = attribute.i18n;
|
15100 | }
|
15101 | if (normalizedName.startsWith(TEMPLATE_ATTR_PREFIX$2)) {
|
15102 | // *-attributes
|
15103 | if (elementHasInlineTemplate) {
|
15104 | this.reportError(`Can't have multiple template bindings on one element. Use only one attribute prefixed with *`, attribute.sourceSpan);
|
15105 | }
|
15106 | isTemplateBinding = true;
|
15107 | elementHasInlineTemplate = true;
|
15108 | const templateValue = attribute.value;
|
15109 | const templateKey = normalizedName.substring(TEMPLATE_ATTR_PREFIX$2.length);
|
15110 | const parsedVariables = [];
|
15111 | const absoluteValueOffset = attribute.valueSpan ?
|
15112 | attribute.valueSpan.start.offset :
|
15113 | // If there is no value span the attribute does not have a value, like `attr` in
|
15114 | //`<div attr></div>`. In this case, point to one character beyond the last character of
|
15115 | // the attribute name.
|
15116 | attribute.sourceSpan.start.offset + attribute.name.length;
|
15117 | this.bindingParser.parseInlineTemplateBinding(templateKey, templateValue, attribute.sourceSpan, absoluteValueOffset, [], templateParsedProperties, parsedVariables, true /* isIvyAst */);
|
15118 | templateVariables.push(...parsedVariables.map(v => new Variable(v.name, v.value, v.sourceSpan, v.keySpan, v.valueSpan)));
|
15119 | }
|
15120 | else {
|
15121 | // Check for variables, events, property bindings, interpolation
|
15122 | hasBinding = this.parseAttribute(isTemplateElement, attribute, [], parsedProperties, boundEvents, variables, references);
|
15123 | }
|
15124 | if (!hasBinding && !isTemplateBinding) {
|
15125 | // don't include the bindings as attributes as well in the AST
|
15126 | attributes.push(this.visitAttribute(attribute));
|
15127 | }
|
15128 | }
|
15129 | const children = visitAll$1(preparsedElement.nonBindable ? NON_BINDABLE_VISITOR$1 : this, element.children);
|
15130 | let parsedElement;
|
15131 | if (preparsedElement.type === PreparsedElementType.NG_CONTENT) {
|
15132 | // `<ng-content>`
|
15133 | if (element.children &&
|
15134 | !element.children.every((node) => isEmptyTextNode(node) || isCommentNode(node))) {
|
15135 | this.reportError(`<ng-content> element cannot have content.`, element.sourceSpan);
|
15136 | }
|
15137 | const selector = preparsedElement.selectAttr;
|
15138 | const attrs = element.attrs.map(attr => this.visitAttribute(attr));
|
15139 | parsedElement = new Content(selector, attrs, element.sourceSpan, element.i18n);
|
15140 | this.ngContentSelectors.push(selector);
|
15141 | }
|
15142 | else if (isTemplateElement) {
|
15143 | // `<ng-template>`
|
15144 | const attrs = this.extractAttributes(element.name, parsedProperties, i18nAttrsMeta);
|
15145 | parsedElement = new Template(element.name, attributes, attrs.bound, boundEvents, [ /* no template attributes */], children, references, variables, element.sourceSpan, element.startSourceSpan, element.endSourceSpan, element.i18n);
|
15146 | }
|
15147 | else {
|
15148 | const attrs = this.extractAttributes(element.name, parsedProperties, i18nAttrsMeta);
|
15149 | parsedElement = new Element(element.name, attributes, attrs.bound, boundEvents, children, references, element.sourceSpan, element.startSourceSpan, element.endSourceSpan, element.i18n);
|
15150 | }
|
15151 | if (elementHasInlineTemplate) {
|
15152 | // If this node is an inline-template (e.g. has *ngFor) then we need to create a template
|
15153 | // node that contains this node.
|
15154 | // Moreover, if the node is an element, then we need to hoist its attributes to the template
|
15155 | // node for matching against content projection selectors.
|
15156 | const attrs = this.extractAttributes('ng-template', templateParsedProperties, i18nAttrsMeta);
|
15157 | const templateAttrs = [];
|
15158 | attrs.literal.forEach(attr => templateAttrs.push(attr));
|
15159 | attrs.bound.forEach(attr => templateAttrs.push(attr));
|
15160 | const hoistedAttrs = parsedElement instanceof Element ?
|
15161 | {
|
15162 | attributes: parsedElement.attributes,
|
15163 | inputs: parsedElement.inputs,
|
15164 | outputs: parsedElement.outputs,
|
15165 | } :
|
15166 | { attributes: [], inputs: [], outputs: [] };
|
15167 | // For <ng-template>s with structural directives on them, avoid passing i18n information to
|
15168 | // the wrapping template to prevent unnecessary i18n instructions from being generated. The
|
15169 | // necessary i18n meta information will be extracted from child elements.
|
15170 | const i18n = isTemplateElement && isI18nRootElement ? undefined : element.i18n;
|
15171 | // TODO(pk): test for this case
|
15172 | parsedElement = new Template(parsedElement.name, hoistedAttrs.attributes, hoistedAttrs.inputs, hoistedAttrs.outputs, templateAttrs, [parsedElement], [ /* no references */], templateVariables, element.sourceSpan, element.startSourceSpan, element.endSourceSpan, i18n);
|
15173 | }
|
15174 | if (isI18nRootElement) {
|
15175 | this.inI18nBlock = false;
|
15176 | }
|
15177 | return parsedElement;
|
15178 | }
|
15179 | visitAttribute(attribute) {
|
15180 | return new TextAttribute(attribute.name, attribute.value, attribute.sourceSpan, attribute.keySpan, attribute.valueSpan, attribute.i18n);
|
15181 | }
|
15182 | visitText(text) {
|
15183 | return this._visitTextWithInterpolation(text.value, text.sourceSpan, text.i18n);
|
15184 | }
|
15185 | visitExpansion(expansion) {
|
15186 | if (!expansion.i18n) {
|
15187 | // do not generate Icu in case it was created
|
15188 | // outside of i18n block in a template
|
15189 | return null;
|
15190 | }
|
15191 | if (!isI18nRootNode(expansion.i18n)) {
|
15192 | throw new Error(`Invalid type "${expansion.i18n.constructor}" for "i18n" property of ${expansion.sourceSpan.toString()}. Expected a "Message"`);
|
15193 | }
|
15194 | const message = expansion.i18n;
|
15195 | const vars = {};
|
15196 | const placeholders = {};
|
15197 | // extract VARs from ICUs - we process them separately while
|
15198 | // assembling resulting message via goog.getMsg function, since
|
15199 | // we need to pass them to top-level goog.getMsg call
|
15200 | Object.keys(message.placeholders).forEach(key => {
|
15201 | const value = message.placeholders[key];
|
15202 | if (key.startsWith(I18N_ICU_VAR_PREFIX)) {
|
15203 | // Currently when the `plural` or `select` keywords in an ICU contain trailing spaces (e.g.
|
15204 | // `{count, select , ...}`), these spaces are also included into the key names in ICU vars
|
15205 | // (e.g. "VAR_SELECT "). These trailing spaces are not desirable, since they will later be
|
15206 | // converted into `_` symbols while normalizing placeholder names, which might lead to
|
15207 | // mismatches at runtime (i.e. placeholder will not be replaced with the correct value).
|
15208 | const formattedKey = key.trim();
|
15209 | const ast = this.bindingParser.parseInterpolationExpression(value.text, value.sourceSpan);
|
15210 | vars[formattedKey] = new BoundText(ast, value.sourceSpan);
|
15211 | }
|
15212 | else {
|
15213 | placeholders[key] = this._visitTextWithInterpolation(value.text, value.sourceSpan);
|
15214 | }
|
15215 | });
|
15216 | return new Icu(vars, placeholders, expansion.sourceSpan, message);
|
15217 | }
|
15218 | visitExpansionCase(expansionCase) {
|
15219 | return null;
|
15220 | }
|
15221 | visitComment(comment) {
|
15222 | return null;
|
15223 | }
|
15224 | // convert view engine `ParsedProperty` to a format suitable for IVY
|
15225 | extractAttributes(elementName, properties, i18nPropsMeta) {
|
15226 | const bound = [];
|
15227 | const literal = [];
|
15228 | properties.forEach(prop => {
|
15229 | const i18n = i18nPropsMeta[prop.name];
|
15230 | if (prop.isLiteral) {
|
15231 | literal.push(new TextAttribute(prop.name, prop.expression.source || '', prop.sourceSpan, prop.keySpan, prop.valueSpan, i18n));
|
15232 | }
|
15233 | else {
|
15234 | // Note that validation is skipped and property mapping is disabled
|
15235 | // due to the fact that we need to make sure a given prop is not an
|
15236 | // input of a directive and directive matching happens at runtime.
|
15237 | const bep = this.bindingParser.createBoundElementProperty(elementName, prop, /* skipValidation */ true, /* mapPropertyName */ false);
|
15238 | bound.push(BoundAttribute.fromBoundElementProperty(bep, i18n));
|
15239 | }
|
15240 | });
|
15241 | return { bound, literal };
|
15242 | }
|
15243 | parseAttribute(isTemplateElement, attribute, matchableAttributes, parsedProperties, boundEvents, variables, references) {
|
15244 | const name = normalizeAttributeName(attribute.name);
|
15245 | const value = attribute.value;
|
15246 | const srcSpan = attribute.sourceSpan;
|
15247 | const absoluteOffset = attribute.valueSpan ? attribute.valueSpan.start.offset : srcSpan.start.offset;
|
15248 | function createKeySpan(srcSpan, prefix, identifier) {
|
15249 | // We need to adjust the start location for the keySpan to account for the removed 'data-'
|
15250 | // prefix from `normalizeAttributeName`.
|
15251 | const normalizationAdjustment = attribute.name.length - name.length;
|
15252 | const keySpanStart = srcSpan.start.moveBy(prefix.length + normalizationAdjustment);
|
15253 | const keySpanEnd = keySpanStart.moveBy(identifier.length);
|
15254 | return new ParseSourceSpan(keySpanStart, keySpanEnd, keySpanStart, identifier);
|
15255 | }
|
15256 | const bindParts = name.match(BIND_NAME_REGEXP$1);
|
15257 | if (bindParts) {
|
15258 | if (bindParts[KW_BIND_IDX$1] != null) {
|
15259 | const identifier = bindParts[IDENT_KW_IDX$1];
|
15260 | const keySpan = createKeySpan(srcSpan, bindParts[KW_BIND_IDX$1], identifier);
|
15261 | this.bindingParser.parsePropertyBinding(identifier, value, false, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
|
15262 | }
|
15263 | else if (bindParts[KW_LET_IDX$1]) {
|
15264 | if (isTemplateElement) {
|
15265 | const identifier = bindParts[IDENT_KW_IDX$1];
|
15266 | const keySpan = createKeySpan(srcSpan, bindParts[KW_LET_IDX$1], identifier);
|
15267 | this.parseVariable(identifier, value, srcSpan, keySpan, attribute.valueSpan, variables);
|
15268 | }
|
15269 | else {
|
15270 | this.reportError(`"let-" is only supported on ng-template elements.`, srcSpan);
|
15271 | }
|
15272 | }
|
15273 | else if (bindParts[KW_REF_IDX$1]) {
|
15274 | const identifier = bindParts[IDENT_KW_IDX$1];
|
15275 | const keySpan = createKeySpan(srcSpan, bindParts[KW_REF_IDX$1], identifier);
|
15276 | this.parseReference(identifier, value, srcSpan, keySpan, attribute.valueSpan, references);
|
15277 | }
|
15278 | else if (bindParts[KW_ON_IDX$1]) {
|
15279 | const events = [];
|
15280 | const identifier = bindParts[IDENT_KW_IDX$1];
|
15281 | const keySpan = createKeySpan(srcSpan, bindParts[KW_ON_IDX$1], identifier);
|
15282 | this.bindingParser.parseEvent(identifier, value, srcSpan, attribute.valueSpan || srcSpan, matchableAttributes, events, keySpan);
|
15283 | addEvents(events, boundEvents);
|
15284 | }
|
15285 | else if (bindParts[KW_BINDON_IDX$1]) {
|
15286 | const identifier = bindParts[IDENT_KW_IDX$1];
|
15287 | const keySpan = createKeySpan(srcSpan, bindParts[KW_BINDON_IDX$1], identifier);
|
15288 | this.bindingParser.parsePropertyBinding(identifier, value, false, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
|
15289 | this.parseAssignmentEvent(identifier, value, srcSpan, attribute.valueSpan, matchableAttributes, boundEvents, keySpan);
|
15290 | }
|
15291 | else if (bindParts[KW_AT_IDX$1]) {
|
15292 | const keySpan = createKeySpan(srcSpan, '', name);
|
15293 | this.bindingParser.parseLiteralAttr(name, value, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
|
15294 | }
|
15295 | return true;
|
15296 | }
|
15297 | // We didn't see a kw-prefixed property binding, but we have not yet checked
|
15298 | // for the []/()/[()] syntax.
|
15299 | let delims = null;
|
15300 | if (name.startsWith(BINDING_DELIMS.BANANA_BOX.start)) {
|
15301 | delims = BINDING_DELIMS.BANANA_BOX;
|
15302 | }
|
15303 | else if (name.startsWith(BINDING_DELIMS.PROPERTY.start)) {
|
15304 | delims = BINDING_DELIMS.PROPERTY;
|
15305 | }
|
15306 | else if (name.startsWith(BINDING_DELIMS.EVENT.start)) {
|
15307 | delims = BINDING_DELIMS.EVENT;
|
15308 | }
|
15309 | if (delims !== null &&
|
15310 | // NOTE: older versions of the parser would match a start/end delimited
|
15311 | // binding iff the property name was terminated by the ending delimiter
|
15312 | // and the identifier in the binding was non-empty.
|
15313 | // TODO(ayazhafiz): update this to handle malformed bindings.
|
15314 | name.endsWith(delims.end) && name.length > delims.start.length + delims.end.length) {
|
15315 | const identifier = name.substring(delims.start.length, name.length - delims.end.length);
|
15316 | const keySpan = createKeySpan(srcSpan, delims.start, identifier);
|
15317 | if (delims.start === BINDING_DELIMS.BANANA_BOX.start) {
|
15318 | this.bindingParser.parsePropertyBinding(identifier, value, false, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
|
15319 | this.parseAssignmentEvent(identifier, value, srcSpan, attribute.valueSpan, matchableAttributes, boundEvents, keySpan);
|
15320 | }
|
15321 | else if (delims.start === BINDING_DELIMS.PROPERTY.start) {
|
15322 | this.bindingParser.parsePropertyBinding(identifier, value, false, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
|
15323 | }
|
15324 | else {
|
15325 | const events = [];
|
15326 | this.bindingParser.parseEvent(identifier, value, srcSpan, attribute.valueSpan || srcSpan, matchableAttributes, events, keySpan);
|
15327 | addEvents(events, boundEvents);
|
15328 | }
|
15329 | return true;
|
15330 | }
|
15331 | // No explicit binding found.
|
15332 | const keySpan = createKeySpan(srcSpan, '' /* prefix */, name);
|
15333 | const hasBinding = this.bindingParser.parsePropertyInterpolation(name, value, srcSpan, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
|
15334 | return hasBinding;
|
15335 | }
|
15336 | _visitTextWithInterpolation(value, sourceSpan, i18n) {
|
15337 | const valueNoNgsp = replaceNgsp(value);
|
15338 | const expr = this.bindingParser.parseInterpolation(valueNoNgsp, sourceSpan);
|
15339 | return expr ? new BoundText(expr, sourceSpan, i18n) : new Text(valueNoNgsp, sourceSpan);
|
15340 | }
|
15341 | parseVariable(identifier, value, sourceSpan, keySpan, valueSpan, variables) {
|
15342 | if (identifier.indexOf('-') > -1) {
|
15343 | this.reportError(`"-" is not allowed in variable names`, sourceSpan);
|
15344 | }
|
15345 | else if (identifier.length === 0) {
|
15346 | this.reportError(`Variable does not have a name`, sourceSpan);
|
15347 | }
|
15348 | variables.push(new Variable(identifier, value, sourceSpan, keySpan, valueSpan));
|
15349 | }
|
15350 | parseReference(identifier, value, sourceSpan, keySpan, valueSpan, references) {
|
15351 | if (identifier.indexOf('-') > -1) {
|
15352 | this.reportError(`"-" is not allowed in reference names`, sourceSpan);
|
15353 | }
|
15354 | else if (identifier.length === 0) {
|
15355 | this.reportError(`Reference does not have a name`, sourceSpan);
|
15356 | }
|
15357 | else if (references.some(reference => reference.name === identifier)) {
|
15358 | this.reportError(`Reference "#${identifier}" is defined more than once`, sourceSpan);
|
15359 | }
|
15360 | references.push(new Reference(identifier, value, sourceSpan, keySpan, valueSpan));
|
15361 | }
|
15362 | parseAssignmentEvent(name, expression, sourceSpan, valueSpan, targetMatchableAttrs, boundEvents, keySpan) {
|
15363 | const events = [];
|
15364 | this.bindingParser.parseEvent(`${name}Change`, `${expression}=$event`, sourceSpan, valueSpan || sourceSpan, targetMatchableAttrs, events, keySpan);
|
15365 | addEvents(events, boundEvents);
|
15366 | }
|
15367 | reportError(message, sourceSpan, level = ParseErrorLevel.ERROR) {
|
15368 | this.errors.push(new ParseError(sourceSpan, message, level));
|
15369 | }
|
15370 | }
|
15371 | class NonBindableVisitor$1 {
|
15372 | visitElement(ast) {
|
15373 | const preparsedElement = preparseElement(ast);
|
15374 | if (preparsedElement.type === PreparsedElementType.SCRIPT ||
|
15375 | preparsedElement.type === PreparsedElementType.STYLE ||
|
15376 | preparsedElement.type === PreparsedElementType.STYLESHEET) {
|
15377 | // Skipping <script> for security reasons
|
15378 | // Skipping <style> and stylesheets as we already processed them
|
15379 | // in the StyleCompiler
|
15380 | return null;
|
15381 | }
|
15382 | const children = visitAll$1(this, ast.children, null);
|
15383 | return new Element(ast.name, visitAll$1(this, ast.attrs),
|
15384 | /* inputs */ [], /* outputs */ [], children, /* references */ [], ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan);
|
15385 | }
|
15386 | visitComment(comment) {
|
15387 | return null;
|
15388 | }
|
15389 | visitAttribute(attribute) {
|
15390 | return new TextAttribute(attribute.name, attribute.value, attribute.sourceSpan, attribute.keySpan, attribute.valueSpan, attribute.i18n);
|
15391 | }
|
15392 | visitText(text) {
|
15393 | return new Text(text.value, text.sourceSpan);
|
15394 | }
|
15395 | visitExpansion(expansion) {
|
15396 | return null;
|
15397 | }
|
15398 | visitExpansionCase(expansionCase) {
|
15399 | return null;
|
15400 | }
|
15401 | }
|
15402 | const NON_BINDABLE_VISITOR$1 = new NonBindableVisitor$1();
|
15403 | function normalizeAttributeName(attrName) {
|
15404 | return /^data-/i.test(attrName) ? attrName.substring(5) : attrName;
|
15405 | }
|
15406 | function addEvents(events, boundEvents) {
|
15407 | boundEvents.push(...events.map(e => BoundEvent.fromParsedEvent(e)));
|
15408 | }
|
15409 | function isEmptyTextNode(node) {
|
15410 | return node instanceof Text$2 && node.value.trim().length == 0;
|
15411 | }
|
15412 | function isCommentNode(node) {
|
15413 | return node instanceof Comment;
|
15414 | }
|
15415 | function textContents(node) {
|
15416 | if (node.children.length !== 1 || !(node.children[0] instanceof Text$2)) {
|
15417 | return null;
|
15418 | }
|
15419 | else {
|
15420 | return node.children[0].value;
|
15421 | }
|
15422 | }
|
15423 |
|
15424 | /**
|
15425 | * @license
|
15426 | * Copyright Google LLC All Rights Reserved.
|
15427 | *
|
15428 | * Use of this source code is governed by an MIT-style license that can be
|
15429 | * found in the LICENSE file at https://angular.io/license
|
15430 | */
|
15431 | var TagType;
|
15432 | (function (TagType) {
|
15433 | TagType[TagType["ELEMENT"] = 0] = "ELEMENT";
|
15434 | TagType[TagType["TEMPLATE"] = 1] = "TEMPLATE";
|
15435 | })(TagType || (TagType = {}));
|
15436 | /**
|
15437 | * Generates an object that is used as a shared state between parent and all child contexts.
|
15438 | */
|
15439 | function setupRegistry() {
|
15440 | return { getUniqueId: getSeqNumberGenerator(), icus: new Map() };
|
15441 | }
|
15442 | /**
|
15443 | * I18nContext is a helper class which keeps track of all i18n-related aspects
|
15444 | * (accumulates placeholders, bindings, etc) between i18nStart and i18nEnd instructions.
|
15445 | *
|
15446 | * When we enter a nested template, the top-level context is being passed down
|
15447 | * to the nested component, which uses this context to generate a child instance
|
15448 | * of I18nContext class (to handle nested template) and at the end, reconciles it back
|
15449 | * with the parent context.
|
15450 | *
|
15451 | * @param index Instruction index of i18nStart, which initiates this context
|
15452 | * @param ref Reference to a translation const that represents the content if thus context
|
15453 | * @param level Nestng level defined for child contexts
|
15454 | * @param templateIndex Instruction index of a template which this context belongs to
|
15455 | * @param meta Meta information (id, meaning, description, etc) associated with this context
|
15456 | */
|
15457 | class I18nContext {
|
15458 | constructor(index, ref, level = 0, templateIndex = null, meta, registry) {
|
15459 | this.index = index;
|
15460 | this.ref = ref;
|
15461 | this.level = level;
|
15462 | this.templateIndex = templateIndex;
|
15463 | this.meta = meta;
|
15464 | this.registry = registry;
|
15465 | this.bindings = new Set();
|
15466 | this.placeholders = new Map();
|
15467 | this.isEmitted = false;
|
15468 | this._unresolvedCtxCount = 0;
|
15469 | this._registry = registry || setupRegistry();
|
15470 | this.id = this._registry.getUniqueId();
|
15471 | }
|
15472 | appendTag(type, node, index, closed) {
|
15473 | if (node.isVoid && closed) {
|
15474 | return; // ignore "close" for void tags
|
15475 | }
|
15476 | const ph = node.isVoid || !closed ? node.startName : node.closeName;
|
15477 | const content = { type, index, ctx: this.id, isVoid: node.isVoid, closed };
|
15478 | updatePlaceholderMap(this.placeholders, ph, content);
|
15479 | }
|
15480 | get icus() {
|
15481 | return this._registry.icus;
|
15482 | }
|
15483 | get isRoot() {
|
15484 | return this.level === 0;
|
15485 | }
|
15486 | get isResolved() {
|
15487 | return this._unresolvedCtxCount === 0;
|
15488 | }
|
15489 | getSerializedPlaceholders() {
|
15490 | const result = new Map();
|
15491 | this.placeholders.forEach((values, key) => result.set(key, values.map(serializePlaceholderValue)));
|
15492 | return result;
|
15493 | }
|
15494 | // public API to accumulate i18n-related content
|
15495 | appendBinding(binding) {
|
15496 | this.bindings.add(binding);
|
15497 | }
|
15498 | appendIcu(name, ref) {
|
15499 | updatePlaceholderMap(this._registry.icus, name, ref);
|
15500 | }
|
15501 | appendBoundText(node) {
|
15502 | const phs = assembleBoundTextPlaceholders(node, this.bindings.size, this.id);
|
15503 | phs.forEach((values, key) => updatePlaceholderMap(this.placeholders, key, ...values));
|
15504 | }
|
15505 | appendTemplate(node, index) {
|
15506 | // add open and close tags at the same time,
|
15507 | // since we process nested templates separately
|
15508 | this.appendTag(TagType.TEMPLATE, node, index, false);
|
15509 | this.appendTag(TagType.TEMPLATE, node, index, true);
|
15510 | this._unresolvedCtxCount++;
|
15511 | }
|
15512 | appendElement(node, index, closed) {
|
15513 | this.appendTag(TagType.ELEMENT, node, index, closed);
|
15514 | }
|
15515 | appendProjection(node, index) {
|
15516 | // Add open and close tags at the same time, since `<ng-content>` has no content,
|
15517 | // so when we come across `<ng-content>` we can register both open and close tags.
|
15518 | // Note: runtime i18n logic doesn't distinguish `<ng-content>` tag placeholders and
|
15519 | // regular element tag placeholders, so we generate element placeholders for both types.
|
15520 | this.appendTag(TagType.ELEMENT, node, index, false);
|
15521 | this.appendTag(TagType.ELEMENT, node, index, true);
|
15522 | }
|
15523 | /**
|
15524 | * Generates an instance of a child context based on the root one,
|
15525 | * when we enter a nested template within I18n section.
|
15526 | *
|
15527 | * @param index Instruction index of corresponding i18nStart, which initiates this context
|
15528 | * @param templateIndex Instruction index of a template which this context belongs to
|
15529 | * @param meta Meta information (id, meaning, description, etc) associated with this context
|
15530 | *
|
15531 | * @returns I18nContext instance
|
15532 | */
|
15533 | forkChildContext(index, templateIndex, meta) {
|
15534 | return new I18nContext(index, this.ref, this.level + 1, templateIndex, meta, this._registry);
|
15535 | }
|
15536 | /**
|
15537 | * Reconciles child context into parent one once the end of the i18n block is reached (i18nEnd).
|
15538 | *
|
15539 | * @param context Child I18nContext instance to be reconciled with parent context.
|
15540 | */
|
15541 | reconcileChildContext(context) {
|
15542 | // set the right context id for open and close
|
15543 | // template tags, so we can use it as sub-block ids
|
15544 | ['start', 'close'].forEach((op) => {
|
15545 | const key = context.meta[`${op}Name`];
|
15546 | const phs = this.placeholders.get(key) || [];
|
15547 | const tag = phs.find(findTemplateFn(this.id, context.templateIndex));
|
15548 | if (tag) {
|
15549 | tag.ctx = context.id;
|
15550 | }
|
15551 | });
|
15552 | // reconcile placeholders
|
15553 | const childPhs = context.placeholders;
|
15554 | childPhs.forEach((values, key) => {
|
15555 | const phs = this.placeholders.get(key);
|
15556 | if (!phs) {
|
15557 | this.placeholders.set(key, values);
|
15558 | return;
|
15559 | }
|
15560 | // try to find matching template...
|
15561 | const tmplIdx = phs.findIndex(findTemplateFn(context.id, context.templateIndex));
|
15562 | if (tmplIdx >= 0) {
|
15563 | // ... if found - replace it with nested template content
|
15564 | const isCloseTag = key.startsWith('CLOSE');
|
15565 | const isTemplateTag = key.endsWith('NG-TEMPLATE');
|
15566 | if (isTemplateTag) {
|
15567 | // current template's content is placed before or after
|
15568 | // parent template tag, depending on the open/close atrribute
|
15569 | phs.splice(tmplIdx + (isCloseTag ? 0 : 1), 0, ...values);
|
15570 | }
|
15571 | else {
|
15572 | const idx = isCloseTag ? values.length - 1 : 0;
|
15573 | values[idx].tmpl = phs[tmplIdx];
|
15574 | phs.splice(tmplIdx, 1, ...values);
|
15575 | }
|
15576 | }
|
15577 | else {
|
15578 | // ... otherwise just append content to placeholder value
|
15579 | phs.push(...values);
|
15580 | }
|
15581 | this.placeholders.set(key, phs);
|
15582 | });
|
15583 | this._unresolvedCtxCount--;
|
15584 | }
|
15585 | }
|
15586 | //
|
15587 | // Helper methods
|
15588 | //
|
15589 | function wrap(symbol, index, contextId, closed) {
|
15590 | const state = closed ? '/' : '';
|
15591 | return wrapI18nPlaceholder(`${state}${symbol}${index}`, contextId);
|
15592 | }
|
15593 | function wrapTag(symbol, { index, ctx, isVoid }, closed) {
|
15594 | return isVoid ? wrap(symbol, index, ctx) + wrap(symbol, index, ctx, true) :
|
15595 | wrap(symbol, index, ctx, closed);
|
15596 | }
|
15597 | function findTemplateFn(ctx, templateIndex) {
|
15598 | return (token) => typeof token === 'object' && token.type === TagType.TEMPLATE &&
|
15599 | token.index === templateIndex && token.ctx === ctx;
|
15600 | }
|
15601 | function serializePlaceholderValue(value) {
|
15602 | const element = (data, closed) => wrapTag('#', data, closed);
|
15603 | const template = (data, closed) => wrapTag('*', data, closed);
|
15604 | switch (value.type) {
|
15605 | case TagType.ELEMENT:
|
15606 | // close element tag
|
15607 | if (value.closed) {
|
15608 | return element(value, true) + (value.tmpl ? template(value.tmpl, true) : '');
|
15609 | }
|
15610 | // open element tag that also initiates a template
|
15611 | if (value.tmpl) {
|
15612 | return template(value.tmpl) + element(value) +
|
15613 | (value.isVoid ? template(value.tmpl, true) : '');
|
15614 | }
|
15615 | return element(value);
|
15616 | case TagType.TEMPLATE:
|
15617 | return template(value, value.closed);
|
15618 | default:
|
15619 | return value;
|
15620 | }
|
15621 | }
|
15622 |
|
15623 | /**
|
15624 | * @license
|
15625 | * Copyright Google LLC All Rights Reserved.
|
15626 | *
|
15627 | * Use of this source code is governed by an MIT-style license that can be
|
15628 | * found in the LICENSE file at https://angular.io/license
|
15629 | */
|
15630 | class IcuSerializerVisitor {
|
15631 | visitText(text) {
|
15632 | return text.value;
|
15633 | }
|
15634 | visitContainer(container) {
|
15635 | return container.children.map(child => child.visit(this)).join('');
|
15636 | }
|
15637 | visitIcu(icu) {
|
15638 | const strCases = Object.keys(icu.cases).map((k) => `${k} {${icu.cases[k].visit(this)}}`);
|
15639 | const result = `{${icu.expressionPlaceholder}, ${icu.type}, ${strCases.join(' ')}}`;
|
15640 | return result;
|
15641 | }
|
15642 | visitTagPlaceholder(ph) {
|
15643 | return ph.isVoid ?
|
15644 | this.formatPh(ph.startName) :
|
15645 | `${this.formatPh(ph.startName)}${ph.children.map(child => child.visit(this)).join('')}${this.formatPh(ph.closeName)}`;
|
15646 | }
|
15647 | visitPlaceholder(ph) {
|
15648 | return this.formatPh(ph.name);
|
15649 | }
|
15650 | visitIcuPlaceholder(ph, context) {
|
15651 | return this.formatPh(ph.name);
|
15652 | }
|
15653 | formatPh(value) {
|
15654 | return `{${formatI18nPlaceholderName(value, /* useCamelCase */ false)}}`;
|
15655 | }
|
15656 | }
|
15657 | const serializer = new IcuSerializerVisitor();
|
15658 | function serializeIcuNode(icu) {
|
15659 | return icu.visit(serializer);
|
15660 | }
|
15661 |
|
15662 | /**
|
15663 | * @license
|
15664 | * Copyright Google LLC All Rights Reserved.
|
15665 | *
|
15666 | * Use of this source code is governed by an MIT-style license that can be
|
15667 | * found in the LICENSE file at https://angular.io/license
|
15668 | */
|
15669 | const TAG_TO_PLACEHOLDER_NAMES = {
|
15670 | 'A': 'LINK',
|
15671 | 'B': 'BOLD_TEXT',
|
15672 | 'BR': 'LINE_BREAK',
|
15673 | 'EM': 'EMPHASISED_TEXT',
|
15674 | 'H1': 'HEADING_LEVEL1',
|
15675 | 'H2': 'HEADING_LEVEL2',
|
15676 | 'H3': 'HEADING_LEVEL3',
|
15677 | 'H4': 'HEADING_LEVEL4',
|
15678 | 'H5': 'HEADING_LEVEL5',
|
15679 | 'H6': 'HEADING_LEVEL6',
|
15680 | 'HR': 'HORIZONTAL_RULE',
|
15681 | 'I': 'ITALIC_TEXT',
|
15682 | 'LI': 'LIST_ITEM',
|
15683 | 'LINK': 'MEDIA_LINK',
|
15684 | 'OL': 'ORDERED_LIST',
|
15685 | 'P': 'PARAGRAPH',
|
15686 | 'Q': 'QUOTATION',
|
15687 | 'S': 'STRIKETHROUGH_TEXT',
|
15688 | 'SMALL': 'SMALL_TEXT',
|
15689 | 'SUB': 'SUBSTRIPT',
|
15690 | 'SUP': 'SUPERSCRIPT',
|
15691 | 'TBODY': 'TABLE_BODY',
|
15692 | 'TD': 'TABLE_CELL',
|
15693 | 'TFOOT': 'TABLE_FOOTER',
|
15694 | 'TH': 'TABLE_HEADER_CELL',
|
15695 | 'THEAD': 'TABLE_HEADER',
|
15696 | 'TR': 'TABLE_ROW',
|
15697 | 'TT': 'MONOSPACED_TEXT',
|
15698 | 'U': 'UNDERLINED_TEXT',
|
15699 | 'UL': 'UNORDERED_LIST',
|
15700 | };
|
15701 | /**
|
15702 | * Creates unique names for placeholder with different content.
|
15703 | *
|
15704 | * Returns the same placeholder name when the content is identical.
|
15705 | */
|
15706 | class PlaceholderRegistry {
|
15707 | constructor() {
|
15708 | // Count the occurrence of the base name top generate a unique name
|
15709 | this._placeHolderNameCounts = {};
|
15710 | // Maps signature to placeholder names
|
15711 | this._signatureToName = {};
|
15712 | }
|
15713 | getStartTagPlaceholderName(tag, attrs, isVoid) {
|
15714 | const signature = this._hashTag(tag, attrs, isVoid);
|
15715 | if (this._signatureToName[signature]) {
|
15716 | return this._signatureToName[signature];
|
15717 | }
|
15718 | const upperTag = tag.toUpperCase();
|
15719 | const baseName = TAG_TO_PLACEHOLDER_NAMES[upperTag] || `TAG_${upperTag}`;
|
15720 | const name = this._generateUniqueName(isVoid ? baseName : `START_${baseName}`);
|
15721 | this._signatureToName[signature] = name;
|
15722 | return name;
|
15723 | }
|
15724 | getCloseTagPlaceholderName(tag) {
|
15725 | const signature = this._hashClosingTag(tag);
|
15726 | if (this._signatureToName[signature]) {
|
15727 | return this._signatureToName[signature];
|
15728 | }
|
15729 | const upperTag = tag.toUpperCase();
|
15730 | const baseName = TAG_TO_PLACEHOLDER_NAMES[upperTag] || `TAG_${upperTag}`;
|
15731 | const name = this._generateUniqueName(`CLOSE_${baseName}`);
|
15732 | this._signatureToName[signature] = name;
|
15733 | return name;
|
15734 | }
|
15735 | getPlaceholderName(name, content) {
|
15736 | const upperName = name.toUpperCase();
|
15737 | const signature = `PH: ${upperName}=${content}`;
|
15738 | if (this._signatureToName[signature]) {
|
15739 | return this._signatureToName[signature];
|
15740 | }
|
15741 | const uniqueName = this._generateUniqueName(upperName);
|
15742 | this._signatureToName[signature] = uniqueName;
|
15743 | return uniqueName;
|
15744 | }
|
15745 | getUniquePlaceholder(name) {
|
15746 | return this._generateUniqueName(name.toUpperCase());
|
15747 | }
|
15748 | // Generate a hash for a tag - does not take attribute order into account
|
15749 | _hashTag(tag, attrs, isVoid) {
|
15750 | const start = `<${tag}`;
|
15751 | const strAttrs = Object.keys(attrs).sort().map((name) => ` ${name}=${attrs[name]}`).join('');
|
15752 | const end = isVoid ? '/>' : `></${tag}>`;
|
15753 | return start + strAttrs + end;
|
15754 | }
|
15755 | _hashClosingTag(tag) {
|
15756 | return this._hashTag(`/${tag}`, {}, false);
|
15757 | }
|
15758 | _generateUniqueName(base) {
|
15759 | const seen = this._placeHolderNameCounts.hasOwnProperty(base);
|
15760 | if (!seen) {
|
15761 | this._placeHolderNameCounts[base] = 1;
|
15762 | return base;
|
15763 | }
|
15764 | const id = this._placeHolderNameCounts[base];
|
15765 | this._placeHolderNameCounts[base] = id + 1;
|
15766 | return `${base}_${id}`;
|
15767 | }
|
15768 | }
|
15769 |
|
15770 | /**
|
15771 | * @license
|
15772 | * Copyright Google LLC All Rights Reserved.
|
15773 | *
|
15774 | * Use of this source code is governed by an MIT-style license that can be
|
15775 | * found in the LICENSE file at https://angular.io/license
|
15776 | */
|
15777 | const _expParser = new Parser$1(new Lexer());
|
15778 | /**
|
15779 | * Returns a function converting html nodes to an i18n Message given an interpolationConfig
|
15780 | */
|
15781 | function createI18nMessageFactory(interpolationConfig) {
|
15782 | const visitor = new _I18nVisitor(_expParser, interpolationConfig);
|
15783 | return (nodes, meaning, description, customId, visitNodeFn) => visitor.toI18nMessage(nodes, meaning, description, customId, visitNodeFn);
|
15784 | }
|
15785 | function noopVisitNodeFn(_html, i18n) {
|
15786 | return i18n;
|
15787 | }
|
15788 | class _I18nVisitor {
|
15789 | constructor(_expressionParser, _interpolationConfig) {
|
15790 | this._expressionParser = _expressionParser;
|
15791 | this._interpolationConfig = _interpolationConfig;
|
15792 | }
|
15793 | toI18nMessage(nodes, meaning = '', description = '', customId = '', visitNodeFn) {
|
15794 | const context = {
|
15795 | isIcu: nodes.length == 1 && nodes[0] instanceof Expansion,
|
15796 | icuDepth: 0,
|
15797 | placeholderRegistry: new PlaceholderRegistry(),
|
15798 | placeholderToContent: {},
|
15799 | placeholderToMessage: {},
|
15800 | visitNodeFn: visitNodeFn || noopVisitNodeFn,
|
15801 | };
|
15802 | const i18nodes = visitAll$1(this, nodes, context);
|
15803 | return new Message(i18nodes, context.placeholderToContent, context.placeholderToMessage, meaning, description, customId);
|
15804 | }
|
15805 | visitElement(el, context) {
|
15806 | var _a;
|
15807 | const children = visitAll$1(this, el.children, context);
|
15808 | const attrs = {};
|
15809 | el.attrs.forEach(attr => {
|
15810 | // Do not visit the attributes, translatable ones are top-level ASTs
|
15811 | attrs[attr.name] = attr.value;
|
15812 | });
|
15813 | const isVoid = getHtmlTagDefinition(el.name).isVoid;
|
15814 | const startPhName = context.placeholderRegistry.getStartTagPlaceholderName(el.name, attrs, isVoid);
|
15815 | context.placeholderToContent[startPhName] = {
|
15816 | text: el.startSourceSpan.toString(),
|
15817 | sourceSpan: el.startSourceSpan,
|
15818 | };
|
15819 | let closePhName = '';
|
15820 | if (!isVoid) {
|
15821 | closePhName = context.placeholderRegistry.getCloseTagPlaceholderName(el.name);
|
15822 | context.placeholderToContent[closePhName] = {
|
15823 | text: `</${el.name}>`,
|
15824 | sourceSpan: (_a = el.endSourceSpan) !== null && _a !== void 0 ? _a : el.sourceSpan,
|
15825 | };
|
15826 | }
|
15827 | const node = new TagPlaceholder(el.name, attrs, startPhName, closePhName, children, isVoid, el.sourceSpan, el.startSourceSpan, el.endSourceSpan);
|
15828 | return context.visitNodeFn(el, node);
|
15829 | }
|
15830 | visitAttribute(attribute, context) {
|
15831 | const node = this._visitTextWithInterpolation(attribute.value, attribute.valueSpan || attribute.sourceSpan, context, attribute.i18n);
|
15832 | return context.visitNodeFn(attribute, node);
|
15833 | }
|
15834 | visitText(text, context) {
|
15835 | const node = this._visitTextWithInterpolation(text.value, text.sourceSpan, context, text.i18n);
|
15836 | return context.visitNodeFn(text, node);
|
15837 | }
|
15838 | visitComment(comment, context) {
|
15839 | return null;
|
15840 | }
|
15841 | visitExpansion(icu, context) {
|
15842 | context.icuDepth++;
|
15843 | const i18nIcuCases = {};
|
15844 | const i18nIcu = new Icu$1(icu.switchValue, icu.type, i18nIcuCases, icu.sourceSpan);
|
15845 | icu.cases.forEach((caze) => {
|
15846 | i18nIcuCases[caze.value] = new Container(caze.expression.map((node) => node.visit(this, context)), caze.expSourceSpan);
|
15847 | });
|
15848 | context.icuDepth--;
|
15849 | if (context.isIcu || context.icuDepth > 0) {
|
15850 | // Returns an ICU node when:
|
15851 | // - the message (vs a part of the message) is an ICU message, or
|
15852 | // - the ICU message is nested.
|
15853 | const expPh = context.placeholderRegistry.getUniquePlaceholder(`VAR_${icu.type}`);
|
15854 | i18nIcu.expressionPlaceholder = expPh;
|
15855 | context.placeholderToContent[expPh] = {
|
15856 | text: icu.switchValue,
|
15857 | sourceSpan: icu.switchValueSourceSpan,
|
15858 | };
|
15859 | return context.visitNodeFn(icu, i18nIcu);
|
15860 | }
|
15861 | // Else returns a placeholder
|
15862 | // ICU placeholders should not be replaced with their original content but with the their
|
15863 | // translations.
|
15864 | // TODO(vicb): add a html.Node -> i18n.Message cache to avoid having to re-create the msg
|
15865 | const phName = context.placeholderRegistry.getPlaceholderName('ICU', icu.sourceSpan.toString());
|
15866 | context.placeholderToMessage[phName] = this.toI18nMessage([icu], '', '', '', undefined);
|
15867 | const node = new IcuPlaceholder(i18nIcu, phName, icu.sourceSpan);
|
15868 | return context.visitNodeFn(icu, node);
|
15869 | }
|
15870 | visitExpansionCase(_icuCase, _context) {
|
15871 | throw new Error('Unreachable code');
|
15872 | }
|
15873 | /**
|
15874 | * Split the, potentially interpolated, text up into text and placeholder pieces.
|
15875 | *
|
15876 | * @param text The potentially interpolated string to be split.
|
15877 | * @param sourceSpan The span of the whole of the `text` string.
|
15878 | * @param context The current context of the visitor, used to compute and store placeholders.
|
15879 | * @param previousI18n Any i18n metadata associated with this `text` from a previous pass.
|
15880 | */
|
15881 | _visitTextWithInterpolation(text, sourceSpan, context, previousI18n) {
|
15882 | const { strings, expressions } = this._expressionParser.splitInterpolation(text, sourceSpan.start.toString(), this._interpolationConfig);
|
15883 | // No expressions, return a single text.
|
15884 | if (expressions.length === 0) {
|
15885 | return new Text$1(text, sourceSpan);
|
15886 | }
|
15887 | // Return a sequence of `Text` and `Placeholder` nodes grouped in a `Container`.
|
15888 | const nodes = [];
|
15889 | for (let i = 0; i < strings.length - 1; i++) {
|
15890 | this._addText(nodes, strings[i], sourceSpan);
|
15891 | this._addPlaceholder(nodes, context, expressions[i], sourceSpan);
|
15892 | }
|
15893 | // The last index contains no expression
|
15894 | this._addText(nodes, strings[strings.length - 1], sourceSpan);
|
15895 | // Whitespace removal may have invalidated the interpolation source-spans.
|
15896 | reusePreviousSourceSpans(nodes, previousI18n);
|
15897 | return new Container(nodes, sourceSpan);
|
15898 | }
|
15899 | /**
|
15900 | * Create a new `Text` node from the `textPiece` and add it to the `nodes` collection.
|
15901 | *
|
15902 | * @param nodes The nodes to which the created `Text` node should be added.
|
15903 | * @param textPiece The text and relative span information for this `Text` node.
|
15904 | * @param interpolationSpan The span of the whole interpolated text.
|
15905 | */
|
15906 | _addText(nodes, textPiece, interpolationSpan) {
|
15907 | if (textPiece.text.length > 0) {
|
15908 | // No need to add empty strings
|
15909 | const stringSpan = getOffsetSourceSpan(interpolationSpan, textPiece);
|
15910 | nodes.push(new Text$1(textPiece.text, stringSpan));
|
15911 | }
|
15912 | }
|
15913 | /**
|
15914 | * Create a new `Placeholder` node from the `expression` and add it to the `nodes` collection.
|
15915 | *
|
15916 | * @param nodes The nodes to which the created `Text` node should be added.
|
15917 | * @param context The current context of the visitor, used to compute and store placeholders.
|
15918 | * @param expression The expression text and relative span information for this `Placeholder`
|
15919 | * node.
|
15920 | * @param interpolationSpan The span of the whole interpolated text.
|
15921 | */
|
15922 | _addPlaceholder(nodes, context, expression, interpolationSpan) {
|
15923 | const sourceSpan = getOffsetSourceSpan(interpolationSpan, expression);
|
15924 | const baseName = extractPlaceholderName(expression.text) || 'INTERPOLATION';
|
15925 | const phName = context.placeholderRegistry.getPlaceholderName(baseName, expression.text);
|
15926 | const text = this._interpolationConfig.start + expression.text + this._interpolationConfig.end;
|
15927 | context.placeholderToContent[phName] = { text, sourceSpan };
|
15928 | nodes.push(new Placeholder(expression.text, phName, sourceSpan));
|
15929 | }
|
15930 | }
|
15931 | /**
|
15932 | * Re-use the source-spans from `previousI18n` metadata for the `nodes`.
|
15933 | *
|
15934 | * Whitespace removal can invalidate the source-spans of interpolation nodes, so we
|
15935 | * reuse the source-span stored from a previous pass before the whitespace was removed.
|
15936 | *
|
15937 | * @param nodes The `Text` and `Placeholder` nodes to be processed.
|
15938 | * @param previousI18n Any i18n metadata for these `nodes` stored from a previous pass.
|
15939 | */
|
15940 | function reusePreviousSourceSpans(nodes, previousI18n) {
|
15941 | if (previousI18n instanceof Message) {
|
15942 | // The `previousI18n` is an i18n `Message`, so we are processing an `Attribute` with i18n
|
15943 | // metadata. The `Message` should consist only of a single `Container` that contains the
|
15944 | // parts (`Text` and `Placeholder`) to process.
|
15945 | assertSingleContainerMessage(previousI18n);
|
15946 | previousI18n = previousI18n.nodes[0];
|
15947 | }
|
15948 | if (previousI18n instanceof Container) {
|
15949 | // The `previousI18n` is a `Container`, which means that this is a second i18n extraction pass
|
15950 | // after whitespace has been removed from the AST ndoes.
|
15951 | assertEquivalentNodes(previousI18n.children, nodes);
|
15952 | // Reuse the source-spans from the first pass.
|
15953 | for (let i = 0; i < nodes.length; i++) {
|
15954 | nodes[i].sourceSpan = previousI18n.children[i].sourceSpan;
|
15955 | }
|
15956 | }
|
15957 | }
|
15958 | /**
|
15959 | * Asserts that the `message` contains exactly one `Container` node.
|
15960 | */
|
15961 | function assertSingleContainerMessage(message) {
|
15962 | const nodes = message.nodes;
|
15963 | if (nodes.length !== 1 || !(nodes[0] instanceof Container)) {
|
15964 | throw new Error('Unexpected previous i18n message - expected it to consist of only a single `Container` node.');
|
15965 | }
|
15966 | }
|
15967 | /**
|
15968 | * Asserts that the `previousNodes` and `node` collections have the same number of elements and
|
15969 | * corresponding elements have the same node type.
|
15970 | */
|
15971 | function assertEquivalentNodes(previousNodes, nodes) {
|
15972 | if (previousNodes.length !== nodes.length) {
|
15973 | throw new Error('The number of i18n message children changed between first and second pass.');
|
15974 | }
|
15975 | if (previousNodes.some((node, i) => nodes[i].constructor !== node.constructor)) {
|
15976 | throw new Error('The types of the i18n message children changed between first and second pass.');
|
15977 | }
|
15978 | }
|
15979 | /**
|
15980 | * Create a new `ParseSourceSpan` from the `sourceSpan`, offset by the `start` and `end` values.
|
15981 | */
|
15982 | function getOffsetSourceSpan(sourceSpan, { start, end }) {
|
15983 | return new ParseSourceSpan(sourceSpan.fullStart.moveBy(start), sourceSpan.fullStart.moveBy(end));
|
15984 | }
|
15985 | const _CUSTOM_PH_EXP = /\/\/[\s\S]*i18n[\s\S]*\([\s\S]*ph[\s\S]*=[\s\S]*("|')([\s\S]*?)\1[\s\S]*\)/g;
|
15986 | function extractPlaceholderName(input) {
|
15987 | return input.split(_CUSTOM_PH_EXP)[2];
|
15988 | }
|
15989 |
|
15990 | /**
|
15991 | * @license
|
15992 | * Copyright Google LLC All Rights Reserved.
|
15993 | *
|
15994 | * Use of this source code is governed by an MIT-style license that can be
|
15995 | * found in the LICENSE file at https://angular.io/license
|
15996 | */
|
15997 | /**
|
15998 | * An i18n error.
|
15999 | */
|
16000 | class I18nError extends ParseError {
|
16001 | constructor(span, msg) {
|
16002 | super(span, msg);
|
16003 | }
|
16004 | }
|
16005 |
|
16006 | /**
|
16007 | * @license
|
16008 | * Copyright Google LLC All Rights Reserved.
|
16009 | *
|
16010 | * Use of this source code is governed by an MIT-style license that can be
|
16011 | * found in the LICENSE file at https://angular.io/license
|
16012 | */
|
16013 | const setI18nRefs = (htmlNode, i18nNode) => {
|
16014 | if (htmlNode instanceof NodeWithI18n) {
|
16015 | if (i18nNode instanceof IcuPlaceholder && htmlNode.i18n instanceof Message) {
|
16016 | // This html node represents an ICU but this is a second processing pass, and the legacy id
|
16017 | // was computed in the previous pass and stored in the `i18n` property as a message.
|
16018 | // We are about to wipe out that property so capture the previous message to be reused when
|
16019 | // generating the message for this ICU later. See `_generateI18nMessage()`.
|
16020 | i18nNode.previousMessage = htmlNode.i18n;
|
16021 | }
|
16022 | htmlNode.i18n = i18nNode;
|
16023 | }
|
16024 | return i18nNode;
|
16025 | };
|
16026 | /**
|
16027 | * This visitor walks over HTML parse tree and converts information stored in
|
16028 | * i18n-related attributes ("i18n" and "i18n-*") into i18n meta object that is
|
16029 | * stored with other element's and attribute's information.
|
16030 | */
|
16031 | class I18nMetaVisitor {
|
16032 | constructor(interpolationConfig = DEFAULT_INTERPOLATION_CONFIG, keepI18nAttrs = false, enableI18nLegacyMessageIdFormat = false) {
|
16033 | this.interpolationConfig = interpolationConfig;
|
16034 | this.keepI18nAttrs = keepI18nAttrs;
|
16035 | this.enableI18nLegacyMessageIdFormat = enableI18nLegacyMessageIdFormat;
|
16036 | // whether visited nodes contain i18n information
|
16037 | this.hasI18nMeta = false;
|
16038 | this._errors = [];
|
16039 | // i18n message generation factory
|
16040 | this._createI18nMessage = createI18nMessageFactory(this.interpolationConfig);
|
16041 | }
|
16042 | _generateI18nMessage(nodes, meta = '', visitNodeFn) {
|
16043 | const { meaning, description, customId } = this._parseMetadata(meta);
|
16044 | const message = this._createI18nMessage(nodes, meaning, description, customId, visitNodeFn);
|
16045 | this._setMessageId(message, meta);
|
16046 | this._setLegacyIds(message, meta);
|
16047 | return message;
|
16048 | }
|
16049 | visitAllWithErrors(nodes) {
|
16050 | const result = nodes.map(node => node.visit(this, null));
|
16051 | return new ParseTreeResult(result, this._errors);
|
16052 | }
|
16053 | visitElement(element) {
|
16054 | if (hasI18nAttrs(element)) {
|
16055 | this.hasI18nMeta = true;
|
16056 | const attrs = [];
|
16057 | const attrsMeta = {};
|
16058 | for (const attr of element.attrs) {
|
16059 | if (attr.name === I18N_ATTR) {
|
16060 | // root 'i18n' node attribute
|
16061 | const i18n = element.i18n || attr.value;
|
16062 | const message = this._generateI18nMessage(element.children, i18n, setI18nRefs);
|
16063 | // do not assign empty i18n meta
|
16064 | if (message.nodes.length) {
|
16065 | element.i18n = message;
|
16066 | }
|
16067 | }
|
16068 | else if (attr.name.startsWith(I18N_ATTR_PREFIX)) {
|
16069 | // 'i18n-*' attributes
|
16070 | const name = attr.name.slice(I18N_ATTR_PREFIX.length);
|
16071 | if (isTrustedTypesSink(element.name, name)) {
|
16072 | this._reportError(attr, `Translating attribute '${name}' is disallowed for security reasons.`);
|
16073 | }
|
16074 | else {
|
16075 | attrsMeta[name] = attr.value;
|
16076 | }
|
16077 | }
|
16078 | else {
|
16079 | // non-i18n attributes
|
16080 | attrs.push(attr);
|
16081 | }
|
16082 | }
|
16083 | // set i18n meta for attributes
|
16084 | if (Object.keys(attrsMeta).length) {
|
16085 | for (const attr of attrs) {
|
16086 | const meta = attrsMeta[attr.name];
|
16087 | // do not create translation for empty attributes
|
16088 | if (meta !== undefined && attr.value) {
|
16089 | attr.i18n = this._generateI18nMessage([attr], attr.i18n || meta);
|
16090 | }
|
16091 | }
|
16092 | }
|
16093 | if (!this.keepI18nAttrs) {
|
16094 | // update element's attributes,
|
16095 | // keeping only non-i18n related ones
|
16096 | element.attrs = attrs;
|
16097 | }
|
16098 | }
|
16099 | visitAll$1(this, element.children, element.i18n);
|
16100 | return element;
|
16101 | }
|
16102 | visitExpansion(expansion, currentMessage) {
|
16103 | let message;
|
16104 | const meta = expansion.i18n;
|
16105 | this.hasI18nMeta = true;
|
16106 | if (meta instanceof IcuPlaceholder) {
|
16107 | // set ICU placeholder name (e.g. "ICU_1"),
|
16108 | // generated while processing root element contents,
|
16109 | // so we can reference it when we output translation
|
16110 | const name = meta.name;
|
16111 | message = this._generateI18nMessage([expansion], meta);
|
16112 | const icu = icuFromI18nMessage(message);
|
16113 | icu.name = name;
|
16114 | }
|
16115 | else {
|
16116 | // ICU is a top level message, try to use metadata from container element if provided via
|
16117 | // `context` argument. Note: context may not be available for standalone ICUs (without
|
16118 | // wrapping element), so fallback to ICU metadata in this case.
|
16119 | message = this._generateI18nMessage([expansion], currentMessage || meta);
|
16120 | }
|
16121 | expansion.i18n = message;
|
16122 | return expansion;
|
16123 | }
|
16124 | visitText(text) {
|
16125 | return text;
|
16126 | }
|
16127 | visitAttribute(attribute) {
|
16128 | return attribute;
|
16129 | }
|
16130 | visitComment(comment) {
|
16131 | return comment;
|
16132 | }
|
16133 | visitExpansionCase(expansionCase) {
|
16134 | return expansionCase;
|
16135 | }
|
16136 | /**
|
16137 | * Parse the general form `meta` passed into extract the explicit metadata needed to create a
|
16138 | * `Message`.
|
16139 | *
|
16140 | * There are three possibilities for the `meta` variable
|
16141 | * 1) a string from an `i18n` template attribute: parse it to extract the metadata values.
|
16142 | * 2) a `Message` from a previous processing pass: reuse the metadata values in the message.
|
16143 | * 4) other: ignore this and just process the message metadata as normal
|
16144 | *
|
16145 | * @param meta the bucket that holds information about the message
|
16146 | * @returns the parsed metadata.
|
16147 | */
|
16148 | _parseMetadata(meta) {
|
16149 | return typeof meta === 'string' ? parseI18nMeta(meta) :
|
16150 | meta instanceof Message ? meta : {};
|
16151 | }
|
16152 | /**
|
16153 | * Generate (or restore) message id if not specified already.
|
16154 | */
|
16155 | _setMessageId(message, meta) {
|
16156 | if (!message.id) {
|
16157 | message.id = meta instanceof Message && meta.id || decimalDigest(message);
|
16158 | }
|
16159 | }
|
16160 | /**
|
16161 | * Update the `message` with a `legacyId` if necessary.
|
16162 | *
|
16163 | * @param message the message whose legacy id should be set
|
16164 | * @param meta information about the message being processed
|
16165 | */
|
16166 | _setLegacyIds(message, meta) {
|
16167 | if (this.enableI18nLegacyMessageIdFormat) {
|
16168 | message.legacyIds = [computeDigest(message), computeDecimalDigest(message)];
|
16169 | }
|
16170 | else if (typeof meta !== 'string') {
|
16171 | // This occurs if we are doing the 2nd pass after whitespace removal (see `parseTemplate()` in
|
16172 | // `packages/compiler/src/render3/view/template.ts`).
|
16173 | // In that case we want to reuse the legacy message generated in the 1st pass (see
|
16174 | // `setI18nRefs()`).
|
16175 | const previousMessage = meta instanceof Message ?
|
16176 | meta :
|
16177 | meta instanceof IcuPlaceholder ? meta.previousMessage : undefined;
|
16178 | message.legacyIds = previousMessage ? previousMessage.legacyIds : [];
|
16179 | }
|
16180 | }
|
16181 | _reportError(node, msg) {
|
16182 | this._errors.push(new I18nError(node.sourceSpan, msg));
|
16183 | }
|
16184 | }
|
16185 | /** I18n separators for metadata **/
|
16186 | const I18N_MEANING_SEPARATOR = '|';
|
16187 | const I18N_ID_SEPARATOR = '@@';
|
16188 | /**
|
16189 | * Parses i18n metas like:
|
16190 | * - "@@id",
|
16191 | * - "description[@@id]",
|
16192 | * - "meaning|description[@@id]"
|
16193 | * and returns an object with parsed output.
|
16194 | *
|
16195 | * @param meta String that represents i18n meta
|
16196 | * @returns Object with id, meaning and description fields
|
16197 | */
|
16198 | function parseI18nMeta(meta = '') {
|
16199 | let customId;
|
16200 | let meaning;
|
16201 | let description;
|
16202 | meta = meta.trim();
|
16203 | if (meta) {
|
16204 | const idIndex = meta.indexOf(I18N_ID_SEPARATOR);
|
16205 | const descIndex = meta.indexOf(I18N_MEANING_SEPARATOR);
|
16206 | let meaningAndDesc;
|
16207 | [meaningAndDesc, customId] =
|
16208 | (idIndex > -1) ? [meta.slice(0, idIndex), meta.slice(idIndex + 2)] : [meta, ''];
|
16209 | [meaning, description] = (descIndex > -1) ?
|
16210 | [meaningAndDesc.slice(0, descIndex), meaningAndDesc.slice(descIndex + 1)] :
|
16211 | ['', meaningAndDesc];
|
16212 | }
|
16213 | return { customId, meaning, description };
|
16214 | }
|
16215 | // Converts i18n meta information for a message (id, description, meaning)
|
16216 | // to a JsDoc statement formatted as expected by the Closure compiler.
|
16217 | function i18nMetaToJSDoc(meta) {
|
16218 | const tags = [];
|
16219 | if (meta.description) {
|
16220 | tags.push({ tagName: "desc" /* Desc */, text: meta.description });
|
16221 | }
|
16222 | if (meta.meaning) {
|
16223 | tags.push({ tagName: "meaning" /* Meaning */, text: meta.meaning });
|
16224 | }
|
16225 | return tags.length == 0 ? null : jsDocComment(tags);
|
16226 | }
|
16227 |
|
16228 | /** Closure uses `goog.getMsg(message)` to lookup translations */
|
16229 | const GOOG_GET_MSG = 'goog.getMsg';
|
16230 | function createGoogleGetMsgStatements(variable$1, message, closureVar, params) {
|
16231 | const messageString = serializeI18nMessageForGetMsg(message);
|
16232 | const args = [literal(messageString)];
|
16233 | if (Object.keys(params).length) {
|
16234 | args.push(mapLiteral(params, true));
|
16235 | }
|
16236 | // /**
|
16237 | // * @desc description of message
|
16238 | // * @meaning meaning of message
|
16239 | // */
|
16240 | // const MSG_... = goog.getMsg(..);
|
16241 | // I18N_X = MSG_...;
|
16242 | const googGetMsgStmt = closureVar.set(variable(GOOG_GET_MSG).callFn(args)).toConstDecl();
|
16243 | const metaComment = i18nMetaToJSDoc(message);
|
16244 | if (metaComment !== null) {
|
16245 | googGetMsgStmt.addLeadingComment(metaComment);
|
16246 | }
|
16247 | const i18nAssignmentStmt = new ExpressionStatement(variable$1.set(closureVar));
|
16248 | return [googGetMsgStmt, i18nAssignmentStmt];
|
16249 | }
|
16250 | /**
|
16251 | * This visitor walks over i18n tree and generates its string representation, including ICUs and
|
16252 | * placeholders in `{$placeholder}` (for plain messages) or `{PLACEHOLDER}` (inside ICUs) format.
|
16253 | */
|
16254 | class GetMsgSerializerVisitor {
|
16255 | formatPh(value) {
|
16256 | return `{$${formatI18nPlaceholderName(value)}}`;
|
16257 | }
|
16258 | visitText(text) {
|
16259 | return text.value;
|
16260 | }
|
16261 | visitContainer(container) {
|
16262 | return container.children.map(child => child.visit(this)).join('');
|
16263 | }
|
16264 | visitIcu(icu) {
|
16265 | return serializeIcuNode(icu);
|
16266 | }
|
16267 | visitTagPlaceholder(ph) {
|
16268 | return ph.isVoid ?
|
16269 | this.formatPh(ph.startName) :
|
16270 | `${this.formatPh(ph.startName)}${ph.children.map(child => child.visit(this)).join('')}${this.formatPh(ph.closeName)}`;
|
16271 | }
|
16272 | visitPlaceholder(ph) {
|
16273 | return this.formatPh(ph.name);
|
16274 | }
|
16275 | visitIcuPlaceholder(ph, context) {
|
16276 | return this.formatPh(ph.name);
|
16277 | }
|
16278 | }
|
16279 | const serializerVisitor$1 = new GetMsgSerializerVisitor();
|
16280 | function serializeI18nMessageForGetMsg(message) {
|
16281 | return message.nodes.map(node => node.visit(serializerVisitor$1, null)).join('');
|
16282 | }
|
16283 |
|
16284 | function createLocalizeStatements(variable, message, params) {
|
16285 | const { messageParts, placeHolders } = serializeI18nMessageForLocalize(message);
|
16286 | const sourceSpan = getSourceSpan(message);
|
16287 | const expressions = placeHolders.map(ph => params[ph.text]);
|
16288 | const localizedString$1 = localizedString(message, messageParts, placeHolders, expressions, sourceSpan);
|
16289 | const variableInitialization = variable.set(localizedString$1);
|
16290 | return [new ExpressionStatement(variableInitialization)];
|
16291 | }
|
16292 | /**
|
16293 | * This visitor walks over an i18n tree, capturing literal strings and placeholders.
|
16294 | *
|
16295 | * The result can be used for generating the `$localize` tagged template literals.
|
16296 | */
|
16297 | class LocalizeSerializerVisitor {
|
16298 | visitText(text, context) {
|
16299 | if (context[context.length - 1] instanceof LiteralPiece) {
|
16300 | // Two literal pieces in a row means that there was some comment node in-between.
|
16301 | context[context.length - 1].text += text.value;
|
16302 | }
|
16303 | else {
|
16304 | context.push(new LiteralPiece(text.value, text.sourceSpan));
|
16305 | }
|
16306 | }
|
16307 | visitContainer(container, context) {
|
16308 | container.children.forEach(child => child.visit(this, context));
|
16309 | }
|
16310 | visitIcu(icu, context) {
|
16311 | context.push(new LiteralPiece(serializeIcuNode(icu), icu.sourceSpan));
|
16312 | }
|
16313 | visitTagPlaceholder(ph, context) {
|
16314 | var _a, _b;
|
16315 | context.push(this.createPlaceholderPiece(ph.startName, (_a = ph.startSourceSpan) !== null && _a !== void 0 ? _a : ph.sourceSpan));
|
16316 | if (!ph.isVoid) {
|
16317 | ph.children.forEach(child => child.visit(this, context));
|
16318 | context.push(this.createPlaceholderPiece(ph.closeName, (_b = ph.endSourceSpan) !== null && _b !== void 0 ? _b : ph.sourceSpan));
|
16319 | }
|
16320 | }
|
16321 | visitPlaceholder(ph, context) {
|
16322 | context.push(this.createPlaceholderPiece(ph.name, ph.sourceSpan));
|
16323 | }
|
16324 | visitIcuPlaceholder(ph, context) {
|
16325 | context.push(this.createPlaceholderPiece(ph.name, ph.sourceSpan));
|
16326 | }
|
16327 | createPlaceholderPiece(name, sourceSpan) {
|
16328 | return new PlaceholderPiece(formatI18nPlaceholderName(name, /* useCamelCase */ false), sourceSpan);
|
16329 | }
|
16330 | }
|
16331 | const serializerVisitor$2 = new LocalizeSerializerVisitor();
|
16332 | /**
|
16333 | * Serialize an i18n message into two arrays: messageParts and placeholders.
|
16334 | *
|
16335 | * These arrays will be used to generate `$localize` tagged template literals.
|
16336 | *
|
16337 | * @param message The message to be serialized.
|
16338 | * @returns an object containing the messageParts and placeholders.
|
16339 | */
|
16340 | function serializeI18nMessageForLocalize(message) {
|
16341 | const pieces = [];
|
16342 | message.nodes.forEach(node => node.visit(serializerVisitor$2, pieces));
|
16343 | return processMessagePieces(pieces);
|
16344 | }
|
16345 | function getSourceSpan(message) {
|
16346 | const startNode = message.nodes[0];
|
16347 | const endNode = message.nodes[message.nodes.length - 1];
|
16348 | return new ParseSourceSpan(startNode.sourceSpan.start, endNode.sourceSpan.end, startNode.sourceSpan.fullStart, startNode.sourceSpan.details);
|
16349 | }
|
16350 | /**
|
16351 | * Convert the list of serialized MessagePieces into two arrays.
|
16352 | *
|
16353 | * One contains the literal string pieces and the other the placeholders that will be replaced by
|
16354 | * expressions when rendering `$localize` tagged template literals.
|
16355 | *
|
16356 | * @param pieces The pieces to process.
|
16357 | * @returns an object containing the messageParts and placeholders.
|
16358 | */
|
16359 | function processMessagePieces(pieces) {
|
16360 | const messageParts = [];
|
16361 | const placeHolders = [];
|
16362 | if (pieces[0] instanceof PlaceholderPiece) {
|
16363 | // The first piece was a placeholder so we need to add an initial empty message part.
|
16364 | messageParts.push(createEmptyMessagePart(pieces[0].sourceSpan.start));
|
16365 | }
|
16366 | for (let i = 0; i < pieces.length; i++) {
|
16367 | const part = pieces[i];
|
16368 | if (part instanceof LiteralPiece) {
|
16369 | messageParts.push(part);
|
16370 | }
|
16371 | else {
|
16372 | placeHolders.push(part);
|
16373 | if (pieces[i - 1] instanceof PlaceholderPiece) {
|
16374 | // There were two placeholders in a row, so we need to add an empty message part.
|
16375 | messageParts.push(createEmptyMessagePart(pieces[i - 1].sourceSpan.end));
|
16376 | }
|
16377 | }
|
16378 | }
|
16379 | if (pieces[pieces.length - 1] instanceof PlaceholderPiece) {
|
16380 | // The last piece was a placeholder so we need to add a final empty message part.
|
16381 | messageParts.push(createEmptyMessagePart(pieces[pieces.length - 1].sourceSpan.end));
|
16382 | }
|
16383 | return { messageParts, placeHolders };
|
16384 | }
|
16385 | function createEmptyMessagePart(location) {
|
16386 | return new LiteralPiece('', new ParseSourceSpan(location, location));
|
16387 | }
|
16388 |
|
16389 | /**
|
16390 | * @license
|
16391 | * Copyright Google LLC All Rights Reserved.
|
16392 | *
|
16393 | * Use of this source code is governed by an MIT-style license that can be
|
16394 | * found in the LICENSE file at https://angular.io/license
|
16395 | */
|
16396 | // Selector attribute name of `<ng-content>`
|
16397 | const NG_CONTENT_SELECT_ATTR$1 = 'select';
|
16398 | // Attribute name of `ngProjectAs`.
|
16399 | const NG_PROJECT_AS_ATTR_NAME = 'ngProjectAs';
|
16400 | // Global symbols available only inside event bindings.
|
16401 | const EVENT_BINDING_SCOPE_GLOBALS = new Set(['$event']);
|
16402 | // List of supported global targets for event listeners
|
16403 | const GLOBAL_TARGET_RESOLVERS = new Map([['window', Identifiers$1.resolveWindow], ['document', Identifiers$1.resolveDocument], ['body', Identifiers$1.resolveBody]]);
|
16404 | const LEADING_TRIVIA_CHARS = [' ', '\n', '\r', '\t'];
|
16405 | // if (rf & flags) { .. }
|
16406 | function renderFlagCheckIfStmt(flags, statements) {
|
16407 | return ifStmt(variable(RENDER_FLAGS).bitwiseAnd(literal(flags), null, false), statements);
|
16408 | }
|
16409 | function prepareEventListenerParameters(eventAst, handlerName = null, scope = null) {
|
16410 | const { type, name, target, phase, handler } = eventAst;
|
16411 | if (target && !GLOBAL_TARGET_RESOLVERS.has(target)) {
|
16412 | throw new Error(`Unexpected global target '${target}' defined for '${name}' event.
|
16413 | Supported list of global targets: ${Array.from(GLOBAL_TARGET_RESOLVERS.keys())}.`);
|
16414 | }
|
16415 | const eventArgumentName = '$event';
|
16416 | const implicitReceiverAccesses = new Set();
|
16417 | const implicitReceiverExpr = (scope === null || scope.bindingLevel === 0) ?
|
16418 | variable(CONTEXT_NAME) :
|
16419 | scope.getOrCreateSharedContextVar(0);
|
16420 | const bindingExpr = convertActionBinding(scope, implicitReceiverExpr, handler, 'b', () => error('Unexpected interpolation'), eventAst.handlerSpan, implicitReceiverAccesses, EVENT_BINDING_SCOPE_GLOBALS);
|
16421 | const statements = [];
|
16422 | if (scope) {
|
16423 | statements.push(...scope.restoreViewStatement());
|
16424 | statements.push(...scope.variableDeclarations());
|
16425 | }
|
16426 | statements.push(...bindingExpr.render3Stmts);
|
16427 | const eventName = type === 1 /* Animation */ ? prepareSyntheticListenerName(name, phase) : name;
|
16428 | const fnName = handlerName && sanitizeIdentifier(handlerName);
|
16429 | const fnArgs = [];
|
16430 | if (implicitReceiverAccesses.has(eventArgumentName)) {
|
16431 | fnArgs.push(new FnParam(eventArgumentName, DYNAMIC_TYPE));
|
16432 | }
|
16433 | const handlerFn = fn(fnArgs, statements, INFERRED_TYPE, null, fnName);
|
16434 | const params = [literal(eventName), handlerFn];
|
16435 | if (target) {
|
16436 | params.push(literal(false), // `useCapture` flag, defaults to `false`
|
16437 | importExpr(GLOBAL_TARGET_RESOLVERS.get(target)));
|
16438 | }
|
16439 | return params;
|
16440 | }
|
16441 | function createComponentDefConsts() {
|
16442 | return {
|
16443 | prepareStatements: [],
|
16444 | constExpressions: [],
|
16445 | i18nVarRefsCache: new Map(),
|
16446 | };
|
16447 | }
|
16448 | class TemplateDefinitionBuilder {
|
16449 | constructor(constantPool, parentBindingScope, level = 0, contextName, i18nContext, templateIndex, templateName, directiveMatcher, directives, pipeTypeByName, pipes, _namespace, relativeContextFilePath, i18nUseExternalIds, _constants = createComponentDefConsts()) {
|
16450 | this.constantPool = constantPool;
|
16451 | this.level = level;
|
16452 | this.contextName = contextName;
|
16453 | this.i18nContext = i18nContext;
|
16454 | this.templateIndex = templateIndex;
|
16455 | this.templateName = templateName;
|
16456 | this.directiveMatcher = directiveMatcher;
|
16457 | this.directives = directives;
|
16458 | this.pipeTypeByName = pipeTypeByName;
|
16459 | this.pipes = pipes;
|
16460 | this._namespace = _namespace;
|
16461 | this.i18nUseExternalIds = i18nUseExternalIds;
|
16462 | this._constants = _constants;
|
16463 | this._dataIndex = 0;
|
16464 | this._bindingContext = 0;
|
16465 | this._prefixCode = [];
|
16466 | /**
|
16467 | * List of callbacks to generate creation mode instructions. We store them here as we process
|
16468 | * the template so bindings in listeners are resolved only once all nodes have been visited.
|
16469 | * This ensures all local refs and context variables are available for matching.
|
16470 | */
|
16471 | this._creationCodeFns = [];
|
16472 | /**
|
16473 | * List of callbacks to generate update mode instructions. We store them here as we process
|
16474 | * the template so bindings are resolved only once all nodes have been visited. This ensures
|
16475 | * all local refs and context variables are available for matching.
|
16476 | */
|
16477 | this._updateCodeFns = [];
|
16478 | /** Index of the currently-selected node. */
|
16479 | this._currentIndex = 0;
|
16480 | /** Temporary variable declarations generated from visiting pipes, literals, etc. */
|
16481 | this._tempVariables = [];
|
16482 | /**
|
16483 | * List of callbacks to build nested templates. Nested templates must not be visited until
|
16484 | * after the parent template has finished visiting all of its nodes. This ensures that all
|
16485 | * local ref bindings in nested templates are able to find local ref values if the refs
|
16486 | * are defined after the template declaration.
|
16487 | */
|
16488 | this._nestedTemplateFns = [];
|
16489 | this._unsupported = unsupported;
|
16490 | // i18n context local to this template
|
16491 | this.i18n = null;
|
16492 | // Number of slots to reserve for pureFunctions
|
16493 | this._pureFunctionSlots = 0;
|
16494 | // Number of binding slots
|
16495 | this._bindingSlots = 0;
|
16496 | // Projection slots found in the template. Projection slots can distribute projected
|
16497 | // nodes based on a selector, or can just use the wildcard selector to match
|
16498 | // all nodes which aren't matching any selector.
|
16499 | this._ngContentReservedSlots = [];
|
16500 | // Number of non-default selectors found in all parent templates of this template. We need to
|
16501 | // track it to properly adjust projection slot index in the `projection` instruction.
|
16502 | this._ngContentSelectorsOffset = 0;
|
16503 | // Expression that should be used as implicit receiver when converting template
|
16504 | // expressions to output AST.
|
16505 | this._implicitReceiverExpr = null;
|
16506 | // These should be handled in the template or element directly.
|
16507 | this.visitReference = invalid$1;
|
16508 | this.visitVariable = invalid$1;
|
16509 | this.visitTextAttribute = invalid$1;
|
16510 | this.visitBoundAttribute = invalid$1;
|
16511 | this.visitBoundEvent = invalid$1;
|
16512 | this._bindingScope = parentBindingScope.nestedScope(level);
|
16513 | // Turn the relative context file path into an identifier by replacing non-alphanumeric
|
16514 | // characters with underscores.
|
16515 | this.fileBasedI18nSuffix = relativeContextFilePath.replace(/[^A-Za-z0-9]/g, '_') + '_';
|
16516 | this._valueConverter = new ValueConverter(constantPool, () => this.allocateDataSlot(), (numSlots) => this.allocatePureFunctionSlots(numSlots), (name, localName, slot, value) => {
|
16517 | const pipeType = pipeTypeByName.get(name);
|
16518 | if (pipeType) {
|
16519 | this.pipes.add(pipeType);
|
16520 | }
|
16521 | this._bindingScope.set(this.level, localName, value);
|
16522 | this.creationInstruction(null, Identifiers$1.pipe, [literal(slot), literal(name)]);
|
16523 | });
|
16524 | }
|
16525 | buildTemplateFunction(nodes, variables, ngContentSelectorsOffset = 0, i18n) {
|
16526 | this._ngContentSelectorsOffset = ngContentSelectorsOffset;
|
16527 | if (this._namespace !== Identifiers$1.namespaceHTML) {
|
16528 | this.creationInstruction(null, this._namespace);
|
16529 | }
|
16530 | // Create variable bindings
|
16531 | variables.forEach(v => this.registerContextVariables(v));
|
16532 | // Initiate i18n context in case:
|
16533 | // - this template has parent i18n context
|
16534 | // - or the template has i18n meta associated with it,
|
16535 | // but it's not initiated by the Element (e.g. <ng-template i18n>)
|
16536 | const initI18nContext = this.i18nContext ||
|
16537 | (isI18nRootNode(i18n) && !isSingleI18nIcu(i18n) &&
|
16538 | !(isSingleElementTemplate(nodes) && nodes[0].i18n === i18n));
|
16539 | const selfClosingI18nInstruction = hasTextChildrenOnly(nodes);
|
16540 | if (initI18nContext) {
|
16541 | this.i18nStart(null, i18n, selfClosingI18nInstruction);
|
16542 | }
|
16543 | // This is the initial pass through the nodes of this template. In this pass, we
|
16544 | // queue all creation mode and update mode instructions for generation in the second
|
16545 | // pass. It's necessary to separate the passes to ensure local refs are defined before
|
16546 | // resolving bindings. We also count bindings in this pass as we walk bound expressions.
|
16547 | visitAll(this, nodes);
|
16548 | // Add total binding count to pure function count so pure function instructions are
|
16549 | // generated with the correct slot offset when update instructions are processed.
|
16550 | this._pureFunctionSlots += this._bindingSlots;
|
16551 | // Pipes are walked in the first pass (to enqueue `pipe()` creation instructions and
|
16552 | // `pipeBind` update instructions), so we have to update the slot offsets manually
|
16553 | // to account for bindings.
|
16554 | this._valueConverter.updatePipeSlotOffsets(this._bindingSlots);
|
16555 | // Nested templates must be processed before creation instructions so template()
|
16556 | // instructions can be generated with the correct internal const count.
|
16557 | this._nestedTemplateFns.forEach(buildTemplateFn => buildTemplateFn());
|
16558 | // Output the `projectionDef` instruction when some `<ng-content>` tags are present.
|
16559 | // The `projectionDef` instruction is only emitted for the component template and
|
16560 | // is skipped for nested templates (<ng-template> tags).
|
16561 | if (this.level === 0 && this._ngContentReservedSlots.length) {
|
16562 | const parameters = [];
|
16563 | // By default the `projectionDef` instructions creates one slot for the wildcard
|
16564 | // selector if no parameters are passed. Therefore we only want to allocate a new
|
16565 | // array for the projection slots if the default projection slot is not sufficient.
|
16566 | if (this._ngContentReservedSlots.length > 1 || this._ngContentReservedSlots[0] !== '*') {
|
16567 | const r3ReservedSlots = this._ngContentReservedSlots.map(s => s !== '*' ? parseSelectorToR3Selector(s) : s);
|
16568 | parameters.push(this.constantPool.getConstLiteral(asLiteral(r3ReservedSlots), true));
|
16569 | }
|
16570 | // Since we accumulate ngContent selectors while processing template elements,
|
16571 | // we *prepend* `projectionDef` to creation instructions block, to put it before
|
16572 | // any `projection` instructions
|
16573 | this.creationInstruction(null, Identifiers$1.projectionDef, parameters, /* prepend */ true);
|
16574 | }
|
16575 | if (initI18nContext) {
|
16576 | this.i18nEnd(null, selfClosingI18nInstruction);
|
16577 | }
|
16578 | // Generate all the creation mode instructions (e.g. resolve bindings in listeners)
|
16579 | const creationStatements = this._creationCodeFns.map((fn) => fn());
|
16580 | // Generate all the update mode instructions (e.g. resolve property or text bindings)
|
16581 | const updateStatements = this._updateCodeFns.map((fn) => fn());
|
16582 | // Variable declaration must occur after binding resolution so we can generate context
|
16583 | // instructions that build on each other.
|
16584 | // e.g. const b = nextContext().$implicit(); const b = nextContext();
|
16585 | const creationVariables = this._bindingScope.viewSnapshotStatements();
|
16586 | const updateVariables = this._bindingScope.variableDeclarations().concat(this._tempVariables);
|
16587 | const creationBlock = creationStatements.length > 0 ?
|
16588 | [renderFlagCheckIfStmt(1 /* Create */, creationVariables.concat(creationStatements))] :
|
16589 | [];
|
16590 | const updateBlock = updateStatements.length > 0 ?
|
16591 | [renderFlagCheckIfStmt(2 /* Update */, updateVariables.concat(updateStatements))] :
|
16592 | [];
|
16593 | return fn(
|
16594 | // i.e. (rf: RenderFlags, ctx: any)
|
16595 | [new FnParam(RENDER_FLAGS, NUMBER_TYPE), new FnParam(CONTEXT_NAME, null)], [
|
16596 | // Temporary variable declarations for query refresh (i.e. let _t: any;)
|
16597 | ...this._prefixCode,
|
16598 | // Creating mode (i.e. if (rf & RenderFlags.Create) { ... })
|
16599 | ...creationBlock,
|
16600 | // Binding and refresh mode (i.e. if (rf & RenderFlags.Update) {...})
|
16601 | ...updateBlock,
|
16602 | ], INFERRED_TYPE, null, this.templateName);
|
16603 | }
|
16604 | // LocalResolver
|
16605 | getLocal(name) {
|
16606 | return this._bindingScope.get(name);
|
16607 | }
|
16608 | // LocalResolver
|
16609 | notifyImplicitReceiverUse() {
|
16610 | this._bindingScope.notifyImplicitReceiverUse();
|
16611 | }
|
16612 | i18nTranslate(message, params = {}, ref, transformFn) {
|
16613 | const _ref = ref || this.i18nGenerateMainBlockVar();
|
16614 | // Closure Compiler requires const names to start with `MSG_` but disallows any other const to
|
16615 | // start with `MSG_`. We define a variable starting with `MSG_` just for the `goog.getMsg` call
|
16616 | const closureVar = this.i18nGenerateClosureVar(message.id);
|
16617 | const statements = getTranslationDeclStmts(message, _ref, closureVar, params, transformFn);
|
16618 | this._constants.prepareStatements.push(...statements);
|
16619 | return _ref;
|
16620 | }
|
16621 | registerContextVariables(variable$1) {
|
16622 | const scopedName = this._bindingScope.freshReferenceName();
|
16623 | const retrievalLevel = this.level;
|
16624 | const lhs = variable(variable$1.name + scopedName);
|
16625 | this._bindingScope.set(retrievalLevel, variable$1.name, lhs, 1 /* CONTEXT */, (scope, relativeLevel) => {
|
16626 | let rhs;
|
16627 | if (scope.bindingLevel === retrievalLevel) {
|
16628 | // e.g. ctx
|
16629 | rhs = variable(CONTEXT_NAME);
|
16630 | }
|
16631 | else {
|
16632 | const sharedCtxVar = scope.getSharedContextName(retrievalLevel);
|
16633 | // e.g. ctx_r0 OR x(2);
|
16634 | rhs = sharedCtxVar ? sharedCtxVar : generateNextContextExpr(relativeLevel);
|
16635 | }
|
16636 | // e.g. const $item$ = x(2).$implicit;
|
16637 | return [lhs.set(rhs.prop(variable$1.value || IMPLICIT_REFERENCE)).toConstDecl()];
|
16638 | });
|
16639 | }
|
16640 | i18nAppendBindings(expressions) {
|
16641 | if (expressions.length > 0) {
|
16642 | expressions.forEach(expression => this.i18n.appendBinding(expression));
|
16643 | }
|
16644 | }
|
16645 | i18nBindProps(props) {
|
16646 | const bound = {};
|
16647 | Object.keys(props).forEach(key => {
|
16648 | const prop = props[key];
|
16649 | if (prop instanceof Text) {
|
16650 | bound[key] = literal(prop.value);
|
16651 | }
|
16652 | else {
|
16653 | const value = prop.value.visit(this._valueConverter);
|
16654 | this.allocateBindingSlots(value);
|
16655 | if (value instanceof Interpolation) {
|
16656 | const { strings, expressions } = value;
|
16657 | const { id, bindings } = this.i18n;
|
16658 | const label = assembleI18nBoundString(strings, bindings.size, id);
|
16659 | this.i18nAppendBindings(expressions);
|
16660 | bound[key] = literal(label);
|
16661 | }
|
16662 | }
|
16663 | });
|
16664 | return bound;
|
16665 | }
|
16666 | // Generates top level vars for i18n blocks (i.e. `i18n_N`).
|
16667 | i18nGenerateMainBlockVar() {
|
16668 | return variable(this.constantPool.uniqueName(TRANSLATION_VAR_PREFIX));
|
16669 | }
|
16670 | // Generates vars with Closure-specific names for i18n blocks (i.e. `MSG_XXX`).
|
16671 | i18nGenerateClosureVar(messageId) {
|
16672 | let name;
|
16673 | const suffix = this.fileBasedI18nSuffix.toUpperCase();
|
16674 | if (this.i18nUseExternalIds) {
|
16675 | const prefix = getTranslationConstPrefix(`EXTERNAL_`);
|
16676 | const uniqueSuffix = this.constantPool.uniqueName(suffix);
|
16677 | name = `${prefix}${sanitizeIdentifier(messageId)}$$${uniqueSuffix}`;
|
16678 | }
|
16679 | else {
|
16680 | const prefix = getTranslationConstPrefix(suffix);
|
16681 | name = this.constantPool.uniqueName(prefix);
|
16682 | }
|
16683 | return variable(name);
|
16684 | }
|
16685 | i18nUpdateRef(context) {
|
16686 | const { icus, meta, isRoot, isResolved, isEmitted } = context;
|
16687 | if (isRoot && isResolved && !isEmitted && !isSingleI18nIcu(meta)) {
|
16688 | context.isEmitted = true;
|
16689 | const placeholders = context.getSerializedPlaceholders();
|
16690 | let icuMapping = {};
|
16691 | let params = placeholders.size ? placeholdersToParams(placeholders) : {};
|
16692 | if (icus.size) {
|
16693 | icus.forEach((refs, key) => {
|
16694 | if (refs.length === 1) {
|
16695 | // if we have one ICU defined for a given
|
16696 | // placeholder - just output its reference
|
16697 | params[key] = refs[0];
|
16698 | }
|
16699 | else {
|
16700 | // ... otherwise we need to activate post-processing
|
16701 | // to replace ICU placeholders with proper values
|
16702 | const placeholder = wrapI18nPlaceholder(`${I18N_ICU_MAPPING_PREFIX}${key}`);
|
16703 | params[key] = literal(placeholder);
|
16704 | icuMapping[key] = literalArr(refs);
|
16705 | }
|
16706 | });
|
16707 | }
|
16708 | // translation requires post processing in 2 cases:
|
16709 | // - if we have placeholders with multiple values (ex. `START_DIV`: [�#1�, �#2�, ...])
|
16710 | // - if we have multiple ICUs that refer to the same placeholder name
|
16711 | const needsPostprocessing = Array.from(placeholders.values()).some((value) => value.length > 1) ||
|
16712 | Object.keys(icuMapping).length;
|
16713 | let transformFn;
|
16714 | if (needsPostprocessing) {
|
16715 | transformFn = (raw) => {
|
16716 | const args = [raw];
|
16717 | if (Object.keys(icuMapping).length) {
|
16718 | args.push(mapLiteral(icuMapping, true));
|
16719 | }
|
16720 | return instruction(null, Identifiers$1.i18nPostprocess, args);
|
16721 | };
|
16722 | }
|
16723 | this.i18nTranslate(meta, params, context.ref, transformFn);
|
16724 | }
|
16725 | }
|
16726 | i18nStart(span = null, meta, selfClosing) {
|
16727 | const index = this.allocateDataSlot();
|
16728 | this.i18n = this.i18nContext ?
|
16729 | this.i18nContext.forkChildContext(index, this.templateIndex, meta) :
|
16730 | new I18nContext(index, this.i18nGenerateMainBlockVar(), 0, this.templateIndex, meta);
|
16731 | // generate i18nStart instruction
|
16732 | const { id, ref } = this.i18n;
|
16733 | const params = [literal(index), this.addToConsts(ref)];
|
16734 | if (id > 0) {
|
16735 | // do not push 3rd argument (sub-block id)
|
16736 | // into i18nStart call for top level i18n context
|
16737 | params.push(literal(id));
|
16738 | }
|
16739 | this.creationInstruction(span, selfClosing ? Identifiers$1.i18n : Identifiers$1.i18nStart, params);
|
16740 | }
|
16741 | i18nEnd(span = null, selfClosing) {
|
16742 | if (!this.i18n) {
|
16743 | throw new Error('i18nEnd is executed with no i18n context present');
|
16744 | }
|
16745 | if (this.i18nContext) {
|
16746 | this.i18nContext.reconcileChildContext(this.i18n);
|
16747 | this.i18nUpdateRef(this.i18nContext);
|
16748 | }
|
16749 | else {
|
16750 | this.i18nUpdateRef(this.i18n);
|
16751 | }
|
16752 | // setup accumulated bindings
|
16753 | const { index, bindings } = this.i18n;
|
16754 | if (bindings.size) {
|
16755 | const chainBindings = [];
|
16756 | bindings.forEach(binding => {
|
16757 | chainBindings.push({ sourceSpan: span, value: () => this.convertPropertyBinding(binding) });
|
16758 | });
|
16759 | // for i18n block, advance to the most recent element index (by taking the current number of
|
16760 | // elements and subtracting one) before invoking `i18nExp` instructions, to make sure the
|
16761 | // necessary lifecycle hooks of components/directives are properly flushed.
|
16762 | this.updateInstructionChainWithAdvance(this.getConstCount() - 1, Identifiers$1.i18nExp, chainBindings);
|
16763 | this.updateInstruction(span, Identifiers$1.i18nApply, [literal(index)]);
|
16764 | }
|
16765 | if (!selfClosing) {
|
16766 | this.creationInstruction(span, Identifiers$1.i18nEnd);
|
16767 | }
|
16768 | this.i18n = null; // reset local i18n context
|
16769 | }
|
16770 | i18nAttributesInstruction(nodeIndex, attrs, sourceSpan) {
|
16771 | let hasBindings = false;
|
16772 | const i18nAttrArgs = [];
|
16773 | const bindings = [];
|
16774 | attrs.forEach(attr => {
|
16775 | const message = attr.i18n;
|
16776 | const converted = attr.value.visit(this._valueConverter);
|
16777 | this.allocateBindingSlots(converted);
|
16778 | if (converted instanceof Interpolation) {
|
16779 | const placeholders = assembleBoundTextPlaceholders(message);
|
16780 | const params = placeholdersToParams(placeholders);
|
16781 | i18nAttrArgs.push(literal(attr.name), this.i18nTranslate(message, params));
|
16782 | converted.expressions.forEach(expression => {
|
16783 | hasBindings = true;
|
16784 | bindings.push({
|
16785 | sourceSpan,
|
16786 | value: () => this.convertPropertyBinding(expression),
|
16787 | });
|
16788 | });
|
16789 | }
|
16790 | });
|
16791 | if (bindings.length > 0) {
|
16792 | this.updateInstructionChainWithAdvance(nodeIndex, Identifiers$1.i18nExp, bindings);
|
16793 | }
|
16794 | if (i18nAttrArgs.length > 0) {
|
16795 | const index = literal(this.allocateDataSlot());
|
16796 | const constIndex = this.addToConsts(literalArr(i18nAttrArgs));
|
16797 | this.creationInstruction(sourceSpan, Identifiers$1.i18nAttributes, [index, constIndex]);
|
16798 | if (hasBindings) {
|
16799 | this.updateInstruction(sourceSpan, Identifiers$1.i18nApply, [index]);
|
16800 | }
|
16801 | }
|
16802 | }
|
16803 | getNamespaceInstruction(namespaceKey) {
|
16804 | switch (namespaceKey) {
|
16805 | case 'math':
|
16806 | return Identifiers$1.namespaceMathML;
|
16807 | case 'svg':
|
16808 | return Identifiers$1.namespaceSVG;
|
16809 | default:
|
16810 | return Identifiers$1.namespaceHTML;
|
16811 | }
|
16812 | }
|
16813 | addNamespaceInstruction(nsInstruction, element) {
|
16814 | this._namespace = nsInstruction;
|
16815 | this.creationInstruction(element.startSourceSpan, nsInstruction);
|
16816 | }
|
16817 | /**
|
16818 | * Adds an update instruction for an interpolated property or attribute, such as
|
16819 | * `prop="{{value}}"` or `attr.title="{{value}}"`
|
16820 | */
|
16821 | interpolatedUpdateInstruction(instruction, elementIndex, attrName, input, value, params) {
|
16822 | this.updateInstructionWithAdvance(elementIndex, input.sourceSpan, instruction, () => [literal(attrName), ...this.getUpdateInstructionArguments(value), ...params]);
|
16823 | }
|
16824 | visitContent(ngContent) {
|
16825 | const slot = this.allocateDataSlot();
|
16826 | const projectionSlotIdx = this._ngContentSelectorsOffset + this._ngContentReservedSlots.length;
|
16827 | const parameters = [literal(slot)];
|
16828 | this._ngContentReservedSlots.push(ngContent.selector);
|
16829 | const nonContentSelectAttributes = ngContent.attributes.filter(attr => attr.name.toLowerCase() !== NG_CONTENT_SELECT_ATTR$1);
|
16830 | const attributes = this.getAttributeExpressions(ngContent.name, nonContentSelectAttributes, [], []);
|
16831 | if (attributes.length > 0) {
|
16832 | parameters.push(literal(projectionSlotIdx), literalArr(attributes));
|
16833 | }
|
16834 | else if (projectionSlotIdx !== 0) {
|
16835 | parameters.push(literal(projectionSlotIdx));
|
16836 | }
|
16837 | this.creationInstruction(ngContent.sourceSpan, Identifiers$1.projection, parameters);
|
16838 | if (this.i18n) {
|
16839 | this.i18n.appendProjection(ngContent.i18n, slot);
|
16840 | }
|
16841 | }
|
16842 | visitElement(element) {
|
16843 | var _a, _b;
|
16844 | const elementIndex = this.allocateDataSlot();
|
16845 | const stylingBuilder = new StylingBuilder(null);
|
16846 | let isNonBindableMode = false;
|
16847 | const isI18nRootElement = isI18nRootNode(element.i18n) && !isSingleI18nIcu(element.i18n);
|
16848 | const outputAttrs = [];
|
16849 | const [namespaceKey, elementName] = splitNsName(element.name);
|
16850 | const isNgContainer$1 = isNgContainer(element.name);
|
16851 | // Handle styling, i18n, ngNonBindable attributes
|
16852 | for (const attr of element.attributes) {
|
16853 | const { name, value } = attr;
|
16854 | if (name === NON_BINDABLE_ATTR) {
|
16855 | isNonBindableMode = true;
|
16856 | }
|
16857 | else if (name === 'style') {
|
16858 | stylingBuilder.registerStyleAttr(value);
|
16859 | }
|
16860 | else if (name === 'class') {
|
16861 | stylingBuilder.registerClassAttr(value);
|
16862 | }
|
16863 | else {
|
16864 | outputAttrs.push(attr);
|
16865 | }
|
16866 | }
|
16867 | // Match directives on non i18n attributes
|
16868 | this.matchDirectives(element.name, element);
|
16869 | // Regular element or ng-container creation mode
|
16870 | const parameters = [literal(elementIndex)];
|
16871 | if (!isNgContainer$1) {
|
16872 | parameters.push(literal(elementName));
|
16873 | }
|
16874 | // Add the attributes
|
16875 | const allOtherInputs = [];
|
16876 | const boundI18nAttrs = [];
|
16877 | element.inputs.forEach(input => {
|
16878 | const stylingInputWasSet = stylingBuilder.registerBoundInput(input);
|
16879 | if (!stylingInputWasSet) {
|
16880 | if (input.type === 0 /* Property */ && input.i18n) {
|
16881 | boundI18nAttrs.push(input);
|
16882 | }
|
16883 | else {
|
16884 | allOtherInputs.push(input);
|
16885 | }
|
16886 | }
|
16887 | });
|
16888 | // add attributes for directive and projection matching purposes
|
16889 | const attributes = this.getAttributeExpressions(element.name, outputAttrs, allOtherInputs, element.outputs, stylingBuilder, [], boundI18nAttrs);
|
16890 | parameters.push(this.addAttrsToConsts(attributes));
|
16891 | // local refs (ex.: <div #foo #bar="baz">)
|
16892 | const refs = this.prepareRefsArray(element.references);
|
16893 | parameters.push(this.addToConsts(refs));
|
16894 | const wasInNamespace = this._namespace;
|
16895 | const currentNamespace = this.getNamespaceInstruction(namespaceKey);
|
16896 | // If the namespace is changing now, include an instruction to change it
|
16897 | // during element creation.
|
16898 | if (currentNamespace !== wasInNamespace) {
|
16899 | this.addNamespaceInstruction(currentNamespace, element);
|
16900 | }
|
16901 | if (this.i18n) {
|
16902 | this.i18n.appendElement(element.i18n, elementIndex);
|
16903 | }
|
16904 | // Note that we do not append text node instructions and ICUs inside i18n section,
|
16905 | // so we exclude them while calculating whether current element has children
|
16906 | const hasChildren = (!isI18nRootElement && this.i18n) ? !hasTextChildrenOnly(element.children) :
|
16907 | element.children.length > 0;
|
16908 | const createSelfClosingInstruction = !stylingBuilder.hasBindingsWithPipes &&
|
16909 | element.outputs.length === 0 && boundI18nAttrs.length === 0 && !hasChildren;
|
16910 | const createSelfClosingI18nInstruction = !createSelfClosingInstruction && hasTextChildrenOnly(element.children);
|
16911 | if (createSelfClosingInstruction) {
|
16912 | this.creationInstruction(element.sourceSpan, isNgContainer$1 ? Identifiers$1.elementContainer : Identifiers$1.element, trimTrailingNulls(parameters));
|
16913 | }
|
16914 | else {
|
16915 | this.creationInstruction(element.startSourceSpan, isNgContainer$1 ? Identifiers$1.elementContainerStart : Identifiers$1.elementStart, trimTrailingNulls(parameters));
|
16916 | if (isNonBindableMode) {
|
16917 | this.creationInstruction(element.startSourceSpan, Identifiers$1.disableBindings);
|
16918 | }
|
16919 | if (boundI18nAttrs.length > 0) {
|
16920 | this.i18nAttributesInstruction(elementIndex, boundI18nAttrs, (_a = element.startSourceSpan) !== null && _a !== void 0 ? _a : element.sourceSpan);
|
16921 | }
|
16922 | // Generate Listeners (outputs)
|
16923 | if (element.outputs.length > 0) {
|
16924 | const listeners = element.outputs.map((outputAst) => ({
|
16925 | sourceSpan: outputAst.sourceSpan,
|
16926 | params: this.prepareListenerParameter(element.name, outputAst, elementIndex)
|
16927 | }));
|
16928 | this.creationInstructionChain(Identifiers$1.listener, listeners);
|
16929 | }
|
16930 | // Note: it's important to keep i18n/i18nStart instructions after i18nAttributes and
|
16931 | // listeners, to make sure i18nAttributes instruction targets current element at runtime.
|
16932 | if (isI18nRootElement) {
|
16933 | this.i18nStart(element.startSourceSpan, element.i18n, createSelfClosingI18nInstruction);
|
16934 | }
|
16935 | }
|
16936 | // the code here will collect all update-level styling instructions and add them to the
|
16937 | // update block of the template function AOT code. Instructions like `styleProp`,
|
16938 | // `styleMap`, `classMap`, `classProp`
|
16939 | // are all generated and assigned in the code below.
|
16940 | const stylingInstructions = stylingBuilder.buildUpdateLevelInstructions(this._valueConverter);
|
16941 | const limit = stylingInstructions.length - 1;
|
16942 | for (let i = 0; i <= limit; i++) {
|
16943 | const instruction = stylingInstructions[i];
|
16944 | this._bindingSlots += this.processStylingUpdateInstruction(elementIndex, instruction);
|
16945 | }
|
16946 | // the reason why `undefined` is used is because the renderer understands this as a
|
16947 | // special value to symbolize that there is no RHS to this binding
|
16948 | // TODO (matsko): revisit this once FW-959 is approached
|
16949 | const emptyValueBindInstruction = literal(undefined);
|
16950 | const propertyBindings = [];
|
16951 | const attributeBindings = [];
|
16952 | // Generate element input bindings
|
16953 | allOtherInputs.forEach(input => {
|
16954 | const inputType = input.type;
|
16955 | if (inputType === 4 /* Animation */) {
|
16956 | const value = input.value.visit(this._valueConverter);
|
16957 | // animation bindings can be presented in the following formats:
|
16958 | // 1. [@binding]="fooExp"
|
16959 | // 2. [@binding]="{value:fooExp, params:{...}}"
|
16960 | // 3. [@binding]
|
16961 | // 4. @binding
|
16962 | // All formats will be valid for when a synthetic binding is created.
|
16963 | // The reasoning for this is because the renderer should get each
|
16964 | // synthetic binding value in the order of the array that they are
|
16965 | // defined in...
|
16966 | const hasValue = value instanceof LiteralPrimitive ? !!value.value : true;
|
16967 | this.allocateBindingSlots(value);
|
16968 | propertyBindings.push({
|
16969 | name: prepareSyntheticPropertyName(input.name),
|
16970 | sourceSpan: input.sourceSpan,
|
16971 | value: () => hasValue ? this.convertPropertyBinding(value) : emptyValueBindInstruction
|
16972 | });
|
16973 | }
|
16974 | else {
|
16975 | // we must skip attributes with associated i18n context, since these attributes are handled
|
16976 | // separately and corresponding `i18nExp` and `i18nApply` instructions will be generated
|
16977 | if (input.i18n)
|
16978 | return;
|
16979 | const value = input.value.visit(this._valueConverter);
|
16980 | if (value !== undefined) {
|
16981 | const params = [];
|
16982 | const [attrNamespace, attrName] = splitNsName(input.name);
|
16983 | const isAttributeBinding = inputType === 1 /* Attribute */;
|
16984 | const sanitizationRef = resolveSanitizationFn(input.securityContext, isAttributeBinding);
|
16985 | if (sanitizationRef)
|
16986 | params.push(sanitizationRef);
|
16987 | if (attrNamespace) {
|
16988 | const namespaceLiteral = literal(attrNamespace);
|
16989 | if (sanitizationRef) {
|
16990 | params.push(namespaceLiteral);
|
16991 | }
|
16992 | else {
|
16993 | // If there wasn't a sanitization ref, we need to add
|
16994 | // an extra param so that we can pass in the namespace.
|
16995 | params.push(literal(null), namespaceLiteral);
|
16996 | }
|
16997 | }
|
16998 | this.allocateBindingSlots(value);
|
16999 | if (inputType === 0 /* Property */) {
|
17000 | if (value instanceof Interpolation) {
|
17001 | // prop="{{value}}" and friends
|
17002 | this.interpolatedUpdateInstruction(getPropertyInterpolationExpression(value), elementIndex, attrName, input, value, params);
|
17003 | }
|
17004 | else {
|
17005 | // [prop]="value"
|
17006 | // Collect all the properties so that we can chain into a single function at the end.
|
17007 | propertyBindings.push({
|
17008 | name: attrName,
|
17009 | sourceSpan: input.sourceSpan,
|
17010 | value: () => this.convertPropertyBinding(value),
|
17011 | params
|
17012 | });
|
17013 | }
|
17014 | }
|
17015 | else if (inputType === 1 /* Attribute */) {
|
17016 | if (value instanceof Interpolation && getInterpolationArgsLength(value) > 1) {
|
17017 | // attr.name="text{{value}}" and friends
|
17018 | this.interpolatedUpdateInstruction(getAttributeInterpolationExpression(value), elementIndex, attrName, input, value, params);
|
17019 | }
|
17020 | else {
|
17021 | const boundValue = value instanceof Interpolation ? value.expressions[0] : value;
|
17022 | // [attr.name]="value" or attr.name="{{value}}"
|
17023 | // Collect the attribute bindings so that they can be chained at the end.
|
17024 | attributeBindings.push({
|
17025 | name: attrName,
|
17026 | sourceSpan: input.sourceSpan,
|
17027 | value: () => this.convertPropertyBinding(boundValue),
|
17028 | params
|
17029 | });
|
17030 | }
|
17031 | }
|
17032 | else {
|
17033 | // class prop
|
17034 | this.updateInstructionWithAdvance(elementIndex, input.sourceSpan, Identifiers$1.classProp, () => {
|
17035 | return [
|
17036 | literal(elementIndex), literal(attrName), this.convertPropertyBinding(value),
|
17037 | ...params
|
17038 | ];
|
17039 | });
|
17040 | }
|
17041 | }
|
17042 | }
|
17043 | });
|
17044 | if (propertyBindings.length > 0) {
|
17045 | this.updateInstructionChainWithAdvance(elementIndex, Identifiers$1.property, propertyBindings);
|
17046 | }
|
17047 | if (attributeBindings.length > 0) {
|
17048 | this.updateInstructionChainWithAdvance(elementIndex, Identifiers$1.attribute, attributeBindings);
|
17049 | }
|
17050 | // Traverse element child nodes
|
17051 | visitAll(this, element.children);
|
17052 | if (!isI18nRootElement && this.i18n) {
|
17053 | this.i18n.appendElement(element.i18n, elementIndex, true);
|
17054 | }
|
17055 | if (!createSelfClosingInstruction) {
|
17056 | // Finish element construction mode.
|
17057 | const span = (_b = element.endSourceSpan) !== null && _b !== void 0 ? _b : element.sourceSpan;
|
17058 | if (isI18nRootElement) {
|
17059 | this.i18nEnd(span, createSelfClosingI18nInstruction);
|
17060 | }
|
17061 | if (isNonBindableMode) {
|
17062 | this.creationInstruction(span, Identifiers$1.enableBindings);
|
17063 | }
|
17064 | this.creationInstruction(span, isNgContainer$1 ? Identifiers$1.elementContainerEnd : Identifiers$1.elementEnd);
|
17065 | }
|
17066 | }
|
17067 | visitTemplate(template) {
|
17068 | var _a;
|
17069 | const NG_TEMPLATE_TAG_NAME = 'ng-template';
|
17070 | const templateIndex = this.allocateDataSlot();
|
17071 | if (this.i18n) {
|
17072 | this.i18n.appendTemplate(template.i18n, templateIndex);
|
17073 | }
|
17074 | const tagName = sanitizeIdentifier(template.tagName || '');
|
17075 | const contextName = `${this.contextName}${tagName ? '_' + tagName : ''}_${templateIndex}`;
|
17076 | const templateName = `${contextName}_Template`;
|
17077 | const parameters = [
|
17078 | literal(templateIndex),
|
17079 | variable(templateName),
|
17080 | // We don't care about the tag's namespace here, because we infer
|
17081 | // it based on the parent nodes inside the template instruction.
|
17082 | literal(template.tagName ? splitNsName(template.tagName)[1] : template.tagName),
|
17083 | ];
|
17084 | // find directives matching on a given <ng-template> node
|
17085 | this.matchDirectives(NG_TEMPLATE_TAG_NAME, template);
|
17086 | // prepare attributes parameter (including attributes used for directive matching)
|
17087 | const attrsExprs = this.getAttributeExpressions(NG_TEMPLATE_TAG_NAME, template.attributes, template.inputs, template.outputs, undefined /* styles */, template.templateAttrs);
|
17088 | parameters.push(this.addAttrsToConsts(attrsExprs));
|
17089 | // local refs (ex.: <ng-template #foo>)
|
17090 | if (template.references && template.references.length) {
|
17091 | const refs = this.prepareRefsArray(template.references);
|
17092 | parameters.push(this.addToConsts(refs));
|
17093 | parameters.push(importExpr(Identifiers$1.templateRefExtractor));
|
17094 | }
|
17095 | // Create the template function
|
17096 | 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);
|
17097 | // Nested templates must not be visited until after their parent templates have completed
|
17098 | // processing, so they are queued here until after the initial pass. Otherwise, we wouldn't
|
17099 | // be able to support bindings in nested templates to local refs that occur after the
|
17100 | // template definition. e.g. <div *ngIf="showing">{{ foo }}</div> <div #foo></div>
|
17101 | this._nestedTemplateFns.push(() => {
|
17102 | const templateFunctionExpr = templateVisitor.buildTemplateFunction(template.children, template.variables, this._ngContentReservedSlots.length + this._ngContentSelectorsOffset, template.i18n);
|
17103 | this.constantPool.statements.push(templateFunctionExpr.toDeclStmt(templateName));
|
17104 | if (templateVisitor._ngContentReservedSlots.length) {
|
17105 | this._ngContentReservedSlots.push(...templateVisitor._ngContentReservedSlots);
|
17106 | }
|
17107 | });
|
17108 | // e.g. template(1, MyComp_Template_1)
|
17109 | this.creationInstruction(template.sourceSpan, Identifiers$1.templateCreate, () => {
|
17110 | parameters.splice(2, 0, literal(templateVisitor.getConstCount()), literal(templateVisitor.getVarCount()));
|
17111 | return trimTrailingNulls(parameters);
|
17112 | });
|
17113 | // handle property bindings e.g. ɵɵproperty('ngForOf', ctx.items), et al;
|
17114 | this.templatePropertyBindings(templateIndex, template.templateAttrs);
|
17115 | // Only add normal input/output binding instructions on explicit <ng-template> elements.
|
17116 | if (template.tagName === NG_TEMPLATE_TAG_NAME) {
|
17117 | const [i18nInputs, inputs] = partitionArray(template.inputs, hasI18nMeta);
|
17118 | // Add i18n attributes that may act as inputs to directives. If such attributes are present,
|
17119 | // generate `i18nAttributes` instruction. Note: we generate it only for explicit <ng-template>
|
17120 | // elements, in case of inline templates, corresponding instructions will be generated in the
|
17121 | // nested template function.
|
17122 | if (i18nInputs.length > 0) {
|
17123 | this.i18nAttributesInstruction(templateIndex, i18nInputs, (_a = template.startSourceSpan) !== null && _a !== void 0 ? _a : template.sourceSpan);
|
17124 | }
|
17125 | // Add the input bindings
|
17126 | if (inputs.length > 0) {
|
17127 | this.templatePropertyBindings(templateIndex, inputs);
|
17128 | }
|
17129 | // Generate listeners for directive output
|
17130 | if (template.outputs.length > 0) {
|
17131 | const listeners = template.outputs.map((outputAst) => ({
|
17132 | sourceSpan: outputAst.sourceSpan,
|
17133 | params: this.prepareListenerParameter('ng_template', outputAst, templateIndex)
|
17134 | }));
|
17135 | this.creationInstructionChain(Identifiers$1.listener, listeners);
|
17136 | }
|
17137 | }
|
17138 | }
|
17139 | visitBoundText(text) {
|
17140 | if (this.i18n) {
|
17141 | const value = text.value.visit(this._valueConverter);
|
17142 | this.allocateBindingSlots(value);
|
17143 | if (value instanceof Interpolation) {
|
17144 | this.i18n.appendBoundText(text.i18n);
|
17145 | this.i18nAppendBindings(value.expressions);
|
17146 | }
|
17147 | return;
|
17148 | }
|
17149 | const nodeIndex = this.allocateDataSlot();
|
17150 | this.creationInstruction(text.sourceSpan, Identifiers$1.text, [literal(nodeIndex)]);
|
17151 | const value = text.value.visit(this._valueConverter);
|
17152 | this.allocateBindingSlots(value);
|
17153 | if (value instanceof Interpolation) {
|
17154 | this.updateInstructionWithAdvance(nodeIndex, text.sourceSpan, getTextInterpolationExpression(value), () => this.getUpdateInstructionArguments(value));
|
17155 | }
|
17156 | else {
|
17157 | error('Text nodes should be interpolated and never bound directly.');
|
17158 | }
|
17159 | }
|
17160 | visitText(text) {
|
17161 | // when a text element is located within a translatable
|
17162 | // block, we exclude this text element from instructions set,
|
17163 | // since it will be captured in i18n content and processed at runtime
|
17164 | if (!this.i18n) {
|
17165 | this.creationInstruction(text.sourceSpan, Identifiers$1.text, [literal(this.allocateDataSlot()), literal(text.value)]);
|
17166 | }
|
17167 | }
|
17168 | visitIcu(icu) {
|
17169 | let initWasInvoked = false;
|
17170 | // if an ICU was created outside of i18n block, we still treat
|
17171 | // it as a translatable entity and invoke i18nStart and i18nEnd
|
17172 | // to generate i18n context and the necessary instructions
|
17173 | if (!this.i18n) {
|
17174 | initWasInvoked = true;
|
17175 | this.i18nStart(null, icu.i18n, true);
|
17176 | }
|
17177 | const i18n = this.i18n;
|
17178 | const vars = this.i18nBindProps(icu.vars);
|
17179 | const placeholders = this.i18nBindProps(icu.placeholders);
|
17180 | // output ICU directly and keep ICU reference in context
|
17181 | const message = icu.i18n;
|
17182 | // we always need post-processing function for ICUs, to make sure that:
|
17183 | // - all placeholders in a form of {PLACEHOLDER} are replaced with actual values (note:
|
17184 | // `goog.getMsg` does not process ICUs and uses the `{PLACEHOLDER}` format for placeholders
|
17185 | // inside ICUs)
|
17186 | // - all ICU vars (such as `VAR_SELECT` or `VAR_PLURAL`) are replaced with correct values
|
17187 | const transformFn = (raw) => {
|
17188 | const params = Object.assign(Object.assign({}, vars), placeholders);
|
17189 | const formatted = i18nFormatPlaceholderNames(params, /* useCamelCase */ false);
|
17190 | return instruction(null, Identifiers$1.i18nPostprocess, [raw, mapLiteral(formatted, true)]);
|
17191 | };
|
17192 | // in case the whole i18n message is a single ICU - we do not need to
|
17193 | // create a separate top-level translation, we can use the root ref instead
|
17194 | // and make this ICU a top-level translation
|
17195 | // note: ICU placeholders are replaced with actual values in `i18nPostprocess` function
|
17196 | // separately, so we do not pass placeholders into `i18nTranslate` function.
|
17197 | if (isSingleI18nIcu(i18n.meta)) {
|
17198 | this.i18nTranslate(message, /* placeholders */ {}, i18n.ref, transformFn);
|
17199 | }
|
17200 | else {
|
17201 | // output ICU directly and keep ICU reference in context
|
17202 | const ref = this.i18nTranslate(message, /* placeholders */ {}, /* ref */ undefined, transformFn);
|
17203 | i18n.appendIcu(icuFromI18nMessage(message).name, ref);
|
17204 | }
|
17205 | if (initWasInvoked) {
|
17206 | this.i18nEnd(null, true);
|
17207 | }
|
17208 | return null;
|
17209 | }
|
17210 | allocateDataSlot() {
|
17211 | return this._dataIndex++;
|
17212 | }
|
17213 | getConstCount() {
|
17214 | return this._dataIndex;
|
17215 | }
|
17216 | getVarCount() {
|
17217 | return this._pureFunctionSlots;
|
17218 | }
|
17219 | getConsts() {
|
17220 | return this._constants;
|
17221 | }
|
17222 | getNgContentSelectors() {
|
17223 | return this._ngContentReservedSlots.length ?
|
17224 | this.constantPool.getConstLiteral(asLiteral(this._ngContentReservedSlots), true) :
|
17225 | null;
|
17226 | }
|
17227 | bindingContext() {
|
17228 | return `${this._bindingContext++}`;
|
17229 | }
|
17230 | templatePropertyBindings(templateIndex, attrs) {
|
17231 | const propertyBindings = [];
|
17232 | attrs.forEach(input => {
|
17233 | if (input instanceof BoundAttribute) {
|
17234 | const value = input.value.visit(this._valueConverter);
|
17235 | if (value !== undefined) {
|
17236 | this.allocateBindingSlots(value);
|
17237 | if (value instanceof Interpolation) {
|
17238 | // Params typically contain attribute namespace and value sanitizer, which is applicable
|
17239 | // for regular HTML elements, but not applicable for <ng-template> (since props act as
|
17240 | // inputs to directives), so keep params array empty.
|
17241 | const params = [];
|
17242 | // prop="{{value}}" case
|
17243 | this.interpolatedUpdateInstruction(getPropertyInterpolationExpression(value), templateIndex, input.name, input, value, params);
|
17244 | }
|
17245 | else {
|
17246 | // [prop]="value" case
|
17247 | propertyBindings.push({
|
17248 | name: input.name,
|
17249 | sourceSpan: input.sourceSpan,
|
17250 | value: () => this.convertPropertyBinding(value)
|
17251 | });
|
17252 | }
|
17253 | }
|
17254 | }
|
17255 | });
|
17256 | if (propertyBindings.length > 0) {
|
17257 | this.updateInstructionChainWithAdvance(templateIndex, Identifiers$1.property, propertyBindings);
|
17258 | }
|
17259 | }
|
17260 | // Bindings must only be resolved after all local refs have been visited, so all
|
17261 | // instructions are queued in callbacks that execute once the initial pass has completed.
|
17262 | // Otherwise, we wouldn't be able to support local refs that are defined after their
|
17263 | // bindings. e.g. {{ foo }} <div #foo></div>
|
17264 | instructionFn(fns, span, reference, paramsOrFn, prepend = false) {
|
17265 | fns[prepend ? 'unshift' : 'push'](() => {
|
17266 | const params = Array.isArray(paramsOrFn) ? paramsOrFn : paramsOrFn();
|
17267 | return instruction(span, reference, params).toStmt();
|
17268 | });
|
17269 | }
|
17270 | processStylingUpdateInstruction(elementIndex, instruction) {
|
17271 | let allocateBindingSlots = 0;
|
17272 | if (instruction) {
|
17273 | const calls = [];
|
17274 | instruction.calls.forEach(call => {
|
17275 | allocateBindingSlots += call.allocateBindingSlots;
|
17276 | calls.push({
|
17277 | sourceSpan: call.sourceSpan,
|
17278 | value: () => {
|
17279 | return call.params(value => (call.supportsInterpolation && value instanceof Interpolation) ?
|
17280 | this.getUpdateInstructionArguments(value) :
|
17281 | this.convertPropertyBinding(value));
|
17282 | }
|
17283 | });
|
17284 | });
|
17285 | this.updateInstructionChainWithAdvance(elementIndex, instruction.reference, calls);
|
17286 | }
|
17287 | return allocateBindingSlots;
|
17288 | }
|
17289 | creationInstruction(span, reference, paramsOrFn, prepend) {
|
17290 | this.instructionFn(this._creationCodeFns, span, reference, paramsOrFn || [], prepend);
|
17291 | }
|
17292 | creationInstructionChain(reference, calls) {
|
17293 | const span = calls.length ? calls[0].sourceSpan : null;
|
17294 | this._creationCodeFns.push(() => {
|
17295 | return chainedInstruction(reference, calls.map(call => call.params()), span).toStmt();
|
17296 | });
|
17297 | }
|
17298 | updateInstructionWithAdvance(nodeIndex, span, reference, paramsOrFn) {
|
17299 | this.addAdvanceInstructionIfNecessary(nodeIndex, span);
|
17300 | this.updateInstruction(span, reference, paramsOrFn);
|
17301 | }
|
17302 | updateInstruction(span, reference, paramsOrFn) {
|
17303 | this.instructionFn(this._updateCodeFns, span, reference, paramsOrFn || []);
|
17304 | }
|
17305 | updateInstructionChain(reference, bindings) {
|
17306 | const span = bindings.length ? bindings[0].sourceSpan : null;
|
17307 | this._updateCodeFns.push(() => {
|
17308 | const calls = bindings.map(property => {
|
17309 | const value = property.value();
|
17310 | const fnParams = Array.isArray(value) ? value : [value];
|
17311 | if (property.params) {
|
17312 | fnParams.push(...property.params);
|
17313 | }
|
17314 | if (property.name) {
|
17315 | // We want the property name to always be the first function parameter.
|
17316 | fnParams.unshift(literal(property.name));
|
17317 | }
|
17318 | return fnParams;
|
17319 | });
|
17320 | return chainedInstruction(reference, calls, span).toStmt();
|
17321 | });
|
17322 | }
|
17323 | updateInstructionChainWithAdvance(nodeIndex, reference, bindings) {
|
17324 | this.addAdvanceInstructionIfNecessary(nodeIndex, bindings.length ? bindings[0].sourceSpan : null);
|
17325 | this.updateInstructionChain(reference, bindings);
|
17326 | }
|
17327 | addAdvanceInstructionIfNecessary(nodeIndex, span) {
|
17328 | if (nodeIndex !== this._currentIndex) {
|
17329 | const delta = nodeIndex - this._currentIndex;
|
17330 | if (delta < 1) {
|
17331 | throw new Error('advance instruction can only go forwards');
|
17332 | }
|
17333 | this.instructionFn(this._updateCodeFns, span, Identifiers$1.advance, [literal(delta)]);
|
17334 | this._currentIndex = nodeIndex;
|
17335 | }
|
17336 | }
|
17337 | allocatePureFunctionSlots(numSlots) {
|
17338 | const originalSlots = this._pureFunctionSlots;
|
17339 | this._pureFunctionSlots += numSlots;
|
17340 | return originalSlots;
|
17341 | }
|
17342 | allocateBindingSlots(value) {
|
17343 | this._bindingSlots += value instanceof Interpolation ? value.expressions.length : 1;
|
17344 | }
|
17345 | /**
|
17346 | * Gets an expression that refers to the implicit receiver. The implicit
|
17347 | * receiver is always the root level context.
|
17348 | */
|
17349 | getImplicitReceiverExpr() {
|
17350 | if (this._implicitReceiverExpr) {
|
17351 | return this._implicitReceiverExpr;
|
17352 | }
|
17353 | return this._implicitReceiverExpr = this.level === 0 ?
|
17354 | variable(CONTEXT_NAME) :
|
17355 | this._bindingScope.getOrCreateSharedContextVar(0);
|
17356 | }
|
17357 | convertPropertyBinding(value) {
|
17358 | const convertedPropertyBinding = convertPropertyBinding(this, this.getImplicitReceiverExpr(), value, this.bindingContext(), BindingForm.Expression, () => error('Unexpected interpolation'));
|
17359 | const valExpr = convertedPropertyBinding.currValExpr;
|
17360 | this._tempVariables.push(...convertedPropertyBinding.stmts);
|
17361 | return valExpr;
|
17362 | }
|
17363 | /**
|
17364 | * Gets a list of argument expressions to pass to an update instruction expression. Also updates
|
17365 | * the temp variables state with temp variables that were identified as needing to be created
|
17366 | * while visiting the arguments.
|
17367 | * @param value The original expression we will be resolving an arguments list from.
|
17368 | */
|
17369 | getUpdateInstructionArguments(value) {
|
17370 | const { args, stmts } = convertUpdateArguments(this, this.getImplicitReceiverExpr(), value, this.bindingContext());
|
17371 | this._tempVariables.push(...stmts);
|
17372 | return args;
|
17373 | }
|
17374 | matchDirectives(elementName, elOrTpl) {
|
17375 | if (this.directiveMatcher) {
|
17376 | const selector = createCssSelector(elementName, getAttrsForDirectiveMatching(elOrTpl));
|
17377 | this.directiveMatcher.match(selector, (cssSelector, staticType) => {
|
17378 | this.directives.add(staticType);
|
17379 | });
|
17380 | }
|
17381 | }
|
17382 | /**
|
17383 | * Prepares all attribute expression values for the `TAttributes` array.
|
17384 | *
|
17385 | * The purpose of this function is to properly construct an attributes array that
|
17386 | * is passed into the `elementStart` (or just `element`) functions. Because there
|
17387 | * are many different types of attributes, the array needs to be constructed in a
|
17388 | * special way so that `elementStart` can properly evaluate them.
|
17389 | *
|
17390 | * The format looks like this:
|
17391 | *
|
17392 | * ```
|
17393 | * attrs = [prop, value, prop2, value2,
|
17394 | * PROJECT_AS, selector,
|
17395 | * CLASSES, class1, class2,
|
17396 | * STYLES, style1, value1, style2, value2,
|
17397 | * BINDINGS, name1, name2, name3,
|
17398 | * TEMPLATE, name4, name5, name6,
|
17399 | * I18N, name7, name8, ...]
|
17400 | * ```
|
17401 | *
|
17402 | * Note that this function will fully ignore all synthetic (@foo) attribute values
|
17403 | * because those values are intended to always be generated as property instructions.
|
17404 | */
|
17405 | getAttributeExpressions(elementName, renderAttributes, inputs, outputs, styles, templateAttrs = [], boundI18nAttrs = []) {
|
17406 | const alreadySeen = new Set();
|
17407 | const attrExprs = [];
|
17408 | let ngProjectAsAttr;
|
17409 | for (const attr of renderAttributes) {
|
17410 | if (attr.name === NG_PROJECT_AS_ATTR_NAME) {
|
17411 | ngProjectAsAttr = attr;
|
17412 | }
|
17413 | // Note that static i18n attributes aren't in the i18n array,
|
17414 | // because they're treated in the same way as regular attributes.
|
17415 | if (attr.i18n) {
|
17416 | // When i18n attributes are present on elements with structural directives
|
17417 | // (e.g. `<div *ngIf title="Hello" i18n-title>`), we want to avoid generating
|
17418 | // duplicate i18n translation blocks for `ɵɵtemplate` and `ɵɵelement` instruction
|
17419 | // attributes. So we do a cache lookup to see if suitable i18n translation block
|
17420 | // already exists.
|
17421 | const { i18nVarRefsCache } = this._constants;
|
17422 | let i18nVarRef;
|
17423 | if (i18nVarRefsCache.has(attr.i18n)) {
|
17424 | i18nVarRef = i18nVarRefsCache.get(attr.i18n);
|
17425 | }
|
17426 | else {
|
17427 | i18nVarRef = this.i18nTranslate(attr.i18n);
|
17428 | i18nVarRefsCache.set(attr.i18n, i18nVarRef);
|
17429 | }
|
17430 | attrExprs.push(literal(attr.name), i18nVarRef);
|
17431 | }
|
17432 | else {
|
17433 | attrExprs.push(...getAttributeNameLiterals(attr.name), trustedConstAttribute(elementName, attr));
|
17434 | }
|
17435 | }
|
17436 | // Keep ngProjectAs next to the other name, value pairs so we can verify that we match
|
17437 | // ngProjectAs marker in the attribute name slot.
|
17438 | if (ngProjectAsAttr) {
|
17439 | attrExprs.push(...getNgProjectAsLiteral(ngProjectAsAttr));
|
17440 | }
|
17441 | function addAttrExpr(key, value) {
|
17442 | if (typeof key === 'string') {
|
17443 | if (!alreadySeen.has(key)) {
|
17444 | attrExprs.push(...getAttributeNameLiterals(key));
|
17445 | value !== undefined && attrExprs.push(value);
|
17446 | alreadySeen.add(key);
|
17447 | }
|
17448 | }
|
17449 | else {
|
17450 | attrExprs.push(literal(key));
|
17451 | }
|
17452 | }
|
17453 | // it's important that this occurs before BINDINGS and TEMPLATE because once `elementStart`
|
17454 | // comes across the BINDINGS or TEMPLATE markers then it will continue reading each value as
|
17455 | // as single property value cell by cell.
|
17456 | if (styles) {
|
17457 | styles.populateInitialStylingAttrs(attrExprs);
|
17458 | }
|
17459 | if (inputs.length || outputs.length) {
|
17460 | const attrsLengthBeforeInputs = attrExprs.length;
|
17461 | for (let i = 0; i < inputs.length; i++) {
|
17462 | const input = inputs[i];
|
17463 | // We don't want the animation and attribute bindings in the
|
17464 | // attributes array since they aren't used for directive matching.
|
17465 | if (input.type !== 4 /* Animation */ && input.type !== 1 /* Attribute */) {
|
17466 | addAttrExpr(input.name);
|
17467 | }
|
17468 | }
|
17469 | for (let i = 0; i < outputs.length; i++) {
|
17470 | const output = outputs[i];
|
17471 | if (output.type !== 1 /* Animation */) {
|
17472 | addAttrExpr(output.name);
|
17473 | }
|
17474 | }
|
17475 | // this is a cheap way of adding the marker only after all the input/output
|
17476 | // values have been filtered (by not including the animation ones) and added
|
17477 | // to the expressions. The marker is important because it tells the runtime
|
17478 | // code that this is where attributes without values start...
|
17479 | if (attrExprs.length !== attrsLengthBeforeInputs) {
|
17480 | attrExprs.splice(attrsLengthBeforeInputs, 0, literal(3 /* Bindings */));
|
17481 | }
|
17482 | }
|
17483 | if (templateAttrs.length) {
|
17484 | attrExprs.push(literal(4 /* Template */));
|
17485 | templateAttrs.forEach(attr => addAttrExpr(attr.name));
|
17486 | }
|
17487 | if (boundI18nAttrs.length) {
|
17488 | attrExprs.push(literal(6 /* I18n */));
|
17489 | boundI18nAttrs.forEach(attr => addAttrExpr(attr.name));
|
17490 | }
|
17491 | return attrExprs;
|
17492 | }
|
17493 | addToConsts(expression) {
|
17494 | if (isNull(expression)) {
|
17495 | return TYPED_NULL_EXPR;
|
17496 | }
|
17497 | const consts = this._constants.constExpressions;
|
17498 | // Try to reuse a literal that's already in the array, if possible.
|
17499 | for (let i = 0; i < consts.length; i++) {
|
17500 | if (consts[i].isEquivalent(expression)) {
|
17501 | return literal(i);
|
17502 | }
|
17503 | }
|
17504 | return literal(consts.push(expression) - 1);
|
17505 | }
|
17506 | addAttrsToConsts(attrs) {
|
17507 | return attrs.length > 0 ? this.addToConsts(literalArr(attrs)) : TYPED_NULL_EXPR;
|
17508 | }
|
17509 | prepareRefsArray(references) {
|
17510 | if (!references || references.length === 0) {
|
17511 | return TYPED_NULL_EXPR;
|
17512 | }
|
17513 | const refsParam = flatten(references.map(reference => {
|
17514 | const slot = this.allocateDataSlot();
|
17515 | // Generate the update temporary.
|
17516 | const variableName = this._bindingScope.freshReferenceName();
|
17517 | const retrievalLevel = this.level;
|
17518 | const lhs = variable(variableName);
|
17519 | this._bindingScope.set(retrievalLevel, reference.name, lhs, 0 /* DEFAULT */, (scope, relativeLevel) => {
|
17520 | // e.g. nextContext(2);
|
17521 | const nextContextStmt = relativeLevel > 0 ? [generateNextContextExpr(relativeLevel).toStmt()] : [];
|
17522 | // e.g. const $foo$ = reference(1);
|
17523 | const refExpr = lhs.set(importExpr(Identifiers$1.reference).callFn([literal(slot)]));
|
17524 | return nextContextStmt.concat(refExpr.toConstDecl());
|
17525 | }, true);
|
17526 | return [reference.name, reference.value];
|
17527 | }));
|
17528 | return asLiteral(refsParam);
|
17529 | }
|
17530 | prepareListenerParameter(tagName, outputAst, index) {
|
17531 | return () => {
|
17532 | const eventName = outputAst.name;
|
17533 | const bindingFnName = outputAst.type === 1 /* Animation */ ?
|
17534 | // synthetic @listener.foo values are treated the exact same as are standard listeners
|
17535 | prepareSyntheticListenerFunctionName(eventName, outputAst.phase) :
|
17536 | sanitizeIdentifier(eventName);
|
17537 | const handlerName = `${this.templateName}_${tagName}_${bindingFnName}_${index}_listener`;
|
17538 | const scope = this._bindingScope.nestedScope(this._bindingScope.bindingLevel, EVENT_BINDING_SCOPE_GLOBALS);
|
17539 | return prepareEventListenerParameters(outputAst, handlerName, scope);
|
17540 | };
|
17541 | }
|
17542 | }
|
17543 | class ValueConverter extends AstMemoryEfficientTransformer {
|
17544 | constructor(constantPool, allocateSlot, allocatePureFunctionSlots, definePipe) {
|
17545 | super();
|
17546 | this.constantPool = constantPool;
|
17547 | this.allocateSlot = allocateSlot;
|
17548 | this.allocatePureFunctionSlots = allocatePureFunctionSlots;
|
17549 | this.definePipe = definePipe;
|
17550 | this._pipeBindExprs = [];
|
17551 | }
|
17552 | // AstMemoryEfficientTransformer
|
17553 | visitPipe(pipe, context) {
|
17554 | // Allocate a slot to create the pipe
|
17555 | const slot = this.allocateSlot();
|
17556 | const slotPseudoLocal = `PIPE:${slot}`;
|
17557 | // Allocate one slot for the result plus one slot per pipe argument
|
17558 | const pureFunctionSlot = this.allocatePureFunctionSlots(2 + pipe.args.length);
|
17559 | const target = new PropertyRead(pipe.span, pipe.sourceSpan, pipe.nameSpan, new ImplicitReceiver(pipe.span, pipe.sourceSpan), slotPseudoLocal);
|
17560 | const { identifier, isVarLength } = pipeBindingCallInfo(pipe.args);
|
17561 | this.definePipe(pipe.name, slotPseudoLocal, slot, importExpr(identifier));
|
17562 | const args = [pipe.exp, ...pipe.args];
|
17563 | const convertedArgs = isVarLength ?
|
17564 | this.visitAll([new LiteralArray(pipe.span, pipe.sourceSpan, args)]) :
|
17565 | this.visitAll(args);
|
17566 | const pipeBindExpr = new FunctionCall(pipe.span, pipe.sourceSpan, target, [
|
17567 | new LiteralPrimitive(pipe.span, pipe.sourceSpan, slot),
|
17568 | new LiteralPrimitive(pipe.span, pipe.sourceSpan, pureFunctionSlot),
|
17569 | ...convertedArgs,
|
17570 | ]);
|
17571 | this._pipeBindExprs.push(pipeBindExpr);
|
17572 | return pipeBindExpr;
|
17573 | }
|
17574 | updatePipeSlotOffsets(bindingSlots) {
|
17575 | this._pipeBindExprs.forEach((pipe) => {
|
17576 | // update the slot offset arg (index 1) to account for binding slots
|
17577 | const slotOffset = pipe.args[1];
|
17578 | slotOffset.value += bindingSlots;
|
17579 | });
|
17580 | }
|
17581 | visitLiteralArray(array, context) {
|
17582 | return new BuiltinFunctionCall(array.span, array.sourceSpan, this.visitAll(array.expressions), values => {
|
17583 | // If the literal has calculated (non-literal) elements transform it into
|
17584 | // calls to literal factories that compose the literal and will cache intermediate
|
17585 | // values.
|
17586 | const literal = literalArr(values);
|
17587 | return getLiteralFactory(this.constantPool, literal, this.allocatePureFunctionSlots);
|
17588 | });
|
17589 | }
|
17590 | visitLiteralMap(map, context) {
|
17591 | return new BuiltinFunctionCall(map.span, map.sourceSpan, this.visitAll(map.values), values => {
|
17592 | // If the literal has calculated (non-literal) elements transform it into
|
17593 | // calls to literal factories that compose the literal and will cache intermediate
|
17594 | // values.
|
17595 | const literal = literalMap(values.map((value, index) => ({ key: map.keys[index].key, value, quoted: map.keys[index].quoted })));
|
17596 | return getLiteralFactory(this.constantPool, literal, this.allocatePureFunctionSlots);
|
17597 | });
|
17598 | }
|
17599 | }
|
17600 | // Pipes always have at least one parameter, the value they operate on
|
17601 | const pipeBindingIdentifiers = [Identifiers$1.pipeBind1, Identifiers$1.pipeBind2, Identifiers$1.pipeBind3, Identifiers$1.pipeBind4];
|
17602 | function pipeBindingCallInfo(args) {
|
17603 | const identifier = pipeBindingIdentifiers[args.length];
|
17604 | return {
|
17605 | identifier: identifier || Identifiers$1.pipeBindV,
|
17606 | isVarLength: !identifier,
|
17607 | };
|
17608 | }
|
17609 | const pureFunctionIdentifiers = [
|
17610 | Identifiers$1.pureFunction0, Identifiers$1.pureFunction1, Identifiers$1.pureFunction2, Identifiers$1.pureFunction3, Identifiers$1.pureFunction4,
|
17611 | Identifiers$1.pureFunction5, Identifiers$1.pureFunction6, Identifiers$1.pureFunction7, Identifiers$1.pureFunction8
|
17612 | ];
|
17613 | function pureFunctionCallInfo(args) {
|
17614 | const identifier = pureFunctionIdentifiers[args.length];
|
17615 | return {
|
17616 | identifier: identifier || Identifiers$1.pureFunctionV,
|
17617 | isVarLength: !identifier,
|
17618 | };
|
17619 | }
|
17620 | function instruction(span, reference, params) {
|
17621 | return importExpr(reference, null, span).callFn(params, span);
|
17622 | }
|
17623 | // e.g. x(2);
|
17624 | function generateNextContextExpr(relativeLevelDiff) {
|
17625 | return importExpr(Identifiers$1.nextContext)
|
17626 | .callFn(relativeLevelDiff > 1 ? [literal(relativeLevelDiff)] : []);
|
17627 | }
|
17628 | function getLiteralFactory(constantPool, literal$1, allocateSlots) {
|
17629 | const { literalFactory, literalFactoryArguments } = constantPool.getLiteralFactory(literal$1);
|
17630 | // Allocate 1 slot for the result plus 1 per argument
|
17631 | const startSlot = allocateSlots(1 + literalFactoryArguments.length);
|
17632 | const { identifier, isVarLength } = pureFunctionCallInfo(literalFactoryArguments);
|
17633 | // Literal factories are pure functions that only need to be re-invoked when the parameters
|
17634 | // change.
|
17635 | const args = [literal(startSlot), literalFactory];
|
17636 | if (isVarLength) {
|
17637 | args.push(literalArr(literalFactoryArguments));
|
17638 | }
|
17639 | else {
|
17640 | args.push(...literalFactoryArguments);
|
17641 | }
|
17642 | return importExpr(identifier).callFn(args);
|
17643 | }
|
17644 | /**
|
17645 | * Gets an array of literals that can be added to an expression
|
17646 | * to represent the name and namespace of an attribute. E.g.
|
17647 | * `:xlink:href` turns into `[AttributeMarker.NamespaceURI, 'xlink', 'href']`.
|
17648 | *
|
17649 | * @param name Name of the attribute, including the namespace.
|
17650 | */
|
17651 | function getAttributeNameLiterals(name) {
|
17652 | const [attributeNamespace, attributeName] = splitNsName(name);
|
17653 | const nameLiteral = literal(attributeName);
|
17654 | if (attributeNamespace) {
|
17655 | return [
|
17656 | literal(0 /* NamespaceURI */), literal(attributeNamespace), nameLiteral
|
17657 | ];
|
17658 | }
|
17659 | return [nameLiteral];
|
17660 | }
|
17661 | /** The prefix used to get a shared context in BindingScope's map. */
|
17662 | const SHARED_CONTEXT_KEY = '$$shared_ctx$$';
|
17663 | class BindingScope {
|
17664 | constructor(bindingLevel = 0, parent = null, globals) {
|
17665 | this.bindingLevel = bindingLevel;
|
17666 | this.parent = parent;
|
17667 | this.globals = globals;
|
17668 | /** Keeps a map from local variables to their BindingData. */
|
17669 | this.map = new Map();
|
17670 | this.referenceNameIndex = 0;
|
17671 | this.restoreViewVariable = null;
|
17672 | if (globals !== undefined) {
|
17673 | for (const name of globals) {
|
17674 | this.set(0, name, variable(name));
|
17675 | }
|
17676 | }
|
17677 | }
|
17678 | static createRootScope() {
|
17679 | return new BindingScope();
|
17680 | }
|
17681 | get(name) {
|
17682 | let current = this;
|
17683 | while (current) {
|
17684 | let value = current.map.get(name);
|
17685 | if (value != null) {
|
17686 | if (current !== this) {
|
17687 | // make a local copy and reset the `declare` state
|
17688 | value = {
|
17689 | retrievalLevel: value.retrievalLevel,
|
17690 | lhs: value.lhs,
|
17691 | declareLocalCallback: value.declareLocalCallback,
|
17692 | declare: false,
|
17693 | priority: value.priority,
|
17694 | localRef: value.localRef
|
17695 | };
|
17696 | // Cache the value locally.
|
17697 | this.map.set(name, value);
|
17698 | // Possibly generate a shared context var
|
17699 | this.maybeGenerateSharedContextVar(value);
|
17700 | this.maybeRestoreView(value.retrievalLevel, value.localRef);
|
17701 | }
|
17702 | if (value.declareLocalCallback && !value.declare) {
|
17703 | value.declare = true;
|
17704 | }
|
17705 | return value.lhs;
|
17706 | }
|
17707 | current = current.parent;
|
17708 | }
|
17709 | // If we get to this point, we are looking for a property on the top level component
|
17710 | // - If level === 0, we are on the top and don't need to re-declare `ctx`.
|
17711 | // - If level > 0, we are in an embedded view. We need to retrieve the name of the
|
17712 | // local var we used to store the component context, e.g. const $comp$ = x();
|
17713 | return this.bindingLevel === 0 ? null : this.getComponentProperty(name);
|
17714 | }
|
17715 | /**
|
17716 | * Create a local variable for later reference.
|
17717 | *
|
17718 | * @param retrievalLevel The level from which this value can be retrieved
|
17719 | * @param name Name of the variable.
|
17720 | * @param lhs AST representing the left hand side of the `let lhs = rhs;`.
|
17721 | * @param priority The sorting priority of this var
|
17722 | * @param declareLocalCallback The callback to invoke when declaring this local var
|
17723 | * @param localRef Whether or not this is a local ref
|
17724 | */
|
17725 | set(retrievalLevel, name, lhs, priority = 0 /* DEFAULT */, declareLocalCallback, localRef) {
|
17726 | if (this.map.has(name)) {
|
17727 | if (localRef) {
|
17728 | // Do not throw an error if it's a local ref and do not update existing value,
|
17729 | // so the first defined ref is always returned.
|
17730 | return this;
|
17731 | }
|
17732 | error(`The name ${name} is already defined in scope to be ${this.map.get(name)}`);
|
17733 | }
|
17734 | this.map.set(name, {
|
17735 | retrievalLevel: retrievalLevel,
|
17736 | lhs: lhs,
|
17737 | declare: false,
|
17738 | declareLocalCallback: declareLocalCallback,
|
17739 | priority: priority,
|
17740 | localRef: localRef || false
|
17741 | });
|
17742 | return this;
|
17743 | }
|
17744 | // Implemented as part of LocalResolver.
|
17745 | getLocal(name) {
|
17746 | return this.get(name);
|
17747 | }
|
17748 | // Implemented as part of LocalResolver.
|
17749 | notifyImplicitReceiverUse() {
|
17750 | if (this.bindingLevel !== 0) {
|
17751 | // Since the implicit receiver is accessed in an embedded view, we need to
|
17752 | // ensure that we declare a shared context variable for the current template
|
17753 | // in the update variables.
|
17754 | this.map.get(SHARED_CONTEXT_KEY + 0).declare = true;
|
17755 | }
|
17756 | }
|
17757 | nestedScope(level, globals) {
|
17758 | const newScope = new BindingScope(level, this, globals);
|
17759 | if (level > 0)
|
17760 | newScope.generateSharedContextVar(0);
|
17761 | return newScope;
|
17762 | }
|
17763 | /**
|
17764 | * Gets or creates a shared context variable and returns its expression. Note that
|
17765 | * this does not mean that the shared variable will be declared. Variables in the
|
17766 | * binding scope will be only declared if they are used.
|
17767 | */
|
17768 | getOrCreateSharedContextVar(retrievalLevel) {
|
17769 | const bindingKey = SHARED_CONTEXT_KEY + retrievalLevel;
|
17770 | if (!this.map.has(bindingKey)) {
|
17771 | this.generateSharedContextVar(retrievalLevel);
|
17772 | }
|
17773 | // Shared context variables are always generated as "ReadVarExpr".
|
17774 | return this.map.get(bindingKey).lhs;
|
17775 | }
|
17776 | getSharedContextName(retrievalLevel) {
|
17777 | const sharedCtxObj = this.map.get(SHARED_CONTEXT_KEY + retrievalLevel);
|
17778 | // Shared context variables are always generated as "ReadVarExpr".
|
17779 | return sharedCtxObj && sharedCtxObj.declare ? sharedCtxObj.lhs : null;
|
17780 | }
|
17781 | maybeGenerateSharedContextVar(value) {
|
17782 | if (value.priority === 1 /* CONTEXT */ &&
|
17783 | value.retrievalLevel < this.bindingLevel) {
|
17784 | const sharedCtxObj = this.map.get(SHARED_CONTEXT_KEY + value.retrievalLevel);
|
17785 | if (sharedCtxObj) {
|
17786 | sharedCtxObj.declare = true;
|
17787 | }
|
17788 | else {
|
17789 | this.generateSharedContextVar(value.retrievalLevel);
|
17790 | }
|
17791 | }
|
17792 | }
|
17793 | generateSharedContextVar(retrievalLevel) {
|
17794 | const lhs = variable(CONTEXT_NAME + this.freshReferenceName());
|
17795 | this.map.set(SHARED_CONTEXT_KEY + retrievalLevel, {
|
17796 | retrievalLevel: retrievalLevel,
|
17797 | lhs: lhs,
|
17798 | declareLocalCallback: (scope, relativeLevel) => {
|
17799 | // const ctx_r0 = nextContext(2);
|
17800 | return [lhs.set(generateNextContextExpr(relativeLevel)).toConstDecl()];
|
17801 | },
|
17802 | declare: false,
|
17803 | priority: 2 /* SHARED_CONTEXT */,
|
17804 | localRef: false
|
17805 | });
|
17806 | }
|
17807 | getComponentProperty(name) {
|
17808 | const componentValue = this.map.get(SHARED_CONTEXT_KEY + 0);
|
17809 | componentValue.declare = true;
|
17810 | this.maybeRestoreView(0, false);
|
17811 | return componentValue.lhs.prop(name);
|
17812 | }
|
17813 | maybeRestoreView(retrievalLevel, localRefLookup) {
|
17814 | // We want to restore the current view in listener fns if:
|
17815 | // 1 - we are accessing a value in a parent view, which requires walking the view tree rather
|
17816 | // than using the ctx arg. In this case, the retrieval and binding level will be different.
|
17817 | // 2 - we are looking up a local ref, which requires restoring the view where the local
|
17818 | // ref is stored
|
17819 | if (this.isListenerScope() && (retrievalLevel < this.bindingLevel || localRefLookup)) {
|
17820 | if (!this.parent.restoreViewVariable) {
|
17821 | // parent saves variable to generate a shared `const $s$ = getCurrentView();` instruction
|
17822 | this.parent.restoreViewVariable = variable(this.parent.freshReferenceName());
|
17823 | }
|
17824 | this.restoreViewVariable = this.parent.restoreViewVariable;
|
17825 | }
|
17826 | }
|
17827 | restoreViewStatement() {
|
17828 | // restoreView($state$);
|
17829 | return this.restoreViewVariable ?
|
17830 | [instruction(null, Identifiers$1.restoreView, [this.restoreViewVariable]).toStmt()] :
|
17831 | [];
|
17832 | }
|
17833 | viewSnapshotStatements() {
|
17834 | // const $state$ = getCurrentView();
|
17835 | const getCurrentViewInstruction = instruction(null, Identifiers$1.getCurrentView, []);
|
17836 | return this.restoreViewVariable ?
|
17837 | [this.restoreViewVariable.set(getCurrentViewInstruction).toConstDecl()] :
|
17838 | [];
|
17839 | }
|
17840 | isListenerScope() {
|
17841 | return this.parent && this.parent.bindingLevel === this.bindingLevel;
|
17842 | }
|
17843 | variableDeclarations() {
|
17844 | let currentContextLevel = 0;
|
17845 | return Array.from(this.map.values())
|
17846 | .filter(value => value.declare)
|
17847 | .sort((a, b) => b.retrievalLevel - a.retrievalLevel || b.priority - a.priority)
|
17848 | .reduce((stmts, value) => {
|
17849 | const levelDiff = this.bindingLevel - value.retrievalLevel;
|
17850 | const currStmts = value.declareLocalCallback(this, levelDiff - currentContextLevel);
|
17851 | currentContextLevel = levelDiff;
|
17852 | return stmts.concat(currStmts);
|
17853 | }, []);
|
17854 | }
|
17855 | freshReferenceName() {
|
17856 | let current = this;
|
17857 | // Find the top scope as it maintains the global reference count
|
17858 | while (current.parent)
|
17859 | current = current.parent;
|
17860 | const ref = `${REFERENCE_PREFIX}${current.referenceNameIndex++}`;
|
17861 | return ref;
|
17862 | }
|
17863 | }
|
17864 | /**
|
17865 | * Creates a `CssSelector` given a tag name and a map of attributes
|
17866 | */
|
17867 | function createCssSelector(elementName, attributes) {
|
17868 | const cssSelector = new CssSelector();
|
17869 | const elementNameNoNs = splitNsName(elementName)[1];
|
17870 | cssSelector.setElement(elementNameNoNs);
|
17871 | Object.getOwnPropertyNames(attributes).forEach((name) => {
|
17872 | const nameNoNs = splitNsName(name)[1];
|
17873 | const value = attributes[name];
|
17874 | cssSelector.addAttribute(nameNoNs, value);
|
17875 | if (name.toLowerCase() === 'class') {
|
17876 | const classes = value.trim().split(/\s+/);
|
17877 | classes.forEach(className => cssSelector.addClassName(className));
|
17878 | }
|
17879 | });
|
17880 | return cssSelector;
|
17881 | }
|
17882 | /**
|
17883 | * Creates an array of expressions out of an `ngProjectAs` attributes
|
17884 | * which can be added to the instruction parameters.
|
17885 | */
|
17886 | function getNgProjectAsLiteral(attribute) {
|
17887 | // Parse the attribute value into a CssSelectorList. Note that we only take the
|
17888 | // first selector, because we don't support multiple selectors in ngProjectAs.
|
17889 | const parsedR3Selector = parseSelectorToR3Selector(attribute.value)[0];
|
17890 | return [literal(5 /* ProjectAs */), asLiteral(parsedR3Selector)];
|
17891 | }
|
17892 | /**
|
17893 | * Gets the instruction to generate for an interpolated property
|
17894 | * @param interpolation An Interpolation AST
|
17895 | */
|
17896 | function getPropertyInterpolationExpression(interpolation) {
|
17897 | switch (getInterpolationArgsLength(interpolation)) {
|
17898 | case 1:
|
17899 | return Identifiers$1.propertyInterpolate;
|
17900 | case 3:
|
17901 | return Identifiers$1.propertyInterpolate1;
|
17902 | case 5:
|
17903 | return Identifiers$1.propertyInterpolate2;
|
17904 | case 7:
|
17905 | return Identifiers$1.propertyInterpolate3;
|
17906 | case 9:
|
17907 | return Identifiers$1.propertyInterpolate4;
|
17908 | case 11:
|
17909 | return Identifiers$1.propertyInterpolate5;
|
17910 | case 13:
|
17911 | return Identifiers$1.propertyInterpolate6;
|
17912 | case 15:
|
17913 | return Identifiers$1.propertyInterpolate7;
|
17914 | case 17:
|
17915 | return Identifiers$1.propertyInterpolate8;
|
17916 | default:
|
17917 | return Identifiers$1.propertyInterpolateV;
|
17918 | }
|
17919 | }
|
17920 | /**
|
17921 | * Gets the instruction to generate for an interpolated attribute
|
17922 | * @param interpolation An Interpolation AST
|
17923 | */
|
17924 | function getAttributeInterpolationExpression(interpolation) {
|
17925 | switch (getInterpolationArgsLength(interpolation)) {
|
17926 | case 3:
|
17927 | return Identifiers$1.attributeInterpolate1;
|
17928 | case 5:
|
17929 | return Identifiers$1.attributeInterpolate2;
|
17930 | case 7:
|
17931 | return Identifiers$1.attributeInterpolate3;
|
17932 | case 9:
|
17933 | return Identifiers$1.attributeInterpolate4;
|
17934 | case 11:
|
17935 | return Identifiers$1.attributeInterpolate5;
|
17936 | case 13:
|
17937 | return Identifiers$1.attributeInterpolate6;
|
17938 | case 15:
|
17939 | return Identifiers$1.attributeInterpolate7;
|
17940 | case 17:
|
17941 | return Identifiers$1.attributeInterpolate8;
|
17942 | default:
|
17943 | return Identifiers$1.attributeInterpolateV;
|
17944 | }
|
17945 | }
|
17946 | /**
|
17947 | * Gets the instruction to generate for interpolated text.
|
17948 | * @param interpolation An Interpolation AST
|
17949 | */
|
17950 | function getTextInterpolationExpression(interpolation) {
|
17951 | switch (getInterpolationArgsLength(interpolation)) {
|
17952 | case 1:
|
17953 | return Identifiers$1.textInterpolate;
|
17954 | case 3:
|
17955 | return Identifiers$1.textInterpolate1;
|
17956 | case 5:
|
17957 | return Identifiers$1.textInterpolate2;
|
17958 | case 7:
|
17959 | return Identifiers$1.textInterpolate3;
|
17960 | case 9:
|
17961 | return Identifiers$1.textInterpolate4;
|
17962 | case 11:
|
17963 | return Identifiers$1.textInterpolate5;
|
17964 | case 13:
|
17965 | return Identifiers$1.textInterpolate6;
|
17966 | case 15:
|
17967 | return Identifiers$1.textInterpolate7;
|
17968 | case 17:
|
17969 | return Identifiers$1.textInterpolate8;
|
17970 | default:
|
17971 | return Identifiers$1.textInterpolateV;
|
17972 | }
|
17973 | }
|
17974 | /**
|
17975 | * Parse a template into render3 `Node`s and additional metadata, with no other dependencies.
|
17976 | *
|
17977 | * @param template text of the template to parse
|
17978 | * @param templateUrl URL to use for source mapping of the parsed template
|
17979 | * @param options options to modify how the template is parsed
|
17980 | */
|
17981 | function parseTemplate(template, templateUrl, options = {}) {
|
17982 | var _a;
|
17983 | const { interpolationConfig, preserveWhitespaces, enableI18nLegacyMessageIdFormat } = options;
|
17984 | const isInline = (_a = options.isInline) !== null && _a !== void 0 ? _a : false;
|
17985 | const bindingParser = makeBindingParser(interpolationConfig);
|
17986 | const htmlParser = new HtmlParser();
|
17987 | const parseResult = htmlParser.parse(template, templateUrl, Object.assign(Object.assign({ leadingTriviaChars: LEADING_TRIVIA_CHARS }, options), { tokenizeExpansionForms: true }));
|
17988 | if (!options.alwaysAttemptHtmlToR3AstConversion && parseResult.errors &&
|
17989 | parseResult.errors.length > 0) {
|
17990 | return {
|
17991 | interpolationConfig,
|
17992 | preserveWhitespaces,
|
17993 | template,
|
17994 | templateUrl,
|
17995 | isInline,
|
17996 | errors: parseResult.errors,
|
17997 | nodes: [],
|
17998 | styleUrls: [],
|
17999 | styles: [],
|
18000 | ngContentSelectors: []
|
18001 | };
|
18002 | }
|
18003 | let rootNodes = parseResult.rootNodes;
|
18004 | // process i18n meta information (scan attributes, generate ids)
|
18005 | // before we run whitespace removal process, because existing i18n
|
18006 | // extraction process (ng extract-i18n) relies on a raw content to generate
|
18007 | // message ids
|
18008 | const i18nMetaVisitor = new I18nMetaVisitor(interpolationConfig, /* keepI18nAttrs */ !preserveWhitespaces, enableI18nLegacyMessageIdFormat);
|
18009 | const i18nMetaResult = i18nMetaVisitor.visitAllWithErrors(rootNodes);
|
18010 | if (!options.alwaysAttemptHtmlToR3AstConversion && i18nMetaResult.errors &&
|
18011 | i18nMetaResult.errors.length > 0) {
|
18012 | return {
|
18013 | interpolationConfig,
|
18014 | preserveWhitespaces,
|
18015 | template,
|
18016 | templateUrl,
|
18017 | isInline,
|
18018 | errors: i18nMetaResult.errors,
|
18019 | nodes: [],
|
18020 | styleUrls: [],
|
18021 | styles: [],
|
18022 | ngContentSelectors: []
|
18023 | };
|
18024 | }
|
18025 | rootNodes = i18nMetaResult.rootNodes;
|
18026 | if (!preserveWhitespaces) {
|
18027 | rootNodes = visitAll$1(new WhitespaceVisitor(), rootNodes);
|
18028 | // run i18n meta visitor again in case whitespaces are removed (because that might affect
|
18029 | // generated i18n message content) and first pass indicated that i18n content is present in a
|
18030 | // template. During this pass i18n IDs generated at the first pass will be preserved, so we can
|
18031 | // mimic existing extraction process (ng extract-i18n)
|
18032 | if (i18nMetaVisitor.hasI18nMeta) {
|
18033 | rootNodes = visitAll$1(new I18nMetaVisitor(interpolationConfig, /* keepI18nAttrs */ false), rootNodes);
|
18034 | }
|
18035 | }
|
18036 | const { nodes, errors, styleUrls, styles, ngContentSelectors } = htmlAstToRender3Ast(rootNodes, bindingParser);
|
18037 | errors.push(...parseResult.errors, ...i18nMetaResult.errors);
|
18038 | return {
|
18039 | interpolationConfig,
|
18040 | preserveWhitespaces,
|
18041 | errors: errors.length > 0 ? errors : null,
|
18042 | template,
|
18043 | templateUrl,
|
18044 | isInline,
|
18045 | nodes,
|
18046 | styleUrls,
|
18047 | styles,
|
18048 | ngContentSelectors
|
18049 | };
|
18050 | }
|
18051 | const elementRegistry = new DomElementSchemaRegistry();
|
18052 | /**
|
18053 | * Construct a `BindingParser` with a default configuration.
|
18054 | */
|
18055 | function makeBindingParser(interpolationConfig = DEFAULT_INTERPOLATION_CONFIG) {
|
18056 | return new BindingParser(new IvyParser(new Lexer()), interpolationConfig, elementRegistry, null, []);
|
18057 | }
|
18058 | function resolveSanitizationFn(context, isAttribute) {
|
18059 | switch (context) {
|
18060 | case SecurityContext.HTML:
|
18061 | return importExpr(Identifiers$1.sanitizeHtml);
|
18062 | case SecurityContext.SCRIPT:
|
18063 | return importExpr(Identifiers$1.sanitizeScript);
|
18064 | case SecurityContext.STYLE:
|
18065 | // the compiler does not fill in an instruction for [style.prop?] binding
|
18066 | // values because the style algorithm knows internally what props are subject
|
18067 | // to sanitization (only [attr.style] values are explicitly sanitized)
|
18068 | return isAttribute ? importExpr(Identifiers$1.sanitizeStyle) : null;
|
18069 | case SecurityContext.URL:
|
18070 | return importExpr(Identifiers$1.sanitizeUrl);
|
18071 | case SecurityContext.RESOURCE_URL:
|
18072 | return importExpr(Identifiers$1.sanitizeResourceUrl);
|
18073 | default:
|
18074 | return null;
|
18075 | }
|
18076 | }
|
18077 | function trustedConstAttribute(tagName, attr) {
|
18078 | const value = asLiteral(attr.value);
|
18079 | if (isTrustedTypesSink(tagName, attr.name)) {
|
18080 | switch (elementRegistry.securityContext(tagName, attr.name, /* isAttribute */ true)) {
|
18081 | case SecurityContext.HTML:
|
18082 | return taggedTemplate(importExpr(Identifiers$1.trustConstantHtml), new TemplateLiteral([new TemplateLiteralElement(attr.value)], []), undefined, attr.valueSpan);
|
18083 | // NB: no SecurityContext.SCRIPT here, as the corresponding tags are stripped by the compiler.
|
18084 | case SecurityContext.RESOURCE_URL:
|
18085 | return taggedTemplate(importExpr(Identifiers$1.trustConstantResourceUrl), new TemplateLiteral([new TemplateLiteralElement(attr.value)], []), undefined, attr.valueSpan);
|
18086 | default:
|
18087 | return value;
|
18088 | }
|
18089 | }
|
18090 | else {
|
18091 | return value;
|
18092 | }
|
18093 | }
|
18094 | function isSingleElementTemplate(children) {
|
18095 | return children.length === 1 && children[0] instanceof Element;
|
18096 | }
|
18097 | function isTextNode(node) {
|
18098 | return node instanceof Text || node instanceof BoundText || node instanceof Icu;
|
18099 | }
|
18100 | function hasTextChildrenOnly(children) {
|
18101 | return children.every(isTextNode);
|
18102 | }
|
18103 | /** Name of the global variable that is used to determine if we use Closure translations or not */
|
18104 | const NG_I18N_CLOSURE_MODE = 'ngI18nClosureMode';
|
18105 | /**
|
18106 | * Generate statements that define a given translation message.
|
18107 | *
|
18108 | * ```
|
18109 | * var I18N_1;
|
18110 | * if (typeof ngI18nClosureMode !== undefined && ngI18nClosureMode) {
|
18111 | * var MSG_EXTERNAL_XXX = goog.getMsg(
|
18112 | * "Some message with {$interpolation}!",
|
18113 | * { "interpolation": "\uFFFD0\uFFFD" }
|
18114 | * );
|
18115 | * I18N_1 = MSG_EXTERNAL_XXX;
|
18116 | * }
|
18117 | * else {
|
18118 | * I18N_1 = $localize`Some message with ${'\uFFFD0\uFFFD'}!`;
|
18119 | * }
|
18120 | * ```
|
18121 | *
|
18122 | * @param message The original i18n AST message node
|
18123 | * @param variable The variable that will be assigned the translation, e.g. `I18N_1`.
|
18124 | * @param closureVar The variable for Closure `goog.getMsg` calls, e.g. `MSG_EXTERNAL_XXX`.
|
18125 | * @param params Object mapping placeholder names to their values (e.g.
|
18126 | * `{ "interpolation": "\uFFFD0\uFFFD" }`).
|
18127 | * @param transformFn Optional transformation function that will be applied to the translation (e.g.
|
18128 | * post-processing).
|
18129 | * @returns An array of statements that defined a given translation.
|
18130 | */
|
18131 | function getTranslationDeclStmts(message, variable, closureVar, params = {}, transformFn) {
|
18132 | const statements = [
|
18133 | declareI18nVariable(variable),
|
18134 | ifStmt(createClosureModeGuard(), createGoogleGetMsgStatements(variable, message, closureVar, i18nFormatPlaceholderNames(params, /* useCamelCase */ true)), createLocalizeStatements(variable, message, i18nFormatPlaceholderNames(params, /* useCamelCase */ false))),
|
18135 | ];
|
18136 | if (transformFn) {
|
18137 | statements.push(new ExpressionStatement(variable.set(transformFn(variable))));
|
18138 | }
|
18139 | return statements;
|
18140 | }
|
18141 | /**
|
18142 | * Create the expression that will be used to guard the closure mode block
|
18143 | * It is equivalent to:
|
18144 | *
|
18145 | * ```
|
18146 | * typeof ngI18nClosureMode !== undefined && ngI18nClosureMode
|
18147 | * ```
|
18148 | */
|
18149 | function createClosureModeGuard() {
|
18150 | return typeofExpr(variable(NG_I18N_CLOSURE_MODE))
|
18151 | .notIdentical(literal('undefined', STRING_TYPE))
|
18152 | .and(variable(NG_I18N_CLOSURE_MODE));
|
18153 | }
|
18154 |
|
18155 | /**
|
18156 | * @license
|
18157 | * Copyright Google LLC All Rights Reserved.
|
18158 | *
|
18159 | * Use of this source code is governed by an MIT-style license that can be
|
18160 | * found in the LICENSE file at https://angular.io/license
|
18161 | */
|
18162 | // This regex matches any binding names that contain the "attr." prefix, e.g. "attr.required"
|
18163 | // If there is a match, the first matching group will contain the attribute name to bind.
|
18164 | const ATTR_REGEX = /attr\.([^\]]+)/;
|
18165 | function baseDirectiveFields(meta, constantPool, bindingParser) {
|
18166 | const definitionMap = new DefinitionMap();
|
18167 | const selectors = parseSelectorToR3Selector(meta.selector);
|
18168 | // e.g. `type: MyDirective`
|
18169 | definitionMap.set('type', meta.internalType);
|
18170 | // e.g. `selectors: [['', 'someDir', '']]`
|
18171 | if (selectors.length > 0) {
|
18172 | definitionMap.set('selectors', asLiteral(selectors));
|
18173 | }
|
18174 | if (meta.queries.length > 0) {
|
18175 | // e.g. `contentQueries: (rf, ctx, dirIndex) => { ... }
|
18176 | definitionMap.set('contentQueries', createContentQueriesFunction(meta.queries, constantPool, meta.name));
|
18177 | }
|
18178 | if (meta.viewQueries.length) {
|
18179 | definitionMap.set('viewQuery', createViewQueriesFunction(meta.viewQueries, constantPool, meta.name));
|
18180 | }
|
18181 | // e.g. `hostBindings: (rf, ctx) => { ... }
|
18182 | definitionMap.set('hostBindings', createHostBindingsFunction(meta.host, meta.typeSourceSpan, bindingParser, constantPool, meta.selector || '', meta.name, definitionMap));
|
18183 | // e.g 'inputs: {a: 'a'}`
|
18184 | definitionMap.set('inputs', conditionallyCreateMapObjectLiteral(meta.inputs, true));
|
18185 | // e.g 'outputs: {a: 'a'}`
|
18186 | definitionMap.set('outputs', conditionallyCreateMapObjectLiteral(meta.outputs));
|
18187 | if (meta.exportAs !== null) {
|
18188 | definitionMap.set('exportAs', literalArr(meta.exportAs.map(e => literal(e))));
|
18189 | }
|
18190 | return definitionMap;
|
18191 | }
|
18192 | /**
|
18193 | * Add features to the definition map.
|
18194 | */
|
18195 | function addFeatures(definitionMap, meta) {
|
18196 | // e.g. `features: [NgOnChangesFeature]`
|
18197 | const features = [];
|
18198 | const providers = meta.providers;
|
18199 | const viewProviders = meta.viewProviders;
|
18200 | if (providers || viewProviders) {
|
18201 | const args = [providers || new LiteralArrayExpr([])];
|
18202 | if (viewProviders) {
|
18203 | args.push(viewProviders);
|
18204 | }
|
18205 | features.push(importExpr(Identifiers$1.ProvidersFeature).callFn(args));
|
18206 | }
|
18207 | if (meta.usesInheritance) {
|
18208 | features.push(importExpr(Identifiers$1.InheritDefinitionFeature));
|
18209 | }
|
18210 | if (meta.fullInheritance) {
|
18211 | features.push(importExpr(Identifiers$1.CopyDefinitionFeature));
|
18212 | }
|
18213 | if (meta.lifecycle.usesOnChanges) {
|
18214 | features.push(importExpr(Identifiers$1.NgOnChangesFeature));
|
18215 | }
|
18216 | if (features.length) {
|
18217 | definitionMap.set('features', literalArr(features));
|
18218 | }
|
18219 | }
|
18220 | /**
|
18221 | * Compile a directive for the render3 runtime as defined by the `R3DirectiveMetadata`.
|
18222 | */
|
18223 | function compileDirectiveFromMetadata(meta, constantPool, bindingParser) {
|
18224 | const definitionMap = baseDirectiveFields(meta, constantPool, bindingParser);
|
18225 | addFeatures(definitionMap, meta);
|
18226 | const expression = importExpr(Identifiers$1.defineDirective).callFn([definitionMap.toLiteralMap()]);
|
18227 | const type = createDirectiveType(meta);
|
18228 | return { expression, type };
|
18229 | }
|
18230 | /**
|
18231 | * Compile a component for the render3 runtime as defined by the `R3ComponentMetadata`.
|
18232 | */
|
18233 | function compileComponentFromMetadata(meta, constantPool, bindingParser) {
|
18234 | const definitionMap = baseDirectiveFields(meta, constantPool, bindingParser);
|
18235 | addFeatures(definitionMap, meta);
|
18236 | const selector = meta.selector && CssSelector.parse(meta.selector);
|
18237 | const firstSelector = selector && selector[0];
|
18238 | // e.g. `attr: ["class", ".my.app"]`
|
18239 | // This is optional an only included if the first selector of a component specifies attributes.
|
18240 | if (firstSelector) {
|
18241 | const selectorAttributes = firstSelector.getAttrs();
|
18242 | if (selectorAttributes.length) {
|
18243 | definitionMap.set('attrs', constantPool.getConstLiteral(literalArr(selectorAttributes.map(value => value != null ? literal(value) : literal(undefined))),
|
18244 | /* forceShared */ true));
|
18245 | }
|
18246 | }
|
18247 | // Generate the CSS matcher that recognize directive
|
18248 | let directiveMatcher = null;
|
18249 | if (meta.directives.length > 0) {
|
18250 | const matcher = new SelectorMatcher();
|
18251 | for (const { selector, type } of meta.directives) {
|
18252 | matcher.addSelectables(CssSelector.parse(selector), type);
|
18253 | }
|
18254 | directiveMatcher = matcher;
|
18255 | }
|
18256 | // e.g. `template: function MyComponent_Template(_ctx, _cm) {...}`
|
18257 | const templateTypeName = meta.name;
|
18258 | const templateName = templateTypeName ? `${templateTypeName}_Template` : null;
|
18259 | const directivesUsed = new Set();
|
18260 | const pipesUsed = new Set();
|
18261 | const changeDetection = meta.changeDetection;
|
18262 | const template = meta.template;
|
18263 | const templateBuilder = new TemplateDefinitionBuilder(constantPool, BindingScope.createRootScope(), 0, templateTypeName, null, null, templateName, directiveMatcher, directivesUsed, meta.pipes, pipesUsed, Identifiers$1.namespaceHTML, meta.relativeContextFilePath, meta.i18nUseExternalIds);
|
18264 | const templateFunctionExpression = templateBuilder.buildTemplateFunction(template.nodes, []);
|
18265 | // We need to provide this so that dynamically generated components know what
|
18266 | // projected content blocks to pass through to the component when it is instantiated.
|
18267 | const ngContentSelectors = templateBuilder.getNgContentSelectors();
|
18268 | if (ngContentSelectors) {
|
18269 | definitionMap.set('ngContentSelectors', ngContentSelectors);
|
18270 | }
|
18271 | // e.g. `decls: 2`
|
18272 | definitionMap.set('decls', literal(templateBuilder.getConstCount()));
|
18273 | // e.g. `vars: 2`
|
18274 | definitionMap.set('vars', literal(templateBuilder.getVarCount()));
|
18275 | // Generate `consts` section of ComponentDef:
|
18276 | // - either as an array:
|
18277 | // `consts: [['one', 'two'], ['three', 'four']]`
|
18278 | // - or as a factory function in case additional statements are present (to support i18n):
|
18279 | // `consts: function() { var i18n_0; if (ngI18nClosureMode) {...} else {...} return [i18n_0]; }`
|
18280 | const { constExpressions, prepareStatements } = templateBuilder.getConsts();
|
18281 | if (constExpressions.length > 0) {
|
18282 | let constsExpr = literalArr(constExpressions);
|
18283 | // Prepare statements are present - turn `consts` into a function.
|
18284 | if (prepareStatements.length > 0) {
|
18285 | constsExpr = fn([], [...prepareStatements, new ReturnStatement(constsExpr)]);
|
18286 | }
|
18287 | definitionMap.set('consts', constsExpr);
|
18288 | }
|
18289 | definitionMap.set('template', templateFunctionExpression);
|
18290 | // e.g. `directives: [MyDirective]`
|
18291 | if (directivesUsed.size) {
|
18292 | const directivesList = literalArr(Array.from(directivesUsed));
|
18293 | const directivesExpr = compileDeclarationList(directivesList, meta.declarationListEmitMode);
|
18294 | definitionMap.set('directives', directivesExpr);
|
18295 | }
|
18296 | // e.g. `pipes: [MyPipe]`
|
18297 | if (pipesUsed.size) {
|
18298 | const pipesList = literalArr(Array.from(pipesUsed));
|
18299 | const pipesExpr = compileDeclarationList(pipesList, meta.declarationListEmitMode);
|
18300 | definitionMap.set('pipes', pipesExpr);
|
18301 | }
|
18302 | if (meta.encapsulation === null) {
|
18303 | meta.encapsulation = ViewEncapsulation.Emulated;
|
18304 | }
|
18305 | // e.g. `styles: [str1, str2]`
|
18306 | if (meta.styles && meta.styles.length) {
|
18307 | const styleValues = meta.encapsulation == ViewEncapsulation.Emulated ?
|
18308 | compileStyles(meta.styles, CONTENT_ATTR, HOST_ATTR) :
|
18309 | meta.styles;
|
18310 | const strings = styleValues.map(str => constantPool.getConstLiteral(literal(str)));
|
18311 | definitionMap.set('styles', literalArr(strings));
|
18312 | }
|
18313 | else if (meta.encapsulation === ViewEncapsulation.Emulated) {
|
18314 | // If there is no style, don't generate css selectors on elements
|
18315 | meta.encapsulation = ViewEncapsulation.None;
|
18316 | }
|
18317 | // Only set view encapsulation if it's not the default value
|
18318 | if (meta.encapsulation !== ViewEncapsulation.Emulated) {
|
18319 | definitionMap.set('encapsulation', literal(meta.encapsulation));
|
18320 | }
|
18321 | // e.g. `animation: [trigger('123', [])]`
|
18322 | if (meta.animations !== null) {
|
18323 | definitionMap.set('data', literalMap([{ key: 'animation', value: meta.animations, quoted: false }]));
|
18324 | }
|
18325 | // Only set the change detection flag if it's defined and it's not the default.
|
18326 | if (changeDetection != null && changeDetection !== ChangeDetectionStrategy.Default) {
|
18327 | definitionMap.set('changeDetection', literal(changeDetection));
|
18328 | }
|
18329 | const expression = importExpr(Identifiers$1.defineComponent).callFn([definitionMap.toLiteralMap()]);
|
18330 | const type = createComponentType(meta);
|
18331 | return { expression, type };
|
18332 | }
|
18333 | /**
|
18334 | * Creates the type specification from the component meta. This type is inserted into .d.ts files
|
18335 | * to be consumed by upstream compilations.
|
18336 | */
|
18337 | function createComponentType(meta) {
|
18338 | const typeParams = createDirectiveTypeParams(meta);
|
18339 | typeParams.push(stringArrayAsType(meta.template.ngContentSelectors));
|
18340 | return expressionType(importExpr(Identifiers$1.ComponentDefWithMeta, typeParams));
|
18341 | }
|
18342 | /**
|
18343 | * Compiles the array literal of declarations into an expression according to the provided emit
|
18344 | * mode.
|
18345 | */
|
18346 | function compileDeclarationList(list, mode) {
|
18347 | switch (mode) {
|
18348 | case 0 /* Direct */:
|
18349 | // directives: [MyDir],
|
18350 | return list;
|
18351 | case 1 /* Closure */:
|
18352 | // directives: function () { return [MyDir]; }
|
18353 | return fn([], [new ReturnStatement(list)]);
|
18354 | case 2 /* ClosureResolved */:
|
18355 | // directives: function () { return [MyDir].map(ng.resolveForwardRef); }
|
18356 | const resolvedList = list.callMethod('map', [importExpr(Identifiers$1.resolveForwardRef)]);
|
18357 | return fn([], [new ReturnStatement(resolvedList)]);
|
18358 | }
|
18359 | }
|
18360 | function prepareQueryParams(query, constantPool) {
|
18361 | const parameters = [getQueryPredicate(query, constantPool), literal(toQueryFlags(query))];
|
18362 | if (query.read) {
|
18363 | parameters.push(query.read);
|
18364 | }
|
18365 | return parameters;
|
18366 | }
|
18367 | /**
|
18368 | * Translates query flags into `TQueryFlags` type in packages/core/src/render3/interfaces/query.ts
|
18369 | * @param query
|
18370 | */
|
18371 | function toQueryFlags(query) {
|
18372 | return (query.descendants ? 1 /* descendants */ : 0 /* none */) |
|
18373 | (query.static ? 2 /* isStatic */ : 0 /* none */) |
|
18374 | (query.emitDistinctChangesOnly ? 4 /* emitDistinctChangesOnly */ : 0 /* none */);
|
18375 | }
|
18376 | function convertAttributesToExpressions(attributes) {
|
18377 | const values = [];
|
18378 | for (let key of Object.getOwnPropertyNames(attributes)) {
|
18379 | const value = attributes[key];
|
18380 | values.push(literal(key), value);
|
18381 | }
|
18382 | return values;
|
18383 | }
|
18384 | // Define and update any content queries
|
18385 | function createContentQueriesFunction(queries, constantPool, name) {
|
18386 | const createStatements = [];
|
18387 | const updateStatements = [];
|
18388 | const tempAllocator = temporaryAllocator(updateStatements, TEMPORARY_NAME);
|
18389 | for (const query of queries) {
|
18390 | // creation, e.g. r3.contentQuery(dirIndex, somePredicate, true, null);
|
18391 | createStatements.push(importExpr(Identifiers$1.contentQuery)
|
18392 | .callFn([variable('dirIndex'), ...prepareQueryParams(query, constantPool)])
|
18393 | .toStmt());
|
18394 | // update, e.g. (r3.queryRefresh(tmp = r3.loadQuery()) && (ctx.someDir = tmp));
|
18395 | const temporary = tempAllocator();
|
18396 | const getQueryList = importExpr(Identifiers$1.loadQuery).callFn([]);
|
18397 | const refresh = importExpr(Identifiers$1.queryRefresh).callFn([temporary.set(getQueryList)]);
|
18398 | const updateDirective = variable(CONTEXT_NAME)
|
18399 | .prop(query.propertyName)
|
18400 | .set(query.first ? temporary.prop('first') : temporary);
|
18401 | updateStatements.push(refresh.and(updateDirective).toStmt());
|
18402 | }
|
18403 | const contentQueriesFnName = name ? `${name}_ContentQueries` : null;
|
18404 | return fn([
|
18405 | new FnParam(RENDER_FLAGS, NUMBER_TYPE), new FnParam(CONTEXT_NAME, null),
|
18406 | new FnParam('dirIndex', null)
|
18407 | ], [
|
18408 | renderFlagCheckIfStmt(1 /* Create */, createStatements),
|
18409 | renderFlagCheckIfStmt(2 /* Update */, updateStatements)
|
18410 | ], INFERRED_TYPE, null, contentQueriesFnName);
|
18411 | }
|
18412 | function stringAsType(str) {
|
18413 | return expressionType(literal(str));
|
18414 | }
|
18415 | function stringMapAsType(map) {
|
18416 | const mapValues = Object.keys(map).map(key => {
|
18417 | const value = Array.isArray(map[key]) ? map[key][0] : map[key];
|
18418 | return {
|
18419 | key,
|
18420 | value: literal(value),
|
18421 | quoted: true,
|
18422 | };
|
18423 | });
|
18424 | return expressionType(literalMap(mapValues));
|
18425 | }
|
18426 | function stringArrayAsType(arr) {
|
18427 | return arr.length > 0 ? expressionType(literalArr(arr.map(value => literal(value)))) :
|
18428 | NONE_TYPE;
|
18429 | }
|
18430 | function createDirectiveTypeParams(meta) {
|
18431 | // On the type side, remove newlines from the selector as it will need to fit into a TypeScript
|
18432 | // string literal, which must be on one line.
|
18433 | const selectorForType = meta.selector !== null ? meta.selector.replace(/\n/g, '') : null;
|
18434 | return [
|
18435 | typeWithParameters(meta.type.type, meta.typeArgumentCount),
|
18436 | selectorForType !== null ? stringAsType(selectorForType) : NONE_TYPE,
|
18437 | meta.exportAs !== null ? stringArrayAsType(meta.exportAs) : NONE_TYPE,
|
18438 | stringMapAsType(meta.inputs),
|
18439 | stringMapAsType(meta.outputs),
|
18440 | stringArrayAsType(meta.queries.map(q => q.propertyName)),
|
18441 | ];
|
18442 | }
|
18443 | /**
|
18444 | * Creates the type specification from the directive meta. This type is inserted into .d.ts files
|
18445 | * to be consumed by upstream compilations.
|
18446 | */
|
18447 | function createDirectiveType(meta) {
|
18448 | const typeParams = createDirectiveTypeParams(meta);
|
18449 | return expressionType(importExpr(Identifiers$1.DirectiveDefWithMeta, typeParams));
|
18450 | }
|
18451 | // Define and update any view queries
|
18452 | function createViewQueriesFunction(viewQueries, constantPool, name) {
|
18453 | const createStatements = [];
|
18454 | const updateStatements = [];
|
18455 | const tempAllocator = temporaryAllocator(updateStatements, TEMPORARY_NAME);
|
18456 | viewQueries.forEach((query) => {
|
18457 | // creation, e.g. r3.viewQuery(somePredicate, true);
|
18458 | const queryDefinition = importExpr(Identifiers$1.viewQuery).callFn(prepareQueryParams(query, constantPool));
|
18459 | createStatements.push(queryDefinition.toStmt());
|
18460 | // update, e.g. (r3.queryRefresh(tmp = r3.loadQuery()) && (ctx.someDir = tmp));
|
18461 | const temporary = tempAllocator();
|
18462 | const getQueryList = importExpr(Identifiers$1.loadQuery).callFn([]);
|
18463 | const refresh = importExpr(Identifiers$1.queryRefresh).callFn([temporary.set(getQueryList)]);
|
18464 | const updateDirective = variable(CONTEXT_NAME)
|
18465 | .prop(query.propertyName)
|
18466 | .set(query.first ? temporary.prop('first') : temporary);
|
18467 | updateStatements.push(refresh.and(updateDirective).toStmt());
|
18468 | });
|
18469 | const viewQueryFnName = name ? `${name}_Query` : null;
|
18470 | return fn([new FnParam(RENDER_FLAGS, NUMBER_TYPE), new FnParam(CONTEXT_NAME, null)], [
|
18471 | renderFlagCheckIfStmt(1 /* Create */, createStatements),
|
18472 | renderFlagCheckIfStmt(2 /* Update */, updateStatements)
|
18473 | ], INFERRED_TYPE, null, viewQueryFnName);
|
18474 | }
|
18475 | // Return a host binding function or null if one is not necessary.
|
18476 | function createHostBindingsFunction(hostBindingsMetadata, typeSourceSpan, bindingParser, constantPool, selector, name, definitionMap) {
|
18477 | const bindingContext = variable(CONTEXT_NAME);
|
18478 | const styleBuilder = new StylingBuilder(bindingContext);
|
18479 | const { styleAttr, classAttr } = hostBindingsMetadata.specialAttributes;
|
18480 | if (styleAttr !== undefined) {
|
18481 | styleBuilder.registerStyleAttr(styleAttr);
|
18482 | }
|
18483 | if (classAttr !== undefined) {
|
18484 | styleBuilder.registerClassAttr(classAttr);
|
18485 | }
|
18486 | const createStatements = [];
|
18487 | const updateStatements = [];
|
18488 | const hostBindingSourceSpan = typeSourceSpan;
|
18489 | const directiveSummary = metadataAsSummary(hostBindingsMetadata);
|
18490 | // Calculate host event bindings
|
18491 | const eventBindings = bindingParser.createDirectiveHostEventAsts(directiveSummary, hostBindingSourceSpan);
|
18492 | if (eventBindings && eventBindings.length) {
|
18493 | const listeners = createHostListeners(eventBindings, name);
|
18494 | createStatements.push(...listeners);
|
18495 | }
|
18496 | // Calculate the host property bindings
|
18497 | const bindings = bindingParser.createBoundHostProperties(directiveSummary, hostBindingSourceSpan);
|
18498 | const allOtherBindings = [];
|
18499 | // We need to calculate the total amount of binding slots required by
|
18500 | // all the instructions together before any value conversions happen.
|
18501 | // Value conversions may require additional slots for interpolation and
|
18502 | // bindings with pipes. These calculates happen after this block.
|
18503 | let totalHostVarsCount = 0;
|
18504 | bindings && bindings.forEach((binding) => {
|
18505 | const stylingInputWasSet = styleBuilder.registerInputBasedOnName(binding.name, binding.expression, hostBindingSourceSpan);
|
18506 | if (stylingInputWasSet) {
|
18507 | totalHostVarsCount += MIN_STYLING_BINDING_SLOTS_REQUIRED;
|
18508 | }
|
18509 | else {
|
18510 | allOtherBindings.push(binding);
|
18511 | totalHostVarsCount++;
|
18512 | }
|
18513 | });
|
18514 | let valueConverter;
|
18515 | const getValueConverter = () => {
|
18516 | if (!valueConverter) {
|
18517 | const hostVarsCountFn = (numSlots) => {
|
18518 | const originalVarsCount = totalHostVarsCount;
|
18519 | totalHostVarsCount += numSlots;
|
18520 | return originalVarsCount;
|
18521 | };
|
18522 | valueConverter = new ValueConverter(constantPool, () => error('Unexpected node'), // new nodes are illegal here
|
18523 | hostVarsCountFn, () => error('Unexpected pipe')); // pipes are illegal here
|
18524 | }
|
18525 | return valueConverter;
|
18526 | };
|
18527 | const propertyBindings = [];
|
18528 | const attributeBindings = [];
|
18529 | const syntheticHostBindings = [];
|
18530 | allOtherBindings.forEach((binding) => {
|
18531 | // resolve literal arrays and literal objects
|
18532 | const value = binding.expression.visit(getValueConverter());
|
18533 | const bindingExpr = bindingFn(bindingContext, value);
|
18534 | const { bindingName, instruction, isAttribute } = getBindingNameAndInstruction(binding);
|
18535 | const securityContexts = bindingParser.calcPossibleSecurityContexts(selector, bindingName, isAttribute)
|
18536 | .filter(context => context !== SecurityContext.NONE);
|
18537 | let sanitizerFn = null;
|
18538 | if (securityContexts.length) {
|
18539 | if (securityContexts.length === 2 &&
|
18540 | securityContexts.indexOf(SecurityContext.URL) > -1 &&
|
18541 | securityContexts.indexOf(SecurityContext.RESOURCE_URL) > -1) {
|
18542 | // Special case for some URL attributes (such as "src" and "href") that may be a part
|
18543 | // of different security contexts. In this case we use special santitization function and
|
18544 | // select the actual sanitizer at runtime based on a tag name that is provided while
|
18545 | // invoking sanitization function.
|
18546 | sanitizerFn = importExpr(Identifiers$1.sanitizeUrlOrResourceUrl);
|
18547 | }
|
18548 | else {
|
18549 | sanitizerFn = resolveSanitizationFn(securityContexts[0], isAttribute);
|
18550 | }
|
18551 | }
|
18552 | const instructionParams = [literal(bindingName), bindingExpr.currValExpr];
|
18553 | if (sanitizerFn) {
|
18554 | instructionParams.push(sanitizerFn);
|
18555 | }
|
18556 | updateStatements.push(...bindingExpr.stmts);
|
18557 | if (instruction === Identifiers$1.hostProperty) {
|
18558 | propertyBindings.push(instructionParams);
|
18559 | }
|
18560 | else if (instruction === Identifiers$1.attribute) {
|
18561 | attributeBindings.push(instructionParams);
|
18562 | }
|
18563 | else if (instruction === Identifiers$1.syntheticHostProperty) {
|
18564 | syntheticHostBindings.push(instructionParams);
|
18565 | }
|
18566 | else {
|
18567 | updateStatements.push(importExpr(instruction).callFn(instructionParams).toStmt());
|
18568 | }
|
18569 | });
|
18570 | if (propertyBindings.length > 0) {
|
18571 | updateStatements.push(chainedInstruction(Identifiers$1.hostProperty, propertyBindings).toStmt());
|
18572 | }
|
18573 | if (attributeBindings.length > 0) {
|
18574 | updateStatements.push(chainedInstruction(Identifiers$1.attribute, attributeBindings).toStmt());
|
18575 | }
|
18576 | if (syntheticHostBindings.length > 0) {
|
18577 | updateStatements.push(chainedInstruction(Identifiers$1.syntheticHostProperty, syntheticHostBindings).toStmt());
|
18578 | }
|
18579 | // since we're dealing with directives/components and both have hostBinding
|
18580 | // functions, we need to generate a special hostAttrs instruction that deals
|
18581 | // with both the assignment of styling as well as static attributes to the host
|
18582 | // element. The instruction below will instruct all initial styling (styling
|
18583 | // that is inside of a host binding within a directive/component) to be attached
|
18584 | // to the host element alongside any of the provided host attributes that were
|
18585 | // collected earlier.
|
18586 | const hostAttrs = convertAttributesToExpressions(hostBindingsMetadata.attributes);
|
18587 | styleBuilder.assignHostAttrs(hostAttrs, definitionMap);
|
18588 | if (styleBuilder.hasBindings) {
|
18589 | // finally each binding that was registered in the statement above will need to be added to
|
18590 | // the update block of a component/directive templateFn/hostBindingsFn so that the bindings
|
18591 | // are evaluated and updated for the element.
|
18592 | styleBuilder.buildUpdateLevelInstructions(getValueConverter()).forEach(instruction => {
|
18593 | if (instruction.calls.length > 0) {
|
18594 | const calls = [];
|
18595 | instruction.calls.forEach(call => {
|
18596 | // we subtract a value of `1` here because the binding slot was already allocated
|
18597 | // at the top of this method when all the input bindings were counted.
|
18598 | totalHostVarsCount +=
|
18599 | Math.max(call.allocateBindingSlots - MIN_STYLING_BINDING_SLOTS_REQUIRED, 0);
|
18600 | calls.push(convertStylingCall(call, bindingContext, bindingFn));
|
18601 | });
|
18602 | updateStatements.push(chainedInstruction(instruction.reference, calls).toStmt());
|
18603 | }
|
18604 | });
|
18605 | }
|
18606 | if (totalHostVarsCount) {
|
18607 | definitionMap.set('hostVars', literal(totalHostVarsCount));
|
18608 | }
|
18609 | if (createStatements.length > 0 || updateStatements.length > 0) {
|
18610 | const hostBindingsFnName = name ? `${name}_HostBindings` : null;
|
18611 | const statements = [];
|
18612 | if (createStatements.length > 0) {
|
18613 | statements.push(renderFlagCheckIfStmt(1 /* Create */, createStatements));
|
18614 | }
|
18615 | if (updateStatements.length > 0) {
|
18616 | statements.push(renderFlagCheckIfStmt(2 /* Update */, updateStatements));
|
18617 | }
|
18618 | return fn([new FnParam(RENDER_FLAGS, NUMBER_TYPE), new FnParam(CONTEXT_NAME, null)], statements, INFERRED_TYPE, null, hostBindingsFnName);
|
18619 | }
|
18620 | return null;
|
18621 | }
|
18622 | function bindingFn(implicit, value) {
|
18623 | return convertPropertyBinding(null, implicit, value, 'b', BindingForm.Expression, () => error('Unexpected interpolation'));
|
18624 | }
|
18625 | function convertStylingCall(call, bindingContext, bindingFn) {
|
18626 | return call.params(value => bindingFn(bindingContext, value).currValExpr);
|
18627 | }
|
18628 | function getBindingNameAndInstruction(binding) {
|
18629 | let bindingName = binding.name;
|
18630 | let instruction;
|
18631 | // Check to see if this is an attr binding or a property binding
|
18632 | const attrMatches = bindingName.match(ATTR_REGEX);
|
18633 | if (attrMatches) {
|
18634 | bindingName = attrMatches[1];
|
18635 | instruction = Identifiers$1.attribute;
|
18636 | }
|
18637 | else {
|
18638 | if (binding.isAnimation) {
|
18639 | bindingName = prepareSyntheticPropertyName(bindingName);
|
18640 | // host bindings that have a synthetic property (e.g. @foo) should always be rendered
|
18641 | // in the context of the component and not the parent. Therefore there is a special
|
18642 | // compatibility instruction available for this purpose.
|
18643 | instruction = Identifiers$1.syntheticHostProperty;
|
18644 | }
|
18645 | else {
|
18646 | instruction = Identifiers$1.hostProperty;
|
18647 | }
|
18648 | }
|
18649 | return { bindingName, instruction, isAttribute: !!attrMatches };
|
18650 | }
|
18651 | function createHostListeners(eventBindings, name) {
|
18652 | const listeners = [];
|
18653 | const syntheticListeners = [];
|
18654 | const instructions = [];
|
18655 | eventBindings.forEach(binding => {
|
18656 | let bindingName = binding.name && sanitizeIdentifier(binding.name);
|
18657 | const bindingFnName = binding.type === 1 /* Animation */ ?
|
18658 | prepareSyntheticListenerFunctionName(bindingName, binding.targetOrPhase) :
|
18659 | bindingName;
|
18660 | const handlerName = name && bindingName ? `${name}_${bindingFnName}_HostBindingHandler` : null;
|
18661 | const params = prepareEventListenerParameters(BoundEvent.fromParsedEvent(binding), handlerName);
|
18662 | if (binding.type == 1 /* Animation */) {
|
18663 | syntheticListeners.push(params);
|
18664 | }
|
18665 | else {
|
18666 | listeners.push(params);
|
18667 | }
|
18668 | });
|
18669 | if (syntheticListeners.length > 0) {
|
18670 | instructions.push(chainedInstruction(Identifiers$1.syntheticHostListener, syntheticListeners).toStmt());
|
18671 | }
|
18672 | if (listeners.length > 0) {
|
18673 | instructions.push(chainedInstruction(Identifiers$1.listener, listeners).toStmt());
|
18674 | }
|
18675 | return instructions;
|
18676 | }
|
18677 | function metadataAsSummary(meta) {
|
18678 | // clang-format off
|
18679 | return {
|
18680 | // This is used by the BindingParser, which only deals with listeners and properties. There's no
|
18681 | // need to pass attributes to it.
|
18682 | hostAttributes: {},
|
18683 | hostListeners: meta.listeners,
|
18684 | hostProperties: meta.properties,
|
18685 | };
|
18686 | // clang-format on
|
18687 | }
|
18688 | const HOST_REG_EXP$1 = /^(?:\[([^\]]+)\])|(?:\(([^\)]+)\))$/;
|
18689 | function parseHostBindings(host) {
|
18690 | const attributes = {};
|
18691 | const listeners = {};
|
18692 | const properties = {};
|
18693 | const specialAttributes = {};
|
18694 | for (const key of Object.keys(host)) {
|
18695 | const value = host[key];
|
18696 | const matches = key.match(HOST_REG_EXP$1);
|
18697 | if (matches === null) {
|
18698 | switch (key) {
|
18699 | case 'class':
|
18700 | if (typeof value !== 'string') {
|
18701 | // TODO(alxhub): make this a diagnostic.
|
18702 | throw new Error(`Class binding must be string`);
|
18703 | }
|
18704 | specialAttributes.classAttr = value;
|
18705 | break;
|
18706 | case 'style':
|
18707 | if (typeof value !== 'string') {
|
18708 | // TODO(alxhub): make this a diagnostic.
|
18709 | throw new Error(`Style binding must be string`);
|
18710 | }
|
18711 | specialAttributes.styleAttr = value;
|
18712 | break;
|
18713 | default:
|
18714 | if (typeof value === 'string') {
|
18715 | attributes[key] = literal(value);
|
18716 | }
|
18717 | else {
|
18718 | attributes[key] = value;
|
18719 | }
|
18720 | }
|
18721 | }
|
18722 | else if (matches[1 /* Binding */] != null) {
|
18723 | if (typeof value !== 'string') {
|
18724 | // TODO(alxhub): make this a diagnostic.
|
18725 | throw new Error(`Property binding must be string`);
|
18726 | }
|
18727 | // synthetic properties (the ones that have a `@` as a prefix)
|
18728 | // are still treated the same as regular properties. Therefore
|
18729 | // there is no point in storing them in a separate map.
|
18730 | properties[matches[1 /* Binding */]] = value;
|
18731 | }
|
18732 | else if (matches[2 /* Event */] != null) {
|
18733 | if (typeof value !== 'string') {
|
18734 | // TODO(alxhub): make this a diagnostic.
|
18735 | throw new Error(`Event binding must be string`);
|
18736 | }
|
18737 | listeners[matches[2 /* Event */]] = value;
|
18738 | }
|
18739 | }
|
18740 | return { attributes, listeners, properties, specialAttributes };
|
18741 | }
|
18742 | /**
|
18743 | * Verifies host bindings and returns the list of errors (if any). Empty array indicates that a
|
18744 | * given set of host bindings has no errors.
|
18745 | *
|
18746 | * @param bindings set of host bindings to verify.
|
18747 | * @param sourceSpan source span where host bindings were defined.
|
18748 | * @returns array of errors associated with a given set of host bindings.
|
18749 | */
|
18750 | function verifyHostBindings(bindings, sourceSpan) {
|
18751 | const summary = metadataAsSummary(bindings);
|
18752 | // TODO: abstract out host bindings verification logic and use it instead of
|
18753 | // creating events and properties ASTs to detect errors (FW-996)
|
18754 | const bindingParser = makeBindingParser();
|
18755 | bindingParser.createDirectiveHostEventAsts(summary, sourceSpan);
|
18756 | bindingParser.createBoundHostProperties(summary, sourceSpan);
|
18757 | return bindingParser.errors;
|
18758 | }
|
18759 | function compileStyles(styles, selector, hostSelector) {
|
18760 | const shadowCss = new ShadowCss();
|
18761 | return styles.map(style => {
|
18762 | return shadowCss.shimCssText(style, selector, hostSelector);
|
18763 | });
|
18764 | }
|
18765 |
|
18766 | /**
|
18767 | * @license
|
18768 | * Copyright Google LLC All Rights Reserved.
|
18769 | *
|
18770 | * Use of this source code is governed by an MIT-style license that can be
|
18771 | * found in the LICENSE file at https://angular.io/license
|
18772 | */
|
18773 | /**
|
18774 | * An interface for retrieving documents by URL that the compiler uses
|
18775 | * to load templates.
|
18776 | */
|
18777 | class ResourceLoader {
|
18778 | get(url) {
|
18779 | return '';
|
18780 | }
|
18781 | }
|
18782 |
|
18783 | /**
|
18784 | * @license
|
18785 | * Copyright Google LLC All Rights Reserved.
|
18786 | *
|
18787 | * Use of this source code is governed by an MIT-style license that can be
|
18788 | * found in the LICENSE file at https://angular.io/license
|
18789 | */
|
18790 | class CompilerFacadeImpl {
|
18791 | constructor(jitEvaluator = new JitEvaluator()) {
|
18792 | this.jitEvaluator = jitEvaluator;
|
18793 | this.R3ResolvedDependencyType = R3ResolvedDependencyType;
|
18794 | this.R3FactoryTarget = R3FactoryTarget;
|
18795 | this.ResourceLoader = ResourceLoader;
|
18796 | this.elementSchemaRegistry = new DomElementSchemaRegistry();
|
18797 | }
|
18798 | compilePipe(angularCoreEnv, sourceMapUrl, facade) {
|
18799 | const metadata = {
|
18800 | name: facade.name,
|
18801 | type: wrapReference(facade.type),
|
18802 | internalType: new WrappedNodeExpr(facade.type),
|
18803 | typeArgumentCount: facade.typeArgumentCount,
|
18804 | deps: convertR3DependencyMetadataArray(facade.deps),
|
18805 | pipeName: facade.pipeName,
|
18806 | pure: facade.pure,
|
18807 | };
|
18808 | const res = compilePipeFromMetadata(metadata);
|
18809 | return this.jitExpression(res.expression, angularCoreEnv, sourceMapUrl, []);
|
18810 | }
|
18811 | compilePipeDeclaration(angularCoreEnv, sourceMapUrl, declaration) {
|
18812 | const meta = convertDeclarePipeFacadeToMetadata(declaration);
|
18813 | const res = compilePipeFromMetadata(meta);
|
18814 | return this.jitExpression(res.expression, angularCoreEnv, sourceMapUrl, []);
|
18815 | }
|
18816 | compileInjectable(angularCoreEnv, sourceMapUrl, facade) {
|
18817 | const { expression, statements } = compileInjectable({
|
18818 | name: facade.name,
|
18819 | type: wrapReference(facade.type),
|
18820 | internalType: new WrappedNodeExpr(facade.type),
|
18821 | typeArgumentCount: facade.typeArgumentCount,
|
18822 | providedIn: computeProvidedIn(facade.providedIn),
|
18823 | useClass: wrapExpression(facade, USE_CLASS),
|
18824 | useFactory: wrapExpression(facade, USE_FACTORY),
|
18825 | useValue: wrapExpression(facade, USE_VALUE),
|
18826 | useExisting: wrapExpression(facade, USE_EXISTING),
|
18827 | userDeps: convertR3DependencyMetadataArray(facade.userDeps) || undefined,
|
18828 | });
|
18829 | return this.jitExpression(expression, angularCoreEnv, sourceMapUrl, statements);
|
18830 | }
|
18831 | compileInjector(angularCoreEnv, sourceMapUrl, facade) {
|
18832 | const meta = {
|
18833 | name: facade.name,
|
18834 | type: wrapReference(facade.type),
|
18835 | internalType: new WrappedNodeExpr(facade.type),
|
18836 | deps: convertR3DependencyMetadataArray(facade.deps),
|
18837 | providers: new WrappedNodeExpr(facade.providers),
|
18838 | imports: facade.imports.map(i => new WrappedNodeExpr(i)),
|
18839 | };
|
18840 | const res = compileInjector(meta);
|
18841 | return this.jitExpression(res.expression, angularCoreEnv, sourceMapUrl, res.statements);
|
18842 | }
|
18843 | compileNgModule(angularCoreEnv, sourceMapUrl, facade) {
|
18844 | const meta = {
|
18845 | type: wrapReference(facade.type),
|
18846 | internalType: new WrappedNodeExpr(facade.type),
|
18847 | adjacentType: new WrappedNodeExpr(facade.type),
|
18848 | bootstrap: facade.bootstrap.map(wrapReference),
|
18849 | declarations: facade.declarations.map(wrapReference),
|
18850 | imports: facade.imports.map(wrapReference),
|
18851 | exports: facade.exports.map(wrapReference),
|
18852 | emitInline: true,
|
18853 | containsForwardDecls: false,
|
18854 | schemas: facade.schemas ? facade.schemas.map(wrapReference) : null,
|
18855 | id: facade.id ? new WrappedNodeExpr(facade.id) : null,
|
18856 | };
|
18857 | const res = compileNgModule(meta);
|
18858 | return this.jitExpression(res.expression, angularCoreEnv, sourceMapUrl, []);
|
18859 | }
|
18860 | compileDirective(angularCoreEnv, sourceMapUrl, facade) {
|
18861 | const meta = convertDirectiveFacadeToMetadata(facade);
|
18862 | return this.compileDirectiveFromMeta(angularCoreEnv, sourceMapUrl, meta);
|
18863 | }
|
18864 | compileDirectiveDeclaration(angularCoreEnv, sourceMapUrl, declaration) {
|
18865 | const typeSourceSpan = this.createParseSourceSpan('Directive', declaration.type.name, sourceMapUrl);
|
18866 | const meta = convertDeclareDirectiveFacadeToMetadata(declaration, typeSourceSpan);
|
18867 | return this.compileDirectiveFromMeta(angularCoreEnv, sourceMapUrl, meta);
|
18868 | }
|
18869 | compileDirectiveFromMeta(angularCoreEnv, sourceMapUrl, meta) {
|
18870 | const constantPool = new ConstantPool();
|
18871 | const bindingParser = makeBindingParser();
|
18872 | const res = compileDirectiveFromMetadata(meta, constantPool, bindingParser);
|
18873 | return this.jitExpression(res.expression, angularCoreEnv, sourceMapUrl, constantPool.statements);
|
18874 | }
|
18875 | compileComponent(angularCoreEnv, sourceMapUrl, facade) {
|
18876 | // Parse the template and check for errors.
|
18877 | const { template, interpolation } = parseJitTemplate(facade.template, facade.name, sourceMapUrl, facade.preserveWhitespaces, facade.interpolation);
|
18878 | // Compile the component metadata, including template, into an expression.
|
18879 | 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) :
|
18880 | null, relativeContextFilePath: '', i18nUseExternalIds: true });
|
18881 | const jitExpressionSourceMap = `ng:///${facade.name}.js`;
|
18882 | return this.compileComponentFromMeta(angularCoreEnv, jitExpressionSourceMap, meta);
|
18883 | }
|
18884 | compileComponentDeclaration(angularCoreEnv, sourceMapUrl, declaration) {
|
18885 | const typeSourceSpan = this.createParseSourceSpan('Component', declaration.type.name, sourceMapUrl);
|
18886 | const meta = convertDeclareComponentFacadeToMetadata(declaration, typeSourceSpan, sourceMapUrl);
|
18887 | return this.compileComponentFromMeta(angularCoreEnv, sourceMapUrl, meta);
|
18888 | }
|
18889 | compileComponentFromMeta(angularCoreEnv, sourceMapUrl, meta) {
|
18890 | const constantPool = new ConstantPool();
|
18891 | const bindingParser = makeBindingParser(meta.interpolation);
|
18892 | const res = compileComponentFromMetadata(meta, constantPool, bindingParser);
|
18893 | return this.jitExpression(res.expression, angularCoreEnv, sourceMapUrl, constantPool.statements);
|
18894 | }
|
18895 | compileFactory(angularCoreEnv, sourceMapUrl, meta) {
|
18896 | const factoryRes = compileFactoryFunction({
|
18897 | name: meta.name,
|
18898 | type: wrapReference(meta.type),
|
18899 | internalType: new WrappedNodeExpr(meta.type),
|
18900 | typeArgumentCount: meta.typeArgumentCount,
|
18901 | deps: convertR3DependencyMetadataArray(meta.deps),
|
18902 | injectFn: meta.injectFn === 'directiveInject' ? Identifiers.directiveInject :
|
18903 | Identifiers.inject,
|
18904 | target: meta.target,
|
18905 | });
|
18906 | return this.jitExpression(factoryRes.factory, angularCoreEnv, sourceMapUrl, factoryRes.statements);
|
18907 | }
|
18908 | createParseSourceSpan(kind, typeName, sourceUrl) {
|
18909 | return r3JitTypeSourceSpan(kind, typeName, sourceUrl);
|
18910 | }
|
18911 | /**
|
18912 | * JIT compiles an expression and returns the result of executing that expression.
|
18913 | *
|
18914 | * @param def the definition which will be compiled and executed to get the value to patch
|
18915 | * @param context an object map of @angular/core symbol names to symbols which will be available
|
18916 | * in the context of the compiled expression
|
18917 | * @param sourceUrl a URL to use for the source map of the compiled expression
|
18918 | * @param preStatements a collection of statements that should be evaluated before the expression.
|
18919 | */
|
18920 | jitExpression(def, context, sourceUrl, preStatements) {
|
18921 | // The ConstantPool may contain Statements which declare variables used in the final expression.
|
18922 | // Therefore, its statements need to precede the actual JIT operation. The final statement is a
|
18923 | // declaration of $def which is set to the expression being compiled.
|
18924 | const statements = [
|
18925 | ...preStatements,
|
18926 | new DeclareVarStmt('$def', def, undefined, [StmtModifier.Exported]),
|
18927 | ];
|
18928 | const res = this.jitEvaluator.evaluateStatements(sourceUrl, statements, new R3JitReflector(context), /* enableSourceMaps */ true);
|
18929 | return res['$def'];
|
18930 | }
|
18931 | }
|
18932 | const USE_CLASS = Object.keys({ useClass: null })[0];
|
18933 | const USE_FACTORY = Object.keys({ useFactory: null })[0];
|
18934 | const USE_VALUE = Object.keys({ useValue: null })[0];
|
18935 | const USE_EXISTING = Object.keys({ useExisting: null })[0];
|
18936 | const wrapReference = function (value) {
|
18937 | const wrapped = new WrappedNodeExpr(value);
|
18938 | return { value: wrapped, type: wrapped };
|
18939 | };
|
18940 | function convertToR3QueryMetadata(facade) {
|
18941 | return Object.assign(Object.assign({}, facade), { predicate: Array.isArray(facade.predicate) ? facade.predicate :
|
18942 | new WrappedNodeExpr(facade.predicate), read: facade.read ? new WrappedNodeExpr(facade.read) : null, static: facade.static, emitDistinctChangesOnly: facade.emitDistinctChangesOnly });
|
18943 | }
|
18944 | function convertQueryDeclarationToMetadata(declaration) {
|
18945 | var _a, _b, _c, _d;
|
18946 | return {
|
18947 | propertyName: declaration.propertyName,
|
18948 | first: (_a = declaration.first) !== null && _a !== void 0 ? _a : false,
|
18949 | predicate: Array.isArray(declaration.predicate) ? declaration.predicate :
|
18950 | new WrappedNodeExpr(declaration.predicate),
|
18951 | descendants: (_b = declaration.descendants) !== null && _b !== void 0 ? _b : false,
|
18952 | read: declaration.read ? new WrappedNodeExpr(declaration.read) : null,
|
18953 | static: (_c = declaration.static) !== null && _c !== void 0 ? _c : false,
|
18954 | emitDistinctChangesOnly: (_d = declaration.emitDistinctChangesOnly) !== null && _d !== void 0 ? _d : true,
|
18955 | };
|
18956 | }
|
18957 | function convertDirectiveFacadeToMetadata(facade) {
|
18958 | const inputsFromMetadata = parseInputOutputs(facade.inputs || []);
|
18959 | const outputsFromMetadata = parseInputOutputs(facade.outputs || []);
|
18960 | const propMetadata = facade.propMetadata;
|
18961 | const inputsFromType = {};
|
18962 | const outputsFromType = {};
|
18963 | for (const field in propMetadata) {
|
18964 | if (propMetadata.hasOwnProperty(field)) {
|
18965 | propMetadata[field].forEach(ann => {
|
18966 | if (isInput(ann)) {
|
18967 | inputsFromType[field] =
|
18968 | ann.bindingPropertyName ? [ann.bindingPropertyName, field] : field;
|
18969 | }
|
18970 | else if (isOutput(ann)) {
|
18971 | outputsFromType[field] = ann.bindingPropertyName || field;
|
18972 | }
|
18973 | });
|
18974 | }
|
18975 | }
|
18976 | 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 });
|
18977 | }
|
18978 | function convertDeclareDirectiveFacadeToMetadata(declaration, typeSourceSpan) {
|
18979 | var _a, _b, _c, _d, _e, _f, _g, _h;
|
18980 | return {
|
18981 | name: declaration.type.name,
|
18982 | type: wrapReference(declaration.type),
|
18983 | typeSourceSpan,
|
18984 | internalType: new WrappedNodeExpr(declaration.type),
|
18985 | selector: (_a = declaration.selector) !== null && _a !== void 0 ? _a : null,
|
18986 | inputs: (_b = declaration.inputs) !== null && _b !== void 0 ? _b : {},
|
18987 | outputs: (_c = declaration.outputs) !== null && _c !== void 0 ? _c : {},
|
18988 | host: convertHostDeclarationToMetadata(declaration.host),
|
18989 | queries: ((_d = declaration.queries) !== null && _d !== void 0 ? _d : []).map(convertQueryDeclarationToMetadata),
|
18990 | viewQueries: ((_e = declaration.viewQueries) !== null && _e !== void 0 ? _e : []).map(convertQueryDeclarationToMetadata),
|
18991 | providers: declaration.providers !== undefined ? new WrappedNodeExpr(declaration.providers) :
|
18992 | null,
|
18993 | exportAs: (_f = declaration.exportAs) !== null && _f !== void 0 ? _f : null,
|
18994 | usesInheritance: (_g = declaration.usesInheritance) !== null && _g !== void 0 ? _g : false,
|
18995 | lifecycle: { usesOnChanges: (_h = declaration.usesOnChanges) !== null && _h !== void 0 ? _h : false },
|
18996 | deps: null,
|
18997 | typeArgumentCount: 0,
|
18998 | fullInheritance: false,
|
18999 | };
|
19000 | }
|
19001 | function convertHostDeclarationToMetadata(host = {}) {
|
19002 | var _a, _b, _c;
|
19003 | return {
|
19004 | attributes: convertOpaqueValuesToExpressions((_a = host.attributes) !== null && _a !== void 0 ? _a : {}),
|
19005 | listeners: (_b = host.listeners) !== null && _b !== void 0 ? _b : {},
|
19006 | properties: (_c = host.properties) !== null && _c !== void 0 ? _c : {},
|
19007 | specialAttributes: {
|
19008 | classAttr: host.classAttribute,
|
19009 | styleAttr: host.styleAttribute,
|
19010 | },
|
19011 | };
|
19012 | }
|
19013 | function convertOpaqueValuesToExpressions(obj) {
|
19014 | const result = {};
|
19015 | for (const key of Object.keys(obj)) {
|
19016 | result[key] = new WrappedNodeExpr(obj[key]);
|
19017 | }
|
19018 | return result;
|
19019 | }
|
19020 | function convertDeclareComponentFacadeToMetadata(declaration, typeSourceSpan, sourceMapUrl) {
|
19021 | var _a, _b, _c, _d, _e;
|
19022 | const { template, interpolation } = parseJitTemplate(declaration.template, declaration.type.name, sourceMapUrl, (_a = declaration.preserveWhitespaces) !== null && _a !== void 0 ? _a : false, declaration.interpolation);
|
19023 | 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 ?
|
19024 | new WrappedNodeExpr(declaration.viewProviders) :
|
19025 | null, animations: declaration.animations !== undefined ? new WrappedNodeExpr(declaration.animations) :
|
19026 | 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 });
|
19027 | }
|
19028 | function convertUsedDirectiveDeclarationToMetadata(declaration) {
|
19029 | var _a, _b, _c;
|
19030 | return {
|
19031 | selector: declaration.selector,
|
19032 | type: new WrappedNodeExpr(declaration.type),
|
19033 | inputs: (_a = declaration.inputs) !== null && _a !== void 0 ? _a : [],
|
19034 | outputs: (_b = declaration.outputs) !== null && _b !== void 0 ? _b : [],
|
19035 | exportAs: (_c = declaration.exportAs) !== null && _c !== void 0 ? _c : null,
|
19036 | };
|
19037 | }
|
19038 | function convertUsedPipesToMetadata(declaredPipes) {
|
19039 | const pipes = new Map();
|
19040 | if (declaredPipes === undefined) {
|
19041 | return pipes;
|
19042 | }
|
19043 | for (const pipeName of Object.keys(declaredPipes)) {
|
19044 | const pipeType = declaredPipes[pipeName];
|
19045 | pipes.set(pipeName, new WrappedNodeExpr(pipeType));
|
19046 | }
|
19047 | return pipes;
|
19048 | }
|
19049 | function parseJitTemplate(template, typeName, sourceMapUrl, preserveWhitespaces, interpolation) {
|
19050 | const interpolationConfig = interpolation ? InterpolationConfig.fromArray(interpolation) : DEFAULT_INTERPOLATION_CONFIG;
|
19051 | // Parse the template and check for errors.
|
19052 | const parsed = parseTemplate(template, sourceMapUrl, { preserveWhitespaces: preserveWhitespaces, interpolationConfig });
|
19053 | if (parsed.errors !== null) {
|
19054 | const errors = parsed.errors.map(err => err.toString()).join(', ');
|
19055 | throw new Error(`Errors during JIT compilation of template for ${typeName}: ${errors}`);
|
19056 | }
|
19057 | return { template: parsed, interpolation: interpolationConfig };
|
19058 | }
|
19059 | function wrapExpression(obj, property) {
|
19060 | if (obj.hasOwnProperty(property)) {
|
19061 | return new WrappedNodeExpr(obj[property]);
|
19062 | }
|
19063 | else {
|
19064 | return undefined;
|
19065 | }
|
19066 | }
|
19067 | function computeProvidedIn(providedIn) {
|
19068 | if (providedIn == null || typeof providedIn === 'string') {
|
19069 | return new LiteralExpr(providedIn);
|
19070 | }
|
19071 | else {
|
19072 | return new WrappedNodeExpr(providedIn);
|
19073 | }
|
19074 | }
|
19075 | function convertR3DependencyMetadata(facade) {
|
19076 | let tokenExpr;
|
19077 | if (facade.token === null) {
|
19078 | tokenExpr = new LiteralExpr(null);
|
19079 | }
|
19080 | else if (facade.resolved === R3ResolvedDependencyType.Attribute) {
|
19081 | tokenExpr = new LiteralExpr(facade.token);
|
19082 | }
|
19083 | else {
|
19084 | tokenExpr = new WrappedNodeExpr(facade.token);
|
19085 | }
|
19086 | return {
|
19087 | token: tokenExpr,
|
19088 | attribute: null,
|
19089 | resolved: facade.resolved,
|
19090 | host: facade.host,
|
19091 | optional: facade.optional,
|
19092 | self: facade.self,
|
19093 | skipSelf: facade.skipSelf,
|
19094 | };
|
19095 | }
|
19096 | function convertR3DependencyMetadataArray(facades) {
|
19097 | return facades == null ? null : facades.map(convertR3DependencyMetadata);
|
19098 | }
|
19099 | function extractHostBindings(propMetadata, sourceSpan, host) {
|
19100 | // First parse the declarations from the metadata.
|
19101 | const bindings = parseHostBindings(host || {});
|
19102 | // After that check host bindings for errors
|
19103 | const errors = verifyHostBindings(bindings, sourceSpan);
|
19104 | if (errors.length) {
|
19105 | throw new Error(errors.map((error) => error.msg).join('\n'));
|
19106 | }
|
19107 | // Next, loop over the properties of the object, looking for @HostBinding and @HostListener.
|
19108 | for (const field in propMetadata) {
|
19109 | if (propMetadata.hasOwnProperty(field)) {
|
19110 | propMetadata[field].forEach(ann => {
|
19111 | if (isHostBinding(ann)) {
|
19112 | // Since this is a decorator, we know that the value is a class member. Always access it
|
19113 | // through `this` so that further down the line it can't be confused for a literal value
|
19114 | // (e.g. if there's a property called `true`).
|
19115 | bindings.properties[ann.hostPropertyName || field] =
|
19116 | getSafePropertyAccessString('this', field);
|
19117 | }
|
19118 | else if (isHostListener(ann)) {
|
19119 | bindings.listeners[ann.eventName || field] = `${field}(${(ann.args || []).join(',')})`;
|
19120 | }
|
19121 | });
|
19122 | }
|
19123 | }
|
19124 | return bindings;
|
19125 | }
|
19126 | function isHostBinding(value) {
|
19127 | return value.ngMetadataName === 'HostBinding';
|
19128 | }
|
19129 | function isHostListener(value) {
|
19130 | return value.ngMetadataName === 'HostListener';
|
19131 | }
|
19132 | function isInput(value) {
|
19133 | return value.ngMetadataName === 'Input';
|
19134 | }
|
19135 | function isOutput(value) {
|
19136 | return value.ngMetadataName === 'Output';
|
19137 | }
|
19138 | function parseInputOutputs(values) {
|
19139 | return values.reduce((map, value) => {
|
19140 | const [field, property] = value.split(',').map(piece => piece.trim());
|
19141 | map[field] = property || field;
|
19142 | return map;
|
19143 | }, {});
|
19144 | }
|
19145 | function convertDeclarePipeFacadeToMetadata(declaration) {
|
19146 | var _a;
|
19147 | return {
|
19148 | name: declaration.type.name,
|
19149 | type: wrapReference(declaration.type),
|
19150 | internalType: new WrappedNodeExpr(declaration.type),
|
19151 | typeArgumentCount: 0,
|
19152 | pipeName: declaration.name,
|
19153 | deps: null,
|
19154 | pure: (_a = declaration.pure) !== null && _a !== void 0 ? _a : true,
|
19155 | };
|
19156 | }
|
19157 | function publishFacade(global) {
|
19158 | const ng = global.ng || (global.ng = {});
|
19159 | ng.ɵcompilerFacade = new CompilerFacadeImpl();
|
19160 | }
|
19161 |
|
19162 | /**
|
19163 | * @license
|
19164 | * Copyright Google LLC All Rights Reserved.
|
19165 | *
|
19166 | * Use of this source code is governed by an MIT-style license that can be
|
19167 | * found in the LICENSE file at https://angular.io/license
|
19168 | */
|
19169 | const VERSION$1 = new Version('11.2.4');
|
19170 |
|
19171 | /**
|
19172 | * @license
|
19173 | * Copyright Google LLC All Rights Reserved.
|
19174 | *
|
19175 | * Use of this source code is governed by an MIT-style license that can be
|
19176 | * found in the LICENSE file at https://angular.io/license
|
19177 | */
|
19178 | class CompilerConfig {
|
19179 | constructor({ defaultEncapsulation = ViewEncapsulation.Emulated, useJit = true, jitDevMode = false, missingTranslation = null, preserveWhitespaces, strictInjectionParameters } = {}) {
|
19180 | this.defaultEncapsulation = defaultEncapsulation;
|
19181 | this.useJit = !!useJit;
|
19182 | this.jitDevMode = !!jitDevMode;
|
19183 | this.missingTranslation = missingTranslation;
|
19184 | this.preserveWhitespaces = preserveWhitespacesDefault(noUndefined(preserveWhitespaces));
|
19185 | this.strictInjectionParameters = strictInjectionParameters === true;
|
19186 | }
|
19187 | }
|
19188 | function preserveWhitespacesDefault(preserveWhitespacesOption, defaultSetting = false) {
|
19189 | return preserveWhitespacesOption === null ? defaultSetting : preserveWhitespacesOption;
|
19190 | }
|
19191 |
|
19192 | /**
|
19193 | * @license
|
19194 | * Copyright Google LLC All Rights Reserved.
|
19195 | *
|
19196 | * Use of this source code is governed by an MIT-style license that can be
|
19197 | * found in the LICENSE file at https://angular.io/license
|
19198 | */
|
19199 | class DirectiveNormalizer {
|
19200 | constructor(_resourceLoader, _urlResolver, _htmlParser, _config) {
|
19201 | this._resourceLoader = _resourceLoader;
|
19202 | this._urlResolver = _urlResolver;
|
19203 | this._htmlParser = _htmlParser;
|
19204 | this._config = _config;
|
19205 | this._resourceLoaderCache = new Map();
|
19206 | }
|
19207 | clearCache() {
|
19208 | this._resourceLoaderCache.clear();
|
19209 | }
|
19210 | clearCacheFor(normalizedDirective) {
|
19211 | if (!normalizedDirective.isComponent) {
|
19212 | return;
|
19213 | }
|
19214 | const template = normalizedDirective.template;
|
19215 | this._resourceLoaderCache.delete(template.templateUrl);
|
19216 | template.externalStylesheets.forEach((stylesheet) => {
|
19217 | this._resourceLoaderCache.delete(stylesheet.moduleUrl);
|
19218 | });
|
19219 | }
|
19220 | _fetch(url) {
|
19221 | let result = this._resourceLoaderCache.get(url);
|
19222 | if (!result) {
|
19223 | result = this._resourceLoader.get(url);
|
19224 | this._resourceLoaderCache.set(url, result);
|
19225 | }
|
19226 | return result;
|
19227 | }
|
19228 | normalizeTemplate(prenormData) {
|
19229 | if (isDefined(prenormData.template)) {
|
19230 | if (isDefined(prenormData.templateUrl)) {
|
19231 | throw syntaxError(`'${stringify(prenormData
|
19232 | .componentType)}' component cannot define both template and templateUrl`);
|
19233 | }
|
19234 | if (typeof prenormData.template !== 'string') {
|
19235 | throw syntaxError(`The template specified for component ${stringify(prenormData.componentType)} is not a string`);
|
19236 | }
|
19237 | }
|
19238 | else if (isDefined(prenormData.templateUrl)) {
|
19239 | if (typeof prenormData.templateUrl !== 'string') {
|
19240 | throw syntaxError(`The templateUrl specified for component ${stringify(prenormData.componentType)} is not a string`);
|
19241 | }
|
19242 | }
|
19243 | else {
|
19244 | throw syntaxError(`No template specified for component ${stringify(prenormData.componentType)}`);
|
19245 | }
|
19246 | if (isDefined(prenormData.preserveWhitespaces) &&
|
19247 | typeof prenormData.preserveWhitespaces !== 'boolean') {
|
19248 | throw syntaxError(`The preserveWhitespaces option for component ${stringify(prenormData.componentType)} must be a boolean`);
|
19249 | }
|
19250 | return SyncAsync.then(this._preParseTemplate(prenormData), (preparsedTemplate) => this._normalizeTemplateMetadata(prenormData, preparsedTemplate));
|
19251 | }
|
19252 | _preParseTemplate(prenomData) {
|
19253 | let template;
|
19254 | let templateUrl;
|
19255 | if (prenomData.template != null) {
|
19256 | template = prenomData.template;
|
19257 | templateUrl = prenomData.moduleUrl;
|
19258 | }
|
19259 | else {
|
19260 | templateUrl = this._urlResolver.resolve(prenomData.moduleUrl, prenomData.templateUrl);
|
19261 | template = this._fetch(templateUrl);
|
19262 | }
|
19263 | return SyncAsync.then(template, (template) => this._preparseLoadedTemplate(prenomData, template, templateUrl));
|
19264 | }
|
19265 | _preparseLoadedTemplate(prenormData, template, templateAbsUrl) {
|
19266 | const isInline = !!prenormData.template;
|
19267 | const interpolationConfig = InterpolationConfig.fromArray(prenormData.interpolation);
|
19268 | const templateUrl = templateSourceUrl({ reference: prenormData.ngModuleType }, { type: { reference: prenormData.componentType } }, { isInline, templateUrl: templateAbsUrl });
|
19269 | const rootNodesAndErrors = this._htmlParser.parse(template, templateUrl, { tokenizeExpansionForms: true, interpolationConfig });
|
19270 | if (rootNodesAndErrors.errors.length > 0) {
|
19271 | const errorString = rootNodesAndErrors.errors.join('\n');
|
19272 | throw syntaxError(`Template parse errors:\n${errorString}`);
|
19273 | }
|
19274 | const templateMetadataStyles = this._normalizeStylesheet(new CompileStylesheetMetadata({ styles: prenormData.styles, moduleUrl: prenormData.moduleUrl }));
|
19275 | const visitor = new TemplatePreparseVisitor();
|
19276 | visitAll$1(visitor, rootNodesAndErrors.rootNodes);
|
19277 | const templateStyles = this._normalizeStylesheet(new CompileStylesheetMetadata({ styles: visitor.styles, styleUrls: visitor.styleUrls, moduleUrl: templateAbsUrl }));
|
19278 | const styles = templateMetadataStyles.styles.concat(templateStyles.styles);
|
19279 | const inlineStyleUrls = templateMetadataStyles.styleUrls.concat(templateStyles.styleUrls);
|
19280 | const styleUrls = this
|
19281 | ._normalizeStylesheet(new CompileStylesheetMetadata({ styleUrls: prenormData.styleUrls, moduleUrl: prenormData.moduleUrl }))
|
19282 | .styleUrls;
|
19283 | return {
|
19284 | template,
|
19285 | templateUrl: templateAbsUrl,
|
19286 | isInline,
|
19287 | htmlAst: rootNodesAndErrors,
|
19288 | styles,
|
19289 | inlineStyleUrls,
|
19290 | styleUrls,
|
19291 | ngContentSelectors: visitor.ngContentSelectors,
|
19292 | };
|
19293 | }
|
19294 | _normalizeTemplateMetadata(prenormData, preparsedTemplate) {
|
19295 | return SyncAsync.then(this._loadMissingExternalStylesheets(preparsedTemplate.styleUrls.concat(preparsedTemplate.inlineStyleUrls)), (externalStylesheets) => this._normalizeLoadedTemplateMetadata(prenormData, preparsedTemplate, externalStylesheets));
|
19296 | }
|
19297 | _normalizeLoadedTemplateMetadata(prenormData, preparsedTemplate, stylesheets) {
|
19298 | // Algorithm:
|
19299 | // - produce exactly 1 entry per original styleUrl in
|
19300 | // CompileTemplateMetadata.externalStylesheets with all styles inlined
|
19301 | // - inline all styles that are referenced by the template into CompileTemplateMetadata.styles.
|
19302 | // Reason: be able to determine how many stylesheets there are even without loading
|
19303 | // the template nor the stylesheets, so we can create a stub for TypeScript always synchronously
|
19304 | // (as resource loading may be async)
|
19305 | const styles = [...preparsedTemplate.styles];
|
19306 | this._inlineStyles(preparsedTemplate.inlineStyleUrls, stylesheets, styles);
|
19307 | const styleUrls = preparsedTemplate.styleUrls;
|
19308 | const externalStylesheets = styleUrls.map(styleUrl => {
|
19309 | const stylesheet = stylesheets.get(styleUrl);
|
19310 | const styles = [...stylesheet.styles];
|
19311 | this._inlineStyles(stylesheet.styleUrls, stylesheets, styles);
|
19312 | return new CompileStylesheetMetadata({ moduleUrl: styleUrl, styles: styles });
|
19313 | });
|
19314 | let encapsulation = prenormData.encapsulation;
|
19315 | if (encapsulation == null) {
|
19316 | encapsulation = this._config.defaultEncapsulation;
|
19317 | }
|
19318 | if (encapsulation === ViewEncapsulation.Emulated && styles.length === 0 &&
|
19319 | styleUrls.length === 0) {
|
19320 | encapsulation = ViewEncapsulation.None;
|
19321 | }
|
19322 | return new CompileTemplateMetadata({
|
19323 | encapsulation,
|
19324 | template: preparsedTemplate.template,
|
19325 | templateUrl: preparsedTemplate.templateUrl,
|
19326 | htmlAst: preparsedTemplate.htmlAst,
|
19327 | styles,
|
19328 | styleUrls,
|
19329 | ngContentSelectors: preparsedTemplate.ngContentSelectors,
|
19330 | animations: prenormData.animations,
|
19331 | interpolation: prenormData.interpolation,
|
19332 | isInline: preparsedTemplate.isInline,
|
19333 | externalStylesheets,
|
19334 | preserveWhitespaces: preserveWhitespacesDefault(prenormData.preserveWhitespaces, this._config.preserveWhitespaces),
|
19335 | });
|
19336 | }
|
19337 | _inlineStyles(styleUrls, stylesheets, targetStyles) {
|
19338 | styleUrls.forEach(styleUrl => {
|
19339 | const stylesheet = stylesheets.get(styleUrl);
|
19340 | stylesheet.styles.forEach(style => targetStyles.push(style));
|
19341 | this._inlineStyles(stylesheet.styleUrls, stylesheets, targetStyles);
|
19342 | });
|
19343 | }
|
19344 | _loadMissingExternalStylesheets(styleUrls, loadedStylesheets = new Map()) {
|
19345 | return SyncAsync.then(SyncAsync.all(styleUrls.filter((styleUrl) => !loadedStylesheets.has(styleUrl))
|
19346 | .map(styleUrl => SyncAsync.then(this._fetch(styleUrl), (loadedStyle) => {
|
19347 | const stylesheet = this._normalizeStylesheet(new CompileStylesheetMetadata({ styles: [loadedStyle], moduleUrl: styleUrl }));
|
19348 | loadedStylesheets.set(styleUrl, stylesheet);
|
19349 | return this._loadMissingExternalStylesheets(stylesheet.styleUrls, loadedStylesheets);
|
19350 | }))), (_) => loadedStylesheets);
|
19351 | }
|
19352 | _normalizeStylesheet(stylesheet) {
|
19353 | const moduleUrl = stylesheet.moduleUrl;
|
19354 | const allStyleUrls = stylesheet.styleUrls.filter(isStyleUrlResolvable)
|
19355 | .map(url => this._urlResolver.resolve(moduleUrl, url));
|
19356 | const allStyles = stylesheet.styles.map(style => {
|
19357 | const styleWithImports = extractStyleUrls(this._urlResolver, moduleUrl, style);
|
19358 | allStyleUrls.push(...styleWithImports.styleUrls);
|
19359 | return styleWithImports.style;
|
19360 | });
|
19361 | return new CompileStylesheetMetadata({ styles: allStyles, styleUrls: allStyleUrls, moduleUrl: moduleUrl });
|
19362 | }
|
19363 | }
|
19364 | class TemplatePreparseVisitor {
|
19365 | constructor() {
|
19366 | this.ngContentSelectors = [];
|
19367 | this.styles = [];
|
19368 | this.styleUrls = [];
|
19369 | this.ngNonBindableStackCount = 0;
|
19370 | }
|
19371 | visitElement(ast, context) {
|
19372 | const preparsedElement = preparseElement(ast);
|
19373 | switch (preparsedElement.type) {
|
19374 | case PreparsedElementType.NG_CONTENT:
|
19375 | if (this.ngNonBindableStackCount === 0) {
|
19376 | this.ngContentSelectors.push(preparsedElement.selectAttr);
|
19377 | }
|
19378 | break;
|
19379 | case PreparsedElementType.STYLE:
|
19380 | let textContent = '';
|
19381 | ast.children.forEach(child => {
|
19382 | if (child instanceof Text$2) {
|
19383 | textContent += child.value;
|
19384 | }
|
19385 | });
|
19386 | this.styles.push(textContent);
|
19387 | break;
|
19388 | case PreparsedElementType.STYLESHEET:
|
19389 | this.styleUrls.push(preparsedElement.hrefAttr);
|
19390 | break;
|
19391 | }
|
19392 | if (preparsedElement.nonBindable) {
|
19393 | this.ngNonBindableStackCount++;
|
19394 | }
|
19395 | visitAll$1(this, ast.children);
|
19396 | if (preparsedElement.nonBindable) {
|
19397 | this.ngNonBindableStackCount--;
|
19398 | }
|
19399 | return null;
|
19400 | }
|
19401 | visitExpansion(ast, context) {
|
19402 | visitAll$1(this, ast.cases);
|
19403 | }
|
19404 | visitExpansionCase(ast, context) {
|
19405 | visitAll$1(this, ast.expression);
|
19406 | }
|
19407 | visitComment(ast, context) {
|
19408 | return null;
|
19409 | }
|
19410 | visitAttribute(ast, context) {
|
19411 | return null;
|
19412 | }
|
19413 | visitText(ast, context) {
|
19414 | return null;
|
19415 | }
|
19416 | }
|
19417 |
|
19418 | /**
|
19419 | * @license
|
19420 | * Copyright Google LLC All Rights Reserved.
|
19421 | *
|
19422 | * Use of this source code is governed by an MIT-style license that can be
|
19423 | * found in the LICENSE file at https://angular.io/license
|
19424 | */
|
19425 | const QUERY_METADATA_IDENTIFIERS = [
|
19426 | createViewChild,
|
19427 | createViewChildren,
|
19428 | createContentChild,
|
19429 | createContentChildren,
|
19430 | ];
|
19431 | /*
|
19432 | * Resolve a `Type` for {@link Directive}.
|
19433 | *
|
19434 | * This interface can be overridden by the application developer to create custom behavior.
|
19435 | *
|
19436 | * See {@link Compiler}
|
19437 | */
|
19438 | class DirectiveResolver {
|
19439 | constructor(_reflector) {
|
19440 | this._reflector = _reflector;
|
19441 | }
|
19442 | isDirective(type) {
|
19443 | const typeMetadata = this._reflector.annotations(resolveForwardRef(type));
|
19444 | return typeMetadata && typeMetadata.some(isDirectiveMetadata);
|
19445 | }
|
19446 | resolve(type, throwIfNotFound = true) {
|
19447 | const typeMetadata = this._reflector.annotations(resolveForwardRef(type));
|
19448 | if (typeMetadata) {
|
19449 | const metadata = findLast(typeMetadata, isDirectiveMetadata);
|
19450 | if (metadata) {
|
19451 | const propertyMetadata = this._reflector.propMetadata(type);
|
19452 | const guards = this._reflector.guards(type);
|
19453 | return this._mergeWithPropertyMetadata(metadata, propertyMetadata, guards, type);
|
19454 | }
|
19455 | }
|
19456 | if (throwIfNotFound) {
|
19457 | throw new Error(`No Directive annotation found on ${stringify(type)}`);
|
19458 | }
|
19459 | return null;
|
19460 | }
|
19461 | _mergeWithPropertyMetadata(dm, propertyMetadata, guards, directiveType) {
|
19462 | const inputs = [];
|
19463 | const outputs = [];
|
19464 | const host = {};
|
19465 | const queries = {};
|
19466 | Object.keys(propertyMetadata).forEach((propName) => {
|
19467 | const input = findLast(propertyMetadata[propName], (a) => createInput.isTypeOf(a));
|
19468 | if (input) {
|
19469 | if (input.bindingPropertyName) {
|
19470 | inputs.push(`${propName}: ${input.bindingPropertyName}`);
|
19471 | }
|
19472 | else {
|
19473 | inputs.push(propName);
|
19474 | }
|
19475 | }
|
19476 | const output = findLast(propertyMetadata[propName], (a) => createOutput.isTypeOf(a));
|
19477 | if (output) {
|
19478 | if (output.bindingPropertyName) {
|
19479 | outputs.push(`${propName}: ${output.bindingPropertyName}`);
|
19480 | }
|
19481 | else {
|
19482 | outputs.push(propName);
|
19483 | }
|
19484 | }
|
19485 | const hostBindings = propertyMetadata[propName].filter(a => createHostBinding.isTypeOf(a));
|
19486 | hostBindings.forEach(hostBinding => {
|
19487 | if (hostBinding.hostPropertyName) {
|
19488 | const startWith = hostBinding.hostPropertyName[0];
|
19489 | if (startWith === '(') {
|
19490 | throw new Error(`@HostBinding can not bind to events. Use @HostListener instead.`);
|
19491 | }
|
19492 | else if (startWith === '[') {
|
19493 | throw new Error(`@HostBinding parameter should be a property name, 'class.<name>', or 'attr.<name>'.`);
|
19494 | }
|
19495 | host[`[${hostBinding.hostPropertyName}]`] = propName;
|
19496 | }
|
19497 | else {
|
19498 | host[`[${propName}]`] = propName;
|
19499 | }
|
19500 | });
|
19501 | const hostListeners = propertyMetadata[propName].filter(a => createHostListener.isTypeOf(a));
|
19502 | hostListeners.forEach(hostListener => {
|
19503 | const args = hostListener.args || [];
|
19504 | host[`(${hostListener.eventName})`] = `${propName}(${args.join(',')})`;
|
19505 | });
|
19506 | const query = findLast(propertyMetadata[propName], (a) => QUERY_METADATA_IDENTIFIERS.some(i => i.isTypeOf(a)));
|
19507 | if (query) {
|
19508 | queries[propName] = query;
|
19509 | }
|
19510 | });
|
19511 | return this._merge(dm, inputs, outputs, host, queries, guards, directiveType);
|
19512 | }
|
19513 | _extractPublicName(def) {
|
19514 | return splitAtColon(def, [null, def])[1].trim();
|
19515 | }
|
19516 | _dedupeBindings(bindings) {
|
19517 | const names = new Set();
|
19518 | const publicNames = new Set();
|
19519 | const reversedResult = [];
|
19520 | // go last to first to allow later entries to overwrite previous entries
|
19521 | for (let i = bindings.length - 1; i >= 0; i--) {
|
19522 | const binding = bindings[i];
|
19523 | const name = this._extractPublicName(binding);
|
19524 | publicNames.add(name);
|
19525 | if (!names.has(name)) {
|
19526 | names.add(name);
|
19527 | reversedResult.push(binding);
|
19528 | }
|
19529 | }
|
19530 | return reversedResult.reverse();
|
19531 | }
|
19532 | _merge(directive, inputs, outputs, host, queries, guards, directiveType) {
|
19533 | const mergedInputs = this._dedupeBindings(directive.inputs ? directive.inputs.concat(inputs) : inputs);
|
19534 | const mergedOutputs = this._dedupeBindings(directive.outputs ? directive.outputs.concat(outputs) : outputs);
|
19535 | const mergedHost = directive.host ? Object.assign(Object.assign({}, directive.host), host) : host;
|
19536 | const mergedQueries = directive.queries ? Object.assign(Object.assign({}, directive.queries), queries) : queries;
|
19537 | if (createComponent.isTypeOf(directive)) {
|
19538 | const comp = directive;
|
19539 | return createComponent({
|
19540 | selector: comp.selector,
|
19541 | inputs: mergedInputs,
|
19542 | outputs: mergedOutputs,
|
19543 | host: mergedHost,
|
19544 | exportAs: comp.exportAs,
|
19545 | moduleId: comp.moduleId,
|
19546 | queries: mergedQueries,
|
19547 | changeDetection: comp.changeDetection,
|
19548 | providers: comp.providers,
|
19549 | viewProviders: comp.viewProviders,
|
19550 | entryComponents: comp.entryComponents,
|
19551 | template: comp.template,
|
19552 | templateUrl: comp.templateUrl,
|
19553 | styles: comp.styles,
|
19554 | styleUrls: comp.styleUrls,
|
19555 | encapsulation: comp.encapsulation,
|
19556 | animations: comp.animations,
|
19557 | interpolation: comp.interpolation,
|
19558 | preserveWhitespaces: directive.preserveWhitespaces,
|
19559 | });
|
19560 | }
|
19561 | else {
|
19562 | return createDirective({
|
19563 | selector: directive.selector,
|
19564 | inputs: mergedInputs,
|
19565 | outputs: mergedOutputs,
|
19566 | host: mergedHost,
|
19567 | exportAs: directive.exportAs,
|
19568 | queries: mergedQueries,
|
19569 | providers: directive.providers,
|
19570 | guards
|
19571 | });
|
19572 | }
|
19573 | }
|
19574 | }
|
19575 | function isDirectiveMetadata(type) {
|
19576 | return createDirective.isTypeOf(type) || createComponent.isTypeOf(type);
|
19577 | }
|
19578 | function findLast(arr, condition) {
|
19579 | for (let i = arr.length - 1; i >= 0; i--) {
|
19580 | if (condition(arr[i])) {
|
19581 | return arr[i];
|
19582 | }
|
19583 | }
|
19584 | return null;
|
19585 | }
|
19586 |
|
19587 | /**
|
19588 | * @license
|
19589 | * Copyright Google LLC All Rights Reserved.
|
19590 | *
|
19591 | * Use of this source code is governed by an MIT-style license that can be
|
19592 | * found in the LICENSE file at https://angular.io/license
|
19593 | */
|
19594 | var _VisitorMode;
|
19595 | (function (_VisitorMode) {
|
19596 | _VisitorMode[_VisitorMode["Extract"] = 0] = "Extract";
|
19597 | _VisitorMode[_VisitorMode["Merge"] = 1] = "Merge";
|
19598 | })(_VisitorMode || (_VisitorMode = {}));
|
19599 |
|
19600 | /**
|
19601 | * @license
|
19602 | * Copyright Google LLC All Rights Reserved.
|
19603 | *
|
19604 | * Use of this source code is governed by an MIT-style license that can be
|
19605 | * found in the LICENSE file at https://angular.io/license
|
19606 | */
|
19607 | const STRIP_SRC_FILE_SUFFIXES = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/;
|
19608 | const GENERATED_FILE = /\.ngfactory\.|\.ngsummary\./;
|
19609 | const JIT_SUMMARY_FILE = /\.ngsummary\./;
|
19610 | const JIT_SUMMARY_NAME = /NgSummary$/;
|
19611 | function ngfactoryFilePath(filePath, forceSourceFile = false) {
|
19612 | const urlWithSuffix = splitTypescriptSuffix(filePath, forceSourceFile);
|
19613 | return `${urlWithSuffix[0]}.ngfactory${normalizeGenFileSuffix(urlWithSuffix[1])}`;
|
19614 | }
|
19615 | function stripGeneratedFileSuffix(filePath) {
|
19616 | return filePath.replace(GENERATED_FILE, '.');
|
19617 | }
|
19618 | function isGeneratedFile(filePath) {
|
19619 | return GENERATED_FILE.test(filePath);
|
19620 | }
|
19621 | function splitTypescriptSuffix(path, forceSourceFile = false) {
|
19622 | if (path.endsWith('.d.ts')) {
|
19623 | return [path.slice(0, -5), forceSourceFile ? '.ts' : '.d.ts'];
|
19624 | }
|
19625 | const lastDot = path.lastIndexOf('.');
|
19626 | if (lastDot !== -1) {
|
19627 | return [path.substring(0, lastDot), path.substring(lastDot)];
|
19628 | }
|
19629 | return [path, ''];
|
19630 | }
|
19631 | function normalizeGenFileSuffix(srcFileSuffix) {
|
19632 | return srcFileSuffix === '.tsx' ? '.ts' : srcFileSuffix;
|
19633 | }
|
19634 | function summaryFileName(fileName) {
|
19635 | const fileNameWithoutSuffix = fileName.replace(STRIP_SRC_FILE_SUFFIXES, '');
|
19636 | return `${fileNameWithoutSuffix}.ngsummary.json`;
|
19637 | }
|
19638 | function summaryForJitFileName(fileName, forceSourceFile = false) {
|
19639 | const urlWithSuffix = splitTypescriptSuffix(stripGeneratedFileSuffix(fileName), forceSourceFile);
|
19640 | return `${urlWithSuffix[0]}.ngsummary${urlWithSuffix[1]}`;
|
19641 | }
|
19642 | function stripSummaryForJitFileSuffix(filePath) {
|
19643 | return filePath.replace(JIT_SUMMARY_FILE, '.');
|
19644 | }
|
19645 | function summaryForJitName(symbolName) {
|
19646 | return `${symbolName}NgSummary`;
|
19647 | }
|
19648 | function stripSummaryForJitNameSuffix(symbolName) {
|
19649 | return symbolName.replace(JIT_SUMMARY_NAME, '');
|
19650 | }
|
19651 |
|
19652 | /**
|
19653 | * @license
|
19654 | * Copyright Google LLC All Rights Reserved.
|
19655 | *
|
19656 | * Use of this source code is governed by an MIT-style license that can be
|
19657 | * found in the LICENSE file at https://angular.io/license
|
19658 | */
|
19659 | var LifecycleHooks;
|
19660 | (function (LifecycleHooks) {
|
19661 | LifecycleHooks[LifecycleHooks["OnInit"] = 0] = "OnInit";
|
19662 | LifecycleHooks[LifecycleHooks["OnDestroy"] = 1] = "OnDestroy";
|
19663 | LifecycleHooks[LifecycleHooks["DoCheck"] = 2] = "DoCheck";
|
19664 | LifecycleHooks[LifecycleHooks["OnChanges"] = 3] = "OnChanges";
|
19665 | LifecycleHooks[LifecycleHooks["AfterContentInit"] = 4] = "AfterContentInit";
|
19666 | LifecycleHooks[LifecycleHooks["AfterContentChecked"] = 5] = "AfterContentChecked";
|
19667 | LifecycleHooks[LifecycleHooks["AfterViewInit"] = 6] = "AfterViewInit";
|
19668 | LifecycleHooks[LifecycleHooks["AfterViewChecked"] = 7] = "AfterViewChecked";
|
19669 | })(LifecycleHooks || (LifecycleHooks = {}));
|
19670 | const LIFECYCLE_HOOKS_VALUES = [
|
19671 | LifecycleHooks.OnInit, LifecycleHooks.OnDestroy, LifecycleHooks.DoCheck, LifecycleHooks.OnChanges,
|
19672 | LifecycleHooks.AfterContentInit, LifecycleHooks.AfterContentChecked, LifecycleHooks.AfterViewInit,
|
19673 | LifecycleHooks.AfterViewChecked
|
19674 | ];
|
19675 | function hasLifecycleHook(reflector, hook, token) {
|
19676 | return reflector.hasLifecycleHook(token, getHookName(hook));
|
19677 | }
|
19678 | function getAllLifecycleHooks(reflector, token) {
|
19679 | return LIFECYCLE_HOOKS_VALUES.filter(hook => hasLifecycleHook(reflector, hook, token));
|
19680 | }
|
19681 | function getHookName(hook) {
|
19682 | switch (hook) {
|
19683 | case LifecycleHooks.OnInit:
|
19684 | return 'ngOnInit';
|
19685 | case LifecycleHooks.OnDestroy:
|
19686 | return 'ngOnDestroy';
|
19687 | case LifecycleHooks.DoCheck:
|
19688 | return 'ngDoCheck';
|
19689 | case LifecycleHooks.OnChanges:
|
19690 | return 'ngOnChanges';
|
19691 | case LifecycleHooks.AfterContentInit:
|
19692 | return 'ngAfterContentInit';
|
19693 | case LifecycleHooks.AfterContentChecked:
|
19694 | return 'ngAfterContentChecked';
|
19695 | case LifecycleHooks.AfterViewInit:
|
19696 | return 'ngAfterViewInit';
|
19697 | case LifecycleHooks.AfterViewChecked:
|
19698 | return 'ngAfterViewChecked';
|
19699 | default:
|
19700 | // This default case is not needed by TypeScript compiler, as the switch is exhaustive.
|
19701 | // However Closure Compiler does not understand that and reports an error in typed mode.
|
19702 | // The `throw new Error` below works around the problem, and the unexpected: never variable
|
19703 | // makes sure tsc still checks this code is unreachable.
|
19704 | const unexpected = hook;
|
19705 | throw new Error(`unexpected ${unexpected}`);
|
19706 | }
|
19707 | }
|
19708 |
|
19709 | /**
|
19710 | * @license
|
19711 | * Copyright Google LLC All Rights Reserved.
|
19712 | *
|
19713 | * Use of this source code is governed by an MIT-style license that can be
|
19714 | * found in the LICENSE file at https://angular.io/license
|
19715 | */
|
19716 | const ERROR_COMPONENT_TYPE = 'ngComponentType';
|
19717 | // Design notes:
|
19718 | // - don't lazily create metadata:
|
19719 | // For some metadata, we need to do async work sometimes,
|
19720 | // so the user has to kick off this loading.
|
19721 | // But we want to report errors even when the async work is
|
19722 | // not required to check that the user would have been able
|
19723 | // to wait correctly.
|
19724 | class CompileMetadataResolver {
|
19725 | constructor(_config, _htmlParser, _ngModuleResolver, _directiveResolver, _pipeResolver, _summaryResolver, _schemaRegistry, _directiveNormalizer, _console, _staticSymbolCache, _reflector, _errorCollector) {
|
19726 | this._config = _config;
|
19727 | this._htmlParser = _htmlParser;
|
19728 | this._ngModuleResolver = _ngModuleResolver;
|
19729 | this._directiveResolver = _directiveResolver;
|
19730 | this._pipeResolver = _pipeResolver;
|
19731 | this._summaryResolver = _summaryResolver;
|
19732 | this._schemaRegistry = _schemaRegistry;
|
19733 | this._directiveNormalizer = _directiveNormalizer;
|
19734 | this._console = _console;
|
19735 | this._staticSymbolCache = _staticSymbolCache;
|
19736 | this._reflector = _reflector;
|
19737 | this._errorCollector = _errorCollector;
|
19738 | this._nonNormalizedDirectiveCache = new Map();
|
19739 | this._directiveCache = new Map();
|
19740 | this._summaryCache = new Map();
|
19741 | this._pipeCache = new Map();
|
19742 | this._ngModuleCache = new Map();
|
19743 | this._ngModuleOfTypes = new Map();
|
19744 | this._shallowModuleCache = new Map();
|
19745 | }
|
19746 | getReflector() {
|
19747 | return this._reflector;
|
19748 | }
|
19749 | clearCacheFor(type) {
|
19750 | const dirMeta = this._directiveCache.get(type);
|
19751 | this._directiveCache.delete(type);
|
19752 | this._nonNormalizedDirectiveCache.delete(type);
|
19753 | this._summaryCache.delete(type);
|
19754 | this._pipeCache.delete(type);
|
19755 | this._ngModuleOfTypes.delete(type);
|
19756 | // Clear all of the NgModule as they contain transitive information!
|
19757 | this._ngModuleCache.clear();
|
19758 | if (dirMeta) {
|
19759 | this._directiveNormalizer.clearCacheFor(dirMeta);
|
19760 | }
|
19761 | }
|
19762 | clearCache() {
|
19763 | this._directiveCache.clear();
|
19764 | this._nonNormalizedDirectiveCache.clear();
|
19765 | this._summaryCache.clear();
|
19766 | this._pipeCache.clear();
|
19767 | this._ngModuleCache.clear();
|
19768 | this._ngModuleOfTypes.clear();
|
19769 | this._directiveNormalizer.clearCache();
|
19770 | }
|
19771 | _createProxyClass(baseType, name) {
|
19772 | let delegate = null;
|
19773 | const proxyClass = function () {
|
19774 | if (!delegate) {
|
19775 | throw new Error(`Illegal state: Class ${name} for type ${stringify(baseType)} is not compiled yet!`);
|
19776 | }
|
19777 | return delegate.apply(this, arguments);
|
19778 | };
|
19779 | proxyClass.setDelegate = (d) => {
|
19780 | delegate = d;
|
19781 | proxyClass.prototype = d.prototype;
|
19782 | };
|
19783 | // Make stringify work correctly
|
19784 | proxyClass.overriddenName = name;
|
19785 | return proxyClass;
|
19786 | }
|
19787 | getGeneratedClass(dirType, name) {
|
19788 | if (dirType instanceof StaticSymbol) {
|
19789 | return this._staticSymbolCache.get(ngfactoryFilePath(dirType.filePath), name);
|
19790 | }
|
19791 | else {
|
19792 | return this._createProxyClass(dirType, name);
|
19793 | }
|
19794 | }
|
19795 | getComponentViewClass(dirType) {
|
19796 | return this.getGeneratedClass(dirType, viewClassName(dirType, 0));
|
19797 | }
|
19798 | getHostComponentViewClass(dirType) {
|
19799 | return this.getGeneratedClass(dirType, hostViewClassName(dirType));
|
19800 | }
|
19801 | getHostComponentType(dirType) {
|
19802 | const name = `${identifierName({ reference: dirType })}_Host`;
|
19803 | if (dirType instanceof StaticSymbol) {
|
19804 | return this._staticSymbolCache.get(dirType.filePath, name);
|
19805 | }
|
19806 | return this._createProxyClass(dirType, name);
|
19807 | }
|
19808 | getRendererType(dirType) {
|
19809 | if (dirType instanceof StaticSymbol) {
|
19810 | return this._staticSymbolCache.get(ngfactoryFilePath(dirType.filePath), rendererTypeName(dirType));
|
19811 | }
|
19812 | else {
|
19813 | // returning an object as proxy,
|
19814 | // that we fill later during runtime compilation.
|
19815 | return {};
|
19816 | }
|
19817 | }
|
19818 | getComponentFactory(selector, dirType, inputs, outputs) {
|
19819 | if (dirType instanceof StaticSymbol) {
|
19820 | return this._staticSymbolCache.get(ngfactoryFilePath(dirType.filePath), componentFactoryName(dirType));
|
19821 | }
|
19822 | else {
|
19823 | const hostView = this.getHostComponentViewClass(dirType);
|
19824 | // Note: ngContentSelectors will be filled later once the template is
|
19825 | // loaded.
|
19826 | const createComponentFactory = this._reflector.resolveExternalReference(Identifiers.createComponentFactory);
|
19827 | return createComponentFactory(selector, dirType, hostView, inputs, outputs, []);
|
19828 | }
|
19829 | }
|
19830 | initComponentFactory(factory, ngContentSelectors) {
|
19831 | if (!(factory instanceof StaticSymbol)) {
|
19832 | factory.ngContentSelectors.push(...ngContentSelectors);
|
19833 | }
|
19834 | }
|
19835 | _loadSummary(type, kind) {
|
19836 | let typeSummary = this._summaryCache.get(type);
|
19837 | if (!typeSummary) {
|
19838 | const summary = this._summaryResolver.resolveSummary(type);
|
19839 | typeSummary = summary ? summary.type : null;
|
19840 | this._summaryCache.set(type, typeSummary || null);
|
19841 | }
|
19842 | return typeSummary && typeSummary.summaryKind === kind ? typeSummary : null;
|
19843 | }
|
19844 | getHostComponentMetadata(compMeta, hostViewType) {
|
19845 | const hostType = this.getHostComponentType(compMeta.type.reference);
|
19846 | if (!hostViewType) {
|
19847 | hostViewType = this.getHostComponentViewClass(hostType);
|
19848 | }
|
19849 | // Note: ! is ok here as this method should only be called with normalized directive
|
19850 | // metadata, which always fills in the selector.
|
19851 | const template = CssSelector.parse(compMeta.selector)[0].getMatchingElementTemplate();
|
19852 | const templateUrl = '';
|
19853 | const htmlAst = this._htmlParser.parse(template, templateUrl);
|
19854 | return CompileDirectiveMetadata.create({
|
19855 | isHost: true,
|
19856 | type: { reference: hostType, diDeps: [], lifecycleHooks: [] },
|
19857 | template: new CompileTemplateMetadata({
|
19858 | encapsulation: ViewEncapsulation.None,
|
19859 | template,
|
19860 | templateUrl,
|
19861 | htmlAst,
|
19862 | styles: [],
|
19863 | styleUrls: [],
|
19864 | ngContentSelectors: [],
|
19865 | animations: [],
|
19866 | isInline: true,
|
19867 | externalStylesheets: [],
|
19868 | interpolation: null,
|
19869 | preserveWhitespaces: false,
|
19870 | }),
|
19871 | exportAs: null,
|
19872 | changeDetection: ChangeDetectionStrategy.Default,
|
19873 | inputs: [],
|
19874 | outputs: [],
|
19875 | host: {},
|
19876 | isComponent: true,
|
19877 | selector: '*',
|
19878 | providers: [],
|
19879 | viewProviders: [],
|
19880 | queries: [],
|
19881 | guards: {},
|
19882 | viewQueries: [],
|
19883 | componentViewType: hostViewType,
|
19884 | rendererType: { id: '__Host__', encapsulation: ViewEncapsulation.None, styles: [], data: {} },
|
19885 | entryComponents: [],
|
19886 | componentFactory: null
|
19887 | });
|
19888 | }
|
19889 | loadDirectiveMetadata(ngModuleType, directiveType, isSync) {
|
19890 | if (this._directiveCache.has(directiveType)) {
|
19891 | return null;
|
19892 | }
|
19893 | directiveType = resolveForwardRef(directiveType);
|
19894 | const { annotation, metadata } = this.getNonNormalizedDirectiveMetadata(directiveType);
|
19895 | const createDirectiveMetadata = (templateMetadata) => {
|
19896 | const normalizedDirMeta = new CompileDirectiveMetadata({
|
19897 | isHost: false,
|
19898 | type: metadata.type,
|
19899 | isComponent: metadata.isComponent,
|
19900 | selector: metadata.selector,
|
19901 | exportAs: metadata.exportAs,
|
19902 | changeDetection: metadata.changeDetection,
|
19903 | inputs: metadata.inputs,
|
19904 | outputs: metadata.outputs,
|
19905 | hostListeners: metadata.hostListeners,
|
19906 | hostProperties: metadata.hostProperties,
|
19907 | hostAttributes: metadata.hostAttributes,
|
19908 | providers: metadata.providers,
|
19909 | viewProviders: metadata.viewProviders,
|
19910 | queries: metadata.queries,
|
19911 | guards: metadata.guards,
|
19912 | viewQueries: metadata.viewQueries,
|
19913 | entryComponents: metadata.entryComponents,
|
19914 | componentViewType: metadata.componentViewType,
|
19915 | rendererType: metadata.rendererType,
|
19916 | componentFactory: metadata.componentFactory,
|
19917 | template: templateMetadata
|
19918 | });
|
19919 | if (templateMetadata) {
|
19920 | this.initComponentFactory(metadata.componentFactory, templateMetadata.ngContentSelectors);
|
19921 | }
|
19922 | this._directiveCache.set(directiveType, normalizedDirMeta);
|
19923 | this._summaryCache.set(directiveType, normalizedDirMeta.toSummary());
|
19924 | return null;
|
19925 | };
|
19926 | if (metadata.isComponent) {
|
19927 | const template = metadata.template;
|
19928 | const templateMeta = this._directiveNormalizer.normalizeTemplate({
|
19929 | ngModuleType,
|
19930 | componentType: directiveType,
|
19931 | moduleUrl: this._reflector.componentModuleUrl(directiveType, annotation),
|
19932 | encapsulation: template.encapsulation,
|
19933 | template: template.template,
|
19934 | templateUrl: template.templateUrl,
|
19935 | styles: template.styles,
|
19936 | styleUrls: template.styleUrls,
|
19937 | animations: template.animations,
|
19938 | interpolation: template.interpolation,
|
19939 | preserveWhitespaces: template.preserveWhitespaces
|
19940 | });
|
19941 | if (isPromise(templateMeta) && isSync) {
|
19942 | this._reportError(componentStillLoadingError(directiveType), directiveType);
|
19943 | return null;
|
19944 | }
|
19945 | return SyncAsync.then(templateMeta, createDirectiveMetadata);
|
19946 | }
|
19947 | else {
|
19948 | // directive
|
19949 | createDirectiveMetadata(null);
|
19950 | return null;
|
19951 | }
|
19952 | }
|
19953 | getNonNormalizedDirectiveMetadata(directiveType) {
|
19954 | directiveType = resolveForwardRef(directiveType);
|
19955 | if (!directiveType) {
|
19956 | return null;
|
19957 | }
|
19958 | let cacheEntry = this._nonNormalizedDirectiveCache.get(directiveType);
|
19959 | if (cacheEntry) {
|
19960 | return cacheEntry;
|
19961 | }
|
19962 | const dirMeta = this._directiveResolver.resolve(directiveType, false);
|
19963 | if (!dirMeta) {
|
19964 | return null;
|
19965 | }
|
19966 | let nonNormalizedTemplateMetadata = undefined;
|
19967 | if (createComponent.isTypeOf(dirMeta)) {
|
19968 | // component
|
19969 | const compMeta = dirMeta;
|
19970 | assertArrayOfStrings('styles', compMeta.styles);
|
19971 | assertArrayOfStrings('styleUrls', compMeta.styleUrls);
|
19972 | assertInterpolationSymbols('interpolation', compMeta.interpolation);
|
19973 | const animations = compMeta.animations;
|
19974 | nonNormalizedTemplateMetadata = new CompileTemplateMetadata({
|
19975 | encapsulation: noUndefined(compMeta.encapsulation),
|
19976 | template: noUndefined(compMeta.template),
|
19977 | templateUrl: noUndefined(compMeta.templateUrl),
|
19978 | htmlAst: null,
|
19979 | styles: compMeta.styles || [],
|
19980 | styleUrls: compMeta.styleUrls || [],
|
19981 | animations: animations || [],
|
19982 | interpolation: noUndefined(compMeta.interpolation),
|
19983 | isInline: !!compMeta.template,
|
19984 | externalStylesheets: [],
|
19985 | ngContentSelectors: [],
|
19986 | preserveWhitespaces: noUndefined(dirMeta.preserveWhitespaces),
|
19987 | });
|
19988 | }
|
19989 | let changeDetectionStrategy = null;
|
19990 | let viewProviders = [];
|
19991 | let entryComponentMetadata = [];
|
19992 | let selector = dirMeta.selector;
|
19993 | if (createComponent.isTypeOf(dirMeta)) {
|
19994 | // Component
|
19995 | const compMeta = dirMeta;
|
19996 | changeDetectionStrategy = compMeta.changeDetection;
|
19997 | if (compMeta.viewProviders) {
|
19998 | viewProviders = this._getProvidersMetadata(compMeta.viewProviders, entryComponentMetadata, `viewProviders for "${stringifyType(directiveType)}"`, [], directiveType);
|
19999 | }
|
20000 | if (compMeta.entryComponents) {
|
20001 | entryComponentMetadata = flattenAndDedupeArray(compMeta.entryComponents)
|
20002 | .map((type) => this._getEntryComponentMetadata(type))
|
20003 | .concat(entryComponentMetadata);
|
20004 | }
|
20005 | if (!selector) {
|
20006 | selector = this._schemaRegistry.getDefaultComponentElementName();
|
20007 | }
|
20008 | }
|
20009 | else {
|
20010 | // Directive
|
20011 | if (!selector) {
|
20012 | selector = null;
|
20013 | }
|
20014 | }
|
20015 | let providers = [];
|
20016 | if (dirMeta.providers != null) {
|
20017 | providers = this._getProvidersMetadata(dirMeta.providers, entryComponentMetadata, `providers for "${stringifyType(directiveType)}"`, [], directiveType);
|
20018 | }
|
20019 | let queries = [];
|
20020 | let viewQueries = [];
|
20021 | if (dirMeta.queries != null) {
|
20022 | queries = this._getQueriesMetadata(dirMeta.queries, false, directiveType);
|
20023 | viewQueries = this._getQueriesMetadata(dirMeta.queries, true, directiveType);
|
20024 | }
|
20025 | const metadata = CompileDirectiveMetadata.create({
|
20026 | isHost: false,
|
20027 | selector: selector,
|
20028 | exportAs: noUndefined(dirMeta.exportAs),
|
20029 | isComponent: !!nonNormalizedTemplateMetadata,
|
20030 | type: this._getTypeMetadata(directiveType),
|
20031 | template: nonNormalizedTemplateMetadata,
|
20032 | changeDetection: changeDetectionStrategy,
|
20033 | inputs: dirMeta.inputs || [],
|
20034 | outputs: dirMeta.outputs || [],
|
20035 | host: dirMeta.host || {},
|
20036 | providers: providers || [],
|
20037 | viewProviders: viewProviders || [],
|
20038 | queries: queries || [],
|
20039 | guards: dirMeta.guards || {},
|
20040 | viewQueries: viewQueries || [],
|
20041 | entryComponents: entryComponentMetadata,
|
20042 | componentViewType: nonNormalizedTemplateMetadata ? this.getComponentViewClass(directiveType) :
|
20043 | null,
|
20044 | rendererType: nonNormalizedTemplateMetadata ? this.getRendererType(directiveType) : null,
|
20045 | componentFactory: null
|
20046 | });
|
20047 | if (nonNormalizedTemplateMetadata) {
|
20048 | metadata.componentFactory =
|
20049 | this.getComponentFactory(selector, directiveType, metadata.inputs, metadata.outputs);
|
20050 | }
|
20051 | cacheEntry = { metadata, annotation: dirMeta };
|
20052 | this._nonNormalizedDirectiveCache.set(directiveType, cacheEntry);
|
20053 | return cacheEntry;
|
20054 | }
|
20055 | /**
|
20056 | * Gets the metadata for the given directive.
|
20057 | * This assumes `loadNgModuleDirectiveAndPipeMetadata` has been called first.
|
20058 | */
|
20059 | getDirectiveMetadata(directiveType) {
|
20060 | const dirMeta = this._directiveCache.get(directiveType);
|
20061 | if (!dirMeta) {
|
20062 | this._reportError(syntaxError(`Illegal state: getDirectiveMetadata can only be called after loadNgModuleDirectiveAndPipeMetadata for a module that declares it. Directive ${stringifyType(directiveType)}.`), directiveType);
|
20063 | }
|
20064 | return dirMeta;
|
20065 | }
|
20066 | getDirectiveSummary(dirType) {
|
20067 | const dirSummary = this._loadSummary(dirType, CompileSummaryKind.Directive);
|
20068 | if (!dirSummary) {
|
20069 | this._reportError(syntaxError(`Illegal state: Could not load the summary for directive ${stringifyType(dirType)}.`), dirType);
|
20070 | }
|
20071 | return dirSummary;
|
20072 | }
|
20073 | isDirective(type) {
|
20074 | return !!this._loadSummary(type, CompileSummaryKind.Directive) ||
|
20075 | this._directiveResolver.isDirective(type);
|
20076 | }
|
20077 | isAbstractDirective(type) {
|
20078 | const summary = this._loadSummary(type, CompileSummaryKind.Directive);
|
20079 | if (summary && !summary.isComponent) {
|
20080 | return !summary.selector;
|
20081 | }
|
20082 | const meta = this._directiveResolver.resolve(type, false);
|
20083 | if (meta && !createComponent.isTypeOf(meta)) {
|
20084 | return !meta.selector;
|
20085 | }
|
20086 | return false;
|
20087 | }
|
20088 | isPipe(type) {
|
20089 | return !!this._loadSummary(type, CompileSummaryKind.Pipe) ||
|
20090 | this._pipeResolver.isPipe(type);
|
20091 | }
|
20092 | isNgModule(type) {
|
20093 | return !!this._loadSummary(type, CompileSummaryKind.NgModule) ||
|
20094 | this._ngModuleResolver.isNgModule(type);
|
20095 | }
|
20096 | getNgModuleSummary(moduleType, alreadyCollecting = null) {
|
20097 | let moduleSummary = this._loadSummary(moduleType, CompileSummaryKind.NgModule);
|
20098 | if (!moduleSummary) {
|
20099 | const moduleMeta = this.getNgModuleMetadata(moduleType, false, alreadyCollecting);
|
20100 | moduleSummary = moduleMeta ? moduleMeta.toSummary() : null;
|
20101 | if (moduleSummary) {
|
20102 | this._summaryCache.set(moduleType, moduleSummary);
|
20103 | }
|
20104 | }
|
20105 | return moduleSummary;
|
20106 | }
|
20107 | /**
|
20108 | * Loads the declared directives and pipes of an NgModule.
|
20109 | */
|
20110 | loadNgModuleDirectiveAndPipeMetadata(moduleType, isSync, throwIfNotFound = true) {
|
20111 | const ngModule = this.getNgModuleMetadata(moduleType, throwIfNotFound);
|
20112 | const loading = [];
|
20113 | if (ngModule) {
|
20114 | ngModule.declaredDirectives.forEach((id) => {
|
20115 | const promise = this.loadDirectiveMetadata(moduleType, id.reference, isSync);
|
20116 | if (promise) {
|
20117 | loading.push(promise);
|
20118 | }
|
20119 | });
|
20120 | ngModule.declaredPipes.forEach((id) => this._loadPipeMetadata(id.reference));
|
20121 | }
|
20122 | return Promise.all(loading);
|
20123 | }
|
20124 | getShallowModuleMetadata(moduleType) {
|
20125 | let compileMeta = this._shallowModuleCache.get(moduleType);
|
20126 | if (compileMeta) {
|
20127 | return compileMeta;
|
20128 | }
|
20129 | const ngModuleMeta = findLast(this._reflector.shallowAnnotations(moduleType), createNgModule.isTypeOf);
|
20130 | compileMeta = {
|
20131 | type: this._getTypeMetadata(moduleType),
|
20132 | rawExports: ngModuleMeta.exports,
|
20133 | rawImports: ngModuleMeta.imports,
|
20134 | rawProviders: ngModuleMeta.providers,
|
20135 | };
|
20136 | this._shallowModuleCache.set(moduleType, compileMeta);
|
20137 | return compileMeta;
|
20138 | }
|
20139 | getNgModuleMetadata(moduleType, throwIfNotFound = true, alreadyCollecting = null) {
|
20140 | moduleType = resolveForwardRef(moduleType);
|
20141 | let compileMeta = this._ngModuleCache.get(moduleType);
|
20142 | if (compileMeta) {
|
20143 | return compileMeta;
|
20144 | }
|
20145 | const meta = this._ngModuleResolver.resolve(moduleType, throwIfNotFound);
|
20146 | if (!meta) {
|
20147 | return null;
|
20148 | }
|
20149 | const declaredDirectives = [];
|
20150 | const exportedNonModuleIdentifiers = [];
|
20151 | const declaredPipes = [];
|
20152 | const importedModules = [];
|
20153 | const exportedModules = [];
|
20154 | const providers = [];
|
20155 | const entryComponents = [];
|
20156 | const bootstrapComponents = [];
|
20157 | const schemas = [];
|
20158 | if (meta.imports) {
|
20159 | flattenAndDedupeArray(meta.imports).forEach((importedType) => {
|
20160 | let importedModuleType = undefined;
|
20161 | if (isValidType(importedType)) {
|
20162 | importedModuleType = importedType;
|
20163 | }
|
20164 | else if (importedType && importedType.ngModule) {
|
20165 | const moduleWithProviders = importedType;
|
20166 | importedModuleType = moduleWithProviders.ngModule;
|
20167 | if (moduleWithProviders.providers) {
|
20168 | providers.push(...this._getProvidersMetadata(moduleWithProviders.providers, entryComponents, `provider for the NgModule '${stringifyType(importedModuleType)}'`, [], importedType));
|
20169 | }
|
20170 | }
|
20171 | if (importedModuleType) {
|
20172 | if (this._checkSelfImport(moduleType, importedModuleType))
|
20173 | return;
|
20174 | if (!alreadyCollecting)
|
20175 | alreadyCollecting = new Set();
|
20176 | if (alreadyCollecting.has(importedModuleType)) {
|
20177 | this._reportError(syntaxError(`${this._getTypeDescriptor(importedModuleType)} '${stringifyType(importedType)}' is imported recursively by the module '${stringifyType(moduleType)}'.`), moduleType);
|
20178 | return;
|
20179 | }
|
20180 | alreadyCollecting.add(importedModuleType);
|
20181 | const importedModuleSummary = this.getNgModuleSummary(importedModuleType, alreadyCollecting);
|
20182 | alreadyCollecting.delete(importedModuleType);
|
20183 | if (!importedModuleSummary) {
|
20184 | this._reportError(syntaxError(`Unexpected ${this._getTypeDescriptor(importedType)} '${stringifyType(importedType)}' imported by the module '${stringifyType(moduleType)}'. Please add a @NgModule annotation.`), moduleType);
|
20185 | return;
|
20186 | }
|
20187 | importedModules.push(importedModuleSummary);
|
20188 | }
|
20189 | else {
|
20190 | this._reportError(syntaxError(`Unexpected value '${stringifyType(importedType)}' imported by the module '${stringifyType(moduleType)}'`), moduleType);
|
20191 | return;
|
20192 | }
|
20193 | });
|
20194 | }
|
20195 | if (meta.exports) {
|
20196 | flattenAndDedupeArray(meta.exports).forEach((exportedType) => {
|
20197 | if (!isValidType(exportedType)) {
|
20198 | this._reportError(syntaxError(`Unexpected value '${stringifyType(exportedType)}' exported by the module '${stringifyType(moduleType)}'`), moduleType);
|
20199 | return;
|
20200 | }
|
20201 | if (!alreadyCollecting)
|
20202 | alreadyCollecting = new Set();
|
20203 | if (alreadyCollecting.has(exportedType)) {
|
20204 | this._reportError(syntaxError(`${this._getTypeDescriptor(exportedType)} '${stringify(exportedType)}' is exported recursively by the module '${stringifyType(moduleType)}'`), moduleType);
|
20205 | return;
|
20206 | }
|
20207 | alreadyCollecting.add(exportedType);
|
20208 | const exportedModuleSummary = this.getNgModuleSummary(exportedType, alreadyCollecting);
|
20209 | alreadyCollecting.delete(exportedType);
|
20210 | if (exportedModuleSummary) {
|
20211 | exportedModules.push(exportedModuleSummary);
|
20212 | }
|
20213 | else {
|
20214 | exportedNonModuleIdentifiers.push(this._getIdentifierMetadata(exportedType));
|
20215 | }
|
20216 | });
|
20217 | }
|
20218 | // Note: This will be modified later, so we rely on
|
20219 | // getting a new instance every time!
|
20220 | const transitiveModule = this._getTransitiveNgModuleMetadata(importedModules, exportedModules);
|
20221 | if (meta.declarations) {
|
20222 | flattenAndDedupeArray(meta.declarations).forEach((declaredType) => {
|
20223 | if (!isValidType(declaredType)) {
|
20224 | this._reportError(syntaxError(`Unexpected value '${stringifyType(declaredType)}' declared by the module '${stringifyType(moduleType)}'`), moduleType);
|
20225 | return;
|
20226 | }
|
20227 | const declaredIdentifier = this._getIdentifierMetadata(declaredType);
|
20228 | if (this.isDirective(declaredType)) {
|
20229 | if (this.isAbstractDirective(declaredType)) {
|
20230 | this._reportError(syntaxError(`Directive ${stringifyType(declaredType)} has no selector, please add it!`), declaredType);
|
20231 | }
|
20232 | transitiveModule.addDirective(declaredIdentifier);
|
20233 | declaredDirectives.push(declaredIdentifier);
|
20234 | this._addTypeToModule(declaredType, moduleType);
|
20235 | }
|
20236 | else if (this.isPipe(declaredType)) {
|
20237 | transitiveModule.addPipe(declaredIdentifier);
|
20238 | transitiveModule.pipes.push(declaredIdentifier);
|
20239 | declaredPipes.push(declaredIdentifier);
|
20240 | this._addTypeToModule(declaredType, moduleType);
|
20241 | }
|
20242 | else {
|
20243 | this._reportError(syntaxError(`Unexpected ${this._getTypeDescriptor(declaredType)} '${stringifyType(declaredType)}' declared by the module '${stringifyType(moduleType)}'. Please add a @Pipe/@Directive/@Component annotation.`), moduleType);
|
20244 | return;
|
20245 | }
|
20246 | });
|
20247 | }
|
20248 | const exportedDirectives = [];
|
20249 | const exportedPipes = [];
|
20250 | exportedNonModuleIdentifiers.forEach((exportedId) => {
|
20251 | if (transitiveModule.directivesSet.has(exportedId.reference)) {
|
20252 | exportedDirectives.push(exportedId);
|
20253 | transitiveModule.addExportedDirective(exportedId);
|
20254 | }
|
20255 | else if (transitiveModule.pipesSet.has(exportedId.reference)) {
|
20256 | exportedPipes.push(exportedId);
|
20257 | transitiveModule.addExportedPipe(exportedId);
|
20258 | }
|
20259 | else {
|
20260 | this._reportError(syntaxError(`Can't export ${this._getTypeDescriptor(exportedId.reference)} ${stringifyType(exportedId.reference)} from ${stringifyType(moduleType)} as it was neither declared nor imported!`), moduleType);
|
20261 | return;
|
20262 | }
|
20263 | });
|
20264 | // The providers of the module have to go last
|
20265 | // so that they overwrite any other provider we already added.
|
20266 | if (meta.providers) {
|
20267 | providers.push(...this._getProvidersMetadata(meta.providers, entryComponents, `provider for the NgModule '${stringifyType(moduleType)}'`, [], moduleType));
|
20268 | }
|
20269 | if (meta.entryComponents) {
|
20270 | entryComponents.push(...flattenAndDedupeArray(meta.entryComponents)
|
20271 | .map(type => this._getEntryComponentMetadata(type)));
|
20272 | }
|
20273 | if (meta.bootstrap) {
|
20274 | flattenAndDedupeArray(meta.bootstrap).forEach(type => {
|
20275 | if (!isValidType(type)) {
|
20276 | this._reportError(syntaxError(`Unexpected value '${stringifyType(type)}' used in the bootstrap property of module '${stringifyType(moduleType)}'`), moduleType);
|
20277 | return;
|
20278 | }
|
20279 | bootstrapComponents.push(this._getIdentifierMetadata(type));
|
20280 | });
|
20281 | }
|
20282 | entryComponents.push(...bootstrapComponents.map(type => this._getEntryComponentMetadata(type.reference)));
|
20283 | if (meta.schemas) {
|
20284 | schemas.push(...flattenAndDedupeArray(meta.schemas));
|
20285 | }
|
20286 | compileMeta = new CompileNgModuleMetadata({
|
20287 | type: this._getTypeMetadata(moduleType),
|
20288 | providers,
|
20289 | entryComponents,
|
20290 | bootstrapComponents,
|
20291 | schemas,
|
20292 | declaredDirectives,
|
20293 | exportedDirectives,
|
20294 | declaredPipes,
|
20295 | exportedPipes,
|
20296 | importedModules,
|
20297 | exportedModules,
|
20298 | transitiveModule,
|
20299 | id: meta.id || null,
|
20300 | });
|
20301 | entryComponents.forEach((id) => transitiveModule.addEntryComponent(id));
|
20302 | providers.forEach((provider) => transitiveModule.addProvider(provider, compileMeta.type));
|
20303 | transitiveModule.addModule(compileMeta.type);
|
20304 | this._ngModuleCache.set(moduleType, compileMeta);
|
20305 | return compileMeta;
|
20306 | }
|
20307 | _checkSelfImport(moduleType, importedModuleType) {
|
20308 | if (moduleType === importedModuleType) {
|
20309 | this._reportError(syntaxError(`'${stringifyType(moduleType)}' module can't import itself`), moduleType);
|
20310 | return true;
|
20311 | }
|
20312 | return false;
|
20313 | }
|
20314 | _getTypeDescriptor(type) {
|
20315 | if (isValidType(type)) {
|
20316 | if (this.isDirective(type)) {
|
20317 | return 'directive';
|
20318 | }
|
20319 | if (this.isPipe(type)) {
|
20320 | return 'pipe';
|
20321 | }
|
20322 | if (this.isNgModule(type)) {
|
20323 | return 'module';
|
20324 | }
|
20325 | }
|
20326 | if (type.provide) {
|
20327 | return 'provider';
|
20328 | }
|
20329 | return 'value';
|
20330 | }
|
20331 | _addTypeToModule(type, moduleType) {
|
20332 | const oldModule = this._ngModuleOfTypes.get(type);
|
20333 | if (oldModule && oldModule !== moduleType) {
|
20334 | this._reportError(syntaxError(`Type ${stringifyType(type)} is part of the declarations of 2 modules: ${stringifyType(oldModule)} and ${stringifyType(moduleType)}! ` +
|
20335 | `Please consider moving ${stringifyType(type)} to a higher module that imports ${stringifyType(oldModule)} and ${stringifyType(moduleType)}. ` +
|
20336 | `You can also create a new NgModule that exports and includes ${stringifyType(type)} then import that NgModule in ${stringifyType(oldModule)} and ${stringifyType(moduleType)}.`), moduleType);
|
20337 | return;
|
20338 | }
|
20339 | this._ngModuleOfTypes.set(type, moduleType);
|
20340 | }
|
20341 | _getTransitiveNgModuleMetadata(importedModules, exportedModules) {
|
20342 | // collect `providers` / `entryComponents` from all imported and all exported modules
|
20343 | const result = new TransitiveCompileNgModuleMetadata();
|
20344 | const modulesByToken = new Map();
|
20345 | importedModules.concat(exportedModules).forEach((modSummary) => {
|
20346 | modSummary.modules.forEach((mod) => result.addModule(mod));
|
20347 | modSummary.entryComponents.forEach((comp) => result.addEntryComponent(comp));
|
20348 | const addedTokens = new Set();
|
20349 | modSummary.providers.forEach((entry) => {
|
20350 | const tokenRef = tokenReference(entry.provider.token);
|
20351 | let prevModules = modulesByToken.get(tokenRef);
|
20352 | if (!prevModules) {
|
20353 | prevModules = new Set();
|
20354 | modulesByToken.set(tokenRef, prevModules);
|
20355 | }
|
20356 | const moduleRef = entry.module.reference;
|
20357 | // Note: the providers of one module may still contain multiple providers
|
20358 | // per token (e.g. for multi providers), and we need to preserve these.
|
20359 | if (addedTokens.has(tokenRef) || !prevModules.has(moduleRef)) {
|
20360 | prevModules.add(moduleRef);
|
20361 | addedTokens.add(tokenRef);
|
20362 | result.addProvider(entry.provider, entry.module);
|
20363 | }
|
20364 | });
|
20365 | });
|
20366 | exportedModules.forEach((modSummary) => {
|
20367 | modSummary.exportedDirectives.forEach((id) => result.addExportedDirective(id));
|
20368 | modSummary.exportedPipes.forEach((id) => result.addExportedPipe(id));
|
20369 | });
|
20370 | importedModules.forEach((modSummary) => {
|
20371 | modSummary.exportedDirectives.forEach((id) => result.addDirective(id));
|
20372 | modSummary.exportedPipes.forEach((id) => result.addPipe(id));
|
20373 | });
|
20374 | return result;
|
20375 | }
|
20376 | _getIdentifierMetadata(type) {
|
20377 | type = resolveForwardRef(type);
|
20378 | return { reference: type };
|
20379 | }
|
20380 | isInjectable(type) {
|
20381 | const annotations = this._reflector.tryAnnotations(type);
|
20382 | return annotations.some(ann => createInjectable.isTypeOf(ann));
|
20383 | }
|
20384 | getInjectableSummary(type) {
|
20385 | return {
|
20386 | summaryKind: CompileSummaryKind.Injectable,
|
20387 | type: this._getTypeMetadata(type, null, false)
|
20388 | };
|
20389 | }
|
20390 | getInjectableMetadata(type, dependencies = null, throwOnUnknownDeps = true) {
|
20391 | const typeSummary = this._loadSummary(type, CompileSummaryKind.Injectable);
|
20392 | const typeMetadata = typeSummary ?
|
20393 | typeSummary.type :
|
20394 | this._getTypeMetadata(type, dependencies, throwOnUnknownDeps);
|
20395 | const annotations = this._reflector.annotations(type).filter(ann => createInjectable.isTypeOf(ann));
|
20396 | if (annotations.length === 0) {
|
20397 | return null;
|
20398 | }
|
20399 | const meta = annotations[annotations.length - 1];
|
20400 | return {
|
20401 | symbol: type,
|
20402 | type: typeMetadata,
|
20403 | providedIn: meta.providedIn,
|
20404 | useValue: meta.useValue,
|
20405 | useClass: meta.useClass,
|
20406 | useExisting: meta.useExisting,
|
20407 | useFactory: meta.useFactory,
|
20408 | deps: meta.deps,
|
20409 | };
|
20410 | }
|
20411 | _getTypeMetadata(type, dependencies = null, throwOnUnknownDeps = true) {
|
20412 | const identifier = this._getIdentifierMetadata(type);
|
20413 | return {
|
20414 | reference: identifier.reference,
|
20415 | diDeps: this._getDependenciesMetadata(identifier.reference, dependencies, throwOnUnknownDeps),
|
20416 | lifecycleHooks: getAllLifecycleHooks(this._reflector, identifier.reference),
|
20417 | };
|
20418 | }
|
20419 | _getFactoryMetadata(factory, dependencies = null) {
|
20420 | factory = resolveForwardRef(factory);
|
20421 | return { reference: factory, diDeps: this._getDependenciesMetadata(factory, dependencies) };
|
20422 | }
|
20423 | /**
|
20424 | * Gets the metadata for the given pipe.
|
20425 | * This assumes `loadNgModuleDirectiveAndPipeMetadata` has been called first.
|
20426 | */
|
20427 | getPipeMetadata(pipeType) {
|
20428 | const pipeMeta = this._pipeCache.get(pipeType);
|
20429 | if (!pipeMeta) {
|
20430 | this._reportError(syntaxError(`Illegal state: getPipeMetadata can only be called after loadNgModuleDirectiveAndPipeMetadata for a module that declares it. Pipe ${stringifyType(pipeType)}.`), pipeType);
|
20431 | }
|
20432 | return pipeMeta || null;
|
20433 | }
|
20434 | getPipeSummary(pipeType) {
|
20435 | const pipeSummary = this._loadSummary(pipeType, CompileSummaryKind.Pipe);
|
20436 | if (!pipeSummary) {
|
20437 | this._reportError(syntaxError(`Illegal state: Could not load the summary for pipe ${stringifyType(pipeType)}.`), pipeType);
|
20438 | }
|
20439 | return pipeSummary;
|
20440 | }
|
20441 | getOrLoadPipeMetadata(pipeType) {
|
20442 | let pipeMeta = this._pipeCache.get(pipeType);
|
20443 | if (!pipeMeta) {
|
20444 | pipeMeta = this._loadPipeMetadata(pipeType);
|
20445 | }
|
20446 | return pipeMeta;
|
20447 | }
|
20448 | _loadPipeMetadata(pipeType) {
|
20449 | pipeType = resolveForwardRef(pipeType);
|
20450 | const pipeAnnotation = this._pipeResolver.resolve(pipeType);
|
20451 | const pipeMeta = new CompilePipeMetadata({
|
20452 | type: this._getTypeMetadata(pipeType),
|
20453 | name: pipeAnnotation.name,
|
20454 | pure: !!pipeAnnotation.pure
|
20455 | });
|
20456 | this._pipeCache.set(pipeType, pipeMeta);
|
20457 | this._summaryCache.set(pipeType, pipeMeta.toSummary());
|
20458 | return pipeMeta;
|
20459 | }
|
20460 | _getDependenciesMetadata(typeOrFunc, dependencies, throwOnUnknownDeps = true) {
|
20461 | let hasUnknownDeps = false;
|
20462 | const params = dependencies || this._reflector.parameters(typeOrFunc) || [];
|
20463 | const dependenciesMetadata = params.map((param) => {
|
20464 | let isAttribute = false;
|
20465 | let isHost = false;
|
20466 | let isSelf = false;
|
20467 | let isSkipSelf = false;
|
20468 | let isOptional = false;
|
20469 | let token = null;
|
20470 | if (Array.isArray(param)) {
|
20471 | param.forEach((paramEntry) => {
|
20472 | if (createHost.isTypeOf(paramEntry)) {
|
20473 | isHost = true;
|
20474 | }
|
20475 | else if (createSelf.isTypeOf(paramEntry)) {
|
20476 | isSelf = true;
|
20477 | }
|
20478 | else if (createSkipSelf.isTypeOf(paramEntry)) {
|
20479 | isSkipSelf = true;
|
20480 | }
|
20481 | else if (createOptional.isTypeOf(paramEntry)) {
|
20482 | isOptional = true;
|
20483 | }
|
20484 | else if (createAttribute.isTypeOf(paramEntry)) {
|
20485 | isAttribute = true;
|
20486 | token = paramEntry.attributeName;
|
20487 | }
|
20488 | else if (createInject.isTypeOf(paramEntry)) {
|
20489 | token = paramEntry.token;
|
20490 | }
|
20491 | else if (createInjectionToken.isTypeOf(paramEntry) ||
|
20492 | paramEntry instanceof StaticSymbol) {
|
20493 | token = paramEntry;
|
20494 | }
|
20495 | else if (isValidType(paramEntry) && token == null) {
|
20496 | token = paramEntry;
|
20497 | }
|
20498 | });
|
20499 | }
|
20500 | else {
|
20501 | token = param;
|
20502 | }
|
20503 | if (token == null) {
|
20504 | hasUnknownDeps = true;
|
20505 | return {};
|
20506 | }
|
20507 | return {
|
20508 | isAttribute,
|
20509 | isHost,
|
20510 | isSelf,
|
20511 | isSkipSelf,
|
20512 | isOptional,
|
20513 | token: this._getTokenMetadata(token)
|
20514 | };
|
20515 | });
|
20516 | if (hasUnknownDeps) {
|
20517 | const depsTokens = dependenciesMetadata.map((dep) => dep.token ? stringifyType(dep.token) : '?').join(', ');
|
20518 | const message = `Can't resolve all parameters for ${stringifyType(typeOrFunc)}: (${depsTokens}).`;
|
20519 | if (throwOnUnknownDeps || this._config.strictInjectionParameters) {
|
20520 | this._reportError(syntaxError(message), typeOrFunc);
|
20521 | }
|
20522 | }
|
20523 | return dependenciesMetadata;
|
20524 | }
|
20525 | _getTokenMetadata(token) {
|
20526 | token = resolveForwardRef(token);
|
20527 | let compileToken;
|
20528 | if (typeof token === 'string') {
|
20529 | compileToken = { value: token };
|
20530 | }
|
20531 | else {
|
20532 | compileToken = { identifier: { reference: token } };
|
20533 | }
|
20534 | return compileToken;
|
20535 | }
|
20536 | _getProvidersMetadata(providers, targetEntryComponents, debugInfo, compileProviders = [], type) {
|
20537 | providers.forEach((provider, providerIdx) => {
|
20538 | if (Array.isArray(provider)) {
|
20539 | this._getProvidersMetadata(provider, targetEntryComponents, debugInfo, compileProviders);
|
20540 | }
|
20541 | else {
|
20542 | provider = resolveForwardRef(provider);
|
20543 | let providerMeta = undefined;
|
20544 | if (provider && typeof provider === 'object' && provider.hasOwnProperty('provide')) {
|
20545 | this._validateProvider(provider);
|
20546 | providerMeta = new ProviderMeta(provider.provide, provider);
|
20547 | }
|
20548 | else if (isValidType(provider)) {
|
20549 | providerMeta = new ProviderMeta(provider, { useClass: provider });
|
20550 | }
|
20551 | else if (provider === void 0) {
|
20552 | this._reportError(syntaxError(`Encountered undefined provider! Usually this means you have a circular dependencies. This might be caused by using 'barrel' index.ts files.`));
|
20553 | return;
|
20554 | }
|
20555 | else {
|
20556 | const providersInfo = providers
|
20557 | .reduce((soFar, seenProvider, seenProviderIdx) => {
|
20558 | if (seenProviderIdx < providerIdx) {
|
20559 | soFar.push(`${stringifyType(seenProvider)}`);
|
20560 | }
|
20561 | else if (seenProviderIdx == providerIdx) {
|
20562 | soFar.push(`?${stringifyType(seenProvider)}?`);
|
20563 | }
|
20564 | else if (seenProviderIdx == providerIdx + 1) {
|
20565 | soFar.push('...');
|
20566 | }
|
20567 | return soFar;
|
20568 | }, [])
|
20569 | .join(', ');
|
20570 | this._reportError(syntaxError(`Invalid ${debugInfo ?
|
20571 | debugInfo :
|
20572 | 'provider'} - only instances of Provider and Type are allowed, got: [${providersInfo}]`), type);
|
20573 | return;
|
20574 | }
|
20575 | if (providerMeta.token ===
|
20576 | this._reflector.resolveExternalReference(Identifiers.ANALYZE_FOR_ENTRY_COMPONENTS)) {
|
20577 | targetEntryComponents.push(...this._getEntryComponentsFromProvider(providerMeta, type));
|
20578 | }
|
20579 | else {
|
20580 | compileProviders.push(this.getProviderMetadata(providerMeta));
|
20581 | }
|
20582 | }
|
20583 | });
|
20584 | return compileProviders;
|
20585 | }
|
20586 | _validateProvider(provider) {
|
20587 | if (provider.hasOwnProperty('useClass') && provider.useClass == null) {
|
20588 | this._reportError(syntaxError(`Invalid provider for ${stringifyType(provider.provide)}. useClass cannot be ${provider.useClass}.
|
20589 | Usually it happens when:
|
20590 | 1. There's a circular dependency (might be caused by using index.ts (barrel) files).
|
20591 | 2. Class was used before it was declared. Use forwardRef in this case.`));
|
20592 | }
|
20593 | }
|
20594 | _getEntryComponentsFromProvider(provider, type) {
|
20595 | const components = [];
|
20596 | const collectedIdentifiers = [];
|
20597 | if (provider.useFactory || provider.useExisting || provider.useClass) {
|
20598 | this._reportError(syntaxError(`The ANALYZE_FOR_ENTRY_COMPONENTS token only supports useValue!`), type);
|
20599 | return [];
|
20600 | }
|
20601 | if (!provider.multi) {
|
20602 | this._reportError(syntaxError(`The ANALYZE_FOR_ENTRY_COMPONENTS token only supports 'multi = true'!`), type);
|
20603 | return [];
|
20604 | }
|
20605 | extractIdentifiers(provider.useValue, collectedIdentifiers);
|
20606 | collectedIdentifiers.forEach((identifier) => {
|
20607 | const entry = this._getEntryComponentMetadata(identifier.reference, false);
|
20608 | if (entry) {
|
20609 | components.push(entry);
|
20610 | }
|
20611 | });
|
20612 | return components;
|
20613 | }
|
20614 | _getEntryComponentMetadata(dirType, throwIfNotFound = true) {
|
20615 | const dirMeta = this.getNonNormalizedDirectiveMetadata(dirType);
|
20616 | if (dirMeta && dirMeta.metadata.isComponent) {
|
20617 | return { componentType: dirType, componentFactory: dirMeta.metadata.componentFactory };
|
20618 | }
|
20619 | const dirSummary = this._loadSummary(dirType, CompileSummaryKind.Directive);
|
20620 | if (dirSummary && dirSummary.isComponent) {
|
20621 | return { componentType: dirType, componentFactory: dirSummary.componentFactory };
|
20622 | }
|
20623 | if (throwIfNotFound) {
|
20624 | throw syntaxError(`${dirType.name} cannot be used as an entry component.`);
|
20625 | }
|
20626 | return null;
|
20627 | }
|
20628 | _getInjectableTypeMetadata(type, dependencies = null) {
|
20629 | const typeSummary = this._loadSummary(type, CompileSummaryKind.Injectable);
|
20630 | if (typeSummary) {
|
20631 | return typeSummary.type;
|
20632 | }
|
20633 | return this._getTypeMetadata(type, dependencies);
|
20634 | }
|
20635 | getProviderMetadata(provider) {
|
20636 | let compileDeps = undefined;
|
20637 | let compileTypeMetadata = null;
|
20638 | let compileFactoryMetadata = null;
|
20639 | let token = this._getTokenMetadata(provider.token);
|
20640 | if (provider.useClass) {
|
20641 | compileTypeMetadata =
|
20642 | this._getInjectableTypeMetadata(provider.useClass, provider.dependencies);
|
20643 | compileDeps = compileTypeMetadata.diDeps;
|
20644 | if (provider.token === provider.useClass) {
|
20645 | // use the compileTypeMetadata as it contains information about lifecycleHooks...
|
20646 | token = { identifier: compileTypeMetadata };
|
20647 | }
|
20648 | }
|
20649 | else if (provider.useFactory) {
|
20650 | compileFactoryMetadata = this._getFactoryMetadata(provider.useFactory, provider.dependencies);
|
20651 | compileDeps = compileFactoryMetadata.diDeps;
|
20652 | }
|
20653 | return {
|
20654 | token: token,
|
20655 | useClass: compileTypeMetadata,
|
20656 | useValue: provider.useValue,
|
20657 | useFactory: compileFactoryMetadata,
|
20658 | useExisting: provider.useExisting ? this._getTokenMetadata(provider.useExisting) : undefined,
|
20659 | deps: compileDeps,
|
20660 | multi: provider.multi
|
20661 | };
|
20662 | }
|
20663 | _getQueriesMetadata(queries, isViewQuery, directiveType) {
|
20664 | const res = [];
|
20665 | Object.keys(queries).forEach((propertyName) => {
|
20666 | const query = queries[propertyName];
|
20667 | if (query.isViewQuery === isViewQuery) {
|
20668 | res.push(this._getQueryMetadata(query, propertyName, directiveType));
|
20669 | }
|
20670 | });
|
20671 | return res;
|
20672 | }
|
20673 | _queryVarBindings(selector) {
|
20674 | return selector.split(/\s*,\s*/);
|
20675 | }
|
20676 | _getQueryMetadata(q, propertyName, typeOrFunc) {
|
20677 | let selectors;
|
20678 | if (typeof q.selector === 'string') {
|
20679 | selectors =
|
20680 | this._queryVarBindings(q.selector).map(varName => this._getTokenMetadata(varName));
|
20681 | }
|
20682 | else {
|
20683 | if (!q.selector) {
|
20684 | this._reportError(syntaxError(`Can't construct a query for the property "${propertyName}" of "${stringifyType(typeOrFunc)}" since the query selector wasn't defined.`), typeOrFunc);
|
20685 | selectors = [];
|
20686 | }
|
20687 | else {
|
20688 | selectors = [this._getTokenMetadata(q.selector)];
|
20689 | }
|
20690 | }
|
20691 | return {
|
20692 | selectors,
|
20693 | first: q.first,
|
20694 | descendants: q.descendants,
|
20695 | emitDistinctChangesOnly: q.emitDistinctChangesOnly,
|
20696 | propertyName,
|
20697 | read: q.read ? this._getTokenMetadata(q.read) : null,
|
20698 | static: q.static
|
20699 | };
|
20700 | }
|
20701 | _reportError(error, type, otherType) {
|
20702 | if (this._errorCollector) {
|
20703 | this._errorCollector(error, type);
|
20704 | if (otherType) {
|
20705 | this._errorCollector(error, otherType);
|
20706 | }
|
20707 | }
|
20708 | else {
|
20709 | throw error;
|
20710 | }
|
20711 | }
|
20712 | }
|
20713 | function flattenArray(tree, out = []) {
|
20714 | if (tree) {
|
20715 | for (let i = 0; i < tree.length; i++) {
|
20716 | const item = resolveForwardRef(tree[i]);
|
20717 | if (Array.isArray(item)) {
|
20718 | flattenArray(item, out);
|
20719 | }
|
20720 | else {
|
20721 | out.push(item);
|
20722 | }
|
20723 | }
|
20724 | }
|
20725 | return out;
|
20726 | }
|
20727 | function dedupeArray(array) {
|
20728 | if (array) {
|
20729 | return Array.from(new Set(array));
|
20730 | }
|
20731 | return [];
|
20732 | }
|
20733 | function flattenAndDedupeArray(tree) {
|
20734 | return dedupeArray(flattenArray(tree));
|
20735 | }
|
20736 | function isValidType(value) {
|
20737 | return (value instanceof StaticSymbol) || (value instanceof Type);
|
20738 | }
|
20739 | function extractIdentifiers(value, targetIdentifiers) {
|
20740 | visitValue(value, new _CompileValueConverter(), targetIdentifiers);
|
20741 | }
|
20742 | class _CompileValueConverter extends ValueTransformer {
|
20743 | visitOther(value, targetIdentifiers) {
|
20744 | targetIdentifiers.push({ reference: value });
|
20745 | }
|
20746 | }
|
20747 | function stringifyType(type) {
|
20748 | if (type instanceof StaticSymbol) {
|
20749 | return `${type.name} in ${type.filePath}`;
|
20750 | }
|
20751 | else {
|
20752 | return stringify(type);
|
20753 | }
|
20754 | }
|
20755 | /**
|
20756 | * Indicates that a component is still being loaded in a synchronous compile.
|
20757 | */
|
20758 | function componentStillLoadingError(compType) {
|
20759 | const error = Error(`Can't compile synchronously as ${stringify(compType)} is still being loaded!`);
|
20760 | error[ERROR_COMPONENT_TYPE] = compType;
|
20761 | return error;
|
20762 | }
|
20763 |
|
20764 | /**
|
20765 | * @license
|
20766 | * Copyright Google LLC All Rights Reserved.
|
20767 | *
|
20768 | * Use of this source code is governed by an MIT-style license that can be
|
20769 | * found in the LICENSE file at https://angular.io/license
|
20770 | */
|
20771 | const LOG_VAR = variable('_l');
|
20772 |
|
20773 | /**
|
20774 | * @license
|
20775 | * Copyright Google LLC All Rights Reserved.
|
20776 | *
|
20777 | * Use of this source code is governed by an MIT-style license that can be
|
20778 | * found in the LICENSE file at https://angular.io/license
|
20779 | */
|
20780 | /**
|
20781 | * Resolves types to {@link NgModule}.
|
20782 | */
|
20783 | class NgModuleResolver {
|
20784 | constructor(_reflector) {
|
20785 | this._reflector = _reflector;
|
20786 | }
|
20787 | isNgModule(type) {
|
20788 | return this._reflector.annotations(type).some(createNgModule.isTypeOf);
|
20789 | }
|
20790 | resolve(type, throwIfNotFound = true) {
|
20791 | const ngModuleMeta = findLast(this._reflector.annotations(type), createNgModule.isTypeOf);
|
20792 | if (ngModuleMeta) {
|
20793 | return ngModuleMeta;
|
20794 | }
|
20795 | else {
|
20796 | if (throwIfNotFound) {
|
20797 | throw new Error(`No NgModule metadata found for '${stringify(type)}'.`);
|
20798 | }
|
20799 | return null;
|
20800 | }
|
20801 | }
|
20802 | }
|
20803 |
|
20804 | /**
|
20805 | * @license
|
20806 | * Copyright Google LLC All Rights Reserved.
|
20807 | *
|
20808 | * Use of this source code is governed by an MIT-style license that can be
|
20809 | * found in the LICENSE file at https://angular.io/license
|
20810 | */
|
20811 | /**
|
20812 | * Resolve a `Type` for {@link Pipe}.
|
20813 | *
|
20814 | * This interface can be overridden by the application developer to create custom behavior.
|
20815 | *
|
20816 | * See {@link Compiler}
|
20817 | */
|
20818 | class PipeResolver {
|
20819 | constructor(_reflector) {
|
20820 | this._reflector = _reflector;
|
20821 | }
|
20822 | isPipe(type) {
|
20823 | const typeMetadata = this._reflector.annotations(resolveForwardRef(type));
|
20824 | return typeMetadata && typeMetadata.some(createPipe.isTypeOf);
|
20825 | }
|
20826 | /**
|
20827 | * Return {@link Pipe} for a given `Type`.
|
20828 | */
|
20829 | resolve(type, throwIfNotFound = true) {
|
20830 | const metas = this._reflector.annotations(resolveForwardRef(type));
|
20831 | if (metas) {
|
20832 | const annotation = findLast(metas, createPipe.isTypeOf);
|
20833 | if (annotation) {
|
20834 | return annotation;
|
20835 | }
|
20836 | }
|
20837 | if (throwIfNotFound) {
|
20838 | throw new Error(`No Pipe decorator found on ${stringify(type)}`);
|
20839 | }
|
20840 | return null;
|
20841 | }
|
20842 | }
|
20843 |
|
20844 | /**
|
20845 | * @license
|
20846 | * Copyright Google LLC All Rights Reserved.
|
20847 | *
|
20848 | * Use of this source code is governed by an MIT-style license that can be
|
20849 | * found in the LICENSE file at https://angular.io/license
|
20850 | */
|
20851 | const LOG_VAR$1 = variable('_l');
|
20852 | const VIEW_VAR = variable('_v');
|
20853 | const CHECK_VAR = variable('_ck');
|
20854 | const COMP_VAR = variable('_co');
|
20855 | const EVENT_NAME_VAR = variable('en');
|
20856 | const ALLOW_DEFAULT_VAR = variable(`ad`);
|
20857 |
|
20858 | /**
|
20859 | * @license
|
20860 | * Copyright Google LLC All Rights Reserved.
|
20861 | *
|
20862 | * Use of this source code is governed by an MIT-style license that can be
|
20863 | * found in the LICENSE file at https://angular.io/license
|
20864 | */
|
20865 | const TS = /^(?!.*\.d\.ts$).*\.ts$/;
|
20866 | class ResolvedStaticSymbol {
|
20867 | constructor(symbol, metadata) {
|
20868 | this.symbol = symbol;
|
20869 | this.metadata = metadata;
|
20870 | }
|
20871 | }
|
20872 | const SUPPORTED_SCHEMA_VERSION = 4;
|
20873 | /**
|
20874 | * This class is responsible for loading metadata per symbol,
|
20875 | * and normalizing references between symbols.
|
20876 | *
|
20877 | * Internally, it only uses symbols without members,
|
20878 | * and deduces the values for symbols with members based
|
20879 | * on these symbols.
|
20880 | */
|
20881 | class StaticSymbolResolver {
|
20882 | constructor(host, staticSymbolCache, summaryResolver, errorRecorder) {
|
20883 | this.host = host;
|
20884 | this.staticSymbolCache = staticSymbolCache;
|
20885 | this.summaryResolver = summaryResolver;
|
20886 | this.errorRecorder = errorRecorder;
|
20887 | this.metadataCache = new Map();
|
20888 | // Note: this will only contain StaticSymbols without members!
|
20889 | this.resolvedSymbols = new Map();
|
20890 | // Note: this will only contain StaticSymbols without members!
|
20891 | this.importAs = new Map();
|
20892 | this.symbolResourcePaths = new Map();
|
20893 | this.symbolFromFile = new Map();
|
20894 | this.knownFileNameToModuleNames = new Map();
|
20895 | }
|
20896 | resolveSymbol(staticSymbol) {
|
20897 | if (staticSymbol.members.length > 0) {
|
20898 | return this._resolveSymbolMembers(staticSymbol);
|
20899 | }
|
20900 | // Note: always ask for a summary first,
|
20901 | // as we might have read shallow metadata via a .d.ts file
|
20902 | // for the symbol.
|
20903 | const resultFromSummary = this._resolveSymbolFromSummary(staticSymbol);
|
20904 | if (resultFromSummary) {
|
20905 | return resultFromSummary;
|
20906 | }
|
20907 | const resultFromCache = this.resolvedSymbols.get(staticSymbol);
|
20908 | if (resultFromCache) {
|
20909 | return resultFromCache;
|
20910 | }
|
20911 | // Note: Some users use libraries that were not compiled with ngc, i.e. they don't
|
20912 | // have summaries, only .d.ts files. So we always need to check both, the summary
|
20913 | // and metadata.
|
20914 | this._createSymbolsOf(staticSymbol.filePath);
|
20915 | return this.resolvedSymbols.get(staticSymbol);
|
20916 | }
|
20917 | /**
|
20918 | * getImportAs produces a symbol that can be used to import the given symbol.
|
20919 | * The import might be different than the symbol if the symbol is exported from
|
20920 | * a library with a summary; in which case we want to import the symbol from the
|
20921 | * ngfactory re-export instead of directly to avoid introducing a direct dependency
|
20922 | * on an otherwise indirect dependency.
|
20923 | *
|
20924 | * @param staticSymbol the symbol for which to generate a import symbol
|
20925 | */
|
20926 | getImportAs(staticSymbol, useSummaries = true) {
|
20927 | if (staticSymbol.members.length) {
|
20928 | const baseSymbol = this.getStaticSymbol(staticSymbol.filePath, staticSymbol.name);
|
20929 | const baseImportAs = this.getImportAs(baseSymbol, useSummaries);
|
20930 | return baseImportAs ?
|
20931 | this.getStaticSymbol(baseImportAs.filePath, baseImportAs.name, staticSymbol.members) :
|
20932 | null;
|
20933 | }
|
20934 | const summarizedFileName = stripSummaryForJitFileSuffix(staticSymbol.filePath);
|
20935 | if (summarizedFileName !== staticSymbol.filePath) {
|
20936 | const summarizedName = stripSummaryForJitNameSuffix(staticSymbol.name);
|
20937 | const baseSymbol = this.getStaticSymbol(summarizedFileName, summarizedName, staticSymbol.members);
|
20938 | const baseImportAs = this.getImportAs(baseSymbol, useSummaries);
|
20939 | return baseImportAs ? this.getStaticSymbol(summaryForJitFileName(baseImportAs.filePath), summaryForJitName(baseImportAs.name), baseSymbol.members) :
|
20940 | null;
|
20941 | }
|
20942 | let result = (useSummaries && this.summaryResolver.getImportAs(staticSymbol)) || null;
|
20943 | if (!result) {
|
20944 | result = this.importAs.get(staticSymbol);
|
20945 | }
|
20946 | return result;
|
20947 | }
|
20948 | /**
|
20949 | * getResourcePath produces the path to the original location of the symbol and should
|
20950 | * be used to determine the relative location of resource references recorded in
|
20951 | * symbol metadata.
|
20952 | */
|
20953 | getResourcePath(staticSymbol) {
|
20954 | return this.symbolResourcePaths.get(staticSymbol) || staticSymbol.filePath;
|
20955 | }
|
20956 | /**
|
20957 | * getTypeArity returns the number of generic type parameters the given symbol
|
20958 | * has. If the symbol is not a type the result is null.
|
20959 | */
|
20960 | getTypeArity(staticSymbol) {
|
20961 | // If the file is a factory/ngsummary file, don't resolve the symbol as doing so would
|
20962 | // cause the metadata for an factory/ngsummary file to be loaded which doesn't exist.
|
20963 | // All references to generated classes must include the correct arity whenever
|
20964 | // generating code.
|
20965 | if (isGeneratedFile(staticSymbol.filePath)) {
|
20966 | return null;
|
20967 | }
|
20968 | let resolvedSymbol = unwrapResolvedMetadata(this.resolveSymbol(staticSymbol));
|
20969 | while (resolvedSymbol && resolvedSymbol.metadata instanceof StaticSymbol) {
|
20970 | resolvedSymbol = unwrapResolvedMetadata(this.resolveSymbol(resolvedSymbol.metadata));
|
20971 | }
|
20972 | return (resolvedSymbol && resolvedSymbol.metadata && resolvedSymbol.metadata.arity) || null;
|
20973 | }
|
20974 | getKnownModuleName(filePath) {
|
20975 | return this.knownFileNameToModuleNames.get(filePath) || null;
|
20976 | }
|
20977 | recordImportAs(sourceSymbol, targetSymbol) {
|
20978 | sourceSymbol.assertNoMembers();
|
20979 | targetSymbol.assertNoMembers();
|
20980 | this.importAs.set(sourceSymbol, targetSymbol);
|
20981 | }
|
20982 | recordModuleNameForFileName(fileName, moduleName) {
|
20983 | this.knownFileNameToModuleNames.set(fileName, moduleName);
|
20984 | }
|
20985 | /**
|
20986 | * Invalidate all information derived from the given file and return the
|
20987 | * static symbols contained in the file.
|
20988 | *
|
20989 | * @param fileName the file to invalidate
|
20990 | */
|
20991 | invalidateFile(fileName) {
|
20992 | this.metadataCache.delete(fileName);
|
20993 | const symbols = this.symbolFromFile.get(fileName);
|
20994 | if (!symbols) {
|
20995 | return [];
|
20996 | }
|
20997 | this.symbolFromFile.delete(fileName);
|
20998 | for (const symbol of symbols) {
|
20999 | this.resolvedSymbols.delete(symbol);
|
21000 | this.importAs.delete(symbol);
|
21001 | this.symbolResourcePaths.delete(symbol);
|
21002 | }
|
21003 | return symbols;
|
21004 | }
|
21005 | /** @internal */
|
21006 | ignoreErrorsFor(cb) {
|
21007 | const recorder = this.errorRecorder;
|
21008 | this.errorRecorder = () => { };
|
21009 | try {
|
21010 | return cb();
|
21011 | }
|
21012 | finally {
|
21013 | this.errorRecorder = recorder;
|
21014 | }
|
21015 | }
|
21016 | _resolveSymbolMembers(staticSymbol) {
|
21017 | const members = staticSymbol.members;
|
21018 | const baseResolvedSymbol = this.resolveSymbol(this.getStaticSymbol(staticSymbol.filePath, staticSymbol.name));
|
21019 | if (!baseResolvedSymbol) {
|
21020 | return null;
|
21021 | }
|
21022 | let baseMetadata = unwrapResolvedMetadata(baseResolvedSymbol.metadata);
|
21023 | if (baseMetadata instanceof StaticSymbol) {
|
21024 | return new ResolvedStaticSymbol(staticSymbol, this.getStaticSymbol(baseMetadata.filePath, baseMetadata.name, members));
|
21025 | }
|
21026 | else if (baseMetadata && baseMetadata.__symbolic === 'class') {
|
21027 | if (baseMetadata.statics && members.length === 1) {
|
21028 | return new ResolvedStaticSymbol(staticSymbol, baseMetadata.statics[members[0]]);
|
21029 | }
|
21030 | }
|
21031 | else {
|
21032 | let value = baseMetadata;
|
21033 | for (let i = 0; i < members.length && value; i++) {
|
21034 | value = value[members[i]];
|
21035 | }
|
21036 | return new ResolvedStaticSymbol(staticSymbol, value);
|
21037 | }
|
21038 | return null;
|
21039 | }
|
21040 | _resolveSymbolFromSummary(staticSymbol) {
|
21041 | const summary = this.summaryResolver.resolveSummary(staticSymbol);
|
21042 | return summary ? new ResolvedStaticSymbol(staticSymbol, summary.metadata) : null;
|
21043 | }
|
21044 | /**
|
21045 | * getStaticSymbol produces a Type whose metadata is known but whose implementation is not loaded.
|
21046 | * All types passed to the StaticResolver should be pseudo-types returned by this method.
|
21047 | *
|
21048 | * @param declarationFile the absolute path of the file where the symbol is declared
|
21049 | * @param name the name of the type.
|
21050 | * @param members a symbol for a static member of the named type
|
21051 | */
|
21052 | getStaticSymbol(declarationFile, name, members) {
|
21053 | return this.staticSymbolCache.get(declarationFile, name, members);
|
21054 | }
|
21055 | /**
|
21056 | * hasDecorators checks a file's metadata for the presence of decorators without evaluating the
|
21057 | * metadata.
|
21058 | *
|
21059 | * @param filePath the absolute path to examine for decorators.
|
21060 | * @returns true if any class in the file has a decorator.
|
21061 | */
|
21062 | hasDecorators(filePath) {
|
21063 | const metadata = this.getModuleMetadata(filePath);
|
21064 | if (metadata['metadata']) {
|
21065 | return Object.keys(metadata['metadata']).some((metadataKey) => {
|
21066 | const entry = metadata['metadata'][metadataKey];
|
21067 | return entry && entry.__symbolic === 'class' && entry.decorators;
|
21068 | });
|
21069 | }
|
21070 | return false;
|
21071 | }
|
21072 | getSymbolsOf(filePath) {
|
21073 | const summarySymbols = this.summaryResolver.getSymbolsOf(filePath);
|
21074 | if (summarySymbols) {
|
21075 | return summarySymbols;
|
21076 | }
|
21077 | // Note: Some users use libraries that were not compiled with ngc, i.e. they don't
|
21078 | // have summaries, only .d.ts files, but `summaryResolver.isLibraryFile` returns true.
|
21079 | this._createSymbolsOf(filePath);
|
21080 | return this.symbolFromFile.get(filePath) || [];
|
21081 | }
|
21082 | _createSymbolsOf(filePath) {
|
21083 | if (this.symbolFromFile.has(filePath)) {
|
21084 | return;
|
21085 | }
|
21086 | const resolvedSymbols = [];
|
21087 | const metadata = this.getModuleMetadata(filePath);
|
21088 | if (metadata['importAs']) {
|
21089 | // Index bundle indices should use the importAs module name defined
|
21090 | // in the bundle.
|
21091 | this.knownFileNameToModuleNames.set(filePath, metadata['importAs']);
|
21092 | }
|
21093 | // handle the symbols in one of the re-export location
|
21094 | if (metadata['exports']) {
|
21095 | for (const moduleExport of metadata['exports']) {
|
21096 | // handle the symbols in the list of explicitly re-exported symbols.
|
21097 | if (moduleExport.export) {
|
21098 | moduleExport.export.forEach((exportSymbol) => {
|
21099 | let symbolName;
|
21100 | if (typeof exportSymbol === 'string') {
|
21101 | symbolName = exportSymbol;
|
21102 | }
|
21103 | else {
|
21104 | symbolName = exportSymbol.as;
|
21105 | }
|
21106 | symbolName = unescapeIdentifier(symbolName);
|
21107 | let symName = symbolName;
|
21108 | if (typeof exportSymbol !== 'string') {
|
21109 | symName = unescapeIdentifier(exportSymbol.name);
|
21110 | }
|
21111 | const resolvedModule = this.resolveModule(moduleExport.from, filePath);
|
21112 | if (resolvedModule) {
|
21113 | const targetSymbol = this.getStaticSymbol(resolvedModule, symName);
|
21114 | const sourceSymbol = this.getStaticSymbol(filePath, symbolName);
|
21115 | resolvedSymbols.push(this.createExport(sourceSymbol, targetSymbol));
|
21116 | }
|
21117 | });
|
21118 | }
|
21119 | else {
|
21120 | // Handle the symbols loaded by 'export *' directives.
|
21121 | const resolvedModule = this.resolveModule(moduleExport.from, filePath);
|
21122 | if (resolvedModule && resolvedModule !== filePath) {
|
21123 | const nestedExports = this.getSymbolsOf(resolvedModule);
|
21124 | nestedExports.forEach((targetSymbol) => {
|
21125 | const sourceSymbol = this.getStaticSymbol(filePath, targetSymbol.name);
|
21126 | resolvedSymbols.push(this.createExport(sourceSymbol, targetSymbol));
|
21127 | });
|
21128 | }
|
21129 | }
|
21130 | }
|
21131 | }
|
21132 | // handle the actual metadata. Has to be after the exports
|
21133 | // as there might be collisions in the names, and we want the symbols
|
21134 | // of the current module to win ofter reexports.
|
21135 | if (metadata['metadata']) {
|
21136 | // handle direct declarations of the symbol
|
21137 | const topLevelSymbolNames = new Set(Object.keys(metadata['metadata']).map(unescapeIdentifier));
|
21138 | const origins = metadata['origins'] || {};
|
21139 | Object.keys(metadata['metadata']).forEach((metadataKey) => {
|
21140 | const symbolMeta = metadata['metadata'][metadataKey];
|
21141 | const name = unescapeIdentifier(metadataKey);
|
21142 | const symbol = this.getStaticSymbol(filePath, name);
|
21143 | const origin = origins.hasOwnProperty(metadataKey) && origins[metadataKey];
|
21144 | if (origin) {
|
21145 | // If the symbol is from a bundled index, use the declaration location of the
|
21146 | // symbol so relative references (such as './my.html') will be calculated
|
21147 | // correctly.
|
21148 | const originFilePath = this.resolveModule(origin, filePath);
|
21149 | if (!originFilePath) {
|
21150 | this.reportError(new Error(`Couldn't resolve original symbol for ${origin} from ${this.host.getOutputName(filePath)}`));
|
21151 | }
|
21152 | else {
|
21153 | this.symbolResourcePaths.set(symbol, originFilePath);
|
21154 | }
|
21155 | }
|
21156 | resolvedSymbols.push(this.createResolvedSymbol(symbol, filePath, topLevelSymbolNames, symbolMeta));
|
21157 | });
|
21158 | }
|
21159 | const uniqueSymbols = new Set();
|
21160 | for (const resolvedSymbol of resolvedSymbols) {
|
21161 | this.resolvedSymbols.set(resolvedSymbol.symbol, resolvedSymbol);
|
21162 | uniqueSymbols.add(resolvedSymbol.symbol);
|
21163 | }
|
21164 | this.symbolFromFile.set(filePath, Array.from(uniqueSymbols));
|
21165 | }
|
21166 | createResolvedSymbol(sourceSymbol, topLevelPath, topLevelSymbolNames, metadata) {
|
21167 | // For classes that don't have Angular summaries / metadata,
|
21168 | // we only keep their arity, but nothing else
|
21169 | // (e.g. their constructor parameters).
|
21170 | // We do this to prevent introducing deep imports
|
21171 | // as we didn't generate .ngfactory.ts files with proper reexports.
|
21172 | const isTsFile = TS.test(sourceSymbol.filePath);
|
21173 | if (this.summaryResolver.isLibraryFile(sourceSymbol.filePath) && !isTsFile && metadata &&
|
21174 | metadata['__symbolic'] === 'class') {
|
21175 | const transformedMeta = { __symbolic: 'class', arity: metadata.arity };
|
21176 | return new ResolvedStaticSymbol(sourceSymbol, transformedMeta);
|
21177 | }
|
21178 | let _originalFileMemo;
|
21179 | const getOriginalName = () => {
|
21180 | if (!_originalFileMemo) {
|
21181 | // Guess what the original file name is from the reference. If it has a `.d.ts` extension
|
21182 | // replace it with `.ts`. If it already has `.ts` just leave it in place. If it doesn't have
|
21183 | // .ts or .d.ts, append `.ts'. Also, if it is in `node_modules`, trim the `node_module`
|
21184 | // location as it is not important to finding the file.
|
21185 | _originalFileMemo =
|
21186 | this.host.getOutputName(topLevelPath.replace(/((\.ts)|(\.d\.ts)|)$/, '.ts')
|
21187 | .replace(/^.*node_modules[/\\]/, ''));
|
21188 | }
|
21189 | return _originalFileMemo;
|
21190 | };
|
21191 | const self = this;
|
21192 | class ReferenceTransformer extends ValueTransformer {
|
21193 | visitStringMap(map, functionParams) {
|
21194 | const symbolic = map['__symbolic'];
|
21195 | if (symbolic === 'function') {
|
21196 | const oldLen = functionParams.length;
|
21197 | functionParams.push(...(map['parameters'] || []));
|
21198 | const result = super.visitStringMap(map, functionParams);
|
21199 | functionParams.length = oldLen;
|
21200 | return result;
|
21201 | }
|
21202 | else if (symbolic === 'reference') {
|
21203 | const module = map['module'];
|
21204 | const name = map['name'] ? unescapeIdentifier(map['name']) : map['name'];
|
21205 | if (!name) {
|
21206 | return null;
|
21207 | }
|
21208 | let filePath;
|
21209 | if (module) {
|
21210 | filePath = self.resolveModule(module, sourceSymbol.filePath);
|
21211 | if (!filePath) {
|
21212 | return {
|
21213 | __symbolic: 'error',
|
21214 | message: `Could not resolve ${module} relative to ${self.host.getMetadataFor(sourceSymbol.filePath)}.`,
|
21215 | line: map['line'],
|
21216 | character: map['character'],
|
21217 | fileName: getOriginalName()
|
21218 | };
|
21219 | }
|
21220 | return {
|
21221 | __symbolic: 'resolved',
|
21222 | symbol: self.getStaticSymbol(filePath, name),
|
21223 | line: map['line'],
|
21224 | character: map['character'],
|
21225 | fileName: getOriginalName()
|
21226 | };
|
21227 | }
|
21228 | else if (functionParams.indexOf(name) >= 0) {
|
21229 | // reference to a function parameter
|
21230 | return { __symbolic: 'reference', name: name };
|
21231 | }
|
21232 | else {
|
21233 | if (topLevelSymbolNames.has(name)) {
|
21234 | return self.getStaticSymbol(topLevelPath, name);
|
21235 | }
|
21236 | }
|
21237 | }
|
21238 | else if (symbolic === 'error') {
|
21239 | return Object.assign(Object.assign({}, map), { fileName: getOriginalName() });
|
21240 | }
|
21241 | else {
|
21242 | return super.visitStringMap(map, functionParams);
|
21243 | }
|
21244 | }
|
21245 | }
|
21246 | const transformedMeta = visitValue(metadata, new ReferenceTransformer(), []);
|
21247 | let unwrappedTransformedMeta = unwrapResolvedMetadata(transformedMeta);
|
21248 | if (unwrappedTransformedMeta instanceof StaticSymbol) {
|
21249 | return this.createExport(sourceSymbol, unwrappedTransformedMeta);
|
21250 | }
|
21251 | return new ResolvedStaticSymbol(sourceSymbol, transformedMeta);
|
21252 | }
|
21253 | createExport(sourceSymbol, targetSymbol) {
|
21254 | sourceSymbol.assertNoMembers();
|
21255 | targetSymbol.assertNoMembers();
|
21256 | if (this.summaryResolver.isLibraryFile(sourceSymbol.filePath) &&
|
21257 | this.summaryResolver.isLibraryFile(targetSymbol.filePath)) {
|
21258 | // This case is for an ng library importing symbols from a plain ts library
|
21259 | // transitively.
|
21260 | // Note: We rely on the fact that we discover symbols in the direction
|
21261 | // from source files to library files
|
21262 | this.importAs.set(targetSymbol, this.getImportAs(sourceSymbol) || sourceSymbol);
|
21263 | }
|
21264 | return new ResolvedStaticSymbol(sourceSymbol, targetSymbol);
|
21265 | }
|
21266 | reportError(error, context, path) {
|
21267 | if (this.errorRecorder) {
|
21268 | this.errorRecorder(error, (context && context.filePath) || path);
|
21269 | }
|
21270 | else {
|
21271 | throw error;
|
21272 | }
|
21273 | }
|
21274 | /**
|
21275 | * @param module an absolute path to a module file.
|
21276 | */
|
21277 | getModuleMetadata(module) {
|
21278 | let moduleMetadata = this.metadataCache.get(module);
|
21279 | if (!moduleMetadata) {
|
21280 | const moduleMetadatas = this.host.getMetadataFor(module);
|
21281 | if (moduleMetadatas) {
|
21282 | let maxVersion = -1;
|
21283 | moduleMetadatas.forEach((md) => {
|
21284 | if (md && md['version'] > maxVersion) {
|
21285 | maxVersion = md['version'];
|
21286 | moduleMetadata = md;
|
21287 | }
|
21288 | });
|
21289 | }
|
21290 | if (!moduleMetadata) {
|
21291 | moduleMetadata =
|
21292 | { __symbolic: 'module', version: SUPPORTED_SCHEMA_VERSION, module: module, metadata: {} };
|
21293 | }
|
21294 | if (moduleMetadata['version'] != SUPPORTED_SCHEMA_VERSION) {
|
21295 | const errorMessage = moduleMetadata['version'] == 2 ?
|
21296 | `Unsupported metadata version ${moduleMetadata['version']} for module ${module}. This module should be compiled with a newer version of ngc` :
|
21297 | `Metadata version mismatch for module ${this.host.getOutputName(module)}, found version ${moduleMetadata['version']}, expected ${SUPPORTED_SCHEMA_VERSION}`;
|
21298 | this.reportError(new Error(errorMessage));
|
21299 | }
|
21300 | this.metadataCache.set(module, moduleMetadata);
|
21301 | }
|
21302 | return moduleMetadata;
|
21303 | }
|
21304 | getSymbolByModule(module, symbolName, containingFile) {
|
21305 | const filePath = this.resolveModule(module, containingFile);
|
21306 | if (!filePath) {
|
21307 | this.reportError(new Error(`Could not resolve module ${module}${containingFile ? ' relative to ' + this.host.getOutputName(containingFile) : ''}`));
|
21308 | return this.getStaticSymbol(`ERROR:${module}`, symbolName);
|
21309 | }
|
21310 | return this.getStaticSymbol(filePath, symbolName);
|
21311 | }
|
21312 | resolveModule(module, containingFile) {
|
21313 | try {
|
21314 | return this.host.moduleNameToFileName(module, containingFile);
|
21315 | }
|
21316 | catch (e) {
|
21317 | console.error(`Could not resolve module '${module}' relative to file ${containingFile}`);
|
21318 | this.reportError(e, undefined, containingFile);
|
21319 | }
|
21320 | return null;
|
21321 | }
|
21322 | }
|
21323 | // Remove extra underscore from escaped identifier.
|
21324 | // See https://github.com/Microsoft/TypeScript/blob/master/src/compiler/utilities.ts
|
21325 | function unescapeIdentifier(identifier) {
|
21326 | return identifier.startsWith('___') ? identifier.substr(1) : identifier;
|
21327 | }
|
21328 | function unwrapResolvedMetadata(metadata) {
|
21329 | if (metadata && metadata.__symbolic === 'resolved') {
|
21330 | return metadata.symbol;
|
21331 | }
|
21332 | return metadata;
|
21333 | }
|
21334 |
|
21335 | /**
|
21336 | * @license
|
21337 | * Copyright Google LLC All Rights Reserved.
|
21338 | *
|
21339 | * Use of this source code is governed by an MIT-style license that can be
|
21340 | * found in the LICENSE file at https://angular.io/license
|
21341 | */
|
21342 | function deserializeSummaries(symbolCache, summaryResolver, libraryFileName, json) {
|
21343 | const deserializer = new FromJsonDeserializer(symbolCache, summaryResolver);
|
21344 | return deserializer.deserialize(libraryFileName, json);
|
21345 | }
|
21346 | class FromJsonDeserializer extends ValueTransformer {
|
21347 | constructor(symbolCache, summaryResolver) {
|
21348 | super();
|
21349 | this.symbolCache = symbolCache;
|
21350 | this.summaryResolver = summaryResolver;
|
21351 | }
|
21352 | deserialize(libraryFileName, json) {
|
21353 | const data = JSON.parse(json);
|
21354 | const allImportAs = [];
|
21355 | this.symbols = data.symbols.map((serializedSymbol) => this.symbolCache.get(this.summaryResolver.fromSummaryFileName(serializedSymbol.filePath, libraryFileName), serializedSymbol.name));
|
21356 | data.symbols.forEach((serializedSymbol, index) => {
|
21357 | const symbol = this.symbols[index];
|
21358 | const importAs = serializedSymbol.importAs;
|
21359 | if (typeof importAs === 'number') {
|
21360 | allImportAs.push({ symbol, importAs: this.symbols[importAs] });
|
21361 | }
|
21362 | else if (typeof importAs === 'string') {
|
21363 | allImportAs.push({ symbol, importAs: this.symbolCache.get(ngfactoryFilePath(libraryFileName), importAs) });
|
21364 | }
|
21365 | });
|
21366 | const summaries = visitValue(data.summaries, this, null);
|
21367 | return { moduleName: data.moduleName, summaries, importAs: allImportAs };
|
21368 | }
|
21369 | visitStringMap(map, context) {
|
21370 | if ('__symbol' in map) {
|
21371 | const baseSymbol = this.symbols[map['__symbol']];
|
21372 | const members = map['members'];
|
21373 | return members.length ? this.symbolCache.get(baseSymbol.filePath, baseSymbol.name, members) :
|
21374 | baseSymbol;
|
21375 | }
|
21376 | else {
|
21377 | return super.visitStringMap(map, context);
|
21378 | }
|
21379 | }
|
21380 | }
|
21381 |
|
21382 | /**
|
21383 | * @license
|
21384 | * Copyright Google LLC All Rights Reserved.
|
21385 | *
|
21386 | * Use of this source code is governed by an MIT-style license that can be
|
21387 | * found in the LICENSE file at https://angular.io/license
|
21388 | */
|
21389 | function analyzeNgModules(fileNames, host, staticSymbolResolver, metadataResolver) {
|
21390 | const files = _analyzeFilesIncludingNonProgramFiles(fileNames, host, staticSymbolResolver, metadataResolver);
|
21391 | return mergeAnalyzedFiles(files);
|
21392 | }
|
21393 | // Analyzes all of the program files,
|
21394 | // including files that are not part of the program
|
21395 | // but are referenced by an NgModule.
|
21396 | function _analyzeFilesIncludingNonProgramFiles(fileNames, host, staticSymbolResolver, metadataResolver) {
|
21397 | const seenFiles = new Set();
|
21398 | const files = [];
|
21399 | const visitFile = (fileName) => {
|
21400 | if (seenFiles.has(fileName) || !host.isSourceFile(fileName)) {
|
21401 | return false;
|
21402 | }
|
21403 | seenFiles.add(fileName);
|
21404 | const analyzedFile = analyzeFile(host, staticSymbolResolver, metadataResolver, fileName);
|
21405 | files.push(analyzedFile);
|
21406 | analyzedFile.ngModules.forEach(ngModule => {
|
21407 | ngModule.transitiveModule.modules.forEach(modMeta => visitFile(modMeta.reference.filePath));
|
21408 | });
|
21409 | };
|
21410 | fileNames.forEach((fileName) => visitFile(fileName));
|
21411 | return files;
|
21412 | }
|
21413 | function analyzeFile(host, staticSymbolResolver, metadataResolver, fileName) {
|
21414 | const abstractDirectives = [];
|
21415 | const directives = [];
|
21416 | const pipes = [];
|
21417 | const injectables = [];
|
21418 | const ngModules = [];
|
21419 | const hasDecorators = staticSymbolResolver.hasDecorators(fileName);
|
21420 | let exportsNonSourceFiles = false;
|
21421 | const isDeclarationFile = fileName.endsWith('.d.ts');
|
21422 | // Don't analyze .d.ts files that have no decorators as a shortcut
|
21423 | // to speed up the analysis. This prevents us from
|
21424 | // resolving the references in these files.
|
21425 | // Note: exportsNonSourceFiles is only needed when compiling with summaries,
|
21426 | // which is not the case when .d.ts files are treated as input files.
|
21427 | if (!isDeclarationFile || hasDecorators) {
|
21428 | staticSymbolResolver.getSymbolsOf(fileName).forEach((symbol) => {
|
21429 | const resolvedSymbol = staticSymbolResolver.resolveSymbol(symbol);
|
21430 | const symbolMeta = resolvedSymbol.metadata;
|
21431 | if (!symbolMeta || symbolMeta.__symbolic === 'error') {
|
21432 | return;
|
21433 | }
|
21434 | let isNgSymbol = false;
|
21435 | if (symbolMeta.__symbolic === 'class') {
|
21436 | if (metadataResolver.isDirective(symbol)) {
|
21437 | isNgSymbol = true;
|
21438 | // This directive either has a selector or doesn't. Selector-less directives get tracked
|
21439 | // in abstractDirectives, not directives. The compiler doesn't deal with selector-less
|
21440 | // directives at all, really, other than to persist their metadata. This is done so that
|
21441 | // apps will have an easier time migrating to Ivy, which requires the selector-less
|
21442 | // annotations to be applied.
|
21443 | if (!metadataResolver.isAbstractDirective(symbol)) {
|
21444 | // The directive is an ordinary directive.
|
21445 | directives.push(symbol);
|
21446 | }
|
21447 | else {
|
21448 | // The directive has no selector and is an "abstract" directive, so track it
|
21449 | // accordingly.
|
21450 | abstractDirectives.push(symbol);
|
21451 | }
|
21452 | }
|
21453 | else if (metadataResolver.isPipe(symbol)) {
|
21454 | isNgSymbol = true;
|
21455 | pipes.push(symbol);
|
21456 | }
|
21457 | else if (metadataResolver.isNgModule(symbol)) {
|
21458 | const ngModule = metadataResolver.getNgModuleMetadata(symbol, false);
|
21459 | if (ngModule) {
|
21460 | isNgSymbol = true;
|
21461 | ngModules.push(ngModule);
|
21462 | }
|
21463 | }
|
21464 | else if (metadataResolver.isInjectable(symbol)) {
|
21465 | isNgSymbol = true;
|
21466 | const injectable = metadataResolver.getInjectableMetadata(symbol, null, false);
|
21467 | if (injectable) {
|
21468 | injectables.push(injectable);
|
21469 | }
|
21470 | }
|
21471 | }
|
21472 | if (!isNgSymbol) {
|
21473 | exportsNonSourceFiles =
|
21474 | exportsNonSourceFiles || isValueExportingNonSourceFile(host, symbolMeta);
|
21475 | }
|
21476 | });
|
21477 | }
|
21478 | return {
|
21479 | fileName,
|
21480 | directives,
|
21481 | abstractDirectives,
|
21482 | pipes,
|
21483 | ngModules,
|
21484 | injectables,
|
21485 | exportsNonSourceFiles,
|
21486 | };
|
21487 | }
|
21488 | function isValueExportingNonSourceFile(host, metadata) {
|
21489 | let exportsNonSourceFiles = false;
|
21490 | class Visitor {
|
21491 | visitArray(arr, context) {
|
21492 | arr.forEach(v => visitValue(v, this, context));
|
21493 | }
|
21494 | visitStringMap(map, context) {
|
21495 | Object.keys(map).forEach((key) => visitValue(map[key], this, context));
|
21496 | }
|
21497 | visitPrimitive(value, context) { }
|
21498 | visitOther(value, context) {
|
21499 | if (value instanceof StaticSymbol && !host.isSourceFile(value.filePath)) {
|
21500 | exportsNonSourceFiles = true;
|
21501 | }
|
21502 | }
|
21503 | }
|
21504 | visitValue(metadata, new Visitor(), null);
|
21505 | return exportsNonSourceFiles;
|
21506 | }
|
21507 | function mergeAnalyzedFiles(analyzedFiles) {
|
21508 | const allNgModules = [];
|
21509 | const ngModuleByPipeOrDirective = new Map();
|
21510 | const allPipesAndDirectives = new Set();
|
21511 | analyzedFiles.forEach(af => {
|
21512 | af.ngModules.forEach(ngModule => {
|
21513 | allNgModules.push(ngModule);
|
21514 | ngModule.declaredDirectives.forEach(d => ngModuleByPipeOrDirective.set(d.reference, ngModule));
|
21515 | ngModule.declaredPipes.forEach(p => ngModuleByPipeOrDirective.set(p.reference, ngModule));
|
21516 | });
|
21517 | af.directives.forEach(d => allPipesAndDirectives.add(d));
|
21518 | af.pipes.forEach(p => allPipesAndDirectives.add(p));
|
21519 | });
|
21520 | const symbolsMissingModule = [];
|
21521 | allPipesAndDirectives.forEach(ref => {
|
21522 | if (!ngModuleByPipeOrDirective.has(ref)) {
|
21523 | symbolsMissingModule.push(ref);
|
21524 | }
|
21525 | });
|
21526 | return {
|
21527 | ngModules: allNgModules,
|
21528 | ngModuleByPipeOrDirective,
|
21529 | symbolsMissingModule,
|
21530 | files: analyzedFiles
|
21531 | };
|
21532 | }
|
21533 |
|
21534 | /**
|
21535 | * @license
|
21536 | * Copyright Google LLC All Rights Reserved.
|
21537 | *
|
21538 | * Use of this source code is governed by an MIT-style license that can be
|
21539 | * found in the LICENSE file at https://angular.io/license
|
21540 | */
|
21541 | const FORMATTED_MESSAGE = 'ngFormattedMessage';
|
21542 | function indentStr(level) {
|
21543 | if (level <= 0)
|
21544 | return '';
|
21545 | if (level < 6)
|
21546 | return ['', ' ', ' ', ' ', ' ', ' '][level];
|
21547 | const half = indentStr(Math.floor(level / 2));
|
21548 | return half + half + (level % 2 === 1 ? ' ' : '');
|
21549 | }
|
21550 | function formatChain(chain, indent = 0) {
|
21551 | if (!chain)
|
21552 | return '';
|
21553 | const position = chain.position ?
|
21554 | `${chain.position.fileName}(${chain.position.line + 1},${chain.position.column + 1})` :
|
21555 | '';
|
21556 | const prefix = position && indent === 0 ? `${position}: ` : '';
|
21557 | const postfix = position && indent !== 0 ? ` at ${position}` : '';
|
21558 | let message = `${prefix}${chain.message}${postfix}`;
|
21559 | if (chain.next) {
|
21560 | for (const kid of chain.next) {
|
21561 | message += '\n' + formatChain(kid, indent + 2);
|
21562 | }
|
21563 | }
|
21564 | return `${indentStr(indent)}${message}`;
|
21565 | }
|
21566 | function formattedError(chain) {
|
21567 | const message = formatChain(chain) + '.';
|
21568 | const error = syntaxError(message);
|
21569 | error[FORMATTED_MESSAGE] = true;
|
21570 | error.chain = chain;
|
21571 | error.position = chain.position;
|
21572 | return error;
|
21573 | }
|
21574 | function isFormattedError(error) {
|
21575 | return !!error[FORMATTED_MESSAGE];
|
21576 | }
|
21577 |
|
21578 | /**
|
21579 | * @license
|
21580 | * Copyright Google LLC All Rights Reserved.
|
21581 | *
|
21582 | * Use of this source code is governed by an MIT-style license that can be
|
21583 | * found in the LICENSE file at https://angular.io/license
|
21584 | */
|
21585 | const ANGULAR_CORE = '@angular/core';
|
21586 | const ANGULAR_ROUTER = '@angular/router';
|
21587 | const HIDDEN_KEY = /^\$.*\$$/;
|
21588 | const IGNORE = {
|
21589 | __symbolic: 'ignore'
|
21590 | };
|
21591 | const USE_VALUE$1 = 'useValue';
|
21592 | const PROVIDE = 'provide';
|
21593 | const REFERENCE_SET = new Set([USE_VALUE$1, 'useFactory', 'data', 'id', 'loadChildren']);
|
21594 | const TYPEGUARD_POSTFIX = 'TypeGuard';
|
21595 | const USE_IF = 'UseIf';
|
21596 | function shouldIgnore(value) {
|
21597 | return value && value.__symbolic == 'ignore';
|
21598 | }
|
21599 | /**
|
21600 | * A static reflector implements enough of the Reflector API that is necessary to compile
|
21601 | * templates statically.
|
21602 | */
|
21603 | class StaticReflector {
|
21604 | constructor(summaryResolver, symbolResolver, knownMetadataClasses = [], knownMetadataFunctions = [], errorRecorder) {
|
21605 | this.summaryResolver = summaryResolver;
|
21606 | this.symbolResolver = symbolResolver;
|
21607 | this.errorRecorder = errorRecorder;
|
21608 | this.annotationCache = new Map();
|
21609 | this.shallowAnnotationCache = new Map();
|
21610 | this.propertyCache = new Map();
|
21611 | this.parameterCache = new Map();
|
21612 | this.methodCache = new Map();
|
21613 | this.staticCache = new Map();
|
21614 | this.conversionMap = new Map();
|
21615 | this.resolvedExternalReferences = new Map();
|
21616 | this.annotationForParentClassWithSummaryKind = new Map();
|
21617 | this.initializeConversionMap();
|
21618 | knownMetadataClasses.forEach((kc) => this._registerDecoratorOrConstructor(this.getStaticSymbol(kc.filePath, kc.name), kc.ctor));
|
21619 | knownMetadataFunctions.forEach((kf) => this._registerFunction(this.getStaticSymbol(kf.filePath, kf.name), kf.fn));
|
21620 | this.annotationForParentClassWithSummaryKind.set(CompileSummaryKind.Directive, [createDirective, createComponent]);
|
21621 | this.annotationForParentClassWithSummaryKind.set(CompileSummaryKind.Pipe, [createPipe]);
|
21622 | this.annotationForParentClassWithSummaryKind.set(CompileSummaryKind.NgModule, [createNgModule]);
|
21623 | this.annotationForParentClassWithSummaryKind.set(CompileSummaryKind.Injectable, [createInjectable, createPipe, createDirective, createComponent, createNgModule]);
|
21624 | }
|
21625 | componentModuleUrl(typeOrFunc) {
|
21626 | const staticSymbol = this.findSymbolDeclaration(typeOrFunc);
|
21627 | return this.symbolResolver.getResourcePath(staticSymbol);
|
21628 | }
|
21629 | /**
|
21630 | * Invalidate the specified `symbols` on program change.
|
21631 | * @param symbols
|
21632 | */
|
21633 | invalidateSymbols(symbols) {
|
21634 | for (const symbol of symbols) {
|
21635 | this.annotationCache.delete(symbol);
|
21636 | this.shallowAnnotationCache.delete(symbol);
|
21637 | this.propertyCache.delete(symbol);
|
21638 | this.parameterCache.delete(symbol);
|
21639 | this.methodCache.delete(symbol);
|
21640 | this.staticCache.delete(symbol);
|
21641 | this.conversionMap.delete(symbol);
|
21642 | }
|
21643 | }
|
21644 | resolveExternalReference(ref, containingFile) {
|
21645 | let key = undefined;
|
21646 | if (!containingFile) {
|
21647 | key = `${ref.moduleName}:${ref.name}`;
|
21648 | const declarationSymbol = this.resolvedExternalReferences.get(key);
|
21649 | if (declarationSymbol)
|
21650 | return declarationSymbol;
|
21651 | }
|
21652 | const refSymbol = this.symbolResolver.getSymbolByModule(ref.moduleName, ref.name, containingFile);
|
21653 | const declarationSymbol = this.findSymbolDeclaration(refSymbol);
|
21654 | if (!containingFile) {
|
21655 | this.symbolResolver.recordModuleNameForFileName(refSymbol.filePath, ref.moduleName);
|
21656 | this.symbolResolver.recordImportAs(declarationSymbol, refSymbol);
|
21657 | }
|
21658 | if (key) {
|
21659 | this.resolvedExternalReferences.set(key, declarationSymbol);
|
21660 | }
|
21661 | return declarationSymbol;
|
21662 | }
|
21663 | findDeclaration(moduleUrl, name, containingFile) {
|
21664 | return this.findSymbolDeclaration(this.symbolResolver.getSymbolByModule(moduleUrl, name, containingFile));
|
21665 | }
|
21666 | tryFindDeclaration(moduleUrl, name, containingFile) {
|
21667 | return this.symbolResolver.ignoreErrorsFor(() => this.findDeclaration(moduleUrl, name, containingFile));
|
21668 | }
|
21669 | findSymbolDeclaration(symbol) {
|
21670 | const resolvedSymbol = this.symbolResolver.resolveSymbol(symbol);
|
21671 | if (resolvedSymbol) {
|
21672 | let resolvedMetadata = resolvedSymbol.metadata;
|
21673 | if (resolvedMetadata && resolvedMetadata.__symbolic === 'resolved') {
|
21674 | resolvedMetadata = resolvedMetadata.symbol;
|
21675 | }
|
21676 | if (resolvedMetadata instanceof StaticSymbol) {
|
21677 | return this.findSymbolDeclaration(resolvedSymbol.metadata);
|
21678 | }
|
21679 | }
|
21680 | return symbol;
|
21681 | }
|
21682 | tryAnnotations(type) {
|
21683 | const originalRecorder = this.errorRecorder;
|
21684 | this.errorRecorder = (error, fileName) => { };
|
21685 | try {
|
21686 | return this.annotations(type);
|
21687 | }
|
21688 | finally {
|
21689 | this.errorRecorder = originalRecorder;
|
21690 | }
|
21691 | }
|
21692 | annotations(type) {
|
21693 | return this._annotations(type, (type, decorators) => this.simplify(type, decorators), this.annotationCache);
|
21694 | }
|
21695 | shallowAnnotations(type) {
|
21696 | return this._annotations(type, (type, decorators) => this.simplify(type, decorators, true), this.shallowAnnotationCache);
|
21697 | }
|
21698 | _annotations(type, simplify, annotationCache) {
|
21699 | let annotations = annotationCache.get(type);
|
21700 | if (!annotations) {
|
21701 | annotations = [];
|
21702 | const classMetadata = this.getTypeMetadata(type);
|
21703 | const parentType = this.findParentType(type, classMetadata);
|
21704 | if (parentType) {
|
21705 | const parentAnnotations = this.annotations(parentType);
|
21706 | annotations.push(...parentAnnotations);
|
21707 | }
|
21708 | let ownAnnotations = [];
|
21709 | if (classMetadata['decorators']) {
|
21710 | ownAnnotations = simplify(type, classMetadata['decorators']);
|
21711 | if (ownAnnotations) {
|
21712 | annotations.push(...ownAnnotations);
|
21713 | }
|
21714 | }
|
21715 | if (parentType && !this.summaryResolver.isLibraryFile(type.filePath) &&
|
21716 | this.summaryResolver.isLibraryFile(parentType.filePath)) {
|
21717 | const summary = this.summaryResolver.resolveSummary(parentType);
|
21718 | if (summary && summary.type) {
|
21719 | const requiredAnnotationTypes = this.annotationForParentClassWithSummaryKind.get(summary.type.summaryKind);
|
21720 | const typeHasRequiredAnnotation = requiredAnnotationTypes.some((requiredType) => ownAnnotations.some(ann => requiredType.isTypeOf(ann)));
|
21721 | if (!typeHasRequiredAnnotation) {
|
21722 | this.reportError(formatMetadataError(metadataError(`Class ${type.name} in ${type.filePath} extends from a ${CompileSummaryKind[summary.type.summaryKind]} in another compilation unit without duplicating the decorator`,
|
21723 | /* summary */ undefined, `Please add a ${requiredAnnotationTypes.map((type) => type.ngMetadataName)
|
21724 | .join(' or ')} decorator to the class`), type), type);
|
21725 | }
|
21726 | }
|
21727 | }
|
21728 | annotationCache.set(type, annotations.filter(ann => !!ann));
|
21729 | }
|
21730 | return annotations;
|
21731 | }
|
21732 | propMetadata(type) {
|
21733 | let propMetadata = this.propertyCache.get(type);
|
21734 | if (!propMetadata) {
|
21735 | const classMetadata = this.getTypeMetadata(type);
|
21736 | propMetadata = {};
|
21737 | const parentType = this.findParentType(type, classMetadata);
|
21738 | if (parentType) {
|
21739 | const parentPropMetadata = this.propMetadata(parentType);
|
21740 | Object.keys(parentPropMetadata).forEach((parentProp) => {
|
21741 | propMetadata[parentProp] = parentPropMetadata[parentProp];
|
21742 | });
|
21743 | }
|
21744 | const members = classMetadata['members'] || {};
|
21745 | Object.keys(members).forEach((propName) => {
|
21746 | const propData = members[propName];
|
21747 | const prop = propData
|
21748 | .find(a => a['__symbolic'] == 'property' || a['__symbolic'] == 'method');
|
21749 | const decorators = [];
|
21750 | // hasOwnProperty() is used here to make sure we do not look up methods
|
21751 | // on `Object.prototype`.
|
21752 | if (propMetadata === null || propMetadata === void 0 ? void 0 : propMetadata.hasOwnProperty(propName)) {
|
21753 | decorators.push(...propMetadata[propName]);
|
21754 | }
|
21755 | propMetadata[propName] = decorators;
|
21756 | if (prop && prop['decorators']) {
|
21757 | decorators.push(...this.simplify(type, prop['decorators']));
|
21758 | }
|
21759 | });
|
21760 | this.propertyCache.set(type, propMetadata);
|
21761 | }
|
21762 | return propMetadata;
|
21763 | }
|
21764 | parameters(type) {
|
21765 | if (!(type instanceof StaticSymbol)) {
|
21766 | this.reportError(new Error(`parameters received ${JSON.stringify(type)} which is not a StaticSymbol`), type);
|
21767 | return [];
|
21768 | }
|
21769 | try {
|
21770 | let parameters = this.parameterCache.get(type);
|
21771 | if (!parameters) {
|
21772 | const classMetadata = this.getTypeMetadata(type);
|
21773 | const parentType = this.findParentType(type, classMetadata);
|
21774 | const members = classMetadata ? classMetadata['members'] : null;
|
21775 | const ctorData = members ? members['__ctor__'] : null;
|
21776 | if (ctorData) {
|
21777 | const ctor = ctorData.find(a => a['__symbolic'] == 'constructor');
|
21778 | const rawParameterTypes = ctor['parameters'] || [];
|
21779 | const parameterDecorators = this.simplify(type, ctor['parameterDecorators'] || []);
|
21780 | parameters = [];
|
21781 | rawParameterTypes.forEach((rawParamType, index) => {
|
21782 | const nestedResult = [];
|
21783 | const paramType = this.trySimplify(type, rawParamType);
|
21784 | if (paramType)
|
21785 | nestedResult.push(paramType);
|
21786 | const decorators = parameterDecorators ? parameterDecorators[index] : null;
|
21787 | if (decorators) {
|
21788 | nestedResult.push(...decorators);
|
21789 | }
|
21790 | parameters.push(nestedResult);
|
21791 | });
|
21792 | }
|
21793 | else if (parentType) {
|
21794 | parameters = this.parameters(parentType);
|
21795 | }
|
21796 | if (!parameters) {
|
21797 | parameters = [];
|
21798 | }
|
21799 | this.parameterCache.set(type, parameters);
|
21800 | }
|
21801 | return parameters;
|
21802 | }
|
21803 | catch (e) {
|
21804 | console.error(`Failed on type ${JSON.stringify(type)} with error ${e}`);
|
21805 | throw e;
|
21806 | }
|
21807 | }
|
21808 | _methodNames(type) {
|
21809 | let methodNames = this.methodCache.get(type);
|
21810 | if (!methodNames) {
|
21811 | const classMetadata = this.getTypeMetadata(type);
|
21812 | methodNames = {};
|
21813 | const parentType = this.findParentType(type, classMetadata);
|
21814 | if (parentType) {
|
21815 | const parentMethodNames = this._methodNames(parentType);
|
21816 | Object.keys(parentMethodNames).forEach((parentProp) => {
|
21817 | methodNames[parentProp] = parentMethodNames[parentProp];
|
21818 | });
|
21819 | }
|
21820 | const members = classMetadata['members'] || {};
|
21821 | Object.keys(members).forEach((propName) => {
|
21822 | const propData = members[propName];
|
21823 | const isMethod = propData.some(a => a['__symbolic'] == 'method');
|
21824 | methodNames[propName] = methodNames[propName] || isMethod;
|
21825 | });
|
21826 | this.methodCache.set(type, methodNames);
|
21827 | }
|
21828 | return methodNames;
|
21829 | }
|
21830 | _staticMembers(type) {
|
21831 | let staticMembers = this.staticCache.get(type);
|
21832 | if (!staticMembers) {
|
21833 | const classMetadata = this.getTypeMetadata(type);
|
21834 | const staticMemberData = classMetadata['statics'] || {};
|
21835 | staticMembers = Object.keys(staticMemberData);
|
21836 | this.staticCache.set(type, staticMembers);
|
21837 | }
|
21838 | return staticMembers;
|
21839 | }
|
21840 | findParentType(type, classMetadata) {
|
21841 | const parentType = this.trySimplify(type, classMetadata['extends']);
|
21842 | if (parentType instanceof StaticSymbol) {
|
21843 | return parentType;
|
21844 | }
|
21845 | }
|
21846 | hasLifecycleHook(type, lcProperty) {
|
21847 | if (!(type instanceof StaticSymbol)) {
|
21848 | this.reportError(new Error(`hasLifecycleHook received ${JSON.stringify(type)} which is not a StaticSymbol`), type);
|
21849 | }
|
21850 | try {
|
21851 | return !!this._methodNames(type)[lcProperty];
|
21852 | }
|
21853 | catch (e) {
|
21854 | console.error(`Failed on type ${JSON.stringify(type)} with error ${e}`);
|
21855 | throw e;
|
21856 | }
|
21857 | }
|
21858 | guards(type) {
|
21859 | if (!(type instanceof StaticSymbol)) {
|
21860 | this.reportError(new Error(`guards received ${JSON.stringify(type)} which is not a StaticSymbol`), type);
|
21861 | return {};
|
21862 | }
|
21863 | const staticMembers = this._staticMembers(type);
|
21864 | const result = {};
|
21865 | for (let name of staticMembers) {
|
21866 | if (name.endsWith(TYPEGUARD_POSTFIX)) {
|
21867 | let property = name.substr(0, name.length - TYPEGUARD_POSTFIX.length);
|
21868 | let value;
|
21869 | if (property.endsWith(USE_IF)) {
|
21870 | property = name.substr(0, property.length - USE_IF.length);
|
21871 | value = USE_IF;
|
21872 | }
|
21873 | else {
|
21874 | value = this.getStaticSymbol(type.filePath, type.name, [name]);
|
21875 | }
|
21876 | result[property] = value;
|
21877 | }
|
21878 | }
|
21879 | return result;
|
21880 | }
|
21881 | _registerDecoratorOrConstructor(type, ctor) {
|
21882 | this.conversionMap.set(type, (context, args) => new ctor(...args));
|
21883 | }
|
21884 | _registerFunction(type, fn) {
|
21885 | this.conversionMap.set(type, (context, args) => fn.apply(undefined, args));
|
21886 | }
|
21887 | initializeConversionMap() {
|
21888 | this._registerDecoratorOrConstructor(this.findDeclaration(ANGULAR_CORE, 'Injectable'), createInjectable);
|
21889 | this.injectionToken = this.findDeclaration(ANGULAR_CORE, 'InjectionToken');
|
21890 | this.opaqueToken = this.findDeclaration(ANGULAR_CORE, 'OpaqueToken');
|
21891 | this.ROUTES = this.tryFindDeclaration(ANGULAR_ROUTER, 'ROUTES');
|
21892 | this.ANALYZE_FOR_ENTRY_COMPONENTS =
|
21893 | this.findDeclaration(ANGULAR_CORE, 'ANALYZE_FOR_ENTRY_COMPONENTS');
|
21894 | this._registerDecoratorOrConstructor(this.findDeclaration(ANGULAR_CORE, 'Host'), createHost);
|
21895 | this._registerDecoratorOrConstructor(this.findDeclaration(ANGULAR_CORE, 'Self'), createSelf);
|
21896 | this._registerDecoratorOrConstructor(this.findDeclaration(ANGULAR_CORE, 'SkipSelf'), createSkipSelf);
|
21897 | this._registerDecoratorOrConstructor(this.findDeclaration(ANGULAR_CORE, 'Inject'), createInject);
|
21898 | this._registerDecoratorOrConstructor(this.findDeclaration(ANGULAR_CORE, 'Optional'), createOptional);
|
21899 | this._registerDecoratorOrConstructor(this.findDeclaration(ANGULAR_CORE, 'Attribute'), createAttribute);
|
21900 | this._registerDecoratorOrConstructor(this.findDeclaration(ANGULAR_CORE, 'ContentChild'), createContentChild);
|
21901 | this._registerDecoratorOrConstructor(this.findDeclaration(ANGULAR_CORE, 'ContentChildren'), createContentChildren);
|
21902 | this._registerDecoratorOrConstructor(this.findDeclaration(ANGULAR_CORE, 'ViewChild'), createViewChild);
|
21903 | this._registerDecoratorOrConstructor(this.findDeclaration(ANGULAR_CORE, 'ViewChildren'), createViewChildren);
|
21904 | this._registerDecoratorOrConstructor(this.findDeclaration(ANGULAR_CORE, 'Input'), createInput);
|
21905 | this._registerDecoratorOrConstructor(this.findDeclaration(ANGULAR_CORE, 'Output'), createOutput);
|
21906 | this._registerDecoratorOrConstructor(this.findDeclaration(ANGULAR_CORE, 'Pipe'), createPipe);
|
21907 | this._registerDecoratorOrConstructor(this.findDeclaration(ANGULAR_CORE, 'HostBinding'), createHostBinding);
|
21908 | this._registerDecoratorOrConstructor(this.findDeclaration(ANGULAR_CORE, 'HostListener'), createHostListener);
|
21909 | this._registerDecoratorOrConstructor(this.findDeclaration(ANGULAR_CORE, 'Directive'), createDirective);
|
21910 | this._registerDecoratorOrConstructor(this.findDeclaration(ANGULAR_CORE, 'Component'), createComponent);
|
21911 | this._registerDecoratorOrConstructor(this.findDeclaration(ANGULAR_CORE, 'NgModule'), createNgModule);
|
21912 | // Note: Some metadata classes can be used directly with Provider.deps.
|
21913 | this._registerDecoratorOrConstructor(this.findDeclaration(ANGULAR_CORE, 'Host'), createHost);
|
21914 | this._registerDecoratorOrConstructor(this.findDeclaration(ANGULAR_CORE, 'Self'), createSelf);
|
21915 | this._registerDecoratorOrConstructor(this.findDeclaration(ANGULAR_CORE, 'SkipSelf'), createSkipSelf);
|
21916 | this._registerDecoratorOrConstructor(this.findDeclaration(ANGULAR_CORE, 'Optional'), createOptional);
|
21917 | }
|
21918 | /**
|
21919 | * getStaticSymbol produces a Type whose metadata is known but whose implementation is not loaded.
|
21920 | * All types passed to the StaticResolver should be pseudo-types returned by this method.
|
21921 | *
|
21922 | * @param declarationFile the absolute path of the file where the symbol is declared
|
21923 | * @param name the name of the type.
|
21924 | */
|
21925 | getStaticSymbol(declarationFile, name, members) {
|
21926 | return this.symbolResolver.getStaticSymbol(declarationFile, name, members);
|
21927 | }
|
21928 | /**
|
21929 | * Simplify but discard any errors
|
21930 | */
|
21931 | trySimplify(context, value) {
|
21932 | const originalRecorder = this.errorRecorder;
|
21933 | this.errorRecorder = (error, fileName) => { };
|
21934 | const result = this.simplify(context, value);
|
21935 | this.errorRecorder = originalRecorder;
|
21936 | return result;
|
21937 | }
|
21938 | /** @internal */
|
21939 | simplify(context, value, lazy = false) {
|
21940 | const self = this;
|
21941 | let scope = BindingScope$1.empty;
|
21942 | const calling = new Map();
|
21943 | function simplifyInContext(context, value, depth, references) {
|
21944 | function resolveReferenceValue(staticSymbol) {
|
21945 | const resolvedSymbol = self.symbolResolver.resolveSymbol(staticSymbol);
|
21946 | return resolvedSymbol ? resolvedSymbol.metadata : null;
|
21947 | }
|
21948 | function simplifyEagerly(value) {
|
21949 | return simplifyInContext(context, value, depth, 0);
|
21950 | }
|
21951 | function simplifyLazily(value) {
|
21952 | return simplifyInContext(context, value, depth, references + 1);
|
21953 | }
|
21954 | function simplifyNested(nestedContext, value) {
|
21955 | if (nestedContext === context) {
|
21956 | // If the context hasn't changed let the exception propagate unmodified.
|
21957 | return simplifyInContext(nestedContext, value, depth + 1, references);
|
21958 | }
|
21959 | try {
|
21960 | return simplifyInContext(nestedContext, value, depth + 1, references);
|
21961 | }
|
21962 | catch (e) {
|
21963 | if (isMetadataError(e)) {
|
21964 | // Propagate the message text up but add a message to the chain that explains how we got
|
21965 | // here.
|
21966 | // e.chain implies e.symbol
|
21967 | const summaryMsg = e.chain ? 'references \'' + e.symbol.name + '\'' : errorSummary(e);
|
21968 | const summary = `'${nestedContext.name}' ${summaryMsg}`;
|
21969 | const chain = { message: summary, position: e.position, next: e.chain };
|
21970 | // TODO(chuckj): retrieve the position information indirectly from the collectors node
|
21971 | // map if the metadata is from a .ts file.
|
21972 | self.error({
|
21973 | message: e.message,
|
21974 | advise: e.advise,
|
21975 | context: e.context,
|
21976 | chain,
|
21977 | symbol: nestedContext
|
21978 | }, context);
|
21979 | }
|
21980 | else {
|
21981 | // It is probably an internal error.
|
21982 | throw e;
|
21983 | }
|
21984 | }
|
21985 | }
|
21986 | function simplifyCall(functionSymbol, targetFunction, args, targetExpression) {
|
21987 | if (targetFunction && targetFunction['__symbolic'] == 'function') {
|
21988 | if (calling.get(functionSymbol)) {
|
21989 | self.error({
|
21990 | message: 'Recursion is not supported',
|
21991 | summary: `called '${functionSymbol.name}' recursively`,
|
21992 | value: targetFunction
|
21993 | }, functionSymbol);
|
21994 | }
|
21995 | try {
|
21996 | const value = targetFunction['value'];
|
21997 | if (value && (depth != 0 || value.__symbolic != 'error')) {
|
21998 | const parameters = targetFunction['parameters'];
|
21999 | const defaults = targetFunction.defaults;
|
22000 | args = args.map(arg => simplifyNested(context, arg))
|
22001 | .map(arg => shouldIgnore(arg) ? undefined : arg);
|
22002 | if (defaults && defaults.length > args.length) {
|
22003 | args.push(...defaults.slice(args.length).map((value) => simplify(value)));
|
22004 | }
|
22005 | calling.set(functionSymbol, true);
|
22006 | const functionScope = BindingScope$1.build();
|
22007 | for (let i = 0; i < parameters.length; i++) {
|
22008 | functionScope.define(parameters[i], args[i]);
|
22009 | }
|
22010 | const oldScope = scope;
|
22011 | let result;
|
22012 | try {
|
22013 | scope = functionScope.done();
|
22014 | result = simplifyNested(functionSymbol, value);
|
22015 | }
|
22016 | finally {
|
22017 | scope = oldScope;
|
22018 | }
|
22019 | return result;
|
22020 | }
|
22021 | }
|
22022 | finally {
|
22023 | calling.delete(functionSymbol);
|
22024 | }
|
22025 | }
|
22026 | if (depth === 0) {
|
22027 | // If depth is 0 we are evaluating the top level expression that is describing element
|
22028 | // decorator. In this case, it is a decorator we don't understand, such as a custom
|
22029 | // non-angular decorator, and we should just ignore it.
|
22030 | return IGNORE;
|
22031 | }
|
22032 | let position = undefined;
|
22033 | if (targetExpression && targetExpression.__symbolic == 'resolved') {
|
22034 | const line = targetExpression.line;
|
22035 | const character = targetExpression.character;
|
22036 | const fileName = targetExpression.fileName;
|
22037 | if (fileName != null && line != null && character != null) {
|
22038 | position = { fileName, line, column: character };
|
22039 | }
|
22040 | }
|
22041 | self.error({
|
22042 | message: FUNCTION_CALL_NOT_SUPPORTED,
|
22043 | context: functionSymbol,
|
22044 | value: targetFunction,
|
22045 | position
|
22046 | }, context);
|
22047 | }
|
22048 | function simplify(expression) {
|
22049 | if (isPrimitive(expression)) {
|
22050 | return expression;
|
22051 | }
|
22052 | if (Array.isArray(expression)) {
|
22053 | const result = [];
|
22054 | for (const item of expression) {
|
22055 | // Check for a spread expression
|
22056 | if (item && item.__symbolic === 'spread') {
|
22057 | // We call with references as 0 because we require the actual value and cannot
|
22058 | // tolerate a reference here.
|
22059 | const spreadArray = simplifyEagerly(item.expression);
|
22060 | if (Array.isArray(spreadArray)) {
|
22061 | for (const spreadItem of spreadArray) {
|
22062 | result.push(spreadItem);
|
22063 | }
|
22064 | continue;
|
22065 | }
|
22066 | }
|
22067 | const value = simplify(item);
|
22068 | if (shouldIgnore(value)) {
|
22069 | continue;
|
22070 | }
|
22071 | result.push(value);
|
22072 | }
|
22073 | return result;
|
22074 | }
|
22075 | if (expression instanceof StaticSymbol) {
|
22076 | // Stop simplification at builtin symbols or if we are in a reference context and
|
22077 | // the symbol doesn't have members.
|
22078 | if (expression === self.injectionToken || self.conversionMap.has(expression) ||
|
22079 | (references > 0 && !expression.members.length)) {
|
22080 | return expression;
|
22081 | }
|
22082 | else {
|
22083 | const staticSymbol = expression;
|
22084 | const declarationValue = resolveReferenceValue(staticSymbol);
|
22085 | if (declarationValue != null) {
|
22086 | return simplifyNested(staticSymbol, declarationValue);
|
22087 | }
|
22088 | else {
|
22089 | return staticSymbol;
|
22090 | }
|
22091 | }
|
22092 | }
|
22093 | if (expression) {
|
22094 | if (expression['__symbolic']) {
|
22095 | let staticSymbol;
|
22096 | switch (expression['__symbolic']) {
|
22097 | case 'binop':
|
22098 | let left = simplify(expression['left']);
|
22099 | if (shouldIgnore(left))
|
22100 | return left;
|
22101 | let right = simplify(expression['right']);
|
22102 | if (shouldIgnore(right))
|
22103 | return right;
|
22104 | switch (expression['operator']) {
|
22105 | case '&&':
|
22106 | return left && right;
|
22107 | case '||':
|
22108 | return left || right;
|
22109 | case '|':
|
22110 | return left | right;
|
22111 | case '^':
|
22112 | return left ^ right;
|
22113 | case '&':
|
22114 | return left & right;
|
22115 | case '==':
|
22116 | return left == right;
|
22117 | case '!=':
|
22118 | return left != right;
|
22119 | case '===':
|
22120 | return left === right;
|
22121 | case '!==':
|
22122 | return left !== right;
|
22123 | case '<':
|
22124 | return left < right;
|
22125 | case '>':
|
22126 | return left > right;
|
22127 | case '<=':
|
22128 | return left <= right;
|
22129 | case '>=':
|
22130 | return left >= right;
|
22131 | case '<<':
|
22132 | return left << right;
|
22133 | case '>>':
|
22134 | return left >> right;
|
22135 | case '+':
|
22136 | return left + right;
|
22137 | case '-':
|
22138 | return left - right;
|
22139 | case '*':
|
22140 | return left * right;
|
22141 | case '/':
|
22142 | return left / right;
|
22143 | case '%':
|
22144 | return left % right;
|
22145 | }
|
22146 | return null;
|
22147 | case 'if':
|
22148 | let condition = simplify(expression['condition']);
|
22149 | return condition ? simplify(expression['thenExpression']) :
|
22150 | simplify(expression['elseExpression']);
|
22151 | case 'pre':
|
22152 | let operand = simplify(expression['operand']);
|
22153 | if (shouldIgnore(operand))
|
22154 | return operand;
|
22155 | switch (expression['operator']) {
|
22156 | case '+':
|
22157 | return operand;
|
22158 | case '-':
|
22159 | return -operand;
|
22160 | case '!':
|
22161 | return !operand;
|
22162 | case '~':
|
22163 | return ~operand;
|
22164 | }
|
22165 | return null;
|
22166 | case 'index':
|
22167 | let indexTarget = simplifyEagerly(expression['expression']);
|
22168 | let index = simplifyEagerly(expression['index']);
|
22169 | if (indexTarget && isPrimitive(index))
|
22170 | return indexTarget[index];
|
22171 | return null;
|
22172 | case 'select':
|
22173 | const member = expression['member'];
|
22174 | let selectContext = context;
|
22175 | let selectTarget = simplify(expression['expression']);
|
22176 | if (selectTarget instanceof StaticSymbol) {
|
22177 | const members = selectTarget.members.concat(member);
|
22178 | selectContext =
|
22179 | self.getStaticSymbol(selectTarget.filePath, selectTarget.name, members);
|
22180 | const declarationValue = resolveReferenceValue(selectContext);
|
22181 | if (declarationValue != null) {
|
22182 | return simplifyNested(selectContext, declarationValue);
|
22183 | }
|
22184 | else {
|
22185 | return selectContext;
|
22186 | }
|
22187 | }
|
22188 | if (selectTarget && isPrimitive(member))
|
22189 | return simplifyNested(selectContext, selectTarget[member]);
|
22190 | return null;
|
22191 | case 'reference':
|
22192 | // Note: This only has to deal with variable references, as symbol references have
|
22193 | // been converted into 'resolved'
|
22194 | // in the StaticSymbolResolver.
|
22195 | const name = expression['name'];
|
22196 | const localValue = scope.resolve(name);
|
22197 | if (localValue != BindingScope$1.missing) {
|
22198 | return localValue;
|
22199 | }
|
22200 | break;
|
22201 | case 'resolved':
|
22202 | try {
|
22203 | return simplify(expression.symbol);
|
22204 | }
|
22205 | catch (e) {
|
22206 | // If an error is reported evaluating the symbol record the position of the
|
22207 | // reference in the error so it can
|
22208 | // be reported in the error message generated from the exception.
|
22209 | if (isMetadataError(e) && expression.fileName != null &&
|
22210 | expression.line != null && expression.character != null) {
|
22211 | e.position = {
|
22212 | fileName: expression.fileName,
|
22213 | line: expression.line,
|
22214 | column: expression.character
|
22215 | };
|
22216 | }
|
22217 | throw e;
|
22218 | }
|
22219 | case 'class':
|
22220 | return context;
|
22221 | case 'function':
|
22222 | return context;
|
22223 | case 'new':
|
22224 | case 'call':
|
22225 | // Determine if the function is a built-in conversion
|
22226 | staticSymbol = simplifyInContext(context, expression['expression'], depth + 1, /* references */ 0);
|
22227 | if (staticSymbol instanceof StaticSymbol) {
|
22228 | if (staticSymbol === self.injectionToken || staticSymbol === self.opaqueToken) {
|
22229 | // if somebody calls new InjectionToken, don't create an InjectionToken,
|
22230 | // but rather return the symbol to which the InjectionToken is assigned to.
|
22231 | // OpaqueToken is supported too as it is required by the language service to
|
22232 | // support v4 and prior versions of Angular.
|
22233 | return context;
|
22234 | }
|
22235 | const argExpressions = expression['arguments'] || [];
|
22236 | let converter = self.conversionMap.get(staticSymbol);
|
22237 | if (converter) {
|
22238 | const args = argExpressions.map(arg => simplifyNested(context, arg))
|
22239 | .map(arg => shouldIgnore(arg) ? undefined : arg);
|
22240 | return converter(context, args);
|
22241 | }
|
22242 | else {
|
22243 | // Determine if the function is one we can simplify.
|
22244 | const targetFunction = resolveReferenceValue(staticSymbol);
|
22245 | return simplifyCall(staticSymbol, targetFunction, argExpressions, expression['expression']);
|
22246 | }
|
22247 | }
|
22248 | return IGNORE;
|
22249 | case 'error':
|
22250 | let message = expression.message;
|
22251 | if (expression['line'] != null) {
|
22252 | self.error({
|
22253 | message,
|
22254 | context: expression.context,
|
22255 | value: expression,
|
22256 | position: {
|
22257 | fileName: expression['fileName'],
|
22258 | line: expression['line'],
|
22259 | column: expression['character']
|
22260 | }
|
22261 | }, context);
|
22262 | }
|
22263 | else {
|
22264 | self.error({ message, context: expression.context }, context);
|
22265 | }
|
22266 | return IGNORE;
|
22267 | case 'ignore':
|
22268 | return expression;
|
22269 | }
|
22270 | return null;
|
22271 | }
|
22272 | return mapStringMap(expression, (value, name) => {
|
22273 | if (REFERENCE_SET.has(name)) {
|
22274 | if (name === USE_VALUE$1 && PROVIDE in expression) {
|
22275 | // If this is a provider expression, check for special tokens that need the value
|
22276 | // during analysis.
|
22277 | const provide = simplify(expression.provide);
|
22278 | if (provide === self.ROUTES || provide == self.ANALYZE_FOR_ENTRY_COMPONENTS) {
|
22279 | return simplify(value);
|
22280 | }
|
22281 | }
|
22282 | return simplifyLazily(value);
|
22283 | }
|
22284 | return simplify(value);
|
22285 | });
|
22286 | }
|
22287 | return IGNORE;
|
22288 | }
|
22289 | return simplify(value);
|
22290 | }
|
22291 | let result;
|
22292 | try {
|
22293 | result = simplifyInContext(context, value, 0, lazy ? 1 : 0);
|
22294 | }
|
22295 | catch (e) {
|
22296 | if (this.errorRecorder) {
|
22297 | this.reportError(e, context);
|
22298 | }
|
22299 | else {
|
22300 | throw formatMetadataError(e, context);
|
22301 | }
|
22302 | }
|
22303 | if (shouldIgnore(result)) {
|
22304 | return undefined;
|
22305 | }
|
22306 | return result;
|
22307 | }
|
22308 | getTypeMetadata(type) {
|
22309 | const resolvedSymbol = this.symbolResolver.resolveSymbol(type);
|
22310 | return resolvedSymbol && resolvedSymbol.metadata ? resolvedSymbol.metadata :
|
22311 | { __symbolic: 'class' };
|
22312 | }
|
22313 | reportError(error, context, path) {
|
22314 | if (this.errorRecorder) {
|
22315 | this.errorRecorder(formatMetadataError(error, context), (context && context.filePath) || path);
|
22316 | }
|
22317 | else {
|
22318 | throw error;
|
22319 | }
|
22320 | }
|
22321 | error({ message, summary, advise, position, context, value, symbol, chain }, reportingContext) {
|
22322 | this.reportError(metadataError(message, summary, advise, position, symbol, context, chain), reportingContext);
|
22323 | }
|
22324 | }
|
22325 | const METADATA_ERROR = 'ngMetadataError';
|
22326 | function metadataError(message, summary, advise, position, symbol, context, chain) {
|
22327 | const error = syntaxError(message);
|
22328 | error[METADATA_ERROR] = true;
|
22329 | if (advise)
|
22330 | error.advise = advise;
|
22331 | if (position)
|
22332 | error.position = position;
|
22333 | if (summary)
|
22334 | error.summary = summary;
|
22335 | if (context)
|
22336 | error.context = context;
|
22337 | if (chain)
|
22338 | error.chain = chain;
|
22339 | if (symbol)
|
22340 | error.symbol = symbol;
|
22341 | return error;
|
22342 | }
|
22343 | function isMetadataError(error) {
|
22344 | return !!error[METADATA_ERROR];
|
22345 | }
|
22346 | const REFERENCE_TO_NONEXPORTED_CLASS = 'Reference to non-exported class';
|
22347 | const VARIABLE_NOT_INITIALIZED = 'Variable not initialized';
|
22348 | const DESTRUCTURE_NOT_SUPPORTED = 'Destructuring not supported';
|
22349 | const COULD_NOT_RESOLVE_TYPE = 'Could not resolve type';
|
22350 | const FUNCTION_CALL_NOT_SUPPORTED = 'Function call not supported';
|
22351 | const REFERENCE_TO_LOCAL_SYMBOL = 'Reference to a local symbol';
|
22352 | const LAMBDA_NOT_SUPPORTED = 'Lambda not supported';
|
22353 | function expandedMessage(message, context) {
|
22354 | switch (message) {
|
22355 | case REFERENCE_TO_NONEXPORTED_CLASS:
|
22356 | if (context && context.className) {
|
22357 | return `References to a non-exported class are not supported in decorators but ${context.className} was referenced.`;
|
22358 | }
|
22359 | break;
|
22360 | case VARIABLE_NOT_INITIALIZED:
|
22361 | return 'Only initialized variables and constants can be referenced in decorators because the value of this variable is needed by the template compiler';
|
22362 | case DESTRUCTURE_NOT_SUPPORTED:
|
22363 | return 'Referencing an exported destructured variable or constant is not supported in decorators and this value is needed by the template compiler';
|
22364 | case COULD_NOT_RESOLVE_TYPE:
|
22365 | if (context && context.typeName) {
|
22366 | return `Could not resolve type ${context.typeName}`;
|
22367 | }
|
22368 | break;
|
22369 | case FUNCTION_CALL_NOT_SUPPORTED:
|
22370 | if (context && context.name) {
|
22371 | return `Function calls are not supported in decorators but '${context.name}' was called`;
|
22372 | }
|
22373 | return 'Function calls are not supported in decorators';
|
22374 | case REFERENCE_TO_LOCAL_SYMBOL:
|
22375 | if (context && context.name) {
|
22376 | return `Reference to a local (non-exported) symbols are not supported in decorators but '${context.name}' was referenced`;
|
22377 | }
|
22378 | break;
|
22379 | case LAMBDA_NOT_SUPPORTED:
|
22380 | return `Function expressions are not supported in decorators`;
|
22381 | }
|
22382 | return message;
|
22383 | }
|
22384 | function messageAdvise(message, context) {
|
22385 | switch (message) {
|
22386 | case REFERENCE_TO_NONEXPORTED_CLASS:
|
22387 | if (context && context.className) {
|
22388 | return `Consider exporting '${context.className}'`;
|
22389 | }
|
22390 | break;
|
22391 | case DESTRUCTURE_NOT_SUPPORTED:
|
22392 | return 'Consider simplifying to avoid destructuring';
|
22393 | case REFERENCE_TO_LOCAL_SYMBOL:
|
22394 | if (context && context.name) {
|
22395 | return `Consider exporting '${context.name}'`;
|
22396 | }
|
22397 | break;
|
22398 | case LAMBDA_NOT_SUPPORTED:
|
22399 | return `Consider changing the function expression into an exported function`;
|
22400 | }
|
22401 | return undefined;
|
22402 | }
|
22403 | function errorSummary(error) {
|
22404 | if (error.summary) {
|
22405 | return error.summary;
|
22406 | }
|
22407 | switch (error.message) {
|
22408 | case REFERENCE_TO_NONEXPORTED_CLASS:
|
22409 | if (error.context && error.context.className) {
|
22410 | return `references non-exported class ${error.context.className}`;
|
22411 | }
|
22412 | break;
|
22413 | case VARIABLE_NOT_INITIALIZED:
|
22414 | return 'is not initialized';
|
22415 | case DESTRUCTURE_NOT_SUPPORTED:
|
22416 | return 'is a destructured variable';
|
22417 | case COULD_NOT_RESOLVE_TYPE:
|
22418 | return 'could not be resolved';
|
22419 | case FUNCTION_CALL_NOT_SUPPORTED:
|
22420 | if (error.context && error.context.name) {
|
22421 | return `calls '${error.context.name}'`;
|
22422 | }
|
22423 | return `calls a function`;
|
22424 | case REFERENCE_TO_LOCAL_SYMBOL:
|
22425 | if (error.context && error.context.name) {
|
22426 | return `references local variable ${error.context.name}`;
|
22427 | }
|
22428 | return `references a local variable`;
|
22429 | }
|
22430 | return 'contains the error';
|
22431 | }
|
22432 | function mapStringMap(input, transform) {
|
22433 | if (!input)
|
22434 | return {};
|
22435 | const result = {};
|
22436 | Object.keys(input).forEach((key) => {
|
22437 | const value = transform(input[key], key);
|
22438 | if (!shouldIgnore(value)) {
|
22439 | if (HIDDEN_KEY.test(key)) {
|
22440 | Object.defineProperty(result, key, { enumerable: false, configurable: true, value: value });
|
22441 | }
|
22442 | else {
|
22443 | result[key] = value;
|
22444 | }
|
22445 | }
|
22446 | });
|
22447 | return result;
|
22448 | }
|
22449 | function isPrimitive(o) {
|
22450 | return o === null || (typeof o !== 'function' && typeof o !== 'object');
|
22451 | }
|
22452 | class BindingScope$1 {
|
22453 | static build() {
|
22454 | const current = new Map();
|
22455 | return {
|
22456 | define: function (name, value) {
|
22457 | current.set(name, value);
|
22458 | return this;
|
22459 | },
|
22460 | done: function () {
|
22461 | return current.size > 0 ? new PopulatedScope(current) : BindingScope$1.empty;
|
22462 | }
|
22463 | };
|
22464 | }
|
22465 | }
|
22466 | BindingScope$1.missing = {};
|
22467 | BindingScope$1.empty = { resolve: name => BindingScope$1.missing };
|
22468 | class PopulatedScope extends BindingScope$1 {
|
22469 | constructor(bindings) {
|
22470 | super();
|
22471 | this.bindings = bindings;
|
22472 | }
|
22473 | resolve(name) {
|
22474 | return this.bindings.has(name) ? this.bindings.get(name) : BindingScope$1.missing;
|
22475 | }
|
22476 | }
|
22477 | function formatMetadataMessageChain(chain, advise) {
|
22478 | const expanded = expandedMessage(chain.message, chain.context);
|
22479 | const nesting = chain.symbol ? ` in '${chain.symbol.name}'` : '';
|
22480 | const message = `${expanded}${nesting}`;
|
22481 | const position = chain.position;
|
22482 | const next = chain.next ?
|
22483 | formatMetadataMessageChain(chain.next, advise) :
|
22484 | advise ? { message: advise } : undefined;
|
22485 | return { message, position, next: next ? [next] : undefined };
|
22486 | }
|
22487 | function formatMetadataError(e, context) {
|
22488 | if (isMetadataError(e)) {
|
22489 | // Produce a formatted version of the and leaving enough information in the original error
|
22490 | // to recover the formatting information to eventually produce a diagnostic error message.
|
22491 | const position = e.position;
|
22492 | const chain = {
|
22493 | message: `Error during template compile of '${context.name}'`,
|
22494 | position: position,
|
22495 | next: { message: e.message, next: e.chain, context: e.context, symbol: e.symbol }
|
22496 | };
|
22497 | const advise = e.advise || messageAdvise(e.message, e.context);
|
22498 | return formattedError(formatMetadataMessageChain(chain, advise));
|
22499 | }
|
22500 | return e;
|
22501 | }
|
22502 |
|
22503 | /**
|
22504 | * @license
|
22505 | * Copyright Google LLC All Rights Reserved.
|
22506 | *
|
22507 | * Use of this source code is governed by an MIT-style license that can be
|
22508 | * found in the LICENSE file at https://angular.io/license
|
22509 | */
|
22510 | class AotSummaryResolver {
|
22511 | constructor(host, staticSymbolCache) {
|
22512 | this.host = host;
|
22513 | this.staticSymbolCache = staticSymbolCache;
|
22514 | // Note: this will only contain StaticSymbols without members!
|
22515 | this.summaryCache = new Map();
|
22516 | this.loadedFilePaths = new Map();
|
22517 | // Note: this will only contain StaticSymbols without members!
|
22518 | this.importAs = new Map();
|
22519 | this.knownFileNameToModuleNames = new Map();
|
22520 | }
|
22521 | isLibraryFile(filePath) {
|
22522 | // Note: We need to strip the .ngfactory. file path,
|
22523 | // so this method also works for generated files
|
22524 | // (for which host.isSourceFile will always return false).
|
22525 | return !this.host.isSourceFile(stripGeneratedFileSuffix(filePath));
|
22526 | }
|
22527 | toSummaryFileName(filePath, referringSrcFileName) {
|
22528 | return this.host.toSummaryFileName(filePath, referringSrcFileName);
|
22529 | }
|
22530 | fromSummaryFileName(fileName, referringLibFileName) {
|
22531 | return this.host.fromSummaryFileName(fileName, referringLibFileName);
|
22532 | }
|
22533 | resolveSummary(staticSymbol) {
|
22534 | const rootSymbol = staticSymbol.members.length ?
|
22535 | this.staticSymbolCache.get(staticSymbol.filePath, staticSymbol.name) :
|
22536 | staticSymbol;
|
22537 | let summary = this.summaryCache.get(rootSymbol);
|
22538 | if (!summary) {
|
22539 | this._loadSummaryFile(staticSymbol.filePath);
|
22540 | summary = this.summaryCache.get(staticSymbol);
|
22541 | }
|
22542 | return (rootSymbol === staticSymbol && summary) || null;
|
22543 | }
|
22544 | getSymbolsOf(filePath) {
|
22545 | if (this._loadSummaryFile(filePath)) {
|
22546 | return Array.from(this.summaryCache.keys()).filter((symbol) => symbol.filePath === filePath);
|
22547 | }
|
22548 | return null;
|
22549 | }
|
22550 | getImportAs(staticSymbol) {
|
22551 | staticSymbol.assertNoMembers();
|
22552 | return this.importAs.get(staticSymbol);
|
22553 | }
|
22554 | /**
|
22555 | * Converts a file path to a module name that can be used as an `import`.
|
22556 | */
|
22557 | getKnownModuleName(importedFilePath) {
|
22558 | return this.knownFileNameToModuleNames.get(importedFilePath) || null;
|
22559 | }
|
22560 | addSummary(summary) {
|
22561 | this.summaryCache.set(summary.symbol, summary);
|
22562 | }
|
22563 | _loadSummaryFile(filePath) {
|
22564 | let hasSummary = this.loadedFilePaths.get(filePath);
|
22565 | if (hasSummary != null) {
|
22566 | return hasSummary;
|
22567 | }
|
22568 | let json = null;
|
22569 | if (this.isLibraryFile(filePath)) {
|
22570 | const summaryFilePath = summaryFileName(filePath);
|
22571 | try {
|
22572 | json = this.host.loadSummary(summaryFilePath);
|
22573 | }
|
22574 | catch (e) {
|
22575 | console.error(`Error loading summary file ${summaryFilePath}`);
|
22576 | throw e;
|
22577 | }
|
22578 | }
|
22579 | hasSummary = json != null;
|
22580 | this.loadedFilePaths.set(filePath, hasSummary);
|
22581 | if (json) {
|
22582 | const { moduleName, summaries, importAs } = deserializeSummaries(this.staticSymbolCache, this, filePath, json);
|
22583 | summaries.forEach((summary) => this.summaryCache.set(summary.symbol, summary));
|
22584 | if (moduleName) {
|
22585 | this.knownFileNameToModuleNames.set(filePath, moduleName);
|
22586 | }
|
22587 | importAs.forEach((importAs) => {
|
22588 | this.importAs.set(importAs.symbol, importAs.importAs);
|
22589 | });
|
22590 | }
|
22591 | return hasSummary;
|
22592 | }
|
22593 | }
|
22594 |
|
22595 | class JitSummaryResolver {
|
22596 | constructor() {
|
22597 | this._summaries = new Map();
|
22598 | }
|
22599 | isLibraryFile() {
|
22600 | return false;
|
22601 | }
|
22602 | toSummaryFileName(fileName) {
|
22603 | return fileName;
|
22604 | }
|
22605 | fromSummaryFileName(fileName) {
|
22606 | return fileName;
|
22607 | }
|
22608 | resolveSummary(reference) {
|
22609 | return this._summaries.get(reference) || null;
|
22610 | }
|
22611 | getSymbolsOf() {
|
22612 | return [];
|
22613 | }
|
22614 | getImportAs(reference) {
|
22615 | return reference;
|
22616 | }
|
22617 | getKnownModuleName(fileName) {
|
22618 | return null;
|
22619 | }
|
22620 | addSummary(summary) {
|
22621 | this._summaries.set(summary.symbol, summary);
|
22622 | }
|
22623 | }
|
22624 |
|
22625 | /**
|
22626 | * @license
|
22627 | * Copyright Google LLC All Rights Reserved.
|
22628 | *
|
22629 | * Use of this source code is governed by an MIT-style license that can be
|
22630 | * found in the LICENSE file at https://angular.io/license
|
22631 | */
|
22632 | /**
|
22633 | * The index of each URI component in the return value of goog.uri.utils.split.
|
22634 | * @enum {number}
|
22635 | */
|
22636 | var _ComponentIndex;
|
22637 | (function (_ComponentIndex) {
|
22638 | _ComponentIndex[_ComponentIndex["Scheme"] = 1] = "Scheme";
|
22639 | _ComponentIndex[_ComponentIndex["UserInfo"] = 2] = "UserInfo";
|
22640 | _ComponentIndex[_ComponentIndex["Domain"] = 3] = "Domain";
|
22641 | _ComponentIndex[_ComponentIndex["Port"] = 4] = "Port";
|
22642 | _ComponentIndex[_ComponentIndex["Path"] = 5] = "Path";
|
22643 | _ComponentIndex[_ComponentIndex["QueryData"] = 6] = "QueryData";
|
22644 | _ComponentIndex[_ComponentIndex["Fragment"] = 7] = "Fragment";
|
22645 | })(_ComponentIndex || (_ComponentIndex = {}));
|
22646 |
|
22647 | /**
|
22648 | * @license
|
22649 | * Copyright Google LLC All Rights Reserved.
|
22650 | *
|
22651 | * Use of this source code is governed by an MIT-style license that can be
|
22652 | * found in the LICENSE file at https://angular.io/license
|
22653 | */
|
22654 | // This file only reexports content of the `src` folder. Keep it that way.
|
22655 | // This function call has a global side effects and publishes the compiler into global namespace for
|
22656 | // the late binding of the Compiler to the @angular/core for jit compilation.
|
22657 | publishFacade(_global);
|
22658 |
|
22659 | /**
|
22660 | * @license
|
22661 | * Copyright Google LLC All Rights Reserved.
|
22662 | *
|
22663 | * Use of this source code is governed by an MIT-style license that can be
|
22664 | * found in the LICENSE file at https://angular.io/license
|
22665 | */
|
22666 | /**
|
22667 | * Matches an Angular attribute to a binding type. See `ATTR` for more details.
|
22668 | *
|
22669 | * This is adapted from packages/compiler/src/render3/r3_template_transform.ts
|
22670 | * to allow empty binding names and match template attributes.
|
22671 | */
|
22672 | const BIND_NAME_REGEXP$2 = /^(?:(?:(?:(bind-)|(let-)|(ref-|#)|(on-)|(bindon-)|(@)|(\*))(.*))|\[\(([^\)]*)\)\]|\[([^\]]*)\]|\(([^\)]*)\))$/;
|
22673 | /**
|
22674 | * Represents possible Angular attribute bindings, as indices on a match of `BIND_NAME_REGEXP`.
|
22675 | */
|
22676 | var ATTR;
|
22677 | (function (ATTR) {
|
22678 | /** "bind-" */
|
22679 | ATTR[ATTR["KW_BIND"] = 1] = "KW_BIND";
|
22680 | /** "let-" */
|
22681 | ATTR[ATTR["KW_LET"] = 2] = "KW_LET";
|
22682 | /** "ref-/#" */
|
22683 | ATTR[ATTR["KW_REF"] = 3] = "KW_REF";
|
22684 | /** "on-" */
|
22685 | ATTR[ATTR["KW_ON"] = 4] = "KW_ON";
|
22686 | /** "bindon-" */
|
22687 | ATTR[ATTR["KW_BINDON"] = 5] = "KW_BINDON";
|
22688 | /** "@" */
|
22689 | ATTR[ATTR["KW_AT"] = 6] = "KW_AT";
|
22690 | /**
|
22691 | * "*"
|
22692 | * Microsyntax template starts with '*'. See https://angular.io/api/core/TemplateRef
|
22693 | */
|
22694 | ATTR[ATTR["KW_MICROSYNTAX"] = 7] = "KW_MICROSYNTAX";
|
22695 | /** The identifier after "bind-", "let-", "ref-/#", "on-", "bindon-", "@", or "*" */
|
22696 | ATTR[ATTR["IDENT_KW"] = 8] = "IDENT_KW";
|
22697 | /** Identifier inside [()] */
|
22698 | ATTR[ATTR["IDENT_BANANA_BOX"] = 9] = "IDENT_BANANA_BOX";
|
22699 | /** Identifier inside [] */
|
22700 | ATTR[ATTR["IDENT_PROPERTY"] = 10] = "IDENT_PROPERTY";
|
22701 | /** Identifier inside () */
|
22702 | ATTR[ATTR["IDENT_EVENT"] = 11] = "IDENT_EVENT";
|
22703 | })(ATTR || (ATTR = {}));
|
22704 | /**
|
22705 | * Returns a descriptor for a given Angular attribute, or undefined if the attribute is
|
22706 | * not an Angular attribute.
|
22707 | */
|
22708 | function getBindingDescriptor(attribute) {
|
22709 | const bindParts = attribute.match(BIND_NAME_REGEXP$2);
|
22710 | if (!bindParts)
|
22711 | return;
|
22712 | // The first match element is skipped because it matches the entire attribute text, including the
|
22713 | // binding part.
|
22714 | const kind = bindParts.findIndex((val, i) => i > 0 && val !== undefined);
|
22715 | if (!(kind in ATTR)) {
|
22716 | throw TypeError(`"${kind}" is not a valid Angular binding kind for "${attribute}"`);
|
22717 | }
|
22718 | return {
|
22719 | kind,
|
22720 | name: bindParts[ATTR.IDENT_KW],
|
22721 | };
|
22722 | }
|
22723 |
|
22724 | /**
|
22725 | * @license
|
22726 | * Copyright Google LLC All Rights Reserved.
|
22727 | *
|
22728 | * Use of this source code is governed by an MIT-style license that can be
|
22729 | * found in the LICENSE file at https://angular.io/license
|
22730 | */
|
22731 | const Diagnostic = {
|
22732 | directive_not_in_module: {
|
22733 | message: `%1 '%2' is not included in a module and will not be available inside a template. Consider adding it to a NgModule declaration.`,
|
22734 | kind: 'Suggestion',
|
22735 | },
|
22736 | missing_template_and_templateurl: {
|
22737 | message: `Component '%1' must have a template or templateUrl`,
|
22738 | kind: 'Error',
|
22739 | },
|
22740 | both_template_and_templateurl: {
|
22741 | message: `Component '%1' must not have both template and templateUrl`,
|
22742 | kind: 'Error',
|
22743 | },
|
22744 | invalid_templateurl: {
|
22745 | message: `URL does not point to a valid file`,
|
22746 | kind: 'Error',
|
22747 | },
|
22748 | template_context_missing_member: {
|
22749 | message: `The template context of '%1' does not define %2.\n` +
|
22750 | `If the context type is a base type or 'any', consider refining it to a more specific type.`,
|
22751 | kind: 'Suggestion',
|
22752 | },
|
22753 | callable_expression_expected_method_call: {
|
22754 | message: 'Unexpected callable expression. Expected a method call',
|
22755 | kind: 'Warning',
|
22756 | },
|
22757 | call_target_not_callable: {
|
22758 | message: `Call target '%1' has non-callable type '%2'.`,
|
22759 | kind: 'Error',
|
22760 | },
|
22761 | expression_might_be_null: {
|
22762 | message: 'The expression might be null',
|
22763 | kind: 'Error',
|
22764 | },
|
22765 | expected_a_number_type: {
|
22766 | message: 'Expected a number type',
|
22767 | kind: 'Error',
|
22768 | },
|
22769 | expected_a_string_or_number_type: {
|
22770 | message: 'Expected operands to be a string or number type',
|
22771 | kind: 'Error',
|
22772 | },
|
22773 | expected_operands_of_comparable_types_or_any: {
|
22774 | message: 'Expected operands to be of comparable types or any',
|
22775 | kind: 'Error',
|
22776 | },
|
22777 | unrecognized_operator: {
|
22778 | message: 'Unrecognized operator %1',
|
22779 | kind: 'Error',
|
22780 | },
|
22781 | unrecognized_primitive: {
|
22782 | message: 'Unrecognized primitive %1',
|
22783 | kind: 'Error',
|
22784 | },
|
22785 | no_pipe_found: {
|
22786 | message: 'No pipe of name %1 found',
|
22787 | kind: 'Error',
|
22788 | },
|
22789 | // TODO: Consider a better error message here.
|
22790 | unable_to_resolve_compatible_call_signature: {
|
22791 | message: 'Unable to resolve compatible call signature',
|
22792 | kind: 'Error',
|
22793 | },
|
22794 | unable_to_resolve_signature: {
|
22795 | message: 'Unable to resolve signature for call of %1',
|
22796 | kind: 'Error',
|
22797 | },
|
22798 | could_not_resolve_type: {
|
22799 | message: `Could not resolve the type of '%1'`,
|
22800 | kind: 'Error',
|
22801 | },
|
22802 | identifier_not_callable: {
|
22803 | message: `'%1' is not callable`,
|
22804 | kind: 'Error',
|
22805 | },
|
22806 | identifier_possibly_undefined: {
|
22807 | message: `'%1' is possibly undefined. Consider using the safe navigation operator (%2) or non-null assertion operator (%3).`,
|
22808 | kind: 'Suggestion',
|
22809 | },
|
22810 | identifier_not_defined_in_app_context: {
|
22811 | message: `Identifier '%1' is not defined. The component declaration, template variable declarations, and element references do not contain such a member`,
|
22812 | kind: 'Error',
|
22813 | },
|
22814 | identifier_not_defined_on_receiver: {
|
22815 | message: `Identifier '%1' is not defined. '%2' does not contain such a member`,
|
22816 | kind: 'Error',
|
22817 | },
|
22818 | identifier_is_private: {
|
22819 | message: `Identifier '%1' refers to a private member of %2`,
|
22820 | kind: 'Warning',
|
22821 | },
|
22822 | };
|
22823 | /**
|
22824 | * Creates a language service diagnostic.
|
22825 | * @param span location the diagnostic for
|
22826 | * @param dm diagnostic message
|
22827 | * @param formatArgs run-time arguments to format the diagnostic message with (see the messages in
|
22828 | * the `Diagnostic` object for an example).
|
22829 | * @returns a created diagnostic
|
22830 | */
|
22831 | function createDiagnostic(span, dm, ...formatArgs) {
|
22832 | // Formats "%1 %2" with formatArgs ['a', 'b'] as "a b"
|
22833 | const formattedMessage = dm.message.replace(/%(\d+)/g, (_, index) => formatArgs[+index - 1]);
|
22834 | return {
|
22835 | kind: ts.DiagnosticCategory[dm.kind],
|
22836 | message: formattedMessage,
|
22837 | span,
|
22838 | };
|
22839 | }
|
22840 |
|
22841 | /**
|
22842 | * @license
|
22843 | * Copyright Google LLC All Rights Reserved.
|
22844 | *
|
22845 | * Use of this source code is governed by an MIT-style license that can be
|
22846 | * found in the LICENSE file at https://angular.io/license
|
22847 | */
|
22848 | /**
|
22849 | * An enumeration of basic types.
|
22850 | *
|
22851 | * @publicApi
|
22852 | */
|
22853 | var BuiltinType$1;
|
22854 | (function (BuiltinType) {
|
22855 | /**
|
22856 | * The type is a type that can hold any other type.
|
22857 | */
|
22858 | BuiltinType[BuiltinType["Any"] = -1] = "Any";
|
22859 | /** Unknown types are functionally identical to any. */
|
22860 | BuiltinType[BuiltinType["Unknown"] = -1] = "Unknown";
|
22861 | /**
|
22862 | * The type of a string literal.
|
22863 | */
|
22864 | BuiltinType[BuiltinType["String"] = 1] = "String";
|
22865 | /**
|
22866 | * The type of a numeric literal.
|
22867 | */
|
22868 | BuiltinType[BuiltinType["Number"] = 2] = "Number";
|
22869 | /**
|
22870 | * The type of the `true` and `false` literals.
|
22871 | */
|
22872 | BuiltinType[BuiltinType["Boolean"] = 4] = "Boolean";
|
22873 | /**
|
22874 | * The type of the `undefined` literal.
|
22875 | */
|
22876 | BuiltinType[BuiltinType["Undefined"] = 8] = "Undefined";
|
22877 | /**
|
22878 | * the type of the `null` literal.
|
22879 | */
|
22880 | BuiltinType[BuiltinType["Null"] = 16] = "Null";
|
22881 | /**
|
22882 | * the type is an unbound type parameter.
|
22883 | */
|
22884 | BuiltinType[BuiltinType["Unbound"] = 32] = "Unbound";
|
22885 | /**
|
22886 | * Not a built-in type.
|
22887 | */
|
22888 | BuiltinType[BuiltinType["Other"] = 64] = "Other";
|
22889 | BuiltinType[BuiltinType["Object"] = 128] = "Object";
|
22890 | })(BuiltinType$1 || (BuiltinType$1 = {}));
|
22891 |
|
22892 | /**
|
22893 | * @license
|
22894 | * Copyright Google LLC All Rights Reserved.
|
22895 | *
|
22896 | * Use of this source code is governed by an MIT-style license that can be
|
22897 | * found in the LICENSE file at https://angular.io/license
|
22898 | */
|
22899 | function isParseSourceSpan(value) {
|
22900 | return value && !!value.start;
|
22901 | }
|
22902 | function spanOf(span) {
|
22903 | if (!span)
|
22904 | return undefined;
|
22905 | if (isParseSourceSpan(span)) {
|
22906 | return { start: span.start.offset, end: span.end.offset };
|
22907 | }
|
22908 | else {
|
22909 | if (span.endSourceSpan) {
|
22910 | return { start: span.sourceSpan.start.offset, end: span.endSourceSpan.end.offset };
|
22911 | }
|
22912 | else if (span.children && span.children.length) {
|
22913 | return {
|
22914 | start: span.sourceSpan.start.offset,
|
22915 | end: spanOf(span.children[span.children.length - 1]).end
|
22916 | };
|
22917 | }
|
22918 | return { start: span.sourceSpan.start.offset, end: span.sourceSpan.end.offset };
|
22919 | }
|
22920 | }
|
22921 | function inSpan(position, span, exclusive) {
|
22922 | return span != null &&
|
22923 | (exclusive ? position >= span.start && position < span.end :
|
22924 | position >= span.start && position <= span.end);
|
22925 | }
|
22926 | function offsetSpan(span, amount) {
|
22927 | return { start: span.start + amount, end: span.end + amount };
|
22928 | }
|
22929 | function isNarrower(spanA, spanB) {
|
22930 | return spanA.start >= spanB.start && spanA.end <= spanB.end;
|
22931 | }
|
22932 | function isStructuralDirective(type) {
|
22933 | var _a;
|
22934 | for (const diDep of type.diDeps) {
|
22935 | const diDepName = identifierName((_a = diDep.token) === null || _a === void 0 ? void 0 : _a.identifier);
|
22936 | if (diDepName === Identifiers.TemplateRef.name ||
|
22937 | diDepName === Identifiers.ViewContainerRef.name) {
|
22938 | return true;
|
22939 | }
|
22940 | }
|
22941 | return false;
|
22942 | }
|
22943 | function getSelectors(info) {
|
22944 | const map = new Map();
|
22945 | const results = [];
|
22946 | for (const directive of info.directives) {
|
22947 | const selectors = CssSelector.parse(directive.selector);
|
22948 | for (const selector of selectors) {
|
22949 | results.push(selector);
|
22950 | map.set(selector, directive);
|
22951 | }
|
22952 | }
|
22953 | return { selectors: results, map };
|
22954 | }
|
22955 | function diagnosticInfoFromTemplateInfo(info) {
|
22956 | return {
|
22957 | fileName: info.template.fileName,
|
22958 | offset: info.template.span.start,
|
22959 | query: info.template.query,
|
22960 | members: info.template.members,
|
22961 | htmlAst: info.htmlAst,
|
22962 | templateAst: info.templateAst,
|
22963 | source: info.template.source,
|
22964 | };
|
22965 | }
|
22966 | function findTemplateAstAt(ast, position) {
|
22967 | const path = [];
|
22968 | const visitor = new class extends RecursiveTemplateAstVisitor {
|
22969 | visit(ast) {
|
22970 | let span = spanOf(ast);
|
22971 | if (inSpan(position, span)) {
|
22972 | const len = path.length;
|
22973 | if (!len || isNarrower(span, spanOf(path[len - 1]))) {
|
22974 | path.push(ast);
|
22975 | }
|
22976 | }
|
22977 | else {
|
22978 | // Returning a value here will result in the children being skipped.
|
22979 | return true;
|
22980 | }
|
22981 | }
|
22982 | visitEmbeddedTemplate(ast, context) {
|
22983 | return this.visitChildren(context, visit => {
|
22984 | // Ignore reference, variable and providers
|
22985 | visit(ast.attrs);
|
22986 | visit(ast.directives);
|
22987 | visit(ast.children);
|
22988 | });
|
22989 | }
|
22990 | visitElement(ast, context) {
|
22991 | return this.visitChildren(context, visit => {
|
22992 | // Ingnore providers
|
22993 | visit(ast.attrs);
|
22994 | visit(ast.inputs);
|
22995 | visit(ast.outputs);
|
22996 | visit(ast.references);
|
22997 | visit(ast.directives);
|
22998 | visit(ast.children);
|
22999 | });
|
23000 | }
|
23001 | visitDirective(ast, context) {
|
23002 | // Ignore the host properties of a directive
|
23003 | const result = this.visitChildren(context, visit => {
|
23004 | visit(ast.inputs);
|
23005 | });
|
23006 | // We never care about the diretive itself, just its inputs.
|
23007 | if (path[path.length - 1] === ast) {
|
23008 | path.pop();
|
23009 | }
|
23010 | return result;
|
23011 | }
|
23012 | };
|
23013 | templateVisitAll(visitor, ast);
|
23014 | return new AstPath(path, position);
|
23015 | }
|
23016 | /**
|
23017 | * Find the tightest node at the specified `position` from the AST `nodes`, and
|
23018 | * return the path to the node.
|
23019 | * @param nodes HTML AST nodes
|
23020 | * @param position
|
23021 | */
|
23022 | function getPathToNodeAtPosition(nodes, position) {
|
23023 | const path = [];
|
23024 | const visitor = new class extends RecursiveVisitor {
|
23025 | visit(ast) {
|
23026 | const span = spanOf(ast);
|
23027 | if (inSpan(position, span)) {
|
23028 | path.push(ast);
|
23029 | }
|
23030 | else {
|
23031 | // Returning a truthy value here will skip all children and terminate
|
23032 | // the visit.
|
23033 | return true;
|
23034 | }
|
23035 | }
|
23036 | };
|
23037 | visitAll$1(visitor, nodes);
|
23038 | return new AstPath(path, position);
|
23039 | }
|
23040 | /**
|
23041 | * Inverts an object's key-value pairs.
|
23042 | */
|
23043 | function invertMap(obj) {
|
23044 | const result = {};
|
23045 | for (const name of Object.keys(obj)) {
|
23046 | const v = obj[name];
|
23047 | result[v] = name;
|
23048 | }
|
23049 | return result;
|
23050 | }
|
23051 | /**
|
23052 | * Finds the directive member providing a template output binding, if one exists.
|
23053 | * @param info aggregate template AST information
|
23054 | * @param path narrowing
|
23055 | */
|
23056 | function findOutputBinding(binding, path, query) {
|
23057 | const element = path.first(ElementAst);
|
23058 | if (element) {
|
23059 | for (const directive of element.directives) {
|
23060 | const invertedOutputs = invertMap(directive.directive.outputs);
|
23061 | const fieldName = invertedOutputs[binding.name];
|
23062 | if (fieldName) {
|
23063 | const classSymbol = query.getTypeSymbol(directive.directive.type.reference);
|
23064 | if (classSymbol) {
|
23065 | return classSymbol.members().get(fieldName);
|
23066 | }
|
23067 | }
|
23068 | }
|
23069 | }
|
23070 | }
|
23071 | /**
|
23072 | * Returns an absolute path from the text in `node`. If the text is already
|
23073 | * an absolute path, return it as is, otherwise join the path with the filename
|
23074 | * of the source file.
|
23075 | */
|
23076 | function extractAbsoluteFilePath(node) {
|
23077 | const url = node.text;
|
23078 | return path.isAbsolute(url) ? url : path.join(path.dirname(node.getSourceFile().fileName), url);
|
23079 | }
|
23080 |
|
23081 | /**
|
23082 | * @license
|
23083 | * Copyright Google LLC All Rights Reserved.
|
23084 | *
|
23085 | * Use of this source code is governed by an MIT-style license that can be
|
23086 | * found in the LICENSE file at https://angular.io/license
|
23087 | */
|
23088 | // AstType calculatetype of the ast given AST element.
|
23089 | class AstType {
|
23090 | constructor(scope, query, context, source) {
|
23091 | this.scope = scope;
|
23092 | this.query = query;
|
23093 | this.context = context;
|
23094 | this.source = source;
|
23095 | this.diagnostics = [];
|
23096 | }
|
23097 | getType(ast) {
|
23098 | return ast.visit(this);
|
23099 | }
|
23100 | getDiagnostics(ast) {
|
23101 | const type = ast.visit(this);
|
23102 | if (this.context.inEvent && type.callable) {
|
23103 | this.diagnostics.push(createDiagnostic(refinedSpan(ast), Diagnostic.callable_expression_expected_method_call));
|
23104 | }
|
23105 | return this.diagnostics;
|
23106 | }
|
23107 | visitUnary(ast) {
|
23108 | // Visit the child to produce diagnostics.
|
23109 | ast.expr.visit(this);
|
23110 | // The unary plus and minus operator are always of type number.
|
23111 | // https://github.com/Microsoft/TypeScript/blob/v1.8.10/doc/spec.md#4.18
|
23112 | switch (ast.operator) {
|
23113 | case '-':
|
23114 | case '+':
|
23115 | return this.query.getBuiltinType(BuiltinType$1.Number);
|
23116 | }
|
23117 | this.diagnostics.push(createDiagnostic(refinedSpan(ast), Diagnostic.unrecognized_operator, ast.operator));
|
23118 | return this.anyType;
|
23119 | }
|
23120 | visitBinary(ast) {
|
23121 | const getType = (ast, operation) => {
|
23122 | const type = this.getType(ast);
|
23123 | if (type.nullable) {
|
23124 | switch (operation) {
|
23125 | case '&&':
|
23126 | case '||':
|
23127 | case '==':
|
23128 | case '!=':
|
23129 | case '===':
|
23130 | case '!==':
|
23131 | // Nullable allowed.
|
23132 | break;
|
23133 | default:
|
23134 | this.diagnostics.push(createDiagnostic(refinedSpan(ast), Diagnostic.expression_might_be_null));
|
23135 | break;
|
23136 | }
|
23137 | }
|
23138 | return type;
|
23139 | };
|
23140 | const leftType = getType(ast.left, ast.operation);
|
23141 | const rightType = getType(ast.right, ast.operation);
|
23142 | const leftKind = this.query.getTypeKind(leftType);
|
23143 | const rightKind = this.query.getTypeKind(rightType);
|
23144 | // The following swtich implements operator typing similar to the
|
23145 | // type production tables in the TypeScript specification.
|
23146 | // https://github.com/Microsoft/TypeScript/blob/v1.8.10/doc/spec.md#4.19
|
23147 | const operKind = leftKind << 8 | rightKind;
|
23148 | switch (ast.operation) {
|
23149 | case '*':
|
23150 | case '/':
|
23151 | case '%':
|
23152 | case '-':
|
23153 | case '<<':
|
23154 | case '>>':
|
23155 | case '>>>':
|
23156 | case '&':
|
23157 | case '^':
|
23158 | case '|':
|
23159 | switch (operKind) {
|
23160 | case BuiltinType$1.Any << 8 | BuiltinType$1.Any:
|
23161 | case BuiltinType$1.Number << 8 | BuiltinType$1.Any:
|
23162 | case BuiltinType$1.Any << 8 | BuiltinType$1.Number:
|
23163 | case BuiltinType$1.Number << 8 | BuiltinType$1.Number:
|
23164 | return this.query.getBuiltinType(BuiltinType$1.Number);
|
23165 | default:
|
23166 | let errorAst = ast.left;
|
23167 | switch (leftKind) {
|
23168 | case BuiltinType$1.Any:
|
23169 | case BuiltinType$1.Number:
|
23170 | errorAst = ast.right;
|
23171 | break;
|
23172 | }
|
23173 | this.diagnostics.push(createDiagnostic(errorAst.span, Diagnostic.expected_a_number_type));
|
23174 | return this.anyType;
|
23175 | }
|
23176 | case '+':
|
23177 | switch (operKind) {
|
23178 | case BuiltinType$1.Any << 8 | BuiltinType$1.Any:
|
23179 | case BuiltinType$1.Any << 8 | BuiltinType$1.Boolean:
|
23180 | case BuiltinType$1.Any << 8 | BuiltinType$1.Number:
|
23181 | case BuiltinType$1.Any << 8 | BuiltinType$1.Other:
|
23182 | case BuiltinType$1.Boolean << 8 | BuiltinType$1.Any:
|
23183 | case BuiltinType$1.Number << 8 | BuiltinType$1.Any:
|
23184 | case BuiltinType$1.Other << 8 | BuiltinType$1.Any:
|
23185 | return this.anyType;
|
23186 | case BuiltinType$1.Any << 8 | BuiltinType$1.String:
|
23187 | case BuiltinType$1.Boolean << 8 | BuiltinType$1.String:
|
23188 | case BuiltinType$1.Number << 8 | BuiltinType$1.String:
|
23189 | case BuiltinType$1.String << 8 | BuiltinType$1.Any:
|
23190 | case BuiltinType$1.String << 8 | BuiltinType$1.Boolean:
|
23191 | case BuiltinType$1.String << 8 | BuiltinType$1.Number:
|
23192 | case BuiltinType$1.String << 8 | BuiltinType$1.String:
|
23193 | case BuiltinType$1.String << 8 | BuiltinType$1.Other:
|
23194 | case BuiltinType$1.Other << 8 | BuiltinType$1.String:
|
23195 | return this.query.getBuiltinType(BuiltinType$1.String);
|
23196 | case BuiltinType$1.Number << 8 | BuiltinType$1.Number:
|
23197 | return this.query.getBuiltinType(BuiltinType$1.Number);
|
23198 | case BuiltinType$1.Boolean << 8 | BuiltinType$1.Number:
|
23199 | case BuiltinType$1.Other << 8 | BuiltinType$1.Number:
|
23200 | this.diagnostics.push(createDiagnostic(ast.left.span, Diagnostic.expected_a_number_type));
|
23201 | return this.anyType;
|
23202 | case BuiltinType$1.Number << 8 | BuiltinType$1.Boolean:
|
23203 | case BuiltinType$1.Number << 8 | BuiltinType$1.Other:
|
23204 | this.diagnostics.push(createDiagnostic(ast.right.span, Diagnostic.expected_a_number_type));
|
23205 | return this.anyType;
|
23206 | default:
|
23207 | this.diagnostics.push(createDiagnostic(refinedSpan(ast), Diagnostic.expected_a_string_or_number_type));
|
23208 | return this.anyType;
|
23209 | }
|
23210 | case '>':
|
23211 | case '<':
|
23212 | case '<=':
|
23213 | case '>=':
|
23214 | case '==':
|
23215 | case '!=':
|
23216 | case '===':
|
23217 | case '!==':
|
23218 | if (!(leftKind & rightKind) &&
|
23219 | !((leftKind | rightKind) & (BuiltinType$1.Null | BuiltinType$1.Undefined))) {
|
23220 | // Two values are comparable only if
|
23221 | // - they have some type overlap, or
|
23222 | // - at least one is not defined
|
23223 | this.diagnostics.push(createDiagnostic(refinedSpan(ast), Diagnostic.expected_operands_of_comparable_types_or_any));
|
23224 | }
|
23225 | return this.query.getBuiltinType(BuiltinType$1.Boolean);
|
23226 | case '&&':
|
23227 | return rightType;
|
23228 | case '||':
|
23229 | return this.query.getTypeUnion(leftType, rightType);
|
23230 | }
|
23231 | this.diagnostics.push(createDiagnostic(refinedSpan(ast), Diagnostic.unrecognized_operator, ast.operation));
|
23232 | return this.anyType;
|
23233 | }
|
23234 | visitChain(ast) {
|
23235 | // If we are producing diagnostics, visit the children
|
23236 | for (const expr of ast.expressions) {
|
23237 | expr.visit(this);
|
23238 | }
|
23239 | // The type of a chain is always undefined.
|
23240 | return this.query.getBuiltinType(BuiltinType$1.Undefined);
|
23241 | }
|
23242 | visitConditional(ast) {
|
23243 | // The type of a conditional is the union of the true and false conditions.
|
23244 | ast.condition.visit(this);
|
23245 | ast.trueExp.visit(this);
|
23246 | ast.falseExp.visit(this);
|
23247 | return this.query.getTypeUnion(this.getType(ast.trueExp), this.getType(ast.falseExp));
|
23248 | }
|
23249 | visitFunctionCall(ast) {
|
23250 | // The type of a function call is the return type of the selected signature.
|
23251 | // The signature is selected based on the types of the arguments. Angular doesn't
|
23252 | // support contextual typing of arguments so this is simpler than TypeScript's
|
23253 | // version.
|
23254 | const args = ast.args.map(arg => this.getType(arg));
|
23255 | const target = this.getType(ast.target);
|
23256 | if (!target || !target.callable) {
|
23257 | this.diagnostics.push(createDiagnostic(refinedSpan(ast), Diagnostic.call_target_not_callable, this.sourceOf(ast.target), target.name));
|
23258 | return this.anyType;
|
23259 | }
|
23260 | const signature = target.selectSignature(args);
|
23261 | if (signature) {
|
23262 | return signature.result;
|
23263 | }
|
23264 | // TODO: Consider a better error message here. See `typescript_symbols#selectSignature` for more
|
23265 | // details.
|
23266 | this.diagnostics.push(createDiagnostic(refinedSpan(ast), Diagnostic.unable_to_resolve_compatible_call_signature));
|
23267 | return this.anyType;
|
23268 | }
|
23269 | visitImplicitReceiver(_ast) {
|
23270 | const _this = this;
|
23271 | // Return a pseudo-symbol for the implicit receiver.
|
23272 | // The members of the implicit receiver are what is defined by the
|
23273 | // scope passed into this class.
|
23274 | return {
|
23275 | name: '$implicit',
|
23276 | kind: 'component',
|
23277 | language: 'ng-template',
|
23278 | type: undefined,
|
23279 | container: undefined,
|
23280 | callable: false,
|
23281 | nullable: false,
|
23282 | public: true,
|
23283 | definition: undefined,
|
23284 | documentation: [],
|
23285 | members() {
|
23286 | return _this.scope;
|
23287 | },
|
23288 | signatures() {
|
23289 | return [];
|
23290 | },
|
23291 | selectSignature(_types) {
|
23292 | return undefined;
|
23293 | },
|
23294 | indexed(_argument) {
|
23295 | return undefined;
|
23296 | },
|
23297 | typeArguments() {
|
23298 | return undefined;
|
23299 | },
|
23300 | };
|
23301 | }
|
23302 | visitThisReceiver(_ast) {
|
23303 | return this.visitImplicitReceiver(_ast);
|
23304 | }
|
23305 | visitInterpolation(ast) {
|
23306 | // If we are producing diagnostics, visit the children.
|
23307 | for (const expr of ast.expressions) {
|
23308 | expr.visit(this);
|
23309 | }
|
23310 | return this.undefinedType;
|
23311 | }
|
23312 | visitKeyedRead(ast) {
|
23313 | const targetType = this.getType(ast.obj);
|
23314 | const keyType = this.getType(ast.key);
|
23315 | const result = targetType.indexed(keyType, ast.key instanceof LiteralPrimitive ? ast.key.value : undefined);
|
23316 | return result || this.anyType;
|
23317 | }
|
23318 | visitKeyedWrite(ast) {
|
23319 | // The write of a type is the type of the value being written.
|
23320 | return this.getType(ast.value);
|
23321 | }
|
23322 | visitLiteralArray(ast) {
|
23323 | // A type literal is an array type of the union of the elements
|
23324 | return this.query.getArrayType(this.query.getTypeUnion(...ast.expressions.map(element => this.getType(element))));
|
23325 | }
|
23326 | visitLiteralMap(ast) {
|
23327 | // If we are producing diagnostics, visit the children
|
23328 | for (const value of ast.values) {
|
23329 | value.visit(this);
|
23330 | }
|
23331 | // TODO: Return a composite type.
|
23332 | return this.anyType;
|
23333 | }
|
23334 | visitLiteralPrimitive(ast) {
|
23335 | // The type of a literal primitive depends on the value of the literal.
|
23336 | switch (ast.value) {
|
23337 | case true:
|
23338 | case false:
|
23339 | return this.query.getBuiltinType(BuiltinType$1.Boolean);
|
23340 | case null:
|
23341 | return this.query.getBuiltinType(BuiltinType$1.Null);
|
23342 | case undefined:
|
23343 | return this.query.getBuiltinType(BuiltinType$1.Undefined);
|
23344 | default:
|
23345 | switch (typeof ast.value) {
|
23346 | case 'string':
|
23347 | return this.query.getBuiltinType(BuiltinType$1.String);
|
23348 | case 'number':
|
23349 | return this.query.getBuiltinType(BuiltinType$1.Number);
|
23350 | default:
|
23351 | this.diagnostics.push(createDiagnostic(refinedSpan(ast), Diagnostic.unrecognized_primitive, typeof ast.value));
|
23352 | return this.anyType;
|
23353 | }
|
23354 | }
|
23355 | }
|
23356 | visitMethodCall(ast) {
|
23357 | return this.resolveMethodCall(this.getType(ast.receiver), ast);
|
23358 | }
|
23359 | visitPipe(ast) {
|
23360 | // The type of a pipe node is the return type of the pipe's transform method. The table returned
|
23361 | // by getPipes() is expected to contain symbols with the corresponding transform method type.
|
23362 | const pipe = this.query.getPipes().get(ast.name);
|
23363 | if (!pipe) {
|
23364 | this.diagnostics.push(createDiagnostic(refinedSpan(ast), Diagnostic.no_pipe_found, ast.name));
|
23365 | return this.anyType;
|
23366 | }
|
23367 | const expType = this.getType(ast.exp);
|
23368 | const signature = pipe.selectSignature([expType].concat(ast.args.map(arg => this.getType(arg))));
|
23369 | if (!signature) {
|
23370 | this.diagnostics.push(createDiagnostic(refinedSpan(ast), Diagnostic.unable_to_resolve_signature, ast.name));
|
23371 | return this.anyType;
|
23372 | }
|
23373 | return signature.result;
|
23374 | }
|
23375 | visitPrefixNot(ast) {
|
23376 | // If we are producing diagnostics, visit the children
|
23377 | ast.expression.visit(this);
|
23378 | // The type of a prefix ! is always boolean.
|
23379 | return this.query.getBuiltinType(BuiltinType$1.Boolean);
|
23380 | }
|
23381 | visitNonNullAssert(ast) {
|
23382 | const expressionType = this.getType(ast.expression);
|
23383 | return this.query.getNonNullableType(expressionType);
|
23384 | }
|
23385 | visitPropertyRead(ast) {
|
23386 | return this.resolvePropertyRead(this.getType(ast.receiver), ast);
|
23387 | }
|
23388 | visitPropertyWrite(ast) {
|
23389 | // The type of a write is the type of the value being written.
|
23390 | return this.getType(ast.value);
|
23391 | }
|
23392 | visitQuote(_ast) {
|
23393 | // The type of a quoted expression is any.
|
23394 | return this.query.getBuiltinType(BuiltinType$1.Any);
|
23395 | }
|
23396 | visitSafeMethodCall(ast) {
|
23397 | return this.resolveMethodCall(this.query.getNonNullableType(this.getType(ast.receiver)), ast);
|
23398 | }
|
23399 | visitSafePropertyRead(ast) {
|
23400 | return this.resolvePropertyRead(this.query.getNonNullableType(this.getType(ast.receiver)), ast);
|
23401 | }
|
23402 | /**
|
23403 | * Gets the source of an expession AST.
|
23404 | * The AST's sourceSpan is relative to the start of the template source code, which is contained
|
23405 | * at this.source.
|
23406 | */
|
23407 | sourceOf(ast) {
|
23408 | return this.source.substring(ast.sourceSpan.start, ast.sourceSpan.end);
|
23409 | }
|
23410 | get anyType() {
|
23411 | let result = this._anyType;
|
23412 | if (!result) {
|
23413 | result = this._anyType = this.query.getBuiltinType(BuiltinType$1.Any);
|
23414 | }
|
23415 | return result;
|
23416 | }
|
23417 | get undefinedType() {
|
23418 | let result = this._undefinedType;
|
23419 | if (!result) {
|
23420 | result = this._undefinedType = this.query.getBuiltinType(BuiltinType$1.Undefined);
|
23421 | }
|
23422 | return result;
|
23423 | }
|
23424 | resolveMethodCall(receiverType, ast) {
|
23425 | if (this.isAny(receiverType)) {
|
23426 | return this.anyType;
|
23427 | }
|
23428 | const methodType = this.resolvePropertyRead(receiverType, ast);
|
23429 | if (!methodType) {
|
23430 | this.diagnostics.push(createDiagnostic(refinedSpan(ast), Diagnostic.could_not_resolve_type, ast.name));
|
23431 | return this.anyType;
|
23432 | }
|
23433 | if (this.isAny(methodType)) {
|
23434 | return this.anyType;
|
23435 | }
|
23436 | if (!methodType.callable) {
|
23437 | this.diagnostics.push(createDiagnostic(refinedSpan(ast), Diagnostic.identifier_not_callable, ast.name));
|
23438 | return this.anyType;
|
23439 | }
|
23440 | const signature = methodType.selectSignature(ast.args.map(arg => this.getType(arg)));
|
23441 | if (!signature) {
|
23442 | this.diagnostics.push(createDiagnostic(refinedSpan(ast), Diagnostic.unable_to_resolve_signature, ast.name));
|
23443 | return this.anyType;
|
23444 | }
|
23445 | return signature.result;
|
23446 | }
|
23447 | resolvePropertyRead(receiverType, ast) {
|
23448 | if (this.isAny(receiverType)) {
|
23449 | return this.anyType;
|
23450 | }
|
23451 | // The type of a property read is the seelcted member's type.
|
23452 | const member = receiverType.members().get(ast.name);
|
23453 | if (!member) {
|
23454 | if (receiverType.name === '$implicit') {
|
23455 | this.diagnostics.push(createDiagnostic(refinedSpan(ast), Diagnostic.identifier_not_defined_in_app_context, ast.name));
|
23456 | }
|
23457 | else if (receiverType.nullable && ast.receiver instanceof PropertyRead) {
|
23458 | const receiver = ast.receiver.name;
|
23459 | this.diagnostics.push(createDiagnostic(refinedSpan(ast), Diagnostic.identifier_possibly_undefined, receiver, `${receiver}?.${ast.name}`, `${receiver}!.${ast.name}`));
|
23460 | }
|
23461 | else {
|
23462 | this.diagnostics.push(createDiagnostic(refinedSpan(ast), Diagnostic.identifier_not_defined_on_receiver, ast.name, receiverType.name));
|
23463 | }
|
23464 | return this.anyType;
|
23465 | }
|
23466 | if (!member.public) {
|
23467 | const container = receiverType.name === '$implicit' ? 'the component' : `'${receiverType.name}'`;
|
23468 | this.diagnostics.push(createDiagnostic(refinedSpan(ast), Diagnostic.identifier_is_private, ast.name, container));
|
23469 | }
|
23470 | return member.type;
|
23471 | }
|
23472 | isAny(symbol) {
|
23473 | return !symbol || this.query.getTypeKind(symbol) === BuiltinType$1.Any ||
|
23474 | (!!symbol.type && this.isAny(symbol.type));
|
23475 | }
|
23476 | }
|
23477 | function refinedSpan(ast) {
|
23478 | // nameSpan is an absolute span, but the spans returned by the expression visitor are expected to
|
23479 | // be relative to the start of the expression.
|
23480 | // TODO: migrate to only using absolute spans
|
23481 | const absoluteOffset = ast.sourceSpan.start - ast.span.start;
|
23482 | if (ast instanceof ASTWithName) {
|
23483 | return offsetSpan(ast.nameSpan, -absoluteOffset);
|
23484 | }
|
23485 | return offsetSpan(ast.sourceSpan, -absoluteOffset);
|
23486 | }
|
23487 |
|
23488 | /**
|
23489 | * @license
|
23490 | * Copyright Google LLC All Rights Reserved.
|
23491 | *
|
23492 | * Use of this source code is governed by an MIT-style license that can be
|
23493 | * found in the LICENSE file at https://angular.io/license
|
23494 | */
|
23495 | function getTemplateExpressionDiagnostics(info) {
|
23496 | const visitor = new ExpressionDiagnosticsVisitor(info, (path) => getExpressionScope(info, path));
|
23497 | templateVisitAll(visitor, info.templateAst);
|
23498 | return visitor.diagnostics;
|
23499 | }
|
23500 | function getReferences(info) {
|
23501 | const result = [];
|
23502 | function processReferences(references) {
|
23503 | for (const reference of references) {
|
23504 | let type = undefined;
|
23505 | if (reference.value) {
|
23506 | type = info.query.getTypeSymbol(tokenReference(reference.value));
|
23507 | }
|
23508 | result.push({
|
23509 | name: reference.name,
|
23510 | kind: 'reference',
|
23511 | type: type || info.query.getBuiltinType(BuiltinType$1.Any),
|
23512 | get definition() {
|
23513 | return getDefinitionOf(info, reference);
|
23514 | }
|
23515 | });
|
23516 | }
|
23517 | }
|
23518 | const visitor = new class extends RecursiveTemplateAstVisitor {
|
23519 | visitEmbeddedTemplate(ast, context) {
|
23520 | super.visitEmbeddedTemplate(ast, context);
|
23521 | processReferences(ast.references);
|
23522 | }
|
23523 | visitElement(ast, context) {
|
23524 | super.visitElement(ast, context);
|
23525 | processReferences(ast.references);
|
23526 | }
|
23527 | };
|
23528 | templateVisitAll(visitor, info.templateAst);
|
23529 | return result;
|
23530 | }
|
23531 | function getDefinitionOf(info, ast) {
|
23532 | if (info.fileName) {
|
23533 | const templateOffset = info.offset;
|
23534 | return [{
|
23535 | fileName: info.fileName,
|
23536 | span: {
|
23537 | start: ast.sourceSpan.start.offset + templateOffset,
|
23538 | end: ast.sourceSpan.end.offset + templateOffset
|
23539 | }
|
23540 | }];
|
23541 | }
|
23542 | }
|
23543 | /**
|
23544 | * Resolve all variable declarations in a template by traversing the specified
|
23545 | * `path`.
|
23546 | * @param info
|
23547 | * @param path template AST path
|
23548 | */
|
23549 | function getVarDeclarations(info, path) {
|
23550 | const results = [];
|
23551 | for (let current = path.head; current; current = path.childOf(current)) {
|
23552 | if (!(current instanceof EmbeddedTemplateAst)) {
|
23553 | continue;
|
23554 | }
|
23555 | for (const variable of current.variables) {
|
23556 | let symbol = getVariableTypeFromDirectiveContext(variable.value, info.query, current);
|
23557 | const kind = info.query.getTypeKind(symbol);
|
23558 | if (kind === BuiltinType$1.Any || kind === BuiltinType$1.Unbound) {
|
23559 | // For special cases such as ngFor and ngIf, the any type is not very useful.
|
23560 | // We can do better by resolving the binding value.
|
23561 | const symbolsInScope = info.query.mergeSymbolTable([
|
23562 | info.members,
|
23563 | // Since we are traversing the AST path from head to tail, any variables
|
23564 | // that have been declared so far are also in scope.
|
23565 | info.query.createSymbolTable(results),
|
23566 | ]);
|
23567 | symbol = refinedVariableType(variable.value, symbolsInScope, info, current);
|
23568 | }
|
23569 | results.push({
|
23570 | name: variable.name,
|
23571 | kind: 'variable',
|
23572 | type: symbol,
|
23573 | get definition() {
|
23574 | return getDefinitionOf(info, variable);
|
23575 | },
|
23576 | });
|
23577 | }
|
23578 | }
|
23579 | return results;
|
23580 | }
|
23581 | /**
|
23582 | * Resolve the type for the variable in `templateElement` by finding the structural
|
23583 | * directive which has the context member. Returns any when not found.
|
23584 | * @param value variable value name
|
23585 | * @param query type symbol query
|
23586 | * @param templateElement
|
23587 | */
|
23588 | function getVariableTypeFromDirectiveContext(value, query, templateElement) {
|
23589 | for (const { directive } of templateElement.directives) {
|
23590 | const context = query.getTemplateContext(directive.type.reference);
|
23591 | if (context) {
|
23592 | const member = context.get(value);
|
23593 | if (member && member.type) {
|
23594 | return member.type;
|
23595 | }
|
23596 | }
|
23597 | }
|
23598 | return query.getBuiltinType(BuiltinType$1.Any);
|
23599 | }
|
23600 | /**
|
23601 | * Resolve a more specific type for the variable in `templateElement` by inspecting
|
23602 | * all variables that are in scope in the `mergedTable`. This function is a special
|
23603 | * case for `ngFor` and `ngIf`. If resolution fails, return the `any` type.
|
23604 | * @param value variable value name
|
23605 | * @param mergedTable symbol table for all variables in scope
|
23606 | * @param info available template information
|
23607 | * @param templateElement
|
23608 | */
|
23609 | function refinedVariableType(value, mergedTable, info, templateElement) {
|
23610 | if (value === '$implicit') {
|
23611 | // Special case: ngFor directive
|
23612 | const ngForDirective = templateElement.directives.find(d => {
|
23613 | const name = identifierName(d.directive.type);
|
23614 | return name == 'NgFor' || name == 'NgForOf';
|
23615 | });
|
23616 | if (ngForDirective) {
|
23617 | const ngForOfBinding = ngForDirective.inputs.find(i => i.directiveName == 'ngForOf');
|
23618 | if (ngForOfBinding) {
|
23619 | // Check if there is a known type for the ngFor binding.
|
23620 | const bindingType = new AstType(mergedTable, info.query, {}, info.source).getType(ngForOfBinding.value);
|
23621 | if (bindingType) {
|
23622 | const result = info.query.getElementType(bindingType);
|
23623 | if (result) {
|
23624 | return result;
|
23625 | }
|
23626 | }
|
23627 | }
|
23628 | }
|
23629 | }
|
23630 | if (value === 'ngIf' || value === '$implicit') {
|
23631 | const ngIfDirective = templateElement.directives.find(d => identifierName(d.directive.type) === 'NgIf');
|
23632 | if (ngIfDirective) {
|
23633 | // Special case: ngIf directive. The NgIf structural directive owns a template context with
|
23634 | // "$implicit" and "ngIf" members. These properties are typed as generics. Until the language
|
23635 | // service uses an Ivy and TypecheckBlock backend, we cannot bind these values to a concrete
|
23636 | // type without manual inference. To get the concrete type, look up the type of the "ngIf"
|
23637 | // import on the NgIf directive bound to the template.
|
23638 | //
|
23639 | // See @angular/common/ng_if.ts for more information.
|
23640 | const ngIfBinding = ngIfDirective.inputs.find(i => i.directiveName === 'ngIf');
|
23641 | if (ngIfBinding) {
|
23642 | // Check if there is a known type bound to the ngIf input.
|
23643 | const bindingType = new AstType(mergedTable, info.query, {}, info.source).getType(ngIfBinding.value);
|
23644 | if (bindingType) {
|
23645 | return bindingType;
|
23646 | }
|
23647 | }
|
23648 | }
|
23649 | }
|
23650 | // We can't do better, return any
|
23651 | return info.query.getBuiltinType(BuiltinType$1.Any);
|
23652 | }
|
23653 | function getEventDeclaration(info, path) {
|
23654 | const event = path.tail;
|
23655 | if (!(event instanceof BoundEventAst)) {
|
23656 | // No event available in this context.
|
23657 | return;
|
23658 | }
|
23659 | const genericEvent = {
|
23660 | name: '$event',
|
23661 | kind: 'variable',
|
23662 | type: info.query.getBuiltinType(BuiltinType$1.Any),
|
23663 | };
|
23664 | const outputSymbol = findOutputBinding(event, path, info.query);
|
23665 | if (!outputSymbol) {
|
23666 | // The `$event` variable doesn't belong to an output, so its type can't be refined.
|
23667 | // TODO: type `$event` variables in bindings to DOM events.
|
23668 | return genericEvent;
|
23669 | }
|
23670 | // The raw event type is wrapped in a generic, like EventEmitter<T> or Observable<T>.
|
23671 | const ta = outputSymbol.typeArguments();
|
23672 | if (!ta || ta.length !== 1)
|
23673 | return genericEvent;
|
23674 | const eventType = ta[0];
|
23675 | return Object.assign(Object.assign({}, genericEvent), { type: eventType });
|
23676 | }
|
23677 | /**
|
23678 | * Returns the symbols available in a particular scope of a template.
|
23679 | * @param info parsed template information
|
23680 | * @param path path of template nodes narrowing to the context the expression scope should be
|
23681 | * derived for.
|
23682 | */
|
23683 | function getExpressionScope(info, path) {
|
23684 | let result = info.members;
|
23685 | const references = getReferences(info);
|
23686 | const variables = getVarDeclarations(info, path);
|
23687 | const event = getEventDeclaration(info, path);
|
23688 | if (references.length || variables.length || event) {
|
23689 | const referenceTable = info.query.createSymbolTable(references);
|
23690 | const variableTable = info.query.createSymbolTable(variables);
|
23691 | const eventsTable = info.query.createSymbolTable(event ? [event] : []);
|
23692 | result = info.query.mergeSymbolTable([result, referenceTable, variableTable, eventsTable]);
|
23693 | }
|
23694 | return result;
|
23695 | }
|
23696 | class ExpressionDiagnosticsVisitor extends RecursiveTemplateAstVisitor {
|
23697 | constructor(info, getExpressionScope) {
|
23698 | super();
|
23699 | this.info = info;
|
23700 | this.getExpressionScope = getExpressionScope;
|
23701 | this.diagnostics = [];
|
23702 | this.path = new AstPath([]);
|
23703 | }
|
23704 | visitDirective(ast, context) {
|
23705 | // Override the default child visitor to ignore the host properties of a directive.
|
23706 | if (ast.inputs && ast.inputs.length) {
|
23707 | templateVisitAll(this, ast.inputs, context);
|
23708 | }
|
23709 | }
|
23710 | visitBoundText(ast) {
|
23711 | this.push(ast);
|
23712 | this.diagnoseExpression(ast.value, ast.sourceSpan.start.offset, false);
|
23713 | this.pop();
|
23714 | }
|
23715 | visitDirectiveProperty(ast) {
|
23716 | this.push(ast);
|
23717 | this.diagnoseExpression(ast.value, this.attributeValueLocation(ast), false);
|
23718 | this.pop();
|
23719 | }
|
23720 | visitElementProperty(ast) {
|
23721 | this.push(ast);
|
23722 | this.diagnoseExpression(ast.value, this.attributeValueLocation(ast), false);
|
23723 | this.pop();
|
23724 | }
|
23725 | visitEvent(ast) {
|
23726 | this.push(ast);
|
23727 | this.diagnoseExpression(ast.handler, this.attributeValueLocation(ast), true);
|
23728 | this.pop();
|
23729 | }
|
23730 | visitVariable(ast) {
|
23731 | const directive = this.directiveSummary;
|
23732 | if (directive && ast.value) {
|
23733 | const context = this.info.query.getTemplateContext(directive.type.reference);
|
23734 | if (context && !context.has(ast.value)) {
|
23735 | const missingMember = ast.value === '$implicit' ? 'an implicit value' : `a member called '${ast.value}'`;
|
23736 | const span = this.absSpan(spanOf$1(ast.sourceSpan));
|
23737 | this.diagnostics.push(createDiagnostic(span, Diagnostic.template_context_missing_member, directive.type.reference.name, missingMember));
|
23738 | }
|
23739 | }
|
23740 | }
|
23741 | visitElement(ast, context) {
|
23742 | this.push(ast);
|
23743 | super.visitElement(ast, context);
|
23744 | this.pop();
|
23745 | }
|
23746 | visitEmbeddedTemplate(ast, context) {
|
23747 | const previousDirectiveSummary = this.directiveSummary;
|
23748 | this.push(ast);
|
23749 | // Find directive that references this template
|
23750 | this.directiveSummary =
|
23751 | ast.directives.map(d => d.directive).find(d => hasTemplateReference(d.type));
|
23752 | // Process children
|
23753 | super.visitEmbeddedTemplate(ast, context);
|
23754 | this.pop();
|
23755 | this.directiveSummary = previousDirectiveSummary;
|
23756 | }
|
23757 | attributeValueLocation(ast) {
|
23758 | const path = getPathToNodeAtPosition(this.info.htmlAst, ast.sourceSpan.start.offset);
|
23759 | const last = path.tail;
|
23760 | if (last instanceof Attribute && last.valueSpan) {
|
23761 | return last.valueSpan.start.offset;
|
23762 | }
|
23763 | return ast.sourceSpan.start.offset;
|
23764 | }
|
23765 | diagnoseExpression(ast, offset, inEvent) {
|
23766 | const scope = this.getExpressionScope(this.path, inEvent);
|
23767 | const analyzer = new AstType(scope, this.info.query, { inEvent }, this.info.source);
|
23768 | for (const diagnostic of analyzer.getDiagnostics(ast)) {
|
23769 | diagnostic.span = this.absSpan(diagnostic.span, offset);
|
23770 | this.diagnostics.push(diagnostic);
|
23771 | }
|
23772 | }
|
23773 | push(ast) {
|
23774 | this.path.push(ast);
|
23775 | }
|
23776 | pop() {
|
23777 | this.path.pop();
|
23778 | }
|
23779 | absSpan(span, additionalOffset = 0) {
|
23780 | return {
|
23781 | start: span.start + this.info.offset + additionalOffset,
|
23782 | end: span.end + this.info.offset + additionalOffset,
|
23783 | };
|
23784 | }
|
23785 | }
|
23786 | function hasTemplateReference(type) {
|
23787 | if (type.diDeps) {
|
23788 | for (let diDep of type.diDeps) {
|
23789 | if (diDep.token && diDep.token.identifier &&
|
23790 | identifierName(diDep.token.identifier) == 'TemplateRef')
|
23791 | return true;
|
23792 | }
|
23793 | }
|
23794 | return false;
|
23795 | }
|
23796 | function spanOf$1(sourceSpan) {
|
23797 | return { start: sourceSpan.start.offset, end: sourceSpan.end.offset };
|
23798 | }
|
23799 |
|
23800 | /**
|
23801 | * @license
|
23802 | * Copyright Google LLC All Rights Reserved.
|
23803 | *
|
23804 | * Use of this source code is governed by an MIT-style license that can be
|
23805 | * found in the LICENSE file at https://angular.io/license
|
23806 | */
|
23807 | /**
|
23808 | * The type of Angular directive. Used for QuickInfo in template.
|
23809 | */
|
23810 | var DirectiveKind;
|
23811 | (function (DirectiveKind) {
|
23812 | DirectiveKind["COMPONENT"] = "component";
|
23813 | DirectiveKind["DIRECTIVE"] = "directive";
|
23814 | DirectiveKind["EVENT"] = "event";
|
23815 | })(DirectiveKind || (DirectiveKind = {}));
|
23816 | /**
|
23817 | * ScriptElementKind for completion.
|
23818 | */
|
23819 | var CompletionKind;
|
23820 | (function (CompletionKind) {
|
23821 | CompletionKind["ANGULAR_ELEMENT"] = "angular element";
|
23822 | CompletionKind["ATTRIBUTE"] = "attribute";
|
23823 | CompletionKind["COMPONENT"] = "component";
|
23824 | CompletionKind["ELEMENT"] = "element";
|
23825 | CompletionKind["ENTITY"] = "entity";
|
23826 | CompletionKind["HTML_ATTRIBUTE"] = "html attribute";
|
23827 | CompletionKind["HTML_ELEMENT"] = "html element";
|
23828 | CompletionKind["KEY"] = "key";
|
23829 | CompletionKind["METHOD"] = "method";
|
23830 | CompletionKind["PIPE"] = "pipe";
|
23831 | CompletionKind["PROPERTY"] = "property";
|
23832 | CompletionKind["REFERENCE"] = "reference";
|
23833 | CompletionKind["TYPE"] = "type";
|
23834 | CompletionKind["VARIABLE"] = "variable";
|
23835 | })(CompletionKind || (CompletionKind = {}));
|
23836 |
|
23837 | /**
|
23838 | * @license
|
23839 | * Copyright Google LLC All Rights Reserved.
|
23840 | *
|
23841 | * Use of this source code is governed by an MIT-style license that can be
|
23842 | * found in the LICENSE file at https://angular.io/license
|
23843 | */
|
23844 | function findAstAt(ast, position, excludeEmpty = false) {
|
23845 | const path = [];
|
23846 | const visitor = new class extends RecursiveAstVisitor {
|
23847 | visit(ast) {
|
23848 | if ((!excludeEmpty || ast.sourceSpan.start < ast.sourceSpan.end) &&
|
23849 | inSpan(position, ast.sourceSpan)) {
|
23850 | const isNotNarrower = path.length && !isNarrower(ast.span, path[path.length - 1].span);
|
23851 | if (!isNotNarrower) {
|
23852 | path.push(ast);
|
23853 | }
|
23854 | ast.visit(this);
|
23855 | }
|
23856 | }
|
23857 | };
|
23858 | // We never care about the ASTWithSource node and its visit() method calls its ast's visit so
|
23859 | // the visit() method above would never see it.
|
23860 | if (ast instanceof ASTWithSource) {
|
23861 | ast = ast.ast;
|
23862 | }
|
23863 | // `Interpolation` is useless here except the `expressions` of it.
|
23864 | if (ast instanceof Interpolation) {
|
23865 | ast = ast.expressions.filter((_ast) => inSpan(position, _ast.sourceSpan))[0];
|
23866 | }
|
23867 | if (ast) {
|
23868 | visitor.visit(ast);
|
23869 | }
|
23870 | return new AstPath(path, position);
|
23871 | }
|
23872 | function getExpressionCompletions(scope, ast, position, templateInfo) {
|
23873 | const path = findAstAt(ast, position);
|
23874 | if (path.empty)
|
23875 | return undefined;
|
23876 | const tail = path.tail;
|
23877 | let result = scope;
|
23878 | function getType(ast) {
|
23879 | return new AstType(scope, templateInfo.query, {}, templateInfo.source).getType(ast);
|
23880 | }
|
23881 | // If the completion request is in a not in a pipe or property access then the global scope
|
23882 | // (that is the scope of the implicit receiver) is the right scope as the user is typing the
|
23883 | // beginning of an expression.
|
23884 | tail.visit({
|
23885 | visitUnary(_ast) { },
|
23886 | visitBinary(_ast) { },
|
23887 | visitChain(_ast) { },
|
23888 | visitConditional(_ast) { },
|
23889 | visitFunctionCall(_ast) { },
|
23890 | visitImplicitReceiver(_ast) { },
|
23891 | visitThisReceiver(_ast) { },
|
23892 | visitInterpolation(_ast) {
|
23893 | result = undefined;
|
23894 | },
|
23895 | visitKeyedRead(_ast) { },
|
23896 | visitKeyedWrite(_ast) { },
|
23897 | visitLiteralArray(_ast) { },
|
23898 | visitLiteralMap(_ast) { },
|
23899 | visitLiteralPrimitive(ast) {
|
23900 | // The type `LiteralPrimitive` include the `ERROR`, and it's wrapped as `string`.
|
23901 | // packages/compiler/src/template_parser/binding_parser.ts#L308
|
23902 | // So exclude the `ERROR` here.
|
23903 | if (typeof ast.value === 'string' &&
|
23904 | ast.value ===
|
23905 | templateInfo.source.slice(ast.sourceSpan.start + 1, ast.sourceSpan.end - 1)) {
|
23906 | result = undefined;
|
23907 | }
|
23908 | },
|
23909 | visitMethodCall(_ast) { },
|
23910 | visitPipe(ast) {
|
23911 | if (position >= ast.exp.span.end &&
|
23912 | (!ast.args || !ast.args.length || position < ast.args[0].span.start)) {
|
23913 | // We are in a position a pipe name is expected.
|
23914 | result = templateInfo.query.getPipes();
|
23915 | }
|
23916 | },
|
23917 | visitPrefixNot(_ast) { },
|
23918 | visitNonNullAssert(_ast) { },
|
23919 | visitPropertyRead(ast) {
|
23920 | const receiverType = getType(ast.receiver);
|
23921 | result = receiverType ? receiverType.members() : scope;
|
23922 | },
|
23923 | visitPropertyWrite(ast) {
|
23924 | const receiverType = getType(ast.receiver);
|
23925 | result = receiverType ? receiverType.members() : scope;
|
23926 | },
|
23927 | visitQuote(_ast) {
|
23928 | // For a quote, return the members of any (if there are any).
|
23929 | result = templateInfo.query.getBuiltinType(BuiltinType$1.Any).members();
|
23930 | },
|
23931 | visitSafeMethodCall(ast) {
|
23932 | const receiverType = getType(ast.receiver);
|
23933 | result = receiverType ? receiverType.members() : scope;
|
23934 | },
|
23935 | visitSafePropertyRead(ast) {
|
23936 | const receiverType = getType(ast.receiver);
|
23937 | result = receiverType ? receiverType.members() : scope;
|
23938 | },
|
23939 | });
|
23940 | return result && result.values();
|
23941 | }
|
23942 | /**
|
23943 | * Retrieves the expression symbol at a particular position in a template.
|
23944 | *
|
23945 | * @param scope symbols in scope of the template
|
23946 | * @param ast template AST
|
23947 | * @param position absolute location in template to retrieve symbol at
|
23948 | * @param query type symbol query for the template scope
|
23949 | */
|
23950 | function getExpressionSymbol(scope, ast, position, templateInfo) {
|
23951 | const path = findAstAt(ast, position, /* excludeEmpty */ true);
|
23952 | if (path.empty)
|
23953 | return undefined;
|
23954 | const tail = path.tail;
|
23955 | function getType(ast) {
|
23956 | return new AstType(scope, templateInfo.query, {}, templateInfo.source).getType(ast);
|
23957 | }
|
23958 | function spanFromName(ast) {
|
23959 | // `nameSpan` is an absolute span, but the span expected by the result of this method is
|
23960 | // relative to the start of the expression.
|
23961 | // TODO(ayazhafiz): migrate to only using absolute spans
|
23962 | const offset = ast.sourceSpan.start - ast.span.start;
|
23963 | return {
|
23964 | start: ast.nameSpan.start - offset,
|
23965 | end: ast.nameSpan.end - offset,
|
23966 | };
|
23967 | }
|
23968 | let symbol = undefined;
|
23969 | let span = undefined;
|
23970 | // If the completion request is in a not in a pipe or property access then the global scope
|
23971 | // (that is the scope of the implicit receiver) is the right scope as the user is typing the
|
23972 | // beginning of an expression.
|
23973 | tail.visit({
|
23974 | visitUnary(_ast) { },
|
23975 | visitBinary(_ast) { },
|
23976 | visitChain(_ast) { },
|
23977 | visitConditional(_ast) { },
|
23978 | visitFunctionCall(_ast) { },
|
23979 | visitImplicitReceiver(_ast) { },
|
23980 | visitThisReceiver(_ast) { },
|
23981 | visitInterpolation(_ast) { },
|
23982 | visitKeyedRead(_ast) { },
|
23983 | visitKeyedWrite(_ast) { },
|
23984 | visitLiteralArray(_ast) { },
|
23985 | visitLiteralMap(_ast) { },
|
23986 | visitLiteralPrimitive(_ast) { },
|
23987 | visitMethodCall(ast) {
|
23988 | const receiverType = getType(ast.receiver);
|
23989 | symbol = receiverType && receiverType.members().get(ast.name);
|
23990 | span = spanFromName(ast);
|
23991 | },
|
23992 | visitPipe(ast) {
|
23993 | if (inSpan(position, ast.nameSpan, /* exclusive */ true)) {
|
23994 | // We are in a position a pipe name is expected.
|
23995 | const pipes = templateInfo.query.getPipes();
|
23996 | symbol = pipes.get(ast.name);
|
23997 | span = spanFromName(ast);
|
23998 | }
|
23999 | },
|
24000 | visitPrefixNot(_ast) { },
|
24001 | visitNonNullAssert(_ast) { },
|
24002 | visitPropertyRead(ast) {
|
24003 | const receiverType = getType(ast.receiver);
|
24004 | symbol = receiverType && receiverType.members().get(ast.name);
|
24005 | span = spanFromName(ast);
|
24006 | },
|
24007 | visitPropertyWrite(ast) {
|
24008 | const receiverType = getType(ast.receiver);
|
24009 | symbol = receiverType && receiverType.members().get(ast.name);
|
24010 | span = spanFromName(ast);
|
24011 | },
|
24012 | visitQuote(_ast) { },
|
24013 | visitSafeMethodCall(ast) {
|
24014 | const receiverType = getType(ast.receiver);
|
24015 | symbol = receiverType && receiverType.members().get(ast.name);
|
24016 | span = spanFromName(ast);
|
24017 | },
|
24018 | visitSafePropertyRead(ast) {
|
24019 | const receiverType = getType(ast.receiver);
|
24020 | symbol = receiverType && receiverType.members().get(ast.name);
|
24021 | span = spanFromName(ast);
|
24022 | },
|
24023 | });
|
24024 | if (symbol && span) {
|
24025 | return { symbol, span };
|
24026 | }
|
24027 | }
|
24028 |
|
24029 | /**
|
24030 | * @license
|
24031 | * Copyright Google LLC All Rights Reserved.
|
24032 | *
|
24033 | * Use of this source code is governed by an MIT-style license that can be
|
24034 | * found in the LICENSE file at https://angular.io/license
|
24035 | */
|
24036 | const values = [
|
24037 | 'ID',
|
24038 | 'CDATA',
|
24039 | 'NAME',
|
24040 | ['ltr', 'rtl'],
|
24041 | ['rect', 'circle', 'poly', 'default'],
|
24042 | 'NUMBER',
|
24043 | ['nohref'],
|
24044 | ['ismap'],
|
24045 | ['declare'],
|
24046 | ['DATA', 'REF', 'OBJECT'],
|
24047 | ['GET', 'POST'],
|
24048 | 'IDREF',
|
24049 | ['TEXT', 'PASSWORD', 'CHECKBOX', 'RADIO', 'SUBMIT', 'RESET', 'FILE', 'HIDDEN', 'IMAGE', 'BUTTON'],
|
24050 | ['checked'],
|
24051 | ['disabled'],
|
24052 | ['readonly'],
|
24053 | ['multiple'],
|
24054 | ['selected'],
|
24055 | ['button', 'submit', 'reset'],
|
24056 | ['void', 'above', 'below', 'hsides', 'lhs', 'rhs', 'vsides', 'box', 'border'],
|
24057 | ['none', 'groups', 'rows', 'cols', 'all'],
|
24058 | ['left', 'center', 'right', 'justify', 'char'],
|
24059 | ['top', 'middle', 'bottom', 'baseline'],
|
24060 | 'IDREFS',
|
24061 | ['row', 'col', 'rowgroup', 'colgroup'],
|
24062 | ['defer']
|
24063 | ];
|
24064 | const groups = [
|
24065 | { id: 0 },
|
24066 | {
|
24067 | onclick: 1,
|
24068 | ondblclick: 1,
|
24069 | onmousedown: 1,
|
24070 | onmouseup: 1,
|
24071 | onmouseover: 1,
|
24072 | onmousemove: 1,
|
24073 | onmouseout: 1,
|
24074 | onkeypress: 1,
|
24075 | onkeydown: 1,
|
24076 | onkeyup: 1
|
24077 | },
|
24078 | { lang: 2, dir: 3 },
|
24079 | { onload: 1, onunload: 1 },
|
24080 | { name: 1 },
|
24081 | { href: 1 },
|
24082 | { type: 1 },
|
24083 | { alt: 1 },
|
24084 | { tabindex: 5 },
|
24085 | { media: 1 },
|
24086 | { nohref: 6 },
|
24087 | { usemap: 1 },
|
24088 | { src: 1 },
|
24089 | { onfocus: 1, onblur: 1 },
|
24090 | { charset: 1 },
|
24091 | { declare: 8, classid: 1, codebase: 1, data: 1, codetype: 1, archive: 1, standby: 1 },
|
24092 | { title: 1 },
|
24093 | { value: 1 },
|
24094 | { cite: 1 },
|
24095 | { datetime: 1 },
|
24096 | { accept: 1 },
|
24097 | { shape: 4, coords: 1 },
|
24098 | { for: 11
|
24099 | },
|
24100 | { action: 1, method: 10, enctype: 1, onsubmit: 1, onreset: 1, 'accept-charset': 1 },
|
24101 | { valuetype: 9 },
|
24102 | { longdesc: 1 },
|
24103 | { width: 1 },
|
24104 | { disabled: 14 },
|
24105 | { readonly: 15, onselect: 1 },
|
24106 | { accesskey: 1 },
|
24107 | { size: 5, multiple: 16 },
|
24108 | { onchange: 1 },
|
24109 | { label: 1 },
|
24110 | { selected: 17 },
|
24111 | { type: 12, checked: 13, size: 1, maxlength: 5 },
|
24112 | { rows: 5, cols: 5 },
|
24113 | { type: 18 },
|
24114 | { height: 1 },
|
24115 | { summary: 1, border: 1, frame: 19, rules: 20, cellspacing: 1, cellpadding: 1, datapagesize: 1 },
|
24116 | { align: 21, char: 1, charoff: 1, valign: 22 },
|
24117 | { span: 5 },
|
24118 | { abbr: 1, axis: 1, headers: 23, scope: 24, rowspan: 5, colspan: 5 },
|
24119 | { profile: 1 },
|
24120 | { 'http-equiv': 2, name: 2, content: 1, scheme: 1 },
|
24121 | { class: 1, style: 1 },
|
24122 | { hreflang: 2, rel: 1, rev: 1 },
|
24123 | { ismap: 7 },
|
24124 | {
|
24125 | defer: 25, event: 1, for: 1
|
24126 | }
|
24127 | ];
|
24128 | const elements = {
|
24129 | TT: [0, 1, 2, 16, 44],
|
24130 | I: [0, 1, 2, 16, 44],
|
24131 | B: [0, 1, 2, 16, 44],
|
24132 | BIG: [0, 1, 2, 16, 44],
|
24133 | SMALL: [0, 1, 2, 16, 44],
|
24134 | EM: [0, 1, 2, 16, 44],
|
24135 | STRONG: [0, 1, 2, 16, 44],
|
24136 | DFN: [0, 1, 2, 16, 44],
|
24137 | CODE: [0, 1, 2, 16, 44],
|
24138 | SAMP: [0, 1, 2, 16, 44],
|
24139 | KBD: [0, 1, 2, 16, 44],
|
24140 | VAR: [0, 1, 2, 16, 44],
|
24141 | CITE: [0, 1, 2, 16, 44],
|
24142 | ABBR: [0, 1, 2, 16, 44],
|
24143 | ACRONYM: [0, 1, 2, 16, 44],
|
24144 | SUB: [0, 1, 2, 16, 44],
|
24145 | SUP: [0, 1, 2, 16, 44],
|
24146 | SPAN: [0, 1, 2, 16, 44],
|
24147 | BDO: [0, 2, 16, 44],
|
24148 | BR: [0, 16, 44],
|
24149 | BODY: [0, 1, 2, 3, 16, 44],
|
24150 | ADDRESS: [0, 1, 2, 16, 44],
|
24151 | DIV: [0, 1, 2, 16, 44],
|
24152 | A: [0, 1, 2, 4, 5, 6, 8, 13, 14, 16, 21, 29, 44, 45],
|
24153 | MAP: [0, 1, 2, 4, 16, 44],
|
24154 | AREA: [0, 1, 2, 5, 7, 8, 10, 13, 16, 21, 29, 44],
|
24155 | LINK: [0, 1, 2, 5, 6, 9, 14, 16, 44, 45],
|
24156 | IMG: [0, 1, 2, 4, 7, 11, 12, 16, 25, 26, 37, 44, 46],
|
24157 | OBJECT: [0, 1, 2, 4, 6, 8, 11, 15, 16, 26, 37, 44],
|
24158 | PARAM: [0, 4, 6, 17, 24],
|
24159 | HR: [0, 1, 2, 16, 44],
|
24160 | P: [0, 1, 2, 16, 44],
|
24161 | H1: [0, 1, 2, 16, 44],
|
24162 | H2: [0, 1, 2, 16, 44],
|
24163 | H3: [0, 1, 2, 16, 44],
|
24164 | H4: [0, 1, 2, 16, 44],
|
24165 | H5: [0, 1, 2, 16, 44],
|
24166 | H6: [0, 1, 2, 16, 44],
|
24167 | PRE: [0, 1, 2, 16, 44],
|
24168 | Q: [0, 1, 2, 16, 18, 44],
|
24169 | BLOCKQUOTE: [0, 1, 2, 16, 18, 44],
|
24170 | INS: [0, 1, 2, 16, 18, 19, 44],
|
24171 | DEL: [0, 1, 2, 16, 18, 19, 44],
|
24172 | DL: [0, 1, 2, 16, 44],
|
24173 | DT: [0, 1, 2, 16, 44],
|
24174 | DD: [0, 1, 2, 16, 44],
|
24175 | OL: [0, 1, 2, 16, 44],
|
24176 | UL: [0, 1, 2, 16, 44],
|
24177 | LI: [0, 1, 2, 16, 44],
|
24178 | FORM: [0, 1, 2, 4, 16, 20, 23, 44],
|
24179 | LABEL: [0, 1, 2, 13, 16, 22, 29, 44],
|
24180 | INPUT: [0, 1, 2, 4, 7, 8, 11, 12, 13, 16, 17, 20, 27, 28, 29, 31, 34, 44, 46],
|
24181 | SELECT: [0, 1, 2, 4, 8, 13, 16, 27, 30, 31, 44],
|
24182 | OPTGROUP: [0, 1, 2, 16, 27, 32, 44],
|
24183 | OPTION: [0, 1, 2, 16, 17, 27, 32, 33, 44],
|
24184 | TEXTAREA: [0, 1, 2, 4, 8, 13, 16, 27, 28, 29, 31, 35, 44],
|
24185 | FIELDSET: [0, 1, 2, 16, 44],
|
24186 | LEGEND: [0, 1, 2, 16, 29, 44],
|
24187 | BUTTON: [0, 1, 2, 4, 8, 13, 16, 17, 27, 29, 36, 44],
|
24188 | TABLE: [0, 1, 2, 16, 26, 38, 44],
|
24189 | CAPTION: [0, 1, 2, 16, 44],
|
24190 | COLGROUP: [0, 1, 2, 16, 26, 39, 40, 44],
|
24191 | COL: [0, 1, 2, 16, 26, 39, 40, 44],
|
24192 | THEAD: [0, 1, 2, 16, 39, 44],
|
24193 | TBODY: [0, 1, 2, 16, 39, 44],
|
24194 | TFOOT: [0, 1, 2, 16, 39, 44],
|
24195 | TR: [0, 1, 2, 16, 39, 44],
|
24196 | TH: [0, 1, 2, 16, 39, 41, 44],
|
24197 | TD: [0, 1, 2, 16, 39, 41, 44],
|
24198 | HEAD: [2, 42],
|
24199 | TITLE: [2],
|
24200 | BASE: [5],
|
24201 | META: [2, 43],
|
24202 | STYLE: [2, 6, 9, 16],
|
24203 | SCRIPT: [6, 12, 14, 47],
|
24204 | NOSCRIPT: [0, 1, 2, 16, 44],
|
24205 | HTML: [2]
|
24206 | };
|
24207 | const defaultAttributes = [0, 1, 2, 4];
|
24208 | function elementNames() {
|
24209 | return Object.keys(elements).sort().map(v => v.toLowerCase());
|
24210 | }
|
24211 | function compose(indexes) {
|
24212 | const result = {};
|
24213 | if (indexes) {
|
24214 | for (let index of indexes) {
|
24215 | const group = groups[index];
|
24216 | for (let name in group)
|
24217 | if (group.hasOwnProperty(name))
|
24218 | result[name] = values[group[name]];
|
24219 | }
|
24220 | }
|
24221 | return result;
|
24222 | }
|
24223 | function attributeNames(element) {
|
24224 | return Object.keys(compose(elements[element.toUpperCase()] || defaultAttributes)).sort();
|
24225 | }
|
24226 | // This section is describes the DOM property surface of a DOM element and is derivgulp formated
|
24227 | // from
|
24228 | // from the SCHEMA strings from the security context information. SCHEMA is copied here because
|
24229 | // it would be an unnecessary risk to allow this array to be imported from the security context
|
24230 | // schema registry.
|
24231 | const SCHEMA$1 = [
|
24232 | '[Element]|textContent,%classList,className,id,innerHTML,*beforecopy,*beforecut,*beforepaste,*copy,*cut,*paste,*search,*selectstart,*webkitfullscreenchange,*webkitfullscreenerror,*wheel,outerHTML,#scrollLeft,#scrollTop,slot' +
|
24233 | /* added manually to avoid breaking changes */
|
24234 | ',*message,*mozfullscreenchange,*mozfullscreenerror,*mozpointerlockchange,*mozpointerlockerror,*webglcontextcreationerror,*webglcontextlost,*webglcontextrestored',
|
24235 | '[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',
|
24236 | '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',
|
24237 | 'media^[HTMLElement]|!autoplay,!controls,%controlsList,%crossOrigin,#currentTime,!defaultMuted,#defaultPlaybackRate,!disableRemotePlayback,!loop,!muted,*encrypted,*waitingforkey,#playbackRate,preload,src,%srcObject,#volume',
|
24238 | ':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',
|
24239 | ':svg:graphics^:svg:|',
|
24240 | ':svg:animation^:svg:|*begin,*end,*repeat',
|
24241 | ':svg:geometry^:svg:|',
|
24242 | ':svg:componentTransferFunction^:svg:|',
|
24243 | ':svg:gradient^:svg:|',
|
24244 | ':svg:textContent^:svg:graphics|',
|
24245 | ':svg:textPositioning^:svg:textContent|',
|
24246 | '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',
|
24247 | 'area^[HTMLElement]|alt,coords,download,hash,host,hostname,href,!noHref,password,pathname,ping,port,protocol,referrerPolicy,rel,search,shape,target,username',
|
24248 | 'audio^media|',
|
24249 | 'br^[HTMLElement]|clear',
|
24250 | 'base^[HTMLElement]|href,target',
|
24251 | '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',
|
24252 | 'button^[HTMLElement]|!autofocus,!disabled,formAction,formEnctype,formMethod,!formNoValidate,formTarget,name,type,value',
|
24253 | 'canvas^[HTMLElement]|#height,#width',
|
24254 | 'content^[HTMLElement]|select',
|
24255 | 'dl^[HTMLElement]|!compact',
|
24256 | 'datalist^[HTMLElement]|',
|
24257 | 'details^[HTMLElement]|!open',
|
24258 | 'dialog^[HTMLElement]|!open,returnValue',
|
24259 | 'dir^[HTMLElement]|!compact',
|
24260 | 'div^[HTMLElement]|align',
|
24261 | 'embed^[HTMLElement]|align,height,name,src,type,width',
|
24262 | 'fieldset^[HTMLElement]|!disabled,name',
|
24263 | 'font^[HTMLElement]|color,face,size',
|
24264 | 'form^[HTMLElement]|acceptCharset,action,autocomplete,encoding,enctype,method,name,!noValidate,target',
|
24265 | 'frame^[HTMLElement]|frameBorder,longDesc,marginHeight,marginWidth,name,!noResize,scrolling,src',
|
24266 | 'frameset^[HTMLElement]|cols,*beforeunload,*blur,*error,*focus,*hashchange,*languagechange,*load,*message,*offline,*online,*pagehide,*pageshow,*popstate,*rejectionhandled,*resize,*scroll,*storage,*unhandledrejection,*unload,rows',
|
24267 | 'hr^[HTMLElement]|align,color,!noShade,size,width',
|
24268 | 'head^[HTMLElement]|',
|
24269 | 'h1,h2,h3,h4,h5,h6^[HTMLElement]|align',
|
24270 | 'html^[HTMLElement]|version',
|
24271 | 'iframe^[HTMLElement]|align,!allowFullscreen,frameBorder,height,longDesc,marginHeight,marginWidth,name,referrerPolicy,%sandbox,scrolling,src,srcdoc,width',
|
24272 | 'img^[HTMLElement]|align,alt,border,%crossOrigin,#height,#hspace,!isMap,longDesc,lowsrc,name,referrerPolicy,sizes,src,srcset,useMap,#vspace,#width',
|
24273 | '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',
|
24274 | 'li^[HTMLElement]|type,#value',
|
24275 | 'label^[HTMLElement]|htmlFor',
|
24276 | 'legend^[HTMLElement]|align',
|
24277 | 'link^[HTMLElement]|as,charset,%crossOrigin,!disabled,href,hreflang,integrity,media,referrerPolicy,rel,%relList,rev,%sizes,target,type',
|
24278 | 'map^[HTMLElement]|name',
|
24279 | 'marquee^[HTMLElement]|behavior,bgColor,direction,height,#hspace,#loop,#scrollAmount,#scrollDelay,!trueSpeed,#vspace,width',
|
24280 | 'menu^[HTMLElement]|!compact',
|
24281 | 'meta^[HTMLElement]|content,httpEquiv,name,scheme',
|
24282 | 'meter^[HTMLElement]|#high,#low,#max,#min,#optimum,#value',
|
24283 | 'ins,del^[HTMLElement]|cite,dateTime',
|
24284 | 'ol^[HTMLElement]|!compact,!reversed,#start,type',
|
24285 | 'object^[HTMLElement]|align,archive,border,code,codeBase,codeType,data,!declare,height,#hspace,name,standby,type,useMap,#vspace,width',
|
24286 | 'optgroup^[HTMLElement]|!disabled,label',
|
24287 | 'option^[HTMLElement]|!defaultSelected,!disabled,label,!selected,text,value',
|
24288 | 'output^[HTMLElement]|defaultValue,%htmlFor,name,value',
|
24289 | 'p^[HTMLElement]|align',
|
24290 | 'param^[HTMLElement]|name,type,value,valueType',
|
24291 | 'picture^[HTMLElement]|',
|
24292 | 'pre^[HTMLElement]|#width',
|
24293 | 'progress^[HTMLElement]|#max,#value',
|
24294 | 'q,blockquote,cite^[HTMLElement]|',
|
24295 | 'script^[HTMLElement]|!async,charset,%crossOrigin,!defer,event,htmlFor,integrity,src,text,type',
|
24296 | 'select^[HTMLElement]|autocomplete,!autofocus,!disabled,#length,!multiple,name,!required,#selectedIndex,#size,value',
|
24297 | 'shadow^[HTMLElement]|',
|
24298 | 'slot^[HTMLElement]|name',
|
24299 | 'source^[HTMLElement]|media,sizes,src,srcset,type',
|
24300 | 'span^[HTMLElement]|',
|
24301 | 'style^[HTMLElement]|!disabled,media,type',
|
24302 | 'caption^[HTMLElement]|align',
|
24303 | 'th,td^[HTMLElement]|abbr,align,axis,bgColor,ch,chOff,#colSpan,headers,height,!noWrap,#rowSpan,scope,vAlign,width',
|
24304 | 'col,colgroup^[HTMLElement]|align,ch,chOff,#span,vAlign,width',
|
24305 | 'table^[HTMLElement]|align,bgColor,border,%caption,cellPadding,cellSpacing,frame,rules,summary,%tFoot,%tHead,width',
|
24306 | 'tr^[HTMLElement]|align,bgColor,ch,chOff,vAlign',
|
24307 | 'tfoot,thead,tbody^[HTMLElement]|align,ch,chOff,vAlign',
|
24308 | 'template^[HTMLElement]|',
|
24309 | 'textarea^[HTMLElement]|autocapitalize,autocomplete,!autofocus,#cols,defaultValue,dirName,!disabled,#maxLength,#minLength,name,placeholder,!readOnly,!required,#rows,selectionDirection,#selectionEnd,#selectionStart,value,wrap',
|
24310 | 'title^[HTMLElement]|text',
|
24311 | 'track^[HTMLElement]|!default,kind,label,src,srclang',
|
24312 | 'ul^[HTMLElement]|!compact,type',
|
24313 | 'unknown^[HTMLElement]|',
|
24314 | 'video^media|#height,poster,#width',
|
24315 | ':svg:a^:svg:graphics|',
|
24316 | ':svg:animate^:svg:animation|',
|
24317 | ':svg:animateMotion^:svg:animation|',
|
24318 | ':svg:animateTransform^:svg:animation|',
|
24319 | ':svg:circle^:svg:geometry|',
|
24320 | ':svg:clipPath^:svg:graphics|',
|
24321 | ':svg:defs^:svg:graphics|',
|
24322 | ':svg:desc^:svg:|',
|
24323 | ':svg:discard^:svg:|',
|
24324 | ':svg:ellipse^:svg:geometry|',
|
24325 | ':svg:feBlend^:svg:|',
|
24326 | ':svg:feColorMatrix^:svg:|',
|
24327 | ':svg:feComponentTransfer^:svg:|',
|
24328 | ':svg:feComposite^:svg:|',
|
24329 | ':svg:feConvolveMatrix^:svg:|',
|
24330 | ':svg:feDiffuseLighting^:svg:|',
|
24331 | ':svg:feDisplacementMap^:svg:|',
|
24332 | ':svg:feDistantLight^:svg:|',
|
24333 | ':svg:feDropShadow^:svg:|',
|
24334 | ':svg:feFlood^:svg:|',
|
24335 | ':svg:feFuncA^:svg:componentTransferFunction|',
|
24336 | ':svg:feFuncB^:svg:componentTransferFunction|',
|
24337 | ':svg:feFuncG^:svg:componentTransferFunction|',
|
24338 | ':svg:feFuncR^:svg:componentTransferFunction|',
|
24339 | ':svg:feGaussianBlur^:svg:|',
|
24340 | ':svg:feImage^:svg:|',
|
24341 | ':svg:feMerge^:svg:|',
|
24342 | ':svg:feMergeNode^:svg:|',
|
24343 | ':svg:feMorphology^:svg:|',
|
24344 | ':svg:feOffset^:svg:|',
|
24345 | ':svg:fePointLight^:svg:|',
|
24346 | ':svg:feSpecularLighting^:svg:|',
|
24347 | ':svg:feSpotLight^:svg:|',
|
24348 | ':svg:feTile^:svg:|',
|
24349 | ':svg:feTurbulence^:svg:|',
|
24350 | ':svg:filter^:svg:|',
|
24351 | ':svg:foreignObject^:svg:graphics|',
|
24352 | ':svg:g^:svg:graphics|',
|
24353 | ':svg:image^:svg:graphics|',
|
24354 | ':svg:line^:svg:geometry|',
|
24355 | ':svg:linearGradient^:svg:gradient|',
|
24356 | ':svg:mpath^:svg:|',
|
24357 | ':svg:marker^:svg:|',
|
24358 | ':svg:mask^:svg:|',
|
24359 | ':svg:metadata^:svg:|',
|
24360 | ':svg:path^:svg:geometry|',
|
24361 | ':svg:pattern^:svg:|',
|
24362 | ':svg:polygon^:svg:geometry|',
|
24363 | ':svg:polyline^:svg:geometry|',
|
24364 | ':svg:radialGradient^:svg:gradient|',
|
24365 | ':svg:rect^:svg:geometry|',
|
24366 | ':svg:svg^:svg:graphics|#currentScale,#zoomAndPan',
|
24367 | ':svg:script^:svg:|type',
|
24368 | ':svg:set^:svg:animation|',
|
24369 | ':svg:stop^:svg:|',
|
24370 | ':svg:style^:svg:|!disabled,media,title,type',
|
24371 | ':svg:switch^:svg:graphics|',
|
24372 | ':svg:symbol^:svg:|',
|
24373 | ':svg:tspan^:svg:textPositioning|',
|
24374 | ':svg:text^:svg:textPositioning|',
|
24375 | ':svg:textPath^:svg:textContent|',
|
24376 | ':svg:title^:svg:|',
|
24377 | ':svg:use^:svg:graphics|',
|
24378 | ':svg:view^:svg:|#zoomAndPan',
|
24379 | 'data^[HTMLElement]|value',
|
24380 | 'keygen^[HTMLElement]|!autofocus,challenge,!disabled,form,keytype,name',
|
24381 | 'menuitem^[HTMLElement]|type,label,icon,!disabled,!checked,radiogroup,!default',
|
24382 | 'summary^[HTMLElement]|',
|
24383 | 'time^[HTMLElement]|dateTime',
|
24384 | ':svg:cursor^:svg:|',
|
24385 | ];
|
24386 | const EVENT = 'event';
|
24387 | const BOOLEAN$1 = 'boolean';
|
24388 | const NUMBER$1 = 'number';
|
24389 | const STRING$1 = 'string';
|
24390 | const OBJECT$1 = 'object';
|
24391 | class SchemaInformation {
|
24392 | constructor() {
|
24393 | this.schema = {};
|
24394 | SCHEMA$1.forEach(encodedType => {
|
24395 | const parts = encodedType.split('|');
|
24396 | const properties = parts[1].split(',');
|
24397 | const typeParts = (parts[0] + '^').split('^');
|
24398 | const typeName = typeParts[0];
|
24399 | const type = {};
|
24400 | typeName.split(',').forEach(tag => this.schema[tag.toLowerCase()] = type);
|
24401 | const superName = typeParts[1];
|
24402 | const superType = superName && this.schema[superName.toLowerCase()];
|
24403 | if (superType) {
|
24404 | for (const key in superType) {
|
24405 | type[key] = superType[key];
|
24406 | }
|
24407 | }
|
24408 | properties.forEach((property) => {
|
24409 | if (property === '') ;
|
24410 | else if (property.startsWith('*')) {
|
24411 | type[property.substring(1)] = EVENT;
|
24412 | }
|
24413 | else if (property.startsWith('!')) {
|
24414 | type[property.substring(1)] = BOOLEAN$1;
|
24415 | }
|
24416 | else if (property.startsWith('#')) {
|
24417 | type[property.substring(1)] = NUMBER$1;
|
24418 | }
|
24419 | else if (property.startsWith('%')) {
|
24420 | type[property.substring(1)] = OBJECT$1;
|
24421 | }
|
24422 | else {
|
24423 | type[property] = STRING$1;
|
24424 | }
|
24425 | });
|
24426 | });
|
24427 | }
|
24428 | allKnownElements() {
|
24429 | return Object.keys(this.schema);
|
24430 | }
|
24431 | eventsOf(elementName) {
|
24432 | const elementType = this.schema[elementName.toLowerCase()] || {};
|
24433 | return Object.keys(elementType).filter(property => elementType[property] === EVENT);
|
24434 | }
|
24435 | propertiesOf(elementName) {
|
24436 | const elementType = this.schema[elementName.toLowerCase()] || {};
|
24437 | return Object.keys(elementType).filter(property => elementType[property] !== EVENT);
|
24438 | }
|
24439 | typeOf(elementName, property) {
|
24440 | return (this.schema[elementName.toLowerCase()] || {})[property];
|
24441 | }
|
24442 | static get instance() {
|
24443 | let result = SchemaInformation._instance;
|
24444 | if (!result) {
|
24445 | result = SchemaInformation._instance = new SchemaInformation();
|
24446 | }
|
24447 | return result;
|
24448 | }
|
24449 | }
|
24450 | function eventNames(elementName) {
|
24451 | return SchemaInformation.instance.eventsOf(elementName);
|
24452 | }
|
24453 | function propertyNames(elementName) {
|
24454 | return SchemaInformation.instance.propertiesOf(elementName);
|
24455 | }
|
24456 |
|
24457 | /**
|
24458 | * @license
|
24459 | * Copyright Google LLC All Rights Reserved.
|
24460 | *
|
24461 | * Use of this source code is governed by an MIT-style license that can be
|
24462 | * found in the LICENSE file at https://angular.io/license
|
24463 | */
|
24464 | const EMPTY_SYMBOL_TABLE = {
|
24465 | size: 0,
|
24466 | get: () => undefined,
|
24467 | has: () => false,
|
24468 | values: () => [],
|
24469 | };
|
24470 | /**
|
24471 | * A factory function that returns a symbol table that contains all global symbols
|
24472 | * available in an interpolation scope in a template.
|
24473 | * This function creates the table the first time it is called, and return a cached
|
24474 | * value for all subsequent calls.
|
24475 | */
|
24476 | const createGlobalSymbolTable = (function () {
|
24477 | let GLOBAL_SYMBOL_TABLE;
|
24478 | return function (query) {
|
24479 | if (GLOBAL_SYMBOL_TABLE) {
|
24480 | return GLOBAL_SYMBOL_TABLE;
|
24481 | }
|
24482 | GLOBAL_SYMBOL_TABLE = query.createSymbolTable([
|
24483 | // The `$any()` method casts the type of an expression to `any`.
|
24484 | // https://angular.io/guide/template-syntax#the-any-type-cast-function
|
24485 | {
|
24486 | name: '$any',
|
24487 | kind: 'method',
|
24488 | type: {
|
24489 | name: '$any',
|
24490 | kind: 'method',
|
24491 | type: undefined,
|
24492 | language: 'typescript',
|
24493 | container: undefined,
|
24494 | public: true,
|
24495 | callable: true,
|
24496 | definition: undefined,
|
24497 | nullable: false,
|
24498 | documentation: [{
|
24499 | kind: 'text',
|
24500 | text: 'function to cast an expression to the `any` type',
|
24501 | }],
|
24502 | members: () => EMPTY_SYMBOL_TABLE,
|
24503 | signatures: () => [],
|
24504 | selectSignature(args) {
|
24505 | if (args.length !== 1) {
|
24506 | return;
|
24507 | }
|
24508 | return {
|
24509 | arguments: EMPTY_SYMBOL_TABLE,
|
24510 | result: query.getBuiltinType(BuiltinType$1.Any),
|
24511 | };
|
24512 | },
|
24513 | indexed: () => undefined,
|
24514 | typeArguments: () => undefined,
|
24515 | },
|
24516 | },
|
24517 | ]);
|
24518 | return GLOBAL_SYMBOL_TABLE;
|
24519 | };
|
24520 | })();
|
24521 |
|
24522 | /**
|
24523 | * @license
|
24524 | * Copyright Google LLC All Rights Reserved.
|
24525 | *
|
24526 | * Use of this source code is governed by an MIT-style license that can be
|
24527 | * found in the LICENSE file at https://angular.io/license
|
24528 | */
|
24529 | // In TypeScript 2.1 these flags moved
|
24530 | // These helpers work for both 2.0 and 2.1.
|
24531 | const isPrivate = ts.ModifierFlags ?
|
24532 | ((node) => !!(ts.getCombinedModifierFlags(node) & ts.ModifierFlags.Private)) :
|
24533 | ((node) => !!(node.flags & ts.NodeFlags.Private));
|
24534 | const isReferenceType = ts.ObjectFlags ?
|
24535 | ((type) => !!(type.flags & ts.TypeFlags.Object &&
|
24536 | type.objectFlags & ts.ObjectFlags.Reference)) :
|
24537 | ((type) => !!(type.flags & ts.TypeFlags.Reference));
|
24538 | function getSymbolQuery(program, checker, source, fetchPipes) {
|
24539 | return new TypeScriptSymbolQuery(program, checker, source, fetchPipes);
|
24540 | }
|
24541 | function getClassMembersFromDeclaration(program, checker, source, declaration) {
|
24542 | const type = checker.getTypeAtLocation(declaration);
|
24543 | return new TypeWrapper(type, { node: source, program, checker }).members();
|
24544 | }
|
24545 | function getPipesTable(source, program, checker, pipes) {
|
24546 | return new PipesTable(pipes, { program, checker, node: source });
|
24547 | }
|
24548 | class TypeScriptSymbolQuery {
|
24549 | constructor(program, checker, source, fetchPipes) {
|
24550 | this.program = program;
|
24551 | this.checker = checker;
|
24552 | this.source = source;
|
24553 | this.fetchPipes = fetchPipes;
|
24554 | this.typeCache = new Map();
|
24555 | }
|
24556 | getTypeKind(symbol) {
|
24557 | const type = symbol instanceof TypeWrapper ? symbol.tsType : undefined;
|
24558 | return typeKindOf(type);
|
24559 | }
|
24560 | getBuiltinType(kind) {
|
24561 | let result = this.typeCache.get(kind);
|
24562 | if (!result) {
|
24563 | const type = getTsTypeFromBuiltinType(kind, {
|
24564 | checker: this.checker,
|
24565 | node: this.source,
|
24566 | program: this.program,
|
24567 | });
|
24568 | result =
|
24569 | new TypeWrapper(type, { program: this.program, checker: this.checker, node: this.source });
|
24570 | this.typeCache.set(kind, result);
|
24571 | }
|
24572 | return result;
|
24573 | }
|
24574 | getTypeUnion(...types) {
|
24575 | // No API exists so return any if the types are not all the same type.
|
24576 | let result = undefined;
|
24577 | if (types.length) {
|
24578 | result = types[0];
|
24579 | for (let i = 1; i < types.length; i++) {
|
24580 | if (types[i] != result) {
|
24581 | result = undefined;
|
24582 | break;
|
24583 | }
|
24584 | }
|
24585 | }
|
24586 | return result || this.getBuiltinType(BuiltinType$1.Any);
|
24587 | }
|
24588 | getArrayType(_type) {
|
24589 | return this.getBuiltinType(BuiltinType$1.Any);
|
24590 | }
|
24591 | getElementType(type) {
|
24592 | if (type instanceof TypeWrapper) {
|
24593 | const ty = type.tsType;
|
24594 | const tyArgs = type.typeArguments();
|
24595 | // TODO(ayazhafiz): Track https://github.com/microsoft/TypeScript/issues/37711 to expose
|
24596 | // `isArrayLikeType` as a public method.
|
24597 | if (!this.checker.isArrayLikeType(ty) || (tyArgs === null || tyArgs === void 0 ? void 0 : tyArgs.length) !== 1)
|
24598 | return;
|
24599 | return tyArgs[0];
|
24600 | }
|
24601 | }
|
24602 | getNonNullableType(symbol) {
|
24603 | if (symbol instanceof TypeWrapper && (typeof this.checker.getNonNullableType == 'function')) {
|
24604 | const tsType = symbol.tsType;
|
24605 | const nonNullableType = this.checker.getNonNullableType(tsType);
|
24606 | if (nonNullableType != tsType) {
|
24607 | return new TypeWrapper(nonNullableType, symbol.context);
|
24608 | }
|
24609 | else if (nonNullableType == tsType) {
|
24610 | return symbol;
|
24611 | }
|
24612 | }
|
24613 | return this.getBuiltinType(BuiltinType$1.Any);
|
24614 | }
|
24615 | getPipes() {
|
24616 | let result = this.pipesCache;
|
24617 | if (!result) {
|
24618 | result = this.pipesCache = this.fetchPipes();
|
24619 | }
|
24620 | return result;
|
24621 | }
|
24622 | getTemplateContext(type) {
|
24623 | const context = { node: this.source, program: this.program, checker: this.checker };
|
24624 | const typeSymbol = findClassSymbolInContext(type, context);
|
24625 | if (typeSymbol) {
|
24626 | const contextType = this.getTemplateRefContextType(typeSymbol, context);
|
24627 | if (contextType)
|
24628 | return contextType.members();
|
24629 | }
|
24630 | }
|
24631 | getTypeSymbol(type) {
|
24632 | const context = { node: this.source, program: this.program, checker: this.checker };
|
24633 | const typeSymbol = findClassSymbolInContext(type, context);
|
24634 | return typeSymbol && new SymbolWrapper(typeSymbol, context);
|
24635 | }
|
24636 | createSymbolTable(symbols) {
|
24637 | const result = new MapSymbolTable();
|
24638 | result.addAll(symbols.map(s => new DeclaredSymbol(s)));
|
24639 | return result;
|
24640 | }
|
24641 | mergeSymbolTable(symbolTables) {
|
24642 | const result = new MapSymbolTable();
|
24643 | for (const symbolTable of symbolTables) {
|
24644 | result.addAll(symbolTable.values());
|
24645 | }
|
24646 | return result;
|
24647 | }
|
24648 | getSpanAt(line, column) {
|
24649 | return spanAt(this.source, line, column);
|
24650 | }
|
24651 | getTemplateRefContextType(typeSymbol, context) {
|
24652 | const type = this.checker.getTypeOfSymbolAtLocation(typeSymbol, this.source);
|
24653 | const constructor = type.symbol && type.symbol.members &&
|
24654 | getFromSymbolTable(type.symbol.members, '__constructor');
|
24655 | if (constructor) {
|
24656 | const constructorDeclaration = constructor.declarations[0];
|
24657 | for (const parameter of constructorDeclaration.parameters) {
|
24658 | const type = this.checker.getTypeAtLocation(parameter.type);
|
24659 | if (type.symbol.name == 'TemplateRef' && isReferenceType(type)) {
|
24660 | const typeWrapper = new TypeWrapper(type, context);
|
24661 | const typeArguments = typeWrapper.typeArguments();
|
24662 | if (typeArguments && typeArguments.length === 1) {
|
24663 | return typeArguments[0];
|
24664 | }
|
24665 | }
|
24666 | }
|
24667 | }
|
24668 | }
|
24669 | }
|
24670 | function typeCallable(type) {
|
24671 | const signatures = type.getCallSignatures();
|
24672 | return signatures && signatures.length != 0;
|
24673 | }
|
24674 | function signaturesOf(type, context) {
|
24675 | return type.getCallSignatures().map(s => new SignatureWrapper(s, context));
|
24676 | }
|
24677 | function selectSignature(type, context, types) {
|
24678 | // TODO: Do a better job of selecting the right signature. TypeScript does not currently support a
|
24679 | // Type Relationship API (see https://github.com/angular/vscode-ng-language-service/issues/143).
|
24680 | // Consider creating a TypeCheckBlock host in the language service that may also act as a
|
24681 | // scratchpad for type comparisons.
|
24682 | const signatures = type.getCallSignatures();
|
24683 | const passedInTypes = types.map(type => {
|
24684 | if (type instanceof TypeWrapper) {
|
24685 | return type.tsType;
|
24686 | }
|
24687 | });
|
24688 | // Try to select a matching signature in which all parameter types match.
|
24689 | // Note that this is just a best-effort approach, because we're checking for
|
24690 | // strict type equality rather than compatibility.
|
24691 | // For example, if the signature contains a ReadonlyArray<number> and the
|
24692 | // passed parameter type is an Array<number>, this will fail.
|
24693 | function allParameterTypesMatch(signature) {
|
24694 | const tc = context.checker;
|
24695 | return signature.getParameters().every((parameter, i) => {
|
24696 | const type = tc.getTypeOfSymbolAtLocation(parameter, parameter.valueDeclaration);
|
24697 | return type === passedInTypes[i];
|
24698 | });
|
24699 | }
|
24700 | const exactMatch = signatures.find(allParameterTypesMatch);
|
24701 | if (exactMatch) {
|
24702 | return new SignatureWrapper(exactMatch, context);
|
24703 | }
|
24704 | // If not, fallback to a naive selection
|
24705 | return signatures.length ? new SignatureWrapper(signatures[0], context) : undefined;
|
24706 | }
|
24707 | class TypeWrapper {
|
24708 | constructor(tsType, context) {
|
24709 | this.tsType = tsType;
|
24710 | this.context = context;
|
24711 | this.kind = 'type';
|
24712 | this.language = 'typescript';
|
24713 | this.type = undefined;
|
24714 | this.container = undefined;
|
24715 | this.public = true;
|
24716 | if (!tsType) {
|
24717 | throw Error('Internal: null type');
|
24718 | }
|
24719 | }
|
24720 | get name() {
|
24721 | return this.context.checker.typeToString(this.tsType);
|
24722 | }
|
24723 | get callable() {
|
24724 | return typeCallable(this.tsType);
|
24725 | }
|
24726 | get nullable() {
|
24727 | return this.context.checker.getNonNullableType(this.tsType) != this.tsType;
|
24728 | }
|
24729 | get documentation() {
|
24730 | const symbol = this.tsType.getSymbol();
|
24731 | if (!symbol) {
|
24732 | return [];
|
24733 | }
|
24734 | return symbol.getDocumentationComment(this.context.checker);
|
24735 | }
|
24736 | get definition() {
|
24737 | const symbol = this.tsType.getSymbol();
|
24738 | return symbol ? definitionFromTsSymbol(symbol) : undefined;
|
24739 | }
|
24740 | members() {
|
24741 | // Should call getApparentProperties() instead of getProperties() because
|
24742 | // the former includes properties on the base class whereas the latter does
|
24743 | // not. This provides properties like .bind(), .call(), .apply(), etc for
|
24744 | // functions.
|
24745 | return new SymbolTableWrapper(this.tsType.getApparentProperties(), this.context, this.tsType);
|
24746 | }
|
24747 | signatures() {
|
24748 | return signaturesOf(this.tsType, this.context);
|
24749 | }
|
24750 | selectSignature(types) {
|
24751 | return selectSignature(this.tsType, this.context, types);
|
24752 | }
|
24753 | indexed(type, value) {
|
24754 | if (!(type instanceof TypeWrapper))
|
24755 | return;
|
24756 | const typeKind = typeKindOf(type.tsType);
|
24757 | switch (typeKind) {
|
24758 | case BuiltinType$1.Number:
|
24759 | const nType = this.tsType.getNumberIndexType();
|
24760 | if (nType) {
|
24761 | // get the right tuple type by value, like 'var t: [number, string];'
|
24762 | if (nType.isUnion()) {
|
24763 | // return undefined if array index out of bound.
|
24764 | return nType.types[value] && new TypeWrapper(nType.types[value], this.context);
|
24765 | }
|
24766 | return new TypeWrapper(nType, this.context);
|
24767 | }
|
24768 | return undefined;
|
24769 | case BuiltinType$1.String:
|
24770 | const sType = this.tsType.getStringIndexType();
|
24771 | return sType && new TypeWrapper(sType, this.context);
|
24772 | }
|
24773 | }
|
24774 | typeArguments() {
|
24775 | if (!isReferenceType(this.tsType))
|
24776 | return;
|
24777 | const typeReference = this.tsType;
|
24778 | let typeArguments;
|
24779 | typeArguments = this.context.checker.getTypeArguments(typeReference);
|
24780 | if (!typeArguments)
|
24781 | return undefined;
|
24782 | return typeArguments.map(ta => new TypeWrapper(ta, this.context));
|
24783 | }
|
24784 | }
|
24785 | // If stringIndexType a primitive type(e.g. 'string'), the Symbol is undefined;
|
24786 | // and in AstType.resolvePropertyRead method, the Symbol.type should get the right type.
|
24787 | class StringIndexTypeWrapper extends TypeWrapper {
|
24788 | constructor() {
|
24789 | super(...arguments);
|
24790 | this.type = new TypeWrapper(this.tsType, this.context);
|
24791 | }
|
24792 | }
|
24793 | class SymbolWrapper {
|
24794 | constructor(symbol,
|
24795 | /** TypeScript type context of the symbol. */
|
24796 | context,
|
24797 | /**
|
24798 | * Type of the TypeScript symbol, if known. If not provided, the type of the symbol
|
24799 | * will be determined dynamically; see `SymbolWrapper#tsType`.
|
24800 | */
|
24801 | _tsType) {
|
24802 | this.context = context;
|
24803 | this._tsType = _tsType;
|
24804 | this.nullable = false;
|
24805 | this.language = 'typescript';
|
24806 | this.symbol = symbol && context && (symbol.flags & ts.SymbolFlags.Alias) ?
|
24807 | context.checker.getAliasedSymbol(symbol) :
|
24808 | symbol;
|
24809 | }
|
24810 | get name() {
|
24811 | return this.symbol.name;
|
24812 | }
|
24813 | get kind() {
|
24814 | return this.callable ? 'method' : 'property';
|
24815 | }
|
24816 | get type() {
|
24817 | return new TypeWrapper(this.tsType, this.context);
|
24818 | }
|
24819 | get container() {
|
24820 | return getContainerOf(this.symbol, this.context);
|
24821 | }
|
24822 | get public() {
|
24823 | // Symbols that are not explicitly made private are public.
|
24824 | return !isSymbolPrivate(this.symbol);
|
24825 | }
|
24826 | get callable() {
|
24827 | return typeCallable(this.tsType);
|
24828 | }
|
24829 | get definition() {
|
24830 | return definitionFromTsSymbol(this.symbol);
|
24831 | }
|
24832 | get documentation() {
|
24833 | return this.symbol.getDocumentationComment(this.context.checker);
|
24834 | }
|
24835 | members() {
|
24836 | if (!this._members) {
|
24837 | if ((this.symbol.flags & (ts.SymbolFlags.Class | ts.SymbolFlags.Interface)) != 0) {
|
24838 | const declaredType = this.context.checker.getDeclaredTypeOfSymbol(this.symbol);
|
24839 | const typeWrapper = new TypeWrapper(declaredType, this.context);
|
24840 | this._members = typeWrapper.members();
|
24841 | }
|
24842 | else {
|
24843 | this._members = new SymbolTableWrapper(this.symbol.members, this.context, this.tsType);
|
24844 | }
|
24845 | }
|
24846 | return this._members;
|
24847 | }
|
24848 | signatures() {
|
24849 | return signaturesOf(this.tsType, this.context);
|
24850 | }
|
24851 | selectSignature(types) {
|
24852 | return selectSignature(this.tsType, this.context, types);
|
24853 | }
|
24854 | indexed(_argument) {
|
24855 | return undefined;
|
24856 | }
|
24857 | typeArguments() {
|
24858 | return this.type.typeArguments();
|
24859 | }
|
24860 | get tsType() {
|
24861 | let type = this._tsType;
|
24862 | if (!type) {
|
24863 | type = this._tsType =
|
24864 | this.context.checker.getTypeOfSymbolAtLocation(this.symbol, this.context.node);
|
24865 | }
|
24866 | return type;
|
24867 | }
|
24868 | }
|
24869 | class DeclaredSymbol {
|
24870 | constructor(declaration) {
|
24871 | this.declaration = declaration;
|
24872 | this.language = 'ng-template';
|
24873 | this.nullable = false;
|
24874 | this.public = true;
|
24875 | }
|
24876 | get name() {
|
24877 | return this.declaration.name;
|
24878 | }
|
24879 | get kind() {
|
24880 | return this.declaration.kind;
|
24881 | }
|
24882 | get container() {
|
24883 | return undefined;
|
24884 | }
|
24885 | get type() {
|
24886 | return this.declaration.type;
|
24887 | }
|
24888 | get callable() {
|
24889 | return this.type.callable;
|
24890 | }
|
24891 | get definition() {
|
24892 | return this.declaration.definition;
|
24893 | }
|
24894 | get documentation() {
|
24895 | return this.declaration.type.documentation;
|
24896 | }
|
24897 | members() {
|
24898 | return this.type.members();
|
24899 | }
|
24900 | signatures() {
|
24901 | return this.type.signatures();
|
24902 | }
|
24903 | selectSignature(types) {
|
24904 | return this.type.selectSignature(types);
|
24905 | }
|
24906 | typeArguments() {
|
24907 | return this.type.typeArguments();
|
24908 | }
|
24909 | indexed(_argument) {
|
24910 | return undefined;
|
24911 | }
|
24912 | }
|
24913 | class SignatureWrapper {
|
24914 | constructor(signature, context) {
|
24915 | this.signature = signature;
|
24916 | this.context = context;
|
24917 | }
|
24918 | get arguments() {
|
24919 | return new SymbolTableWrapper(this.signature.getParameters(), this.context);
|
24920 | }
|
24921 | get result() {
|
24922 | return new TypeWrapper(this.signature.getReturnType(), this.context);
|
24923 | }
|
24924 | }
|
24925 | class SignatureResultOverride {
|
24926 | constructor(signature, resultType) {
|
24927 | this.signature = signature;
|
24928 | this.resultType = resultType;
|
24929 | }
|
24930 | get arguments() {
|
24931 | return this.signature.arguments;
|
24932 | }
|
24933 | get result() {
|
24934 | return this.resultType;
|
24935 | }
|
24936 | }
|
24937 | function toSymbolTableFactory(symbols) {
|
24938 | // ∀ Typescript version >= 2.2, `SymbolTable` is implemented as an ES6 `Map`
|
24939 | const result = new Map();
|
24940 | for (const symbol of symbols) {
|
24941 | result.set(symbol.name, symbol);
|
24942 | }
|
24943 | return result;
|
24944 | }
|
24945 | function toSymbols(symbolTable) {
|
24946 | if (!symbolTable)
|
24947 | return [];
|
24948 | const table = symbolTable;
|
24949 | if (typeof table.values === 'function') {
|
24950 | return Array.from(table.values());
|
24951 | }
|
24952 | const result = [];
|
24953 | const own = typeof table.hasOwnProperty === 'function' ?
|
24954 | (name) => table.hasOwnProperty(name) :
|
24955 | (name) => !!table[name];
|
24956 | for (const name in table) {
|
24957 | if (own(name)) {
|
24958 | result.push(table[name]);
|
24959 | }
|
24960 | }
|
24961 | return result;
|
24962 | }
|
24963 | class SymbolTableWrapper {
|
24964 | /**
|
24965 | * Creates a queryable table of symbols belonging to a TypeScript entity.
|
24966 | * @param symbols symbols to query belonging to the entity
|
24967 | * @param context program context
|
24968 | * @param type original TypeScript type of entity owning the symbols, if known
|
24969 | */
|
24970 | constructor(symbols, context, type) {
|
24971 | this.context = context;
|
24972 | symbols = symbols || [];
|
24973 | if (Array.isArray(symbols)) {
|
24974 | this.symbols = symbols;
|
24975 | this.symbolTable = toSymbolTableFactory(symbols);
|
24976 | }
|
24977 | else {
|
24978 | this.symbols = toSymbols(symbols);
|
24979 | this.symbolTable = symbols;
|
24980 | }
|
24981 | if (type) {
|
24982 | this.stringIndexType = type.getStringIndexType();
|
24983 | }
|
24984 | }
|
24985 | get size() {
|
24986 | return this.symbols.length;
|
24987 | }
|
24988 | get(key) {
|
24989 | const symbol = getFromSymbolTable(this.symbolTable, key);
|
24990 | if (symbol) {
|
24991 | return new SymbolWrapper(symbol, this.context);
|
24992 | }
|
24993 | if (this.stringIndexType) {
|
24994 | // If the key does not exist as an explicit symbol on the type, it may be accessing a string
|
24995 | // index signature using dot notation:
|
24996 | //
|
24997 | // const obj<T>: { [key: string]: T };
|
24998 | // obj.stringIndex // equivalent to obj['stringIndex'];
|
24999 | //
|
25000 | // In this case, return the type indexed by an arbitrary string key.
|
25001 | return new StringIndexTypeWrapper(this.stringIndexType, this.context);
|
25002 | }
|
25003 | return undefined;
|
25004 | }
|
25005 | has(key) {
|
25006 | const table = this.symbolTable;
|
25007 | return ((typeof table.has === 'function') ? table.has(key) : table[key] != null) ||
|
25008 | this.stringIndexType !== undefined;
|
25009 | }
|
25010 | values() {
|
25011 | return this.symbols.map(s => new SymbolWrapper(s, this.context));
|
25012 | }
|
25013 | }
|
25014 | class MapSymbolTable {
|
25015 | constructor() {
|
25016 | this.map = new Map();
|
25017 | this._values = [];
|
25018 | }
|
25019 | get size() {
|
25020 | return this.map.size;
|
25021 | }
|
25022 | get(key) {
|
25023 | return this.map.get(key);
|
25024 | }
|
25025 | add(symbol) {
|
25026 | if (this.map.has(symbol.name)) {
|
25027 | const previous = this.map.get(symbol.name);
|
25028 | this._values[this._values.indexOf(previous)] = symbol;
|
25029 | }
|
25030 | this.map.set(symbol.name, symbol);
|
25031 | this._values.push(symbol);
|
25032 | }
|
25033 | addAll(symbols) {
|
25034 | for (const symbol of symbols) {
|
25035 | this.add(symbol);
|
25036 | }
|
25037 | }
|
25038 | has(key) {
|
25039 | return this.map.has(key);
|
25040 | }
|
25041 | values() {
|
25042 | // Switch to this.map.values once iterables are supported by the target language.
|
25043 | return this._values;
|
25044 | }
|
25045 | }
|
25046 | class PipesTable {
|
25047 | constructor(pipes, context) {
|
25048 | this.pipes = pipes;
|
25049 | this.context = context;
|
25050 | }
|
25051 | get size() {
|
25052 | return this.pipes.length;
|
25053 | }
|
25054 | get(key) {
|
25055 | const pipe = this.pipes.find(pipe => pipe.name == key);
|
25056 | if (pipe) {
|
25057 | return new PipeSymbol(pipe, this.context);
|
25058 | }
|
25059 | }
|
25060 | has(key) {
|
25061 | return this.pipes.find(pipe => pipe.name == key) != null;
|
25062 | }
|
25063 | values() {
|
25064 | return this.pipes.map(pipe => new PipeSymbol(pipe, this.context));
|
25065 | }
|
25066 | }
|
25067 | // This matches .d.ts files that look like ".../<package-name>/<package-name>.d.ts",
|
25068 | const INDEX_PATTERN = /[\\/]([^\\/]+)[\\/]\1\.d\.ts$/;
|
25069 | class PipeSymbol {
|
25070 | constructor(pipe, context) {
|
25071 | this.pipe = pipe;
|
25072 | this.context = context;
|
25073 | this.kind = 'pipe';
|
25074 | this.language = 'typescript';
|
25075 | this.container = undefined;
|
25076 | this.callable = true;
|
25077 | this.nullable = false;
|
25078 | this.public = true;
|
25079 | }
|
25080 | get name() {
|
25081 | return this.pipe.name;
|
25082 | }
|
25083 | get type() {
|
25084 | return new TypeWrapper(this.tsType, this.context);
|
25085 | }
|
25086 | get definition() {
|
25087 | const symbol = this.tsType.getSymbol();
|
25088 | return symbol ? definitionFromTsSymbol(symbol) : undefined;
|
25089 | }
|
25090 | get documentation() {
|
25091 | const symbol = this.tsType.getSymbol();
|
25092 | if (!symbol) {
|
25093 | return [];
|
25094 | }
|
25095 | return symbol.getDocumentationComment(this.context.checker);
|
25096 | }
|
25097 | members() {
|
25098 | return EmptyTable.instance;
|
25099 | }
|
25100 | signatures() {
|
25101 | return signaturesOf(this.tsType, this.context);
|
25102 | }
|
25103 | selectSignature(types) {
|
25104 | let signature = selectSignature(this.tsType, this.context, types);
|
25105 | if (types.length > 0) {
|
25106 | const parameterType = types[0];
|
25107 | let resultType = undefined;
|
25108 | switch (this.name) {
|
25109 | case 'async':
|
25110 | // Get type argument of 'Observable', 'Promise', or 'EventEmitter'.
|
25111 | const tArgs = parameterType.typeArguments();
|
25112 | if (tArgs && tArgs.length === 1) {
|
25113 | resultType = tArgs[0];
|
25114 | }
|
25115 | break;
|
25116 | case 'slice':
|
25117 | resultType = parameterType;
|
25118 | break;
|
25119 | }
|
25120 | if (resultType) {
|
25121 | signature = new SignatureResultOverride(signature, resultType);
|
25122 | }
|
25123 | }
|
25124 | return signature;
|
25125 | }
|
25126 | indexed(_argument) {
|
25127 | return undefined;
|
25128 | }
|
25129 | typeArguments() {
|
25130 | return this.type.typeArguments();
|
25131 | }
|
25132 | get tsType() {
|
25133 | let type = this._tsType;
|
25134 | if (!type) {
|
25135 | const classSymbol = this.findClassSymbol(this.pipe.type.reference);
|
25136 | if (classSymbol) {
|
25137 | type = this._tsType = this.findTransformMethodType(classSymbol);
|
25138 | }
|
25139 | if (!type) {
|
25140 | type = this._tsType = getTsTypeFromBuiltinType(BuiltinType$1.Any, this.context);
|
25141 | }
|
25142 | }
|
25143 | return type;
|
25144 | }
|
25145 | findClassSymbol(type) {
|
25146 | return findClassSymbolInContext(type, this.context);
|
25147 | }
|
25148 | findTransformMethodType(classSymbol) {
|
25149 | const classType = this.context.checker.getDeclaredTypeOfSymbol(classSymbol);
|
25150 | if (classType) {
|
25151 | const transform = classType.getProperty('transform');
|
25152 | if (transform) {
|
25153 | return this.context.checker.getTypeOfSymbolAtLocation(transform, this.context.node);
|
25154 | }
|
25155 | }
|
25156 | }
|
25157 | }
|
25158 | function findClassSymbolInContext(type, context) {
|
25159 | let sourceFile = context.program.getSourceFile(type.filePath);
|
25160 | if (!sourceFile) {
|
25161 | // This handles a case where an <packageName>/index.d.ts and a <packageName>/<packageName>.d.ts
|
25162 | // are in the same directory. If we are looking for <packageName>/<packageName> and didn't
|
25163 | // find it, look for <packageName>/index.d.ts as the program might have found that instead.
|
25164 | const p = type.filePath;
|
25165 | const m = p.match(INDEX_PATTERN);
|
25166 | if (m) {
|
25167 | const indexVersion = path.join(path.dirname(p), 'index.d.ts');
|
25168 | sourceFile = context.program.getSourceFile(indexVersion);
|
25169 | }
|
25170 | }
|
25171 | if (sourceFile) {
|
25172 | const moduleSymbol = sourceFile.module || sourceFile.symbol;
|
25173 | const exports = context.checker.getExportsOfModule(moduleSymbol);
|
25174 | return (exports || []).find(symbol => symbol.name == type.name);
|
25175 | }
|
25176 | }
|
25177 | class EmptyTable {
|
25178 | constructor() {
|
25179 | this.size = 0;
|
25180 | }
|
25181 | get(_key) {
|
25182 | return undefined;
|
25183 | }
|
25184 | has(_key) {
|
25185 | return false;
|
25186 | }
|
25187 | values() {
|
25188 | return [];
|
25189 | }
|
25190 | }
|
25191 | EmptyTable.instance = new EmptyTable();
|
25192 | function isSymbolPrivate(s) {
|
25193 | return !!s.valueDeclaration && isPrivate(s.valueDeclaration);
|
25194 | }
|
25195 | function getTsTypeFromBuiltinType(builtinType, ctx) {
|
25196 | let syntaxKind;
|
25197 | switch (builtinType) {
|
25198 | case BuiltinType$1.Any:
|
25199 | syntaxKind = ts.SyntaxKind.AnyKeyword;
|
25200 | break;
|
25201 | case BuiltinType$1.Boolean:
|
25202 | syntaxKind = ts.SyntaxKind.BooleanKeyword;
|
25203 | break;
|
25204 | case BuiltinType$1.Null:
|
25205 | syntaxKind = ts.SyntaxKind.NullKeyword;
|
25206 | break;
|
25207 | case BuiltinType$1.Number:
|
25208 | syntaxKind = ts.SyntaxKind.NumberKeyword;
|
25209 | break;
|
25210 | case BuiltinType$1.String:
|
25211 | syntaxKind = ts.SyntaxKind.StringKeyword;
|
25212 | break;
|
25213 | case BuiltinType$1.Undefined:
|
25214 | syntaxKind = ts.SyntaxKind.UndefinedKeyword;
|
25215 | break;
|
25216 | default:
|
25217 | throw new Error(`Internal error, unhandled literal kind ${builtinType}:${BuiltinType$1[builtinType]}`);
|
25218 | }
|
25219 | const node = ts.createNode(syntaxKind);
|
25220 | node.parent = ts.createEmptyStatement();
|
25221 | return ctx.checker.getTypeAtLocation(node);
|
25222 | }
|
25223 | function spanAt(sourceFile, line, column) {
|
25224 | if (line != null && column != null) {
|
25225 | const position = ts.getPositionOfLineAndCharacter(sourceFile, line, column);
|
25226 | const findChild = function findChild(node) {
|
25227 | if (node.kind > ts.SyntaxKind.LastToken && node.pos <= position && node.end > position) {
|
25228 | const betterNode = ts.forEachChild(node, findChild);
|
25229 | return betterNode || node;
|
25230 | }
|
25231 | };
|
25232 | const node = ts.forEachChild(sourceFile, findChild);
|
25233 | if (node) {
|
25234 | return { start: node.getStart(), end: node.getEnd() };
|
25235 | }
|
25236 | }
|
25237 | }
|
25238 | function definitionFromTsSymbol(symbol) {
|
25239 | const declarations = symbol.declarations;
|
25240 | if (declarations) {
|
25241 | return declarations.map(declaration => {
|
25242 | const sourceFile = declaration.getSourceFile();
|
25243 | return {
|
25244 | fileName: sourceFile.fileName,
|
25245 | span: { start: declaration.getStart(), end: declaration.getEnd() }
|
25246 | };
|
25247 | });
|
25248 | }
|
25249 | }
|
25250 | function parentDeclarationOf(node) {
|
25251 | while (node) {
|
25252 | switch (node.kind) {
|
25253 | case ts.SyntaxKind.ClassDeclaration:
|
25254 | case ts.SyntaxKind.InterfaceDeclaration:
|
25255 | return node;
|
25256 | case ts.SyntaxKind.SourceFile:
|
25257 | return undefined;
|
25258 | }
|
25259 | node = node.parent;
|
25260 | }
|
25261 | }
|
25262 | function getContainerOf(symbol, context) {
|
25263 | if (symbol.getFlags() & ts.SymbolFlags.ClassMember && symbol.declarations) {
|
25264 | for (const declaration of symbol.declarations) {
|
25265 | const parent = parentDeclarationOf(declaration);
|
25266 | if (parent) {
|
25267 | const type = context.checker.getTypeAtLocation(parent);
|
25268 | if (type) {
|
25269 | return new TypeWrapper(type, context);
|
25270 | }
|
25271 | }
|
25272 | }
|
25273 | }
|
25274 | }
|
25275 | function typeKindOf(type) {
|
25276 | if (type) {
|
25277 | if (type.flags & ts.TypeFlags.Any) {
|
25278 | return BuiltinType$1.Any;
|
25279 | }
|
25280 | else if (type.flags & (ts.TypeFlags.String | ts.TypeFlags.StringLike | ts.TypeFlags.StringLiteral)) {
|
25281 | return BuiltinType$1.String;
|
25282 | }
|
25283 | else if (type.flags & (ts.TypeFlags.Number | ts.TypeFlags.NumberLike)) {
|
25284 | return BuiltinType$1.Number;
|
25285 | }
|
25286 | else if (type.flags & ts.TypeFlags.Object) {
|
25287 | return BuiltinType$1.Object;
|
25288 | }
|
25289 | else if (type.flags & (ts.TypeFlags.Undefined)) {
|
25290 | return BuiltinType$1.Undefined;
|
25291 | }
|
25292 | else if (type.flags & (ts.TypeFlags.Null)) {
|
25293 | return BuiltinType$1.Null;
|
25294 | }
|
25295 | else if (type.flags & ts.TypeFlags.Union) {
|
25296 | const unionType = type;
|
25297 | if (unionType.types.length === 0)
|
25298 | return BuiltinType$1.Other;
|
25299 | let ty = 0;
|
25300 | for (const subType of unionType.types) {
|
25301 | ty |= typeKindOf(subType);
|
25302 | }
|
25303 | return ty;
|
25304 | }
|
25305 | else if (type.flags & ts.TypeFlags.TypeParameter) {
|
25306 | return BuiltinType$1.Unbound;
|
25307 | }
|
25308 | }
|
25309 | return BuiltinType$1.Other;
|
25310 | }
|
25311 | function getFromSymbolTable(symbolTable, key) {
|
25312 | const table = symbolTable;
|
25313 | let symbol;
|
25314 | if (typeof table.get === 'function') {
|
25315 | // TS 2.2 uses a Map
|
25316 | symbol = table.get(key);
|
25317 | }
|
25318 | else {
|
25319 | // TS pre-2.2 uses an object
|
25320 | symbol = table[key];
|
25321 | }
|
25322 | return symbol;
|
25323 | }
|
25324 |
|
25325 | /**
|
25326 | * @license
|
25327 | * Copyright Google LLC All Rights Reserved.
|
25328 | *
|
25329 | * Use of this source code is governed by an MIT-style license that can be
|
25330 | * found in the LICENSE file at https://angular.io/license
|
25331 | */
|
25332 | /**
|
25333 | * A base class to represent a template and which component class it is
|
25334 | * associated with. A template source could answer basic questions about
|
25335 | * top-level declarations of its class through the members() and query()
|
25336 | * methods.
|
25337 | */
|
25338 | class BaseTemplate {
|
25339 | constructor(host, classDeclNode, classSymbol) {
|
25340 | this.host = host;
|
25341 | this.classDeclNode = classDeclNode;
|
25342 | this.classSymbol = classSymbol;
|
25343 | this.program = host.program;
|
25344 | }
|
25345 | /**
|
25346 | * Return the Angular StaticSymbol for the class that contains this template.
|
25347 | */
|
25348 | get type() {
|
25349 | return this.classSymbol;
|
25350 | }
|
25351 | /**
|
25352 | * Return a Map-like data structure that allows users to retrieve some or all
|
25353 | * top-level declarations in the associated component class.
|
25354 | */
|
25355 | get members() {
|
25356 | if (!this.membersTable) {
|
25357 | const typeChecker = this.program.getTypeChecker();
|
25358 | const sourceFile = this.classDeclNode.getSourceFile();
|
25359 | this.membersTable = this.query.mergeSymbolTable([
|
25360 | createGlobalSymbolTable(this.query),
|
25361 | getClassMembersFromDeclaration(this.program, typeChecker, sourceFile, this.classDeclNode),
|
25362 | ]);
|
25363 | }
|
25364 | return this.membersTable;
|
25365 | }
|
25366 | /**
|
25367 | * Return an engine that provides more information about symbols in the
|
25368 | * template.
|
25369 | */
|
25370 | get query() {
|
25371 | if (!this.queryCache) {
|
25372 | const program = this.program;
|
25373 | const typeChecker = program.getTypeChecker();
|
25374 | const sourceFile = this.classDeclNode.getSourceFile();
|
25375 | this.queryCache = getSymbolQuery(program, typeChecker, sourceFile, () => {
|
25376 | // Computing the ast is relatively expensive. Do it only when absolutely
|
25377 | // necessary.
|
25378 | // TODO: There is circular dependency here between TemplateSource and
|
25379 | // TypeScriptHost. Consider refactoring the code to break this cycle.
|
25380 | const ast = this.host.getTemplateAst(this);
|
25381 | const pipes = (ast && ast.pipes) || [];
|
25382 | return getPipesTable(sourceFile, program, typeChecker, pipes);
|
25383 | });
|
25384 | }
|
25385 | return this.queryCache;
|
25386 | }
|
25387 | }
|
25388 | /**
|
25389 | * An InlineTemplate represents template defined in a TS file through the
|
25390 | * `template` attribute in the decorator.
|
25391 | */
|
25392 | class InlineTemplate extends BaseTemplate {
|
25393 | constructor(templateNode, classDeclNode, classSymbol, host) {
|
25394 | super(host, classDeclNode, classSymbol);
|
25395 | const sourceFile = templateNode.getSourceFile();
|
25396 | if (sourceFile !== classDeclNode.getSourceFile()) {
|
25397 | throw new Error(`Inline template and component class should belong to the same source file`);
|
25398 | }
|
25399 | this.fileName = sourceFile.fileName;
|
25400 | // node.text returns the TS internal representation of the normalized text,
|
25401 | // and all CR characters are stripped. node.getText() returns the raw text.
|
25402 | this.source = templateNode.getText().slice(1, -1); // strip leading and trailing quotes
|
25403 | this.span = {
|
25404 | // TS string literal includes surrounding quotes in the start/end offsets.
|
25405 | start: templateNode.getStart() + 1,
|
25406 | end: templateNode.getEnd() - 1,
|
25407 | };
|
25408 | }
|
25409 | }
|
25410 | /**
|
25411 | * An ExternalTemplate represents template defined in an external (most likely
|
25412 | * HTML, but not necessarily) file through the `templateUrl` attribute in the
|
25413 | * decorator.
|
25414 | * Note that there is no ts.Node associated with the template because it's not
|
25415 | * a TS file.
|
25416 | */
|
25417 | class ExternalTemplate extends BaseTemplate {
|
25418 | constructor(source, fileName, classDeclNode, classSymbol, host) {
|
25419 | super(host, classDeclNode, classSymbol);
|
25420 | this.source = source;
|
25421 | this.fileName = fileName;
|
25422 | this.span = {
|
25423 | start: 0,
|
25424 | end: source.length,
|
25425 | };
|
25426 | }
|
25427 | }
|
25428 |
|
25429 | /**
|
25430 | * @license
|
25431 | * Copyright Google LLC All Rights Reserved.
|
25432 | *
|
25433 | * Use of this source code is governed by an MIT-style license that can be
|
25434 | * found in the LICENSE file at https://angular.io/license
|
25435 | */
|
25436 | const HIDDEN_HTML_ELEMENTS = new Set(['html', 'script', 'noscript', 'base', 'body', 'title', 'head', 'link']);
|
25437 | const HTML_ELEMENTS = elementNames().filter(name => !HIDDEN_HTML_ELEMENTS.has(name)).map(name => {
|
25438 | return {
|
25439 | name,
|
25440 | kind: CompletionKind.HTML_ELEMENT,
|
25441 | sortText: name,
|
25442 | };
|
25443 | });
|
25444 | const ANGULAR_ELEMENTS = [
|
25445 | {
|
25446 | name: 'ng-container',
|
25447 | kind: CompletionKind.ANGULAR_ELEMENT,
|
25448 | sortText: 'ng-container',
|
25449 | },
|
25450 | {
|
25451 | name: 'ng-content',
|
25452 | kind: CompletionKind.ANGULAR_ELEMENT,
|
25453 | sortText: 'ng-content',
|
25454 | },
|
25455 | {
|
25456 | name: 'ng-template',
|
25457 | kind: CompletionKind.ANGULAR_ELEMENT,
|
25458 | sortText: 'ng-template',
|
25459 | },
|
25460 | ];
|
25461 | function isIdentifierPart$1(code) {
|
25462 | // Identifiers consist of alphanumeric characters, '_', or '$'.
|
25463 | return isAsciiLetter(code) || isDigit(code) || code == $$ || code == $_;
|
25464 | }
|
25465 | /**
|
25466 | * Gets the span of word in a template that surrounds `position`. If there is no word around
|
25467 | * `position`, nothing is returned.
|
25468 | */
|
25469 | function getBoundedWordSpan(templateInfo, position, ast) {
|
25470 | const { template } = templateInfo;
|
25471 | const templateSrc = template.source;
|
25472 | if (!templateSrc)
|
25473 | return;
|
25474 | if (ast instanceof Element$1) {
|
25475 | // The HTML tag may include `-` (e.g. `app-root`),
|
25476 | // so use the HtmlAst to get the span before ayazhafiz refactor the code.
|
25477 | return {
|
25478 | start: templateInfo.template.span.start + ast.startSourceSpan.start.offset + 1,
|
25479 | length: ast.name.length
|
25480 | };
|
25481 | }
|
25482 | // TODO(ayazhafiz): A solution based on word expansion will always be expensive compared to one
|
25483 | // based on ASTs. Whatever penalty we incur is probably manageable for small-length (i.e. the
|
25484 | // majority of) identifiers, but the current solution involes a number of branchings and we can't
|
25485 | // control potentially very long identifiers. Consider moving to an AST-based solution once
|
25486 | // existing difficulties with AST spans are more clearly resolved (see #31898 for discussion of
|
25487 | // known problems, and #33091 for how they affect text replacement).
|
25488 | //
|
25489 | // `templatePosition` represents the right-bound location of a cursor in the template.
|
25490 | // key.ent|ry
|
25491 | // ^---- cursor, at position `r` is at.
|
25492 | // A cursor is not itself a character in the template; it has a left (lower) and right (upper)
|
25493 | // index bound that hugs the cursor itself.
|
25494 | let templatePosition = position - template.span.start;
|
25495 | // To perform word expansion, we want to determine the left and right indices that hug the cursor.
|
25496 | // There are three cases here.
|
25497 | let left, right;
|
25498 | if (templatePosition === 0) {
|
25499 | // 1. Case like
|
25500 | // |rest of template
|
25501 | // the cursor is at the start of the template, hugged only by the right side (0-index).
|
25502 | left = right = 0;
|
25503 | }
|
25504 | else if (templatePosition === templateSrc.length) {
|
25505 | // 2. Case like
|
25506 | // rest of template|
|
25507 | // the cursor is at the end of the template, hugged only by the left side (last-index).
|
25508 | left = right = templateSrc.length - 1;
|
25509 | }
|
25510 | else {
|
25511 | // 3. Case like
|
25512 | // wo|rd
|
25513 | // there is a clear left and right index.
|
25514 | left = templatePosition - 1;
|
25515 | right = templatePosition;
|
25516 | }
|
25517 | if (!isIdentifierPart$1(templateSrc.charCodeAt(left)) &&
|
25518 | !isIdentifierPart$1(templateSrc.charCodeAt(right))) {
|
25519 | // Case like
|
25520 | // .|.
|
25521 | // left ---^ ^--- right
|
25522 | // There is no word here.
|
25523 | return;
|
25524 | }
|
25525 | // Expand on the left and right side until a word boundary is hit. Back up one expansion on both
|
25526 | // side to stay inside the word.
|
25527 | while (left >= 0 && isIdentifierPart$1(templateSrc.charCodeAt(left)))
|
25528 | --left;
|
25529 | ++left;
|
25530 | while (right < templateSrc.length && isIdentifierPart$1(templateSrc.charCodeAt(right)))
|
25531 | ++right;
|
25532 | --right;
|
25533 | const absoluteStartPosition = position - (templatePosition - left);
|
25534 | const length = right - left + 1;
|
25535 | return { start: absoluteStartPosition, length };
|
25536 | }
|
25537 | function getTemplateCompletions(templateInfo, position) {
|
25538 | const { htmlAst, template } = templateInfo;
|
25539 | // Calculate the position relative to the start of the template. This is needed
|
25540 | // because spans in HTML AST are relative. Inline template has non-zero start position.
|
25541 | const templatePosition = position - template.span.start;
|
25542 | const htmlPath = getPathToNodeAtPosition(htmlAst, templatePosition);
|
25543 | const mostSpecific = htmlPath.tail;
|
25544 | const visitor = new HtmlVisitor(templateInfo, htmlPath);
|
25545 | const results = mostSpecific ?
|
25546 | mostSpecific.visit(visitor, null /* context */) :
|
25547 | elementCompletions(templateInfo);
|
25548 | const replacementSpan = getBoundedWordSpan(templateInfo, position, mostSpecific);
|
25549 | return results.map(entry => {
|
25550 | return Object.assign(Object.assign({}, entry), { replacementSpan });
|
25551 | });
|
25552 | }
|
25553 | class HtmlVisitor {
|
25554 | constructor(templateInfo, htmlPath) {
|
25555 | this.templateInfo = templateInfo;
|
25556 | this.htmlPath = htmlPath;
|
25557 | this.relativePosition = htmlPath.position;
|
25558 | }
|
25559 | // Note that every visitor method must explicitly specify return type because
|
25560 | // Visitor returns `any` for all methods.
|
25561 | visitElement(ast) {
|
25562 | const startTagSpan = spanOf(ast.sourceSpan);
|
25563 | const tagLen = ast.name.length;
|
25564 | // + 1 for the opening angle bracket
|
25565 | if (this.relativePosition <= startTagSpan.start + tagLen + 1) {
|
25566 | // If we are in the tag then return the element completions.
|
25567 | return elementCompletions(this.templateInfo);
|
25568 | }
|
25569 | if (this.relativePosition < startTagSpan.end) {
|
25570 | // We are in the attribute section of the element (but not in an attribute).
|
25571 | // Return the attribute completions.
|
25572 | return attributeCompletionsForElement(this.templateInfo, ast.name);
|
25573 | }
|
25574 | return [];
|
25575 | }
|
25576 | visitAttribute(ast) {
|
25577 | // An attribute consists of two parts, LHS="RHS".
|
25578 | // Determine if completions are requested for LHS or RHS
|
25579 | if (ast.valueSpan && inSpan(this.relativePosition, spanOf(ast.valueSpan))) {
|
25580 | // RHS completion
|
25581 | return attributeValueCompletions(this.templateInfo, this.htmlPath);
|
25582 | }
|
25583 | // LHS completion
|
25584 | return attributeCompletions(this.templateInfo, this.htmlPath);
|
25585 | }
|
25586 | visitText() {
|
25587 | var _a;
|
25588 | const templatePath = findTemplateAstAt(this.templateInfo.templateAst, this.relativePosition);
|
25589 | if (templatePath.tail instanceof BoundTextAst) {
|
25590 | // If we know that this is an interpolation then do not try other scenarios.
|
25591 | const visitor = new ExpressionVisitor(this.templateInfo, this.relativePosition, () => getExpressionScope(diagnosticInfoFromTemplateInfo(this.templateInfo), templatePath));
|
25592 | (_a = templatePath.tail) === null || _a === void 0 ? void 0 : _a.visit(visitor, null);
|
25593 | return visitor.results;
|
25594 | }
|
25595 | // TODO(kyliau): Not sure if this check is really needed since we don't have
|
25596 | // any test cases for it.
|
25597 | const element = this.htmlPath.first(Element$1);
|
25598 | if (element &&
|
25599 | getHtmlTagDefinition(element.name).getContentType() !== TagContentType.PARSABLE_DATA) {
|
25600 | return [];
|
25601 | }
|
25602 | // This is to account for cases like <h1> <a> text | </h1> where the
|
25603 | // closest element has no closing tag and thus is considered plain text.
|
25604 | const results = voidElementAttributeCompletions(this.templateInfo, this.htmlPath);
|
25605 | if (results.length) {
|
25606 | return results;
|
25607 | }
|
25608 | return elementCompletions(this.templateInfo);
|
25609 | }
|
25610 | visitComment() {
|
25611 | return [];
|
25612 | }
|
25613 | visitExpansion() {
|
25614 | return [];
|
25615 | }
|
25616 | visitExpansionCase() {
|
25617 | return [];
|
25618 | }
|
25619 | }
|
25620 | function attributeCompletions(info, path) {
|
25621 | const attr = path.tail;
|
25622 | const elem = path.parentOf(attr);
|
25623 | if (!(attr instanceof Attribute) || !(elem instanceof Element$1)) {
|
25624 | return [];
|
25625 | }
|
25626 | // TODO: Consider parsing the attrinute name to a proper AST instead of
|
25627 | // matching using regex. This is because the regexp would incorrectly identify
|
25628 | // bind parts for cases like [()|]
|
25629 | // ^ cursor is here
|
25630 | const binding = getBindingDescriptor(attr.name);
|
25631 | if (!binding) {
|
25632 | // This is a normal HTML attribute, not an Angular attribute.
|
25633 | return attributeCompletionsForElement(info, elem.name);
|
25634 | }
|
25635 | const results = [];
|
25636 | const ngAttrs = angularAttributes(info, elem.name);
|
25637 | switch (binding.kind) {
|
25638 | case ATTR.KW_MICROSYNTAX:
|
25639 | // template reference attribute: *attrName
|
25640 | results.push(...ngAttrs.templateRefs);
|
25641 | break;
|
25642 | case ATTR.KW_BIND:
|
25643 | case ATTR.IDENT_PROPERTY:
|
25644 | // property binding via bind- or []
|
25645 | results.push(...propertyNames(elem.name), ...ngAttrs.inputs);
|
25646 | break;
|
25647 | case ATTR.KW_ON:
|
25648 | case ATTR.IDENT_EVENT:
|
25649 | // event binding via on- or ()
|
25650 | results.push(...eventNames(elem.name), ...ngAttrs.outputs);
|
25651 | break;
|
25652 | case ATTR.KW_BINDON:
|
25653 | case ATTR.IDENT_BANANA_BOX:
|
25654 | // banana-in-a-box binding via bindon- or [()]
|
25655 | results.push(...ngAttrs.bananas);
|
25656 | break;
|
25657 | }
|
25658 | return results.map(name => {
|
25659 | return {
|
25660 | name,
|
25661 | kind: CompletionKind.ATTRIBUTE,
|
25662 | sortText: name,
|
25663 | };
|
25664 | });
|
25665 | }
|
25666 | function attributeCompletionsForElement(info, elementName) {
|
25667 | const results = [];
|
25668 | if (info.template instanceof InlineTemplate) {
|
25669 | // Provide HTML attributes completion only for inline templates
|
25670 | for (const name of attributeNames(elementName)) {
|
25671 | results.push({
|
25672 | name,
|
25673 | kind: CompletionKind.HTML_ATTRIBUTE,
|
25674 | sortText: name,
|
25675 | });
|
25676 | }
|
25677 | }
|
25678 | // Add Angular attributes
|
25679 | const ngAttrs = angularAttributes(info, elementName);
|
25680 | for (const name of ngAttrs.others) {
|
25681 | results.push({
|
25682 | name,
|
25683 | kind: CompletionKind.ATTRIBUTE,
|
25684 | sortText: name,
|
25685 | });
|
25686 | }
|
25687 | return results;
|
25688 | }
|
25689 | /**
|
25690 | * Provide completions to the RHS of an attribute, which is of the form
|
25691 | * LHS="RHS". The template path is computed from the specified `info` whereas
|
25692 | * the context is determined from the specified `htmlPath`.
|
25693 | * @param info Object that contains the template AST
|
25694 | * @param htmlPath Path to the HTML node
|
25695 | */
|
25696 | function attributeValueCompletions(info, htmlPath) {
|
25697 | // Find the corresponding Template AST path.
|
25698 | const templatePath = findTemplateAstAt(info.templateAst, htmlPath.position);
|
25699 | const visitor = new ExpressionVisitor(info, htmlPath.position, () => {
|
25700 | const dinfo = diagnosticInfoFromTemplateInfo(info);
|
25701 | return getExpressionScope(dinfo, templatePath);
|
25702 | });
|
25703 | if (templatePath.tail instanceof AttrAst ||
|
25704 | templatePath.tail instanceof BoundElementPropertyAst ||
|
25705 | templatePath.tail instanceof BoundEventAst) {
|
25706 | templatePath.tail.visit(visitor, null);
|
25707 | return visitor.results;
|
25708 | }
|
25709 | // In order to provide accurate attribute value completion, we need to know
|
25710 | // what the LHS is, and construct the proper AST if it is missing.
|
25711 | const htmlAttr = htmlPath.tail;
|
25712 | const binding = getBindingDescriptor(htmlAttr.name);
|
25713 | if (binding && binding.kind === ATTR.KW_REF) {
|
25714 | let refAst;
|
25715 | let elemAst;
|
25716 | if (templatePath.tail instanceof ReferenceAst) {
|
25717 | refAst = templatePath.tail;
|
25718 | const parent = templatePath.parentOf(refAst);
|
25719 | if (parent instanceof ElementAst) {
|
25720 | elemAst = parent;
|
25721 | }
|
25722 | }
|
25723 | else if (templatePath.tail instanceof ElementAst) {
|
25724 | refAst = new ReferenceAst(htmlAttr.name, null, htmlAttr.value, htmlAttr.valueSpan);
|
25725 | elemAst = templatePath.tail;
|
25726 | }
|
25727 | if (refAst && elemAst) {
|
25728 | refAst.visit(visitor, elemAst);
|
25729 | }
|
25730 | }
|
25731 | else {
|
25732 | // HtmlAst contains the `Attribute` node, however the corresponding `AttrAst`
|
25733 | // node is missing from the TemplateAst.
|
25734 | const attrAst = new AttrAst(htmlAttr.name, htmlAttr.value, htmlAttr.valueSpan);
|
25735 | attrAst.visit(visitor, null);
|
25736 | }
|
25737 | return visitor.results;
|
25738 | }
|
25739 | function elementCompletions(info) {
|
25740 | const results = [...ANGULAR_ELEMENTS];
|
25741 | if (info.template instanceof InlineTemplate) {
|
25742 | // Provide HTML elements completion only for inline templates
|
25743 | results.push(...HTML_ELEMENTS);
|
25744 | }
|
25745 | // Collect the elements referenced by the selectors
|
25746 | const components = new Set();
|
25747 | for (const selector of getSelectors(info).selectors) {
|
25748 | const name = selector.element;
|
25749 | if (name && !components.has(name)) {
|
25750 | components.add(name);
|
25751 | results.push({
|
25752 | name,
|
25753 | kind: CompletionKind.COMPONENT,
|
25754 | sortText: name,
|
25755 | });
|
25756 | }
|
25757 | }
|
25758 | return results;
|
25759 | }
|
25760 | // There is a special case of HTML where text that contains a unclosed tag is treated as
|
25761 | // text. For exaple '<h1> Some <a text </h1>' produces a text nodes inside of the H1
|
25762 | // element "Some <a text". We, however, want to treat this as if the user was requesting
|
25763 | // the attributes of an "a" element, not requesting completion in the a text element. This
|
25764 | // code checks for this case and returns element completions if it is detected or undefined
|
25765 | // if it is not.
|
25766 | function voidElementAttributeCompletions(info, path) {
|
25767 | const tail = path.tail;
|
25768 | if (tail instanceof Text$2) {
|
25769 | const match = tail.value.match(/<(\w(\w|\d|-)*:)?(\w(\w|\d|-)*)\s/);
|
25770 | // The position must be after the match, otherwise we are still in a place where elements
|
25771 | // are expected (such as `<|a` or `<a|`; we only want attributes for `<a |` or after).
|
25772 | if (match &&
|
25773 | path.position >= (match.index || 0) + match[0].length + tail.sourceSpan.start.offset) {
|
25774 | return attributeCompletionsForElement(info, match[3]);
|
25775 | }
|
25776 | }
|
25777 | return [];
|
25778 | }
|
25779 | class ExpressionVisitor extends NullTemplateVisitor {
|
25780 | constructor(info, position, getExpressionScope) {
|
25781 | super();
|
25782 | this.info = info;
|
25783 | this.position = position;
|
25784 | this.getExpressionScope = getExpressionScope;
|
25785 | this.completions = new Map();
|
25786 | }
|
25787 | get results() {
|
25788 | return Array.from(this.completions.values());
|
25789 | }
|
25790 | visitDirectiveProperty(ast) {
|
25791 | this.processExpressionCompletions(ast.value);
|
25792 | }
|
25793 | visitElementProperty(ast) {
|
25794 | this.processExpressionCompletions(ast.value);
|
25795 | }
|
25796 | visitEvent(ast) {
|
25797 | this.processExpressionCompletions(ast.handler);
|
25798 | }
|
25799 | visitElement() {
|
25800 | // no-op for now
|
25801 | }
|
25802 | visitAttr(ast) {
|
25803 | const binding = getBindingDescriptor(ast.name);
|
25804 | if (binding && binding.kind === ATTR.KW_MICROSYNTAX) {
|
25805 | // This a template binding given by micro syntax expression.
|
25806 | // First, verify the attribute consists of some binding we can give completions for.
|
25807 | // The sourceSpan of AttrAst points to the RHS of the attribute
|
25808 | const templateKey = binding.name;
|
25809 | const templateValue = ast.sourceSpan.toString();
|
25810 | const templateUrl = ast.sourceSpan.start.file.url;
|
25811 | // TODO(kyliau): We are unable to determine the absolute offset of the key
|
25812 | // but it is okay here, because we are only looking at the RHS of the attr
|
25813 | const absKeyOffset = 0;
|
25814 | const absValueOffset = ast.sourceSpan.start.offset;
|
25815 | const { templateBindings } = this.info.expressionParser.parseTemplateBindings(templateKey, templateValue, templateUrl, absKeyOffset, absValueOffset);
|
25816 | // Find the nearest template binding to the position.
|
25817 | const lastBindingEnd = templateBindings.length > 0 &&
|
25818 | templateBindings[templateBindings.length - 1].sourceSpan.end;
|
25819 | const normalizedPositionToBinding = lastBindingEnd && this.position > lastBindingEnd ? lastBindingEnd : this.position;
|
25820 | const templateBinding = templateBindings.find(b => inSpan(normalizedPositionToBinding, b.sourceSpan));
|
25821 | if (!templateBinding) {
|
25822 | return;
|
25823 | }
|
25824 | this.microSyntaxInAttributeValue(ast, templateBinding);
|
25825 | }
|
25826 | else {
|
25827 | const expressionAst = this.info.expressionParser.parseBinding(ast.value, ast.sourceSpan.toString(), ast.sourceSpan.start.offset);
|
25828 | this.processExpressionCompletions(expressionAst);
|
25829 | }
|
25830 | }
|
25831 | visitReference(_ast, context) {
|
25832 | context.directives.forEach(dir => {
|
25833 | const { exportAs } = dir.directive;
|
25834 | if (exportAs) {
|
25835 | this.completions.set(exportAs, { name: exportAs, kind: CompletionKind.REFERENCE, sortText: exportAs });
|
25836 | }
|
25837 | });
|
25838 | }
|
25839 | visitBoundText(ast) {
|
25840 | if (inSpan(this.position, ast.value.sourceSpan)) {
|
25841 | const completions = getExpressionCompletions(this.getExpressionScope(), ast.value, this.position, this.info.template);
|
25842 | if (completions) {
|
25843 | this.addSymbolsToCompletions(completions);
|
25844 | }
|
25845 | }
|
25846 | }
|
25847 | processExpressionCompletions(value) {
|
25848 | const symbols = getExpressionCompletions(this.getExpressionScope(), value, this.position, this.info.template);
|
25849 | if (symbols) {
|
25850 | this.addSymbolsToCompletions(symbols);
|
25851 | }
|
25852 | }
|
25853 | addSymbolsToCompletions(symbols) {
|
25854 | for (const s of symbols) {
|
25855 | if (s.name.startsWith('__') || !s.public || this.completions.has(s.name)) {
|
25856 | continue;
|
25857 | }
|
25858 | // The pipe method should not include parentheses.
|
25859 | // e.g. {{ value_expression | slice : start [ : end ] }}
|
25860 | const shouldInsertParentheses = s.callable && s.kind !== CompletionKind.PIPE;
|
25861 | this.completions.set(s.name, {
|
25862 | name: s.name,
|
25863 | kind: s.kind,
|
25864 | sortText: s.name,
|
25865 | insertText: shouldInsertParentheses ? `${s.name}()` : s.name,
|
25866 | });
|
25867 | }
|
25868 | }
|
25869 | /**
|
25870 | * This method handles the completions of attribute values for directives that
|
25871 | * support the microsyntax format. Examples are *ngFor and *ngIf.
|
25872 | * These directives allows declaration of "let" variables, adds context-specific
|
25873 | * symbols like $implicit, index, count, among other behaviors.
|
25874 | * For a complete description of such format, see
|
25875 | * https://angular.io/guide/structural-directives#asterisk
|
25876 | *
|
25877 | * @param attr descriptor for attribute name and value pair
|
25878 | * @param binding template binding for the expression in the attribute
|
25879 | */
|
25880 | microSyntaxInAttributeValue(attr, binding) {
|
25881 | var _a;
|
25882 | const key = attr.name.substring(1); // remove leading asterisk
|
25883 | // Find the selector - eg ngFor, ngIf, etc
|
25884 | const selectorInfo = getSelectors(this.info);
|
25885 | const selector = selectorInfo.selectors.find(s => {
|
25886 | // attributes are listed in (attribute, value) pairs
|
25887 | for (let i = 0; i < s.attrs.length; i += 2) {
|
25888 | if (s.attrs[i] === key) {
|
25889 | return true;
|
25890 | }
|
25891 | }
|
25892 | });
|
25893 | if (!selector) {
|
25894 | return;
|
25895 | }
|
25896 | const valueRelativePosition = this.position - attr.sourceSpan.start.offset;
|
25897 | if (binding instanceof VariableBinding) {
|
25898 | // TODO(kyliau): With expression sourceSpan we shouldn't have to search
|
25899 | // the attribute value string anymore. Just check if position is in the
|
25900 | // expression source span.
|
25901 | const equalLocation = attr.value.indexOf('=');
|
25902 | if (equalLocation > 0 && valueRelativePosition > equalLocation) {
|
25903 | // We are after the '=' in a let clause. The valid values here are the members of the
|
25904 | // template reference's type parameter.
|
25905 | const directiveMetadata = selectorInfo.map.get(selector);
|
25906 | if (directiveMetadata) {
|
25907 | const contextTable = this.info.template.query.getTemplateContext(directiveMetadata.type.reference);
|
25908 | if (contextTable) {
|
25909 | // This adds symbols like $implicit, index, count, etc.
|
25910 | this.addSymbolsToCompletions(contextTable.values());
|
25911 | return;
|
25912 | }
|
25913 | }
|
25914 | }
|
25915 | }
|
25916 | else if (binding instanceof ExpressionBinding) {
|
25917 | if (inSpan(this.position, (_a = binding.value) === null || _a === void 0 ? void 0 : _a.ast.sourceSpan)) {
|
25918 | this.processExpressionCompletions(binding.value.ast);
|
25919 | return;
|
25920 | }
|
25921 | else if (!binding.value && this.position > binding.key.span.end) {
|
25922 | // No expression is defined for the value of the key expression binding, but the cursor is
|
25923 | // in a location where the expression would be defined. This can happen in a case like
|
25924 | // let i of |
|
25925 | // ^-- cursor
|
25926 | // In this case, backfill the value to be an empty expression and retrieve completions.
|
25927 | this.processExpressionCompletions(new EmptyExpr(new ParseSpan(valueRelativePosition, valueRelativePosition), new AbsoluteSourceSpan(this.position, this.position)));
|
25928 | return;
|
25929 | }
|
25930 | }
|
25931 | }
|
25932 | }
|
25933 | /**
|
25934 | * Return all Angular-specific attributes for the element with `elementName`.
|
25935 | * @param info
|
25936 | * @param elementName
|
25937 | */
|
25938 | function angularAttributes(info, elementName) {
|
25939 | const { selectors, map: selectorMap } = getSelectors(info);
|
25940 | const templateRefs = new Set();
|
25941 | const inputs = new Set();
|
25942 | const outputs = new Set();
|
25943 | const bananas = new Set();
|
25944 | const others = new Set();
|
25945 | for (const selector of selectors) {
|
25946 | if (selector.element && selector.element !== elementName) {
|
25947 | continue;
|
25948 | }
|
25949 | const summary = selectorMap.get(selector);
|
25950 | const hasTemplateRef = isStructuralDirective(summary.type);
|
25951 | // attributes are listed in (attribute, value) pairs
|
25952 | for (let i = 0; i < selector.attrs.length; i += 2) {
|
25953 | const attr = selector.attrs[i];
|
25954 | if (hasTemplateRef) {
|
25955 | templateRefs.add(attr);
|
25956 | }
|
25957 | else {
|
25958 | others.add(attr);
|
25959 | }
|
25960 | }
|
25961 | for (const input of Object.values(summary.inputs)) {
|
25962 | inputs.add(input);
|
25963 | }
|
25964 | for (const output of Object.values(summary.outputs)) {
|
25965 | outputs.add(output);
|
25966 | }
|
25967 | }
|
25968 | for (const name of inputs) {
|
25969 | // Add banana-in-a-box syntax
|
25970 | // https://angular.io/guide/template-syntax#two-way-binding-
|
25971 | if (outputs.has(`${name}Change`)) {
|
25972 | bananas.add(name);
|
25973 | }
|
25974 | }
|
25975 | return { templateRefs, inputs, outputs, bananas, others };
|
25976 | }
|
25977 |
|
25978 | /**
|
25979 | * @license
|
25980 | * Copyright Google LLC All Rights Reserved.
|
25981 | *
|
25982 | * Use of this source code is governed by an MIT-style license that can be
|
25983 | * found in the LICENSE file at https://angular.io/license
|
25984 | */
|
25985 | /**
|
25986 | * Traverses a template AST and locates symbol(s) at a specified position.
|
25987 | * @param info template AST information set
|
25988 | * @param position location to locate symbols at
|
25989 | */
|
25990 | function locateSymbols(info, position) {
|
25991 | const templatePosition = position - info.template.span.start;
|
25992 | // TODO: update `findTemplateAstAt` to use absolute positions.
|
25993 | const path = findTemplateAstAt(info.templateAst, templatePosition);
|
25994 | const attribute = findAttribute(info, position);
|
25995 | if (!path.tail)
|
25996 | return [];
|
25997 | const narrowest = spanOf(path.tail);
|
25998 | const toVisit = [];
|
25999 | for (let node = path.tail; node && isNarrower(spanOf(node.sourceSpan), narrowest); node = path.parentOf(node)) {
|
26000 | toVisit.push(node);
|
26001 | }
|
26002 | // For the structural directive, only care about the last template AST.
|
26003 | if (attribute === null || attribute === void 0 ? void 0 : attribute.name.startsWith('*')) {
|
26004 | toVisit.splice(0, toVisit.length - 1);
|
26005 | }
|
26006 | return toVisit.map(ast => locateSymbol(ast, path, info))
|
26007 | .filter((sym) => sym !== undefined);
|
26008 | }
|
26009 | /**
|
26010 | * Visits a template node and locates the symbol in that node at a path position.
|
26011 | * @param ast template AST node to visit
|
26012 | * @param path non-empty set of narrowing AST nodes at a position
|
26013 | * @param info template AST information set
|
26014 | */
|
26015 | function locateSymbol(ast, path, info) {
|
26016 | const templatePosition = path.position;
|
26017 | const position = templatePosition + info.template.span.start;
|
26018 | let symbol;
|
26019 | let span;
|
26020 | let staticSymbol;
|
26021 | const attributeValueSymbol = (ast) => {
|
26022 | const attribute = findAttribute(info, position);
|
26023 | if (attribute) {
|
26024 | if (inSpan(templatePosition, spanOf(attribute.valueSpan))) {
|
26025 | let result;
|
26026 | if (attribute.name.startsWith('*')) {
|
26027 | result = getSymbolInMicrosyntax(info, path, attribute);
|
26028 | }
|
26029 | else {
|
26030 | const dinfo = diagnosticInfoFromTemplateInfo(info);
|
26031 | const scope = getExpressionScope(dinfo, path);
|
26032 | result = getExpressionSymbol(scope, ast, templatePosition, info.template);
|
26033 | }
|
26034 | if (result) {
|
26035 | symbol = result.symbol;
|
26036 | span = offsetSpan(result.span, attribute.valueSpan.start.offset);
|
26037 | }
|
26038 | return true;
|
26039 | }
|
26040 | }
|
26041 | return false;
|
26042 | };
|
26043 | ast.visit({
|
26044 | visitNgContent(_ast) { },
|
26045 | visitEmbeddedTemplate(_ast) { },
|
26046 | visitElement(ast) {
|
26047 | const component = ast.directives.find(d => d.directive.isComponent);
|
26048 | if (component) {
|
26049 | // Need to cast because 'reference' is typed as any
|
26050 | staticSymbol = component.directive.type.reference;
|
26051 | symbol = info.template.query.getTypeSymbol(staticSymbol);
|
26052 | symbol = symbol && new OverrideKindSymbol(symbol, DirectiveKind.COMPONENT);
|
26053 | span = spanOf(ast);
|
26054 | }
|
26055 | else {
|
26056 | // Find a directive that matches the element name
|
26057 | const directive = ast.directives.find(d => d.directive.selector != null && d.directive.selector.indexOf(ast.name) >= 0);
|
26058 | if (directive) {
|
26059 | // Need to cast because 'reference' is typed as any
|
26060 | staticSymbol = directive.directive.type.reference;
|
26061 | symbol = info.template.query.getTypeSymbol(staticSymbol);
|
26062 | symbol = symbol && new OverrideKindSymbol(symbol, DirectiveKind.DIRECTIVE);
|
26063 | span = spanOf(ast);
|
26064 | }
|
26065 | }
|
26066 | },
|
26067 | visitReference(ast) {
|
26068 | symbol = ast.value && info.template.query.getTypeSymbol(tokenReference(ast.value));
|
26069 | span = spanOf(ast);
|
26070 | },
|
26071 | visitVariable(_ast) { },
|
26072 | visitEvent(ast) {
|
26073 | if (!attributeValueSymbol(ast.handler)) {
|
26074 | symbol = findOutputBinding(ast, path, info.template.query);
|
26075 | symbol = symbol && new OverrideKindSymbol(symbol, DirectiveKind.EVENT);
|
26076 | span = spanOf(ast);
|
26077 | }
|
26078 | },
|
26079 | visitElementProperty(ast) {
|
26080 | attributeValueSymbol(ast.value);
|
26081 | },
|
26082 | visitAttr(ast) {
|
26083 | const element = path.first(ElementAst);
|
26084 | if (!element)
|
26085 | return;
|
26086 | // Create a mapping of all directives applied to the element from their selectors.
|
26087 | const matcher = new SelectorMatcher();
|
26088 | for (const dir of element.directives) {
|
26089 | if (!dir.directive.selector)
|
26090 | continue;
|
26091 | matcher.addSelectables(CssSelector.parse(dir.directive.selector), dir);
|
26092 | }
|
26093 | // See if this attribute matches the selector of any directive on the element.
|
26094 | const attributeSelector = `[${ast.name}=${ast.value}]`;
|
26095 | const parsedAttribute = CssSelector.parse(attributeSelector);
|
26096 | if (!parsedAttribute.length)
|
26097 | return;
|
26098 | matcher.match(parsedAttribute[0], (_, { directive }) => {
|
26099 | // Need to cast because 'reference' is typed as any
|
26100 | staticSymbol = directive.type.reference;
|
26101 | symbol = info.template.query.getTypeSymbol(staticSymbol);
|
26102 | symbol = symbol && new OverrideKindSymbol(symbol, DirectiveKind.DIRECTIVE);
|
26103 | span = spanOf(ast);
|
26104 | });
|
26105 | },
|
26106 | visitBoundText(ast) {
|
26107 | const expressionPosition = templatePosition - ast.sourceSpan.start.offset;
|
26108 | if (inSpan(expressionPosition, ast.value.span)) {
|
26109 | const dinfo = diagnosticInfoFromTemplateInfo(info);
|
26110 | const scope = getExpressionScope(dinfo, path);
|
26111 | const result = getExpressionSymbol(scope, ast.value, templatePosition, info.template);
|
26112 | if (result) {
|
26113 | symbol = result.symbol;
|
26114 | span = offsetSpan(result.span, ast.sourceSpan.start.offset);
|
26115 | }
|
26116 | }
|
26117 | },
|
26118 | visitText(_ast) { },
|
26119 | visitDirective(ast) {
|
26120 | // Need to cast because 'reference' is typed as any
|
26121 | staticSymbol = ast.directive.type.reference;
|
26122 | symbol = info.template.query.getTypeSymbol(staticSymbol);
|
26123 | span = spanOf(ast);
|
26124 | },
|
26125 | visitDirectiveProperty(ast) {
|
26126 | if (!attributeValueSymbol(ast.value)) {
|
26127 | const directive = findParentOfBinding(info.templateAst, ast, templatePosition);
|
26128 | const attribute = findAttribute(info, position);
|
26129 | if (directive && attribute) {
|
26130 | if (attribute.name.startsWith('*')) {
|
26131 | const compileTypeSummary = directive.directive;
|
26132 | symbol = info.template.query.getTypeSymbol(compileTypeSummary.type.reference);
|
26133 | symbol = symbol && new OverrideKindSymbol(symbol, DirectiveKind.DIRECTIVE);
|
26134 | // Use 'attribute.sourceSpan' instead of the directive's,
|
26135 | // because the span of the directive is the whole opening tag of an element.
|
26136 | span = spanOf(attribute.sourceSpan);
|
26137 | }
|
26138 | else {
|
26139 | symbol = findInputBinding(info, ast.templateName, directive);
|
26140 | span = spanOf(ast);
|
26141 | }
|
26142 | }
|
26143 | }
|
26144 | }
|
26145 | }, null);
|
26146 | if (symbol && span) {
|
26147 | const { start, end } = offsetSpan(span, info.template.span.start);
|
26148 | return {
|
26149 | symbol,
|
26150 | span: tss.createTextSpanFromBounds(start, end),
|
26151 | staticSymbol,
|
26152 | };
|
26153 | }
|
26154 | }
|
26155 | // Get the symbol in microsyntax at template position.
|
26156 | function getSymbolInMicrosyntax(info, path, attribute) {
|
26157 | var _a;
|
26158 | if (!attribute.valueSpan) {
|
26159 | return;
|
26160 | }
|
26161 | const absValueOffset = attribute.valueSpan.start.offset;
|
26162 | let result;
|
26163 | const { templateBindings } = info.expressionParser.parseTemplateBindings(attribute.name, attribute.value, attribute.sourceSpan.toString(), attribute.sourceSpan.start.offset, attribute.valueSpan.start.offset);
|
26164 | // Find the symbol that contains the position.
|
26165 | for (const tb of templateBindings) {
|
26166 | if (tb instanceof VariableBinding) {
|
26167 | // TODO(kyliau): if binding is variable we should still look for the value
|
26168 | // of the key. For example, "let i=index" => "index" should point to
|
26169 | // NgForOfContext.index
|
26170 | continue;
|
26171 | }
|
26172 | if (inSpan(path.position, (_a = tb.value) === null || _a === void 0 ? void 0 : _a.ast.sourceSpan)) {
|
26173 | const dinfo = diagnosticInfoFromTemplateInfo(info);
|
26174 | const scope = getExpressionScope(dinfo, path);
|
26175 | result = getExpressionSymbol(scope, tb.value, path.position, info.template);
|
26176 | }
|
26177 | else if (inSpan(path.position, tb.sourceSpan)) {
|
26178 | const template = path.first(EmbeddedTemplateAst);
|
26179 | if (template) {
|
26180 | // One element can only have one template binding.
|
26181 | const directiveAst = template.directives[0];
|
26182 | if (directiveAst) {
|
26183 | const symbol = findInputBinding(info, tb.key.source.substring(1), directiveAst);
|
26184 | if (symbol) {
|
26185 | result = {
|
26186 | symbol,
|
26187 | // the span here has to be relative to the start of the template
|
26188 | // value so deduct the absolute offset.
|
26189 | // TODO(kyliau): Use absolute source span throughout completions.
|
26190 | span: offsetSpan(tb.key.span, -absValueOffset),
|
26191 | };
|
26192 | }
|
26193 | }
|
26194 | }
|
26195 | }
|
26196 | }
|
26197 | return result;
|
26198 | }
|
26199 | function findAttribute(info, position) {
|
26200 | const templatePosition = position - info.template.span.start;
|
26201 | const path = getPathToNodeAtPosition(info.htmlAst, templatePosition);
|
26202 | return path.first(Attribute);
|
26203 | }
|
26204 | // TODO: remove this function after the path includes 'DirectiveAst'.
|
26205 | // Find the directive that corresponds to the specified 'binding'
|
26206 | // at the specified 'position' in the 'ast'.
|
26207 | function findParentOfBinding(ast, binding, position) {
|
26208 | let res;
|
26209 | const visitor = new class extends RecursiveTemplateAstVisitor {
|
26210 | visit(ast) {
|
26211 | const span = spanOf(ast);
|
26212 | if (!inSpan(position, span)) {
|
26213 | // Returning a value here will result in the children being skipped.
|
26214 | return true;
|
26215 | }
|
26216 | }
|
26217 | visitEmbeddedTemplate(ast, context) {
|
26218 | return this.visitChildren(context, visit => {
|
26219 | visit(ast.directives);
|
26220 | visit(ast.children);
|
26221 | });
|
26222 | }
|
26223 | visitElement(ast, context) {
|
26224 | return this.visitChildren(context, visit => {
|
26225 | visit(ast.directives);
|
26226 | visit(ast.children);
|
26227 | });
|
26228 | }
|
26229 | visitDirective(ast) {
|
26230 | const result = this.visitChildren(ast, visit => {
|
26231 | visit(ast.inputs);
|
26232 | });
|
26233 | return result;
|
26234 | }
|
26235 | visitDirectiveProperty(ast, context) {
|
26236 | if (ast === binding) {
|
26237 | res = context;
|
26238 | }
|
26239 | }
|
26240 | };
|
26241 | templateVisitAll(visitor, ast);
|
26242 | return res;
|
26243 | }
|
26244 | // Find the symbol of input binding in 'directiveAst' by 'name'.
|
26245 | function findInputBinding(info, name, directiveAst) {
|
26246 | const invertedInput = invertMap(directiveAst.directive.inputs);
|
26247 | const fieldName = invertedInput[name];
|
26248 | if (fieldName) {
|
26249 | const classSymbol = info.template.query.getTypeSymbol(directiveAst.directive.type.reference);
|
26250 | if (classSymbol) {
|
26251 | return classSymbol.members().get(fieldName);
|
26252 | }
|
26253 | }
|
26254 | }
|
26255 | /**
|
26256 | * Wrap a symbol and change its kind to component.
|
26257 | */
|
26258 | class OverrideKindSymbol {
|
26259 | constructor(sym, kindOverride) {
|
26260 | this.sym = sym;
|
26261 | this.kind = kindOverride;
|
26262 | }
|
26263 | get name() {
|
26264 | return this.sym.name;
|
26265 | }
|
26266 | get language() {
|
26267 | return this.sym.language;
|
26268 | }
|
26269 | get type() {
|
26270 | return this.sym.type;
|
26271 | }
|
26272 | get container() {
|
26273 | return this.sym.container;
|
26274 | }
|
26275 | get public() {
|
26276 | return this.sym.public;
|
26277 | }
|
26278 | get callable() {
|
26279 | return this.sym.callable;
|
26280 | }
|
26281 | get nullable() {
|
26282 | return this.sym.nullable;
|
26283 | }
|
26284 | get definition() {
|
26285 | return this.sym.definition;
|
26286 | }
|
26287 | get documentation() {
|
26288 | return this.sym.documentation;
|
26289 | }
|
26290 | members() {
|
26291 | return this.sym.members();
|
26292 | }
|
26293 | signatures() {
|
26294 | return this.sym.signatures();
|
26295 | }
|
26296 | selectSignature(types) {
|
26297 | return this.sym.selectSignature(types);
|
26298 | }
|
26299 | indexed(argument) {
|
26300 | return this.sym.indexed(argument);
|
26301 | }
|
26302 | typeArguments() {
|
26303 | return this.sym.typeArguments();
|
26304 | }
|
26305 | }
|
26306 |
|
26307 | /**
|
26308 | * @license
|
26309 | * Copyright Google LLC All Rights Reserved.
|
26310 | *
|
26311 | * Use of this source code is governed by an MIT-style license that can be
|
26312 | * found in the LICENSE file at https://angular.io/license
|
26313 | */
|
26314 | /**
|
26315 | * Return metadata about `node` if it looks like an Angular directive class.
|
26316 | * In this case, potential matches are `@NgModule`, `@Component`, `@Directive`,
|
26317 | * `@Pipe`, etc.
|
26318 | * These class declarations all share some common attributes, namely their
|
26319 | * decorator takes exactly one parameter and the parameter must be an object
|
26320 | * literal.
|
26321 | *
|
26322 | * For example,
|
26323 | * v---------- `decoratorId`
|
26324 | * @NgModule({ <
|
26325 | * declarations: [], < classDecln-al
|
26326 | * }) <
|
26327 | * class AppModule {} <
|
26328 | * ^----- `classId`
|
26329 | *
|
26330 | * @param node Potential node that represents an Angular directive.
|
26331 | */
|
26332 | function getDirectiveClassLike(node) {
|
26333 | if (!tss.isClassDeclaration(node) || !node.name || !node.decorators) {
|
26334 | return;
|
26335 | }
|
26336 | for (const d of node.decorators) {
|
26337 | const expr = d.expression;
|
26338 | if (!tss.isCallExpression(expr) || expr.arguments.length !== 1 ||
|
26339 | !tss.isIdentifier(expr.expression)) {
|
26340 | continue;
|
26341 | }
|
26342 | const arg = expr.arguments[0];
|
26343 | if (tss.isObjectLiteralExpression(arg)) {
|
26344 | return {
|
26345 | decoratorId: expr.expression,
|
26346 | classId: node.name,
|
26347 | };
|
26348 | }
|
26349 | }
|
26350 | }
|
26351 | /**
|
26352 | * Finds the value of a property assignment that is nested in a TypeScript node and is of a certain
|
26353 | * type T.
|
26354 | *
|
26355 | * @param startNode node to start searching for nested property assignment from
|
26356 | * @param propName property assignment name
|
26357 | * @param predicate function to verify that a node is of type T.
|
26358 | * @return node property assignment value of type T, or undefined if none is found
|
26359 | */
|
26360 | function findPropertyValueOfType(startNode, propName, predicate) {
|
26361 | if (tss.isPropertyAssignment(startNode) && startNode.name.getText() === propName) {
|
26362 | const { initializer } = startNode;
|
26363 | if (predicate(initializer))
|
26364 | return initializer;
|
26365 | }
|
26366 | return startNode.forEachChild(c => findPropertyValueOfType(c, propName, predicate));
|
26367 | }
|
26368 | /**
|
26369 | * Return the node that most tightly encompass the specified `position`.
|
26370 | * @param node
|
26371 | * @param position
|
26372 | */
|
26373 | function findTightestNode(node, position) {
|
26374 | if (node.getStart() <= position && position < node.getEnd()) {
|
26375 | return node.forEachChild(c => findTightestNode(c, position)) || node;
|
26376 | }
|
26377 | }
|
26378 | /**
|
26379 | * Returns a property assignment from the assignment value if the property name
|
26380 | * matches the specified `key`, or `undefined` if there is no match.
|
26381 | */
|
26382 | function getPropertyAssignmentFromValue(value, key) {
|
26383 | const propAssignment = value.parent;
|
26384 | if (!propAssignment || !tss.isPropertyAssignment(propAssignment) ||
|
26385 | propAssignment.name.getText() !== key) {
|
26386 | return;
|
26387 | }
|
26388 | return propAssignment;
|
26389 | }
|
26390 | /**
|
26391 | * Given a decorator property assignment, return the ClassDeclaration node that corresponds to the
|
26392 | * directive class the property applies to.
|
26393 | * If the property assignment is not on a class decorator, no declaration is returned.
|
26394 | *
|
26395 | * For example,
|
26396 | *
|
26397 | * @Component({
|
26398 | * template: '<div></div>'
|
26399 | * ^^^^^^^^^^^^^^^^^^^^^^^---- property assignment
|
26400 | * })
|
26401 | * class AppComponent {}
|
26402 | * ^---- class declaration node
|
26403 | *
|
26404 | * @param propAsgnNode property assignment
|
26405 | */
|
26406 | function getClassDeclFromDecoratorProp(propAsgnNode) {
|
26407 | if (!propAsgnNode.parent || !tss.isObjectLiteralExpression(propAsgnNode.parent)) {
|
26408 | return;
|
26409 | }
|
26410 | const objLitExprNode = propAsgnNode.parent;
|
26411 | if (!objLitExprNode.parent || !tss.isCallExpression(objLitExprNode.parent)) {
|
26412 | return;
|
26413 | }
|
26414 | const callExprNode = objLitExprNode.parent;
|
26415 | if (!callExprNode.parent || !tss.isDecorator(callExprNode.parent)) {
|
26416 | return;
|
26417 | }
|
26418 | const decorator = callExprNode.parent;
|
26419 | if (!decorator.parent || !tss.isClassDeclaration(decorator.parent)) {
|
26420 | return;
|
26421 | }
|
26422 | const classDeclNode = decorator.parent;
|
26423 | return classDeclNode;
|
26424 | }
|
26425 |
|
26426 | /**
|
26427 | * @license
|
26428 | * Copyright Google LLC All Rights Reserved.
|
26429 | *
|
26430 | * Use of this source code is governed by an MIT-style license that can be
|
26431 | * found in the LICENSE file at https://angular.io/license
|
26432 | */
|
26433 | /**
|
26434 | * Convert Angular Span to TypeScript TextSpan. Angular Span has 'start' and
|
26435 | * 'end' whereas TS TextSpan has 'start' and 'length'.
|
26436 | * @param span Angular Span
|
26437 | */
|
26438 | function ngSpanToTsTextSpan(span) {
|
26439 | return {
|
26440 | start: span.start,
|
26441 | length: span.end - span.start,
|
26442 | };
|
26443 | }
|
26444 | /**
|
26445 | * Attempts to get the definition of a file whose URL is specified in a property assignment in a
|
26446 | * directive decorator.
|
26447 | * Currently applies to `templateUrl` and `styleUrls` properties.
|
26448 | */
|
26449 | function getUrlFromProperty(urlNode, tsLsHost) {
|
26450 | // Get the property assignment node corresponding to the `templateUrl` or `styleUrls` assignment.
|
26451 | // These assignments are specified differently; `templateUrl` is a string, and `styleUrls` is
|
26452 | // an array of strings:
|
26453 | // {
|
26454 | // templateUrl: './template.ng.html',
|
26455 | // styleUrls: ['./style.css', './other-style.css']
|
26456 | // }
|
26457 | // `templateUrl`'s property assignment can be found from the string literal node;
|
26458 | // `styleUrls`'s property assignment can be found from the array (parent) node.
|
26459 | //
|
26460 | // First search for `templateUrl`.
|
26461 | let asgn = getPropertyAssignmentFromValue(urlNode, 'templateUrl');
|
26462 | if (!asgn) {
|
26463 | // `templateUrl` assignment not found; search for `styleUrls` array assignment.
|
26464 | asgn = getPropertyAssignmentFromValue(urlNode.parent, 'styleUrls');
|
26465 | if (!asgn) {
|
26466 | // Nothing found, bail.
|
26467 | return;
|
26468 | }
|
26469 | }
|
26470 | // If the property assignment is not a property of a class decorator, don't generate definitions
|
26471 | // for it.
|
26472 | if (!getClassDeclFromDecoratorProp(asgn)) {
|
26473 | return;
|
26474 | }
|
26475 | // Extract url path specified by the url node, which is relative to the TypeScript source file
|
26476 | // the url node is defined in.
|
26477 | const url = extractAbsoluteFilePath(urlNode);
|
26478 | // If the file does not exist, bail. It is possible that the TypeScript language service host
|
26479 | // does not have a `fileExists` method, in which case optimistically assume the file exists.
|
26480 | if (tsLsHost.fileExists && !tsLsHost.fileExists(url))
|
26481 | return;
|
26482 | const templateDefinitions = [{
|
26483 | kind: ts.ScriptElementKind.externalModuleName,
|
26484 | name: url,
|
26485 | containerKind: ts.ScriptElementKind.unknown,
|
26486 | containerName: '',
|
26487 | // Reading the template is expensive, so don't provide a preview.
|
26488 | textSpan: { start: 0, length: 0 },
|
26489 | fileName: url,
|
26490 | }];
|
26491 | return {
|
26492 | definitions: templateDefinitions,
|
26493 | textSpan: {
|
26494 | // Exclude opening and closing quotes in the url span.
|
26495 | start: urlNode.getStart() + 1,
|
26496 | length: urlNode.getWidth() - 2,
|
26497 | },
|
26498 | };
|
26499 | }
|
26500 | /**
|
26501 | * Traverse the template AST and look for the symbol located at `position`, then
|
26502 | * return its definition and span of bound text.
|
26503 | * @param info
|
26504 | * @param position
|
26505 | */
|
26506 | function getDefinitionAndBoundSpan(info, position) {
|
26507 | const symbols = locateSymbols(info, position);
|
26508 | if (!symbols.length) {
|
26509 | return;
|
26510 | }
|
26511 | const seen = new Set();
|
26512 | const definitions = [];
|
26513 | for (const symbolInfo of symbols) {
|
26514 | const { symbol } = symbolInfo;
|
26515 | // symbol.definition is really the locations of the symbol. There could be
|
26516 | // more than one. No meaningful info could be provided without any location.
|
26517 | const { kind, name, container, definition: locations } = symbol;
|
26518 | if (!locations || !locations.length) {
|
26519 | continue;
|
26520 | }
|
26521 | const containerKind = container ? container.kind : ts.ScriptElementKind.unknown;
|
26522 | const containerName = container ? container.name : '';
|
26523 | for (const { fileName, span } of locations) {
|
26524 | const textSpan = ngSpanToTsTextSpan(span);
|
26525 | // In cases like two-way bindings, a request for the definitions of an expression may return
|
26526 | // two of the same definition:
|
26527 | // [(ngModel)]="prop"
|
26528 | // ^^^^ -- one definition for the property binding, one for the event binding
|
26529 | // To prune duplicate definitions, tag definitions with unique location signatures and ignore
|
26530 | // definitions whose locations have already been seen.
|
26531 | const signature = `${textSpan.start}:${textSpan.length}@${fileName}`;
|
26532 | if (seen.has(signature))
|
26533 | continue;
|
26534 | definitions.push({
|
26535 | kind: kind,
|
26536 | name,
|
26537 | containerKind,
|
26538 | containerName,
|
26539 | textSpan: ngSpanToTsTextSpan(span),
|
26540 | fileName: fileName,
|
26541 | });
|
26542 | seen.add(signature);
|
26543 | }
|
26544 | }
|
26545 | return {
|
26546 | definitions,
|
26547 | textSpan: symbols[0].span,
|
26548 | };
|
26549 | }
|
26550 | /**
|
26551 | * Gets an Angular-specific definition in a TypeScript source file.
|
26552 | */
|
26553 | function getTsDefinitionAndBoundSpan(sf, position, tsLsHost) {
|
26554 | const node = findTightestNode(sf, position);
|
26555 | if (!node)
|
26556 | return;
|
26557 | switch (node.kind) {
|
26558 | case ts.SyntaxKind.StringLiteral:
|
26559 | case ts.SyntaxKind.NoSubstitutionTemplateLiteral:
|
26560 | // Attempt to extract definition of a URL in a property assignment.
|
26561 | return getUrlFromProperty(node, tsLsHost);
|
26562 | default:
|
26563 | return undefined;
|
26564 | }
|
26565 | }
|
26566 |
|
26567 | /**
|
26568 | * @license
|
26569 | * Copyright Google LLC All Rights Reserved.
|
26570 | *
|
26571 | * Use of this source code is governed by an MIT-style license that can be
|
26572 | * found in the LICENSE file at https://angular.io/license
|
26573 | */
|
26574 | /**
|
26575 | * Return diagnostic information for the parsed AST of the template.
|
26576 | * @param ast contains HTML and template AST
|
26577 | */
|
26578 | function getTemplateDiagnostics(ast) {
|
26579 | const { parseErrors, templateAst, htmlAst, template } = ast;
|
26580 | if (parseErrors && parseErrors.length) {
|
26581 | return parseErrors.map(e => {
|
26582 | return {
|
26583 | kind: ts.DiagnosticCategory.Error,
|
26584 | span: offsetSpan(spanOf(e.span), template.span.start),
|
26585 | message: e.msg,
|
26586 | };
|
26587 | });
|
26588 | }
|
26589 | return getTemplateExpressionDiagnostics({
|
26590 | templateAst: templateAst,
|
26591 | htmlAst: htmlAst,
|
26592 | offset: template.span.start,
|
26593 | query: template.query,
|
26594 | members: template.members,
|
26595 | source: ast.template.source,
|
26596 | });
|
26597 | }
|
26598 | /**
|
26599 | * Performs a variety diagnostics on directive declarations.
|
26600 | *
|
26601 | * @param declarations Angular directive declarations
|
26602 | * @param modules NgModules in the project
|
26603 | * @param host TypeScript service host used to perform TypeScript queries
|
26604 | * @return diagnosed errors, if any
|
26605 | */
|
26606 | function getDeclarationDiagnostics(declarations, modules, host) {
|
26607 | const directives = new Set();
|
26608 | for (const ngModule of modules.ngModules) {
|
26609 | for (const directive of ngModule.declaredDirectives) {
|
26610 | directives.add(directive.reference);
|
26611 | }
|
26612 | }
|
26613 | const results = [];
|
26614 | for (const declaration of declarations) {
|
26615 | const { errors, metadata, type, declarationSpan } = declaration;
|
26616 | const sf = host.getSourceFile(type.filePath);
|
26617 | if (!sf) {
|
26618 | host.error(`directive ${type.name} exists but has no source file`);
|
26619 | return [];
|
26620 | }
|
26621 | // TypeScript identifier of the directive declaration annotation (e.g. "Component" or
|
26622 | // "Directive") on a directive class.
|
26623 | const directiveIdentifier = findTightestNode(sf, declarationSpan.start);
|
26624 | if (!directiveIdentifier) {
|
26625 | host.error(`directive ${type.name} exists but has no identifier`);
|
26626 | return [];
|
26627 | }
|
26628 | for (const error of errors) {
|
26629 | results.push({
|
26630 | kind: ts.DiagnosticCategory.Error,
|
26631 | message: error.message,
|
26632 | span: error.span,
|
26633 | });
|
26634 | }
|
26635 | if (!modules.ngModuleByPipeOrDirective.has(declaration.type)) {
|
26636 | results.push(createDiagnostic(declarationSpan, Diagnostic.directive_not_in_module, metadata.isComponent ? 'Component' : 'Directive', type.name));
|
26637 | }
|
26638 | if (metadata.isComponent) {
|
26639 | const { template, templateUrl, styleUrls } = metadata.template;
|
26640 | if (template === null && !templateUrl) {
|
26641 | results.push(createDiagnostic(declarationSpan, Diagnostic.missing_template_and_templateurl, type.name));
|
26642 | }
|
26643 | else if (templateUrl) {
|
26644 | if (template) {
|
26645 | results.push(createDiagnostic(declarationSpan, Diagnostic.both_template_and_templateurl, type.name));
|
26646 | }
|
26647 | // Find templateUrl value from the directive call expression, which is the parent of the
|
26648 | // directive identifier.
|
26649 | //
|
26650 | // TODO: We should create an enum of the various properties a directive can have to use
|
26651 | // instead of string literals. We can then perform a mass migration of all literal usages.
|
26652 | const templateUrlNode = findPropertyValueOfType(directiveIdentifier.parent, 'templateUrl', ts.isLiteralExpression);
|
26653 | if (!templateUrlNode) {
|
26654 | host.error(`templateUrl ${templateUrl} exists but its TypeScript node doesn't`);
|
26655 | return [];
|
26656 | }
|
26657 | results.push(...validateUrls([templateUrlNode], host.tsLsHost));
|
26658 | }
|
26659 | if (styleUrls.length > 0) {
|
26660 | // Find styleUrls value from the directive call expression, which is the parent of the
|
26661 | // directive identifier.
|
26662 | const styleUrlsNode = findPropertyValueOfType(directiveIdentifier.parent, 'styleUrls', ts.isArrayLiteralExpression);
|
26663 | if (!styleUrlsNode) {
|
26664 | host.error(`styleUrls property exists but its TypeScript node doesn't'`);
|
26665 | return [];
|
26666 | }
|
26667 | results.push(...validateUrls(styleUrlsNode.elements, host.tsLsHost));
|
26668 | }
|
26669 | }
|
26670 | }
|
26671 | return results;
|
26672 | }
|
26673 | /**
|
26674 | * Checks that URLs on a directive point to a valid file.
|
26675 | * Note that this diagnostic check may require a filesystem hit, and thus may be slower than other
|
26676 | * checks.
|
26677 | *
|
26678 | * @param urls urls to check for validity
|
26679 | * @param tsLsHost TS LS host used for querying filesystem information
|
26680 | * @return diagnosed url errors, if any
|
26681 | */
|
26682 | function validateUrls(urls, tsLsHost) {
|
26683 | if (!tsLsHost.fileExists) {
|
26684 | return [];
|
26685 | }
|
26686 | const allErrors = [];
|
26687 | // TODO(ayazhafiz): most of this logic can be unified with the logic in
|
26688 | // definitions.ts#getUrlFromProperty. Create a utility function to be used by both.
|
26689 | for (let i = 0; i < urls.length; ++i) {
|
26690 | const urlNode = urls[i];
|
26691 | if (!ts.isStringLiteralLike(urlNode)) {
|
26692 | // If a non-string value is assigned to a URL node (like `templateUrl`), a type error will be
|
26693 | // picked up by the TS Language Server.
|
26694 | continue;
|
26695 | }
|
26696 | const url = extractAbsoluteFilePath(urlNode);
|
26697 | if (tsLsHost.fileExists(url))
|
26698 | continue;
|
26699 | // Exclude opening and closing quotes in the url span.
|
26700 | const urlSpan = { start: urlNode.getStart() + 1, end: urlNode.end - 1 };
|
26701 | allErrors.push(createDiagnostic(urlSpan, Diagnostic.invalid_templateurl));
|
26702 | }
|
26703 | return allErrors;
|
26704 | }
|
26705 | /**
|
26706 | * Return a recursive data structure that chains diagnostic messages.
|
26707 | * @param chain
|
26708 | */
|
26709 | function chainDiagnostics(chain) {
|
26710 | return {
|
26711 | messageText: chain.message,
|
26712 | category: ts.DiagnosticCategory.Error,
|
26713 | code: 0,
|
26714 | next: chain.next ? chain.next.map(chainDiagnostics) : undefined
|
26715 | };
|
26716 | }
|
26717 | /**
|
26718 | * Convert ng.Diagnostic to ts.Diagnostic.
|
26719 | * @param d diagnostic
|
26720 | * @param file
|
26721 | */
|
26722 | function ngDiagnosticToTsDiagnostic(d, file) {
|
26723 | return {
|
26724 | file,
|
26725 | start: d.span.start,
|
26726 | length: d.span.end - d.span.start,
|
26727 | messageText: typeof d.message === 'string' ? d.message : chainDiagnostics(d.message),
|
26728 | category: d.kind,
|
26729 | code: 0,
|
26730 | source: 'ng',
|
26731 | };
|
26732 | }
|
26733 |
|
26734 | /**
|
26735 | * @license
|
26736 | * Copyright Google LLC All Rights Reserved.
|
26737 | *
|
26738 | * Use of this source code is governed by an MIT-style license that can be
|
26739 | * found in the LICENSE file at https://angular.io/license
|
26740 | */
|
26741 | /**
|
26742 | * Traverse the template AST and look for the symbol located at `position`, then
|
26743 | * return the corresponding quick info.
|
26744 | * @param info template AST
|
26745 | * @param position location of the symbol
|
26746 | * @param analyzedModules all NgModules in the program.
|
26747 | */
|
26748 | function getTemplateHover(info, position, analyzedModules) {
|
26749 | var _a, _b;
|
26750 | const symbolInfo = locateSymbols(info, position)[0];
|
26751 | if (!symbolInfo) {
|
26752 | return;
|
26753 | }
|
26754 | const { symbol, span, staticSymbol } = symbolInfo;
|
26755 | // The container is either the symbol's container (for example, 'AppComponent'
|
26756 | // is the container of the symbol 'title' in its template) or the NgModule
|
26757 | // that the directive belongs to (the container of AppComponent is AppModule).
|
26758 | let containerName = (_a = symbol.container) === null || _a === void 0 ? void 0 : _a.name;
|
26759 | if (!containerName && staticSymbol) {
|
26760 | // If there is a static symbol then the target is a directive.
|
26761 | const ngModule = analyzedModules.ngModuleByPipeOrDirective.get(staticSymbol);
|
26762 | containerName = ngModule === null || ngModule === void 0 ? void 0 : ngModule.type.reference.name;
|
26763 | }
|
26764 | return createQuickInfo(symbol.name, symbol.kind, span, containerName, (_b = symbol.type) === null || _b === void 0 ? void 0 : _b.name, symbol.documentation);
|
26765 | }
|
26766 | /**
|
26767 | * Get quick info for Angular semantic entities in TypeScript files, like Directives.
|
26768 | * @param position location of the symbol in the source file
|
26769 | * @param declarations All Directive-like declarations in the source file.
|
26770 | * @param analyzedModules all NgModules in the program.
|
26771 | */
|
26772 | function getTsHover(position, declarations, analyzedModules) {
|
26773 | for (const { declarationSpan, metadata } of declarations) {
|
26774 | if (inSpan(position, declarationSpan)) {
|
26775 | const staticSymbol = metadata.type.reference;
|
26776 | const directiveName = staticSymbol.name;
|
26777 | const kind = metadata.isComponent ? 'component' : 'directive';
|
26778 | const textSpan = ts.createTextSpanFromBounds(declarationSpan.start, declarationSpan.end);
|
26779 | const ngModule = analyzedModules.ngModuleByPipeOrDirective.get(staticSymbol);
|
26780 | const moduleName = ngModule === null || ngModule === void 0 ? void 0 : ngModule.type.reference.name;
|
26781 | return createQuickInfo(directiveName, kind, textSpan, moduleName, ts.ScriptElementKind.classElement);
|
26782 | }
|
26783 | }
|
26784 | }
|
26785 | // Reverse mappings of enum would generate strings
|
26786 | const ALIAS_NAME = ts.SymbolDisplayPartKind[ts.SymbolDisplayPartKind.aliasName];
|
26787 | const SYMBOL_INTERFACE = ts.SymbolDisplayPartKind[ts.SymbolDisplayPartKind.interfaceName];
|
26788 | const SYMBOL_PUNC = ts.SymbolDisplayPartKind[ts.SymbolDisplayPartKind.punctuation];
|
26789 | const SYMBOL_SPACE = ts.SymbolDisplayPartKind[ts.SymbolDisplayPartKind.space];
|
26790 | const SYMBOL_TEXT = ts.SymbolDisplayPartKind[ts.SymbolDisplayPartKind.text];
|
26791 | /**
|
26792 | * Construct a QuickInfo object taking into account its container and type.
|
26793 | * @param name Name of the QuickInfo target
|
26794 | * @param kind component, directive, pipe, etc.
|
26795 | * @param textSpan span of the target
|
26796 | * @param containerName either the Symbol's container or the NgModule that contains the directive
|
26797 | * @param type user-friendly name of the type
|
26798 | * @param documentation docstring or comment
|
26799 | */
|
26800 | function createQuickInfo(name, kind, textSpan, containerName, type, documentation) {
|
26801 | const containerDisplayParts = containerName ?
|
26802 | [
|
26803 | { text: containerName, kind: SYMBOL_INTERFACE },
|
26804 | { text: '.', kind: SYMBOL_PUNC },
|
26805 | ] :
|
26806 | [];
|
26807 | const typeDisplayParts = type ?
|
26808 | [
|
26809 | { text: ':', kind: SYMBOL_PUNC },
|
26810 | { text: ' ', kind: SYMBOL_SPACE },
|
26811 | { text: type, kind: SYMBOL_INTERFACE },
|
26812 | ] :
|
26813 | [];
|
26814 | return {
|
26815 | kind: kind,
|
26816 | kindModifiers: ts.ScriptElementKindModifier.none,
|
26817 | textSpan: textSpan,
|
26818 | displayParts: [
|
26819 | { text: '(', kind: SYMBOL_PUNC },
|
26820 | { text: kind, kind: SYMBOL_TEXT },
|
26821 | { text: ')', kind: SYMBOL_PUNC },
|
26822 | { text: ' ', kind: SYMBOL_SPACE },
|
26823 | ...containerDisplayParts,
|
26824 | { text: name, kind: SYMBOL_INTERFACE },
|
26825 | ...typeDisplayParts,
|
26826 | ],
|
26827 | documentation,
|
26828 | };
|
26829 | }
|
26830 |
|
26831 | /**
|
26832 | * @license
|
26833 | * Copyright Google LLC All Rights Reserved.
|
26834 | *
|
26835 | * Use of this source code is governed by an MIT-style license that can be
|
26836 | * found in the LICENSE file at https://angular.io/license
|
26837 | */
|
26838 | /**
|
26839 | * Create an instance of an Angular `LanguageService`.
|
26840 | *
|
26841 | * @publicApi
|
26842 | */
|
26843 | function createLanguageService(host) {
|
26844 | return new LanguageServiceImpl(host);
|
26845 | }
|
26846 | class LanguageServiceImpl {
|
26847 | constructor(host) {
|
26848 | this.host = host;
|
26849 | }
|
26850 | getSemanticDiagnostics(fileName) {
|
26851 | const analyzedModules = this.host.getAnalyzedModules(); // same role as 'synchronizeHostData'
|
26852 | const ngDiagnostics = [];
|
26853 | const templates = this.host.getTemplates(fileName);
|
26854 | for (const template of templates) {
|
26855 | const ast = this.host.getTemplateAst(template);
|
26856 | if (ast) {
|
26857 | ngDiagnostics.push(...getTemplateDiagnostics(ast));
|
26858 | }
|
26859 | }
|
26860 | const declarations = this.host.getDeclarations(fileName);
|
26861 | ngDiagnostics.push(...getDeclarationDiagnostics(declarations, analyzedModules, this.host));
|
26862 | const sourceFile = fileName.endsWith('.ts') ? this.host.getSourceFile(fileName) : undefined;
|
26863 | const tsDiagnostics = ngDiagnostics.map(d => ngDiagnosticToTsDiagnostic(d, sourceFile));
|
26864 | return [...tss.sortAndDeduplicateDiagnostics(tsDiagnostics)];
|
26865 | }
|
26866 | getCompletionsAtPosition(fileName, position, _options) {
|
26867 | this.host.getAnalyzedModules(); // same role as 'synchronizeHostData'
|
26868 | const ast = this.host.getTemplateAstAtPosition(fileName, position);
|
26869 | if (!ast) {
|
26870 | return;
|
26871 | }
|
26872 | const results = getTemplateCompletions(ast, position);
|
26873 | if (!results || !results.length) {
|
26874 | return;
|
26875 | }
|
26876 | return {
|
26877 | isGlobalCompletion: false,
|
26878 | isMemberCompletion: false,
|
26879 | isNewIdentifierLocation: false,
|
26880 | // Cast CompletionEntry.kind from ng.CompletionKind to ts.ScriptElementKind
|
26881 | entries: results,
|
26882 | };
|
26883 | }
|
26884 | getDefinitionAndBoundSpan(fileName, position) {
|
26885 | this.host.getAnalyzedModules(); // same role as 'synchronizeHostData'
|
26886 | const templateInfo = this.host.getTemplateAstAtPosition(fileName, position);
|
26887 | if (templateInfo) {
|
26888 | return getDefinitionAndBoundSpan(templateInfo, position);
|
26889 | }
|
26890 | // Attempt to get Angular-specific definitions in a TypeScript file, like templates defined
|
26891 | // in a `templateUrl` property.
|
26892 | if (fileName.endsWith('.ts')) {
|
26893 | const sf = this.host.getSourceFile(fileName);
|
26894 | if (sf) {
|
26895 | return getTsDefinitionAndBoundSpan(sf, position, this.host.tsLsHost);
|
26896 | }
|
26897 | }
|
26898 | }
|
26899 | getQuickInfoAtPosition(fileName, position) {
|
26900 | const analyzedModules = this.host.getAnalyzedModules(); // same role as 'synchronizeHostData'
|
26901 | const templateInfo = this.host.getTemplateAstAtPosition(fileName, position);
|
26902 | if (templateInfo) {
|
26903 | return getTemplateHover(templateInfo, position, analyzedModules);
|
26904 | }
|
26905 | // Attempt to get Angular-specific hover information in a TypeScript file, the NgModule a
|
26906 | // directive belongs to.
|
26907 | const declarations = this.host.getDeclarations(fileName);
|
26908 | return getTsHover(position, declarations, analyzedModules);
|
26909 | }
|
26910 | getReferencesAtPosition(fileName, position) {
|
26911 | const defAndSpan = this.getDefinitionAndBoundSpan(fileName, position);
|
26912 | if (!(defAndSpan === null || defAndSpan === void 0 ? void 0 : defAndSpan.definitions)) {
|
26913 | return;
|
26914 | }
|
26915 | const { definitions } = defAndSpan;
|
26916 | const tsDef = definitions.find(def => def.fileName.endsWith('.ts'));
|
26917 | if (!tsDef) {
|
26918 | return;
|
26919 | }
|
26920 | return this.host.tsLS.getReferencesAtPosition(tsDef.fileName, tsDef.textSpan.start);
|
26921 | }
|
26922 | }
|
26923 |
|
26924 | /**
|
26925 | * @license
|
26926 | * Copyright Google LLC All Rights Reserved.
|
26927 | *
|
26928 | * Use of this source code is governed by an MIT-style license that can be
|
26929 | * found in the LICENSE file at https://angular.io/license
|
26930 | */
|
26931 | function getClosureSafeProperty(objWithPropertyToExtract) {
|
26932 | for (let key in objWithPropertyToExtract) {
|
26933 | if (objWithPropertyToExtract[key] === getClosureSafeProperty) {
|
26934 | return key;
|
26935 | }
|
26936 | }
|
26937 | throw Error('Could not find renamed property on target object.');
|
26938 | }
|
26939 |
|
26940 | /**
|
26941 | * @license
|
26942 | * Copyright Google LLC All Rights Reserved.
|
26943 | *
|
26944 | * Use of this source code is governed by an MIT-style license that can be
|
26945 | * found in the LICENSE file at https://angular.io/license
|
26946 | */
|
26947 | function stringify$1(token) {
|
26948 | if (typeof token === 'string') {
|
26949 | return token;
|
26950 | }
|
26951 | if (Array.isArray(token)) {
|
26952 | return '[' + token.map(stringify$1).join(', ') + ']';
|
26953 | }
|
26954 | if (token == null) {
|
26955 | return '' + token;
|
26956 | }
|
26957 | if (token.overriddenName) {
|
26958 | return `${token.overriddenName}`;
|
26959 | }
|
26960 | if (token.name) {
|
26961 | return `${token.name}`;
|
26962 | }
|
26963 | const res = token.toString();
|
26964 | if (res == null) {
|
26965 | return '' + res;
|
26966 | }
|
26967 | const newLineIndex = res.indexOf('\n');
|
26968 | return newLineIndex === -1 ? res : res.substring(0, newLineIndex);
|
26969 | }
|
26970 | /**
|
26971 | * Concatenates two strings with separator, allocating new strings only when necessary.
|
26972 | *
|
26973 | * @param before before string.
|
26974 | * @param separator separator string.
|
26975 | * @param after after string.
|
26976 | * @returns concatenated string.
|
26977 | */
|
26978 | function concatStringsWithSpace(before, after) {
|
26979 | return (before == null || before === '') ?
|
26980 | (after === null ? '' : after) :
|
26981 | ((after == null || after === '') ? before : before + ' ' + after);
|
26982 | }
|
26983 |
|
26984 | /**
|
26985 | * @license
|
26986 | * Copyright Google LLC All Rights Reserved.
|
26987 | *
|
26988 | * Use of this source code is governed by an MIT-style license that can be
|
26989 | * found in the LICENSE file at https://angular.io/license
|
26990 | */
|
26991 | const __forward_ref__ = getClosureSafeProperty({ __forward_ref__: getClosureSafeProperty });
|
26992 | /**
|
26993 | * Allows to refer to references which are not yet defined.
|
26994 | *
|
26995 | * For instance, `forwardRef` is used when the `token` which we need to refer to for the purposes of
|
26996 | * DI is declared, but not yet defined. It is also used when the `token` which we use when creating
|
26997 | * a query is not yet defined.
|
26998 | *
|
26999 | * @usageNotes
|
27000 | * ### Example
|
27001 | * {@example core/di/ts/forward_ref/forward_ref_spec.ts region='forward_ref'}
|
27002 | * @publicApi
|
27003 | */
|
27004 | function forwardRef(forwardRefFn) {
|
27005 | forwardRefFn.__forward_ref__ = forwardRef;
|
27006 | forwardRefFn.toString = function () {
|
27007 | return stringify$1(this());
|
27008 | };
|
27009 | return forwardRefFn;
|
27010 | }
|
27011 | /**
|
27012 | * Lazily retrieves the reference value from a forwardRef.
|
27013 | *
|
27014 | * Acts as the identity function when given a non-forward-ref value.
|
27015 | *
|
27016 | * @usageNotes
|
27017 | * ### Example
|
27018 | *
|
27019 | * {@example core/di/ts/forward_ref/forward_ref_spec.ts region='resolve_forward_ref'}
|
27020 | *
|
27021 | * @see `forwardRef`
|
27022 | * @publicApi
|
27023 | */
|
27024 | function resolveForwardRef$1(type) {
|
27025 | return isForwardRef(type) ? type() : type;
|
27026 | }
|
27027 | /** Checks whether a function is wrapped by a `forwardRef`. */
|
27028 | function isForwardRef(fn) {
|
27029 | return typeof fn === 'function' && fn.hasOwnProperty(__forward_ref__) &&
|
27030 | fn.__forward_ref__ === forwardRef;
|
27031 | }
|
27032 |
|
27033 | /**
|
27034 | * @license
|
27035 | * Copyright Google LLC All Rights Reserved.
|
27036 | *
|
27037 | * Use of this source code is governed by an MIT-style license that can be
|
27038 | * found in the LICENSE file at https://angular.io/license
|
27039 | */
|
27040 | // Base URL for the error details page.
|
27041 | // Keep this value in sync with a similar const in
|
27042 | // `packages/compiler-cli/src/ngtsc/diagnostics/src/error_code.ts`.
|
27043 | const ERROR_DETAILS_PAGE_BASE_URL = 'https://angular.io/errors';
|
27044 | class RuntimeError extends Error {
|
27045 | constructor(code, message) {
|
27046 | super(formatRuntimeError(code, message));
|
27047 | this.code = code;
|
27048 | }
|
27049 | }
|
27050 | // Contains a set of error messages that have details guides at angular.io.
|
27051 | // Full list of available error guides can be found at https://angular.io/errors
|
27052 | /* tslint:disable:no-toplevel-property-access */
|
27053 | const RUNTIME_ERRORS_WITH_GUIDES = new Set([
|
27054 | "100" /* EXPRESSION_CHANGED_AFTER_CHECKED */,
|
27055 | "200" /* CYCLIC_DI_DEPENDENCY */,
|
27056 | "201" /* PROVIDER_NOT_FOUND */,
|
27057 | "300" /* MULTIPLE_COMPONENTS_MATCH */,
|
27058 | "301" /* EXPORT_NOT_FOUND */,
|
27059 | "302" /* PIPE_NOT_FOUND */,
|
27060 | ]);
|
27061 | /* tslint:enable:no-toplevel-property-access */
|
27062 | /** Called to format a runtime error */
|
27063 | function formatRuntimeError(code, message) {
|
27064 | const fullCode = code ? `NG0${code}: ` : '';
|
27065 | let errorMessage = `${fullCode}${message}`;
|
27066 | // Some runtime errors are still thrown without `ngDevMode` (for example
|
27067 | // `throwProviderNotFoundError`), so we add `ngDevMode` check here to avoid pulling
|
27068 | // `RUNTIME_ERRORS_WITH_GUIDES` symbol into prod bundles.
|
27069 | // TODO: revisit all instances where `RuntimeError` is thrown and see if `ngDevMode` can be added
|
27070 | // there instead to tree-shake more devmode-only code (and eventually remove `ngDevMode` check
|
27071 | // from this code).
|
27072 | if (ngDevMode && RUNTIME_ERRORS_WITH_GUIDES.has(code)) {
|
27073 | errorMessage = `${errorMessage}. Find more at ${ERROR_DETAILS_PAGE_BASE_URL}/NG0${code}`;
|
27074 | }
|
27075 | return errorMessage;
|
27076 | }
|
27077 |
|
27078 | /**
|
27079 | * @license
|
27080 | * Copyright Google LLC All Rights Reserved.
|
27081 | *
|
27082 | * Use of this source code is governed by an MIT-style license that can be
|
27083 | * found in the LICENSE file at https://angular.io/license
|
27084 | */
|
27085 | /**
|
27086 | * Used for stringify render output in Ivy.
|
27087 | * Important! This function is very performance-sensitive and we should
|
27088 | * be extra careful not to introduce megamorphic reads in it.
|
27089 | * Check `core/test/render3/perf/render_stringify` for benchmarks and alternate implementations.
|
27090 | */
|
27091 | function renderStringify(value) {
|
27092 | if (typeof value === 'string')
|
27093 | return value;
|
27094 | if (value == null)
|
27095 | return '';
|
27096 | // Use `String` so that it invokes the `toString` method of the value. Note that this
|
27097 | // appears to be faster than calling `value.toString` (see `render_stringify` benchmark).
|
27098 | return String(value);
|
27099 | }
|
27100 | /**
|
27101 | * Used to stringify a value so that it can be displayed in an error message.
|
27102 | * Important! This function contains a megamorphic read and should only be
|
27103 | * used for error messages.
|
27104 | */
|
27105 | function stringifyForError(value) {
|
27106 | if (typeof value === 'function')
|
27107 | return value.name || value.toString();
|
27108 | if (typeof value === 'object' && value != null && typeof value.type === 'function') {
|
27109 | return value.type.name || value.type.toString();
|
27110 | }
|
27111 | return renderStringify(value);
|
27112 | }
|
27113 |
|
27114 | /** Called when directives inject each other (creating a circular dependency) */
|
27115 | function throwCyclicDependencyError(token, path) {
|
27116 | const depPath = path ? `. Dependency path: ${path.join(' > ')} > ${token}` : '';
|
27117 | throw new RuntimeError("200" /* CYCLIC_DI_DEPENDENCY */, `Circular dependency in DI detected for ${token}${depPath}`);
|
27118 | }
|
27119 | /** Throws an error when a token is not found in DI. */
|
27120 | function throwProviderNotFoundError(token, injectorName) {
|
27121 | const injectorDetails = injectorName ? ` in ${injectorName}` : '';
|
27122 | throw new RuntimeError("201" /* PROVIDER_NOT_FOUND */, `No provider for ${stringifyForError(token)} found${injectorDetails}`);
|
27123 | }
|
27124 |
|
27125 | /**
|
27126 | * @license
|
27127 | * Copyright Google LLC All Rights Reserved.
|
27128 | *
|
27129 | * Use of this source code is governed by an MIT-style license that can be
|
27130 | * found in the LICENSE file at https://angular.io/license
|
27131 | */
|
27132 | function assertNumber(actual, msg) {
|
27133 | if (!(typeof actual === 'number')) {
|
27134 | throwError(msg, typeof actual, 'number', '===');
|
27135 | }
|
27136 | }
|
27137 | function assertString(actual, msg) {
|
27138 | if (!(typeof actual === 'string')) {
|
27139 | throwError(msg, actual === null ? 'null' : typeof actual, 'string', '===');
|
27140 | }
|
27141 | }
|
27142 | function assertFunction(actual, msg) {
|
27143 | if (!(typeof actual === 'function')) {
|
27144 | throwError(msg, actual === null ? 'null' : typeof actual, 'function', '===');
|
27145 | }
|
27146 | }
|
27147 | function assertEqual(actual, expected, msg) {
|
27148 | if (!(actual == expected)) {
|
27149 | throwError(msg, actual, expected, '==');
|
27150 | }
|
27151 | }
|
27152 | function assertNotEqual(actual, expected, msg) {
|
27153 | if (!(actual != expected)) {
|
27154 | throwError(msg, actual, expected, '!=');
|
27155 | }
|
27156 | }
|
27157 | function assertSame(actual, expected, msg) {
|
27158 | if (!(actual === expected)) {
|
27159 | throwError(msg, actual, expected, '===');
|
27160 | }
|
27161 | }
|
27162 | function assertNotSame(actual, expected, msg) {
|
27163 | if (!(actual !== expected)) {
|
27164 | throwError(msg, actual, expected, '!==');
|
27165 | }
|
27166 | }
|
27167 | function assertLessThan(actual, expected, msg) {
|
27168 | if (!(actual < expected)) {
|
27169 | throwError(msg, actual, expected, '<');
|
27170 | }
|
27171 | }
|
27172 | function assertGreaterThan(actual, expected, msg) {
|
27173 | if (!(actual > expected)) {
|
27174 | throwError(msg, actual, expected, '>');
|
27175 | }
|
27176 | }
|
27177 | function assertGreaterThanOrEqual(actual, expected, msg) {
|
27178 | if (!(actual >= expected)) {
|
27179 | throwError(msg, actual, expected, '>=');
|
27180 | }
|
27181 | }
|
27182 | function assertDefined(actual, msg) {
|
27183 | if (actual == null) {
|
27184 | throwError(msg, actual, null, '!=');
|
27185 | }
|
27186 | }
|
27187 | function throwError(msg, actual, expected, comparison) {
|
27188 | throw new Error(`ASSERTION ERROR: ${msg}` +
|
27189 | (comparison == null ? '' : ` [Expected=> ${expected} ${comparison} ${actual} <=Actual]`));
|
27190 | }
|
27191 | function assertDomNode(node) {
|
27192 | // If we're in a worker, `Node` will not be defined.
|
27193 | if (!(typeof Node !== 'undefined' && node instanceof Node) &&
|
27194 | !(typeof node === 'object' && node != null &&
|
27195 | node.constructor.name === 'WebWorkerRenderNode')) {
|
27196 | throwError(`The provided value must be an instance of a DOM Node but got ${stringify$1(node)}`);
|
27197 | }
|
27198 | }
|
27199 | function assertIndexInRange(arr, index) {
|
27200 | assertDefined(arr, 'Array must be defined.');
|
27201 | const maxLen = arr.length;
|
27202 | if (index < 0 || index >= maxLen) {
|
27203 | throwError(`Index expected to be less than ${maxLen} but got ${index}`);
|
27204 | }
|
27205 | }
|
27206 |
|
27207 | /**
|
27208 | * @license
|
27209 | * Copyright Google LLC All Rights Reserved.
|
27210 | *
|
27211 | * Use of this source code is governed by an MIT-style license that can be
|
27212 | * found in the LICENSE file at https://angular.io/license
|
27213 | */
|
27214 | /**
|
27215 | * Construct an `InjectableDef` which defines how a token will be constructed by the DI system, and
|
27216 | * in which injectors (if any) it will be available.
|
27217 | *
|
27218 | * This should be assigned to a static `ɵprov` field on a type, which will then be an
|
27219 | * `InjectableType`.
|
27220 | *
|
27221 | * Options:
|
27222 | * * `providedIn` determines which injectors will include the injectable, by either associating it
|
27223 | * with an `@NgModule` or other `InjectorType`, or by specifying that this injectable should be
|
27224 | * provided in the `'root'` injector, which will be the application-level injector in most apps.
|
27225 | * * `factory` gives the zero argument function which will create an instance of the injectable.
|
27226 | * The factory can call `inject` to access the `Injector` and request injection of dependencies.
|
27227 | *
|
27228 | * @codeGenApi
|
27229 | * @publicApi This instruction has been emitted by ViewEngine for some time and is deployed to npm.
|
27230 | */
|
27231 | function ɵɵdefineInjectable(opts) {
|
27232 | return {
|
27233 | token: opts.token,
|
27234 | providedIn: opts.providedIn || null,
|
27235 | factory: opts.factory,
|
27236 | value: undefined,
|
27237 | };
|
27238 | }
|
27239 | /**
|
27240 | * Construct an `InjectorDef` which configures an injector.
|
27241 | *
|
27242 | * This should be assigned to a static injector def (`ɵinj`) field on a type, which will then be an
|
27243 | * `InjectorType`.
|
27244 | *
|
27245 | * Options:
|
27246 | *
|
27247 | * * `factory`: an `InjectorType` is an instantiable type, so a zero argument `factory` function to
|
27248 | * create the type must be provided. If that factory function needs to inject arguments, it can
|
27249 | * use the `inject` function.
|
27250 | * * `providers`: an optional array of providers to add to the injector. Each provider must
|
27251 | * either have a factory or point to a type which has a `ɵprov` static property (the
|
27252 | * type must be an `InjectableType`).
|
27253 | * * `imports`: an optional array of imports of other `InjectorType`s or `InjectorTypeWithModule`s
|
27254 | * whose providers will also be added to the injector. Locally provided types will override
|
27255 | * providers from imports.
|
27256 | *
|
27257 | * @codeGenApi
|
27258 | */
|
27259 | function ɵɵdefineInjector(options) {
|
27260 | return {
|
27261 | factory: options.factory,
|
27262 | providers: options.providers || [],
|
27263 | imports: options.imports || [],
|
27264 | };
|
27265 | }
|
27266 | /**
|
27267 | * Read the injectable def (`ɵprov`) for `type` in a way which is immune to accidentally reading
|
27268 | * inherited value.
|
27269 | *
|
27270 | * @param type A type which may have its own (non-inherited) `ɵprov`.
|
27271 | */
|
27272 | function getInjectableDef(type) {
|
27273 | return getOwnDefinition(type, NG_PROV_DEF) || getOwnDefinition(type, NG_INJECTABLE_DEF);
|
27274 | }
|
27275 | /**
|
27276 | * Return definition only if it is defined directly on `type` and is not inherited from a base
|
27277 | * class of `type`.
|
27278 | */
|
27279 | function getOwnDefinition(type, field) {
|
27280 | return type.hasOwnProperty(field) ? type[field] : null;
|
27281 | }
|
27282 | const NG_PROV_DEF = getClosureSafeProperty({ ɵprov: getClosureSafeProperty });
|
27283 | const NG_INJ_DEF = getClosureSafeProperty({ ɵinj: getClosureSafeProperty });
|
27284 | // We need to keep these around so we can read off old defs if new defs are unavailable
|
27285 | const NG_INJECTABLE_DEF = getClosureSafeProperty({ ngInjectableDef: getClosureSafeProperty });
|
27286 | const NG_INJECTOR_DEF = getClosureSafeProperty({ ngInjectorDef: getClosureSafeProperty });
|
27287 |
|
27288 | /**
|
27289 | * @license
|
27290 | * Copyright Google LLC All Rights Reserved.
|
27291 | *
|
27292 | * Use of this source code is governed by an MIT-style license that can be
|
27293 | * found in the LICENSE file at https://angular.io/license
|
27294 | */
|
27295 | /**
|
27296 | * Injection flags for DI.
|
27297 | *
|
27298 | * @publicApi
|
27299 | */
|
27300 | var InjectFlags;
|
27301 | (function (InjectFlags) {
|
27302 | // TODO(alxhub): make this 'const' (and remove `InternalInjectFlags` enum) when ngc no longer
|
27303 | // writes exports of it into ngfactory files.
|
27304 | /** Check self and check parent injector if needed */
|
27305 | InjectFlags[InjectFlags["Default"] = 0] = "Default";
|
27306 | /**
|
27307 | * Specifies that an injector should retrieve a dependency from any injector until reaching the
|
27308 | * host element of the current component. (Only used with Element Injector)
|
27309 | */
|
27310 | InjectFlags[InjectFlags["Host"] = 1] = "Host";
|
27311 | /** Don't ascend to ancestors of the node requesting injection. */
|
27312 | InjectFlags[InjectFlags["Self"] = 2] = "Self";
|
27313 | /** Skip the node that is requesting injection. */
|
27314 | InjectFlags[InjectFlags["SkipSelf"] = 4] = "SkipSelf";
|
27315 | /** Inject `defaultValue` instead if token not found. */
|
27316 | InjectFlags[InjectFlags["Optional"] = 8] = "Optional";
|
27317 | })(InjectFlags || (InjectFlags = {}));
|
27318 |
|
27319 | /**
|
27320 | * @license
|
27321 | * Copyright Google LLC All Rights Reserved.
|
27322 | *
|
27323 | * Use of this source code is governed by an MIT-style license that can be
|
27324 | * found in the LICENSE file at https://angular.io/license
|
27325 | */
|
27326 | /**
|
27327 | * Current implementation of inject.
|
27328 | *
|
27329 | * By default, it is `injectInjectorOnly`, which makes it `Injector`-only aware. It can be changed
|
27330 | * to `directiveInject`, which brings in the `NodeInjector` system of ivy. It is designed this
|
27331 | * way for two reasons:
|
27332 | * 1. `Injector` should not depend on ivy logic.
|
27333 | * 2. To maintain tree shake-ability we don't want to bring in unnecessary code.
|
27334 | */
|
27335 | let _injectImplementation;
|
27336 | function getInjectImplementation() {
|
27337 | return _injectImplementation;
|
27338 | }
|
27339 | /**
|
27340 | * Sets the current inject implementation.
|
27341 | */
|
27342 | function setInjectImplementation(impl) {
|
27343 | const previous = _injectImplementation;
|
27344 | _injectImplementation = impl;
|
27345 | return previous;
|
27346 | }
|
27347 | /**
|
27348 | * Injects `root` tokens in limp mode.
|
27349 | *
|
27350 | * If no injector exists, we can still inject tree-shakable providers which have `providedIn` set to
|
27351 | * `"root"`. This is known as the limp mode injection. In such case the value is stored in the
|
27352 | * `InjectableDef`.
|
27353 | */
|
27354 | function injectRootLimpMode(token, notFoundValue, flags) {
|
27355 | const injectableDef = getInjectableDef(token);
|
27356 | if (injectableDef && injectableDef.providedIn == 'root') {
|
27357 | return injectableDef.value === undefined ? injectableDef.value = injectableDef.factory() :
|
27358 | injectableDef.value;
|
27359 | }
|
27360 | if (flags & InjectFlags.Optional)
|
27361 | return null;
|
27362 | if (notFoundValue !== undefined)
|
27363 | return notFoundValue;
|
27364 | throwProviderNotFoundError(stringify$1(token), 'Injector');
|
27365 | }
|
27366 |
|
27367 | /**
|
27368 | * @license
|
27369 | * Copyright Google LLC All Rights Reserved.
|
27370 | *
|
27371 | * Use of this source code is governed by an MIT-style license that can be
|
27372 | * found in the LICENSE file at https://angular.io/license
|
27373 | */
|
27374 | /**
|
27375 | * Convince closure compiler that the wrapped function has no side-effects.
|
27376 | *
|
27377 | * Closure compiler always assumes that `toString` has no side-effects. We use this quirk to
|
27378 | * allow us to execute a function but have closure compiler mark the call as no-side-effects.
|
27379 | * It is important that the return value for the `noSideEffects` function be assigned
|
27380 | * to something which is retained otherwise the call to `noSideEffects` will be removed by closure
|
27381 | * compiler.
|
27382 | */
|
27383 | function noSideEffects(fn) {
|
27384 | return { toString: fn }.toString();
|
27385 | }
|
27386 |
|
27387 | /**
|
27388 | * @license
|
27389 | * Copyright Google LLC All Rights Reserved.
|
27390 | *
|
27391 | * Use of this source code is governed by an MIT-style license that can be
|
27392 | * found in the LICENSE file at https://angular.io/license
|
27393 | */
|
27394 | /**
|
27395 | * The strategy that the default change detector uses to detect changes.
|
27396 | * When set, takes effect the next time change detection is triggered.
|
27397 | *
|
27398 | * @see {@link ChangeDetectorRef#usage-notes Change detection usage}
|
27399 | *
|
27400 | * @publicApi
|
27401 | */
|
27402 | var ChangeDetectionStrategy$1;
|
27403 | (function (ChangeDetectionStrategy) {
|
27404 | /**
|
27405 | * Use the `CheckOnce` strategy, meaning that automatic change detection is deactivated
|
27406 | * until reactivated by setting the strategy to `Default` (`CheckAlways`).
|
27407 | * Change detection can still be explicitly invoked.
|
27408 | * This strategy applies to all child directives and cannot be overridden.
|
27409 | */
|
27410 | ChangeDetectionStrategy[ChangeDetectionStrategy["OnPush"] = 0] = "OnPush";
|
27411 | /**
|
27412 | * Use the default `CheckAlways` strategy, in which change detection is automatic until
|
27413 | * explicitly deactivated.
|
27414 | */
|
27415 | ChangeDetectionStrategy[ChangeDetectionStrategy["Default"] = 1] = "Default";
|
27416 | })(ChangeDetectionStrategy$1 || (ChangeDetectionStrategy$1 = {}));
|
27417 | /**
|
27418 | * Defines the possible states of the default change detector.
|
27419 | * @see `ChangeDetectorRef`
|
27420 | */
|
27421 | var ChangeDetectorStatus;
|
27422 | (function (ChangeDetectorStatus) {
|
27423 | /**
|
27424 | * A state in which, after calling `detectChanges()`, the change detector
|
27425 | * state becomes `Checked`, and must be explicitly invoked or reactivated.
|
27426 | */
|
27427 | ChangeDetectorStatus[ChangeDetectorStatus["CheckOnce"] = 0] = "CheckOnce";
|
27428 | /**
|
27429 | * A state in which change detection is skipped until the change detector mode
|
27430 | * becomes `CheckOnce`.
|
27431 | */
|
27432 | ChangeDetectorStatus[ChangeDetectorStatus["Checked"] = 1] = "Checked";
|
27433 | /**
|
27434 | * A state in which change detection continues automatically until explicitly
|
27435 | * deactivated.
|
27436 | */
|
27437 | ChangeDetectorStatus[ChangeDetectorStatus["CheckAlways"] = 2] = "CheckAlways";
|
27438 | /**
|
27439 | * A state in which a change detector sub tree is not a part of the main tree and
|
27440 | * should be skipped.
|
27441 | */
|
27442 | ChangeDetectorStatus[ChangeDetectorStatus["Detached"] = 3] = "Detached";
|
27443 | /**
|
27444 | * Indicates that the change detector encountered an error checking a binding
|
27445 | * or calling a directive lifecycle method and is now in an inconsistent state. Change
|
27446 | * detectors in this state do not detect changes.
|
27447 | */
|
27448 | ChangeDetectorStatus[ChangeDetectorStatus["Errored"] = 4] = "Errored";
|
27449 | /**
|
27450 | * Indicates that the change detector has been destroyed.
|
27451 | */
|
27452 | ChangeDetectorStatus[ChangeDetectorStatus["Destroyed"] = 5] = "Destroyed";
|
27453 | })(ChangeDetectorStatus || (ChangeDetectorStatus = {}));
|
27454 |
|
27455 | /**
|
27456 | * @license
|
27457 | * Copyright Google LLC All Rights Reserved.
|
27458 | *
|
27459 | * Use of this source code is governed by an MIT-style license that can be
|
27460 | * found in the LICENSE file at https://angular.io/license
|
27461 | */
|
27462 | /**
|
27463 | * Defines template and style encapsulation options available for Component's {@link Component}.
|
27464 | *
|
27465 | * See {@link Component#encapsulation encapsulation}.
|
27466 | *
|
27467 | * @usageNotes
|
27468 | * ### Example
|
27469 | *
|
27470 | * {@example core/ts/metadata/encapsulation.ts region='longform'}
|
27471 | *
|
27472 | * @publicApi
|
27473 | */
|
27474 | var ViewEncapsulation$1;
|
27475 | (function (ViewEncapsulation) {
|
27476 | /**
|
27477 | * Emulate `Native` scoping of styles by adding an attribute containing surrogate id to the Host
|
27478 | * Element and pre-processing the style rules provided via {@link Component#styles styles} or
|
27479 | * {@link Component#styleUrls styleUrls}, and adding the new Host Element attribute to all
|
27480 | * selectors.
|
27481 | *
|
27482 | * This is the default option.
|
27483 | */
|
27484 | ViewEncapsulation[ViewEncapsulation["Emulated"] = 0] = "Emulated";
|
27485 | // Historically the 1 value was for `Native` encapsulation which has been removed as of v11.
|
27486 | /**
|
27487 | * Don't provide any template or style encapsulation.
|
27488 | */
|
27489 | ViewEncapsulation[ViewEncapsulation["None"] = 2] = "None";
|
27490 | /**
|
27491 | * Use Shadow DOM to encapsulate styles.
|
27492 | *
|
27493 | * For the DOM this means using modern [Shadow
|
27494 | * DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM) and
|
27495 | * creating a ShadowRoot for Component's Host Element.
|
27496 | */
|
27497 | ViewEncapsulation[ViewEncapsulation["ShadowDom"] = 3] = "ShadowDom";
|
27498 | })(ViewEncapsulation$1 || (ViewEncapsulation$1 = {}));
|
27499 |
|
27500 | /**
|
27501 | * @license
|
27502 | * Copyright Google LLC All Rights Reserved.
|
27503 | *
|
27504 | * Use of this source code is governed by an MIT-style license that can be
|
27505 | * found in the LICENSE file at https://angular.io/license
|
27506 | */
|
27507 | const __globalThis = typeof globalThis !== 'undefined' && globalThis;
|
27508 | const __window$1 = typeof window !== 'undefined' && window;
|
27509 | const __self$1 = typeof self !== 'undefined' && typeof WorkerGlobalScope !== 'undefined' &&
|
27510 | self instanceof WorkerGlobalScope && self;
|
27511 | const __global$1 = typeof global !== 'undefined' && global;
|
27512 | // Always use __globalThis if available, which is the spec-defined global variable across all
|
27513 | // environments, then fallback to __global first, because in Node tests both __global and
|
27514 | // __window may be defined and _global should be __global in that case.
|
27515 | const _global$1 = __globalThis || __global$1 || __window$1 || __self$1;
|
27516 |
|
27517 | /**
|
27518 | * @license
|
27519 | * Copyright Google LLC All Rights Reserved.
|
27520 | *
|
27521 | * Use of this source code is governed by an MIT-style license that can be
|
27522 | * found in the LICENSE file at https://angular.io/license
|
27523 | */
|
27524 | function ngDevModeResetPerfCounters() {
|
27525 | const locationString = typeof location !== 'undefined' ? location.toString() : '';
|
27526 | const newCounters = {
|
27527 | namedConstructors: locationString.indexOf('ngDevMode=namedConstructors') != -1,
|
27528 | firstCreatePass: 0,
|
27529 | tNode: 0,
|
27530 | tView: 0,
|
27531 | rendererCreateTextNode: 0,
|
27532 | rendererSetText: 0,
|
27533 | rendererCreateElement: 0,
|
27534 | rendererAddEventListener: 0,
|
27535 | rendererSetAttribute: 0,
|
27536 | rendererRemoveAttribute: 0,
|
27537 | rendererSetProperty: 0,
|
27538 | rendererSetClassName: 0,
|
27539 | rendererAddClass: 0,
|
27540 | rendererRemoveClass: 0,
|
27541 | rendererSetStyle: 0,
|
27542 | rendererRemoveStyle: 0,
|
27543 | rendererDestroy: 0,
|
27544 | rendererDestroyNode: 0,
|
27545 | rendererMoveNode: 0,
|
27546 | rendererRemoveNode: 0,
|
27547 | rendererAppendChild: 0,
|
27548 | rendererInsertBefore: 0,
|
27549 | rendererCreateComment: 0,
|
27550 | };
|
27551 | // Make sure to refer to ngDevMode as ['ngDevMode'] for closure.
|
27552 | const allowNgDevModeTrue = locationString.indexOf('ngDevMode=false') === -1;
|
27553 | _global$1['ngDevMode'] = allowNgDevModeTrue && newCounters;
|
27554 | return newCounters;
|
27555 | }
|
27556 | /**
|
27557 | * This function checks to see if the `ngDevMode` has been set. If yes,
|
27558 | * then we honor it, otherwise we default to dev mode with additional checks.
|
27559 | *
|
27560 | * The idea is that unless we are doing production build where we explicitly
|
27561 | * set `ngDevMode == false` we should be helping the developer by providing
|
27562 | * as much early warning and errors as possible.
|
27563 | *
|
27564 | * `ɵɵdefineComponent` is guaranteed to have been called before any component template functions
|
27565 | * (and thus Ivy instructions), so a single initialization there is sufficient to ensure ngDevMode
|
27566 | * is defined for the entire instruction set.
|
27567 | *
|
27568 | * When checking `ngDevMode` on toplevel, always init it before referencing it
|
27569 | * (e.g. `((typeof ngDevMode === 'undefined' || ngDevMode) && initNgDevMode())`), otherwise you can
|
27570 | * get a `ReferenceError` like in https://github.com/angular/angular/issues/31595.
|
27571 | *
|
27572 | * Details on possible values for `ngDevMode` can be found on its docstring.
|
27573 | *
|
27574 | * NOTE:
|
27575 | * - changes to the `ngDevMode` name must be synced with `compiler-cli/src/tooling.ts`.
|
27576 | */
|
27577 | function initNgDevMode() {
|
27578 | // The below checks are to ensure that calling `initNgDevMode` multiple times does not
|
27579 | // reset the counters.
|
27580 | // If the `ngDevMode` is not an object, then it means we have not created the perf counters
|
27581 | // yet.
|
27582 | if (typeof ngDevMode === 'undefined' || ngDevMode) {
|
27583 | if (typeof ngDevMode !== 'object') {
|
27584 | ngDevModeResetPerfCounters();
|
27585 | }
|
27586 | return typeof ngDevMode !== 'undefined' && !!ngDevMode;
|
27587 | }
|
27588 | return false;
|
27589 | }
|
27590 |
|
27591 | /**
|
27592 | * @license
|
27593 | * Copyright Google LLC All Rights Reserved.
|
27594 | *
|
27595 | * Use of this source code is governed by an MIT-style license that can be
|
27596 | * found in the LICENSE file at https://angular.io/license
|
27597 | */
|
27598 | /**
|
27599 | * This file contains reuseable "empty" symbols that can be used as default return values
|
27600 | * in different parts of the rendering code. Because the same symbols are returned, this
|
27601 | * allows for identity checks against these values to be consistently used by the framework
|
27602 | * code.
|
27603 | */
|
27604 | const EMPTY_OBJ = {};
|
27605 | const EMPTY_ARRAY = [];
|
27606 | // freezing the values prevents any code from accidentally inserting new values in
|
27607 | if ((typeof ngDevMode === 'undefined' || ngDevMode) && initNgDevMode()) {
|
27608 | // These property accesses can be ignored because ngDevMode will be set to false
|
27609 | // when optimizing code and the whole if statement will be dropped.
|
27610 | // tslint:disable-next-line:no-toplevel-property-access
|
27611 | Object.freeze(EMPTY_OBJ);
|
27612 | // tslint:disable-next-line:no-toplevel-property-access
|
27613 | Object.freeze(EMPTY_ARRAY);
|
27614 | }
|
27615 |
|
27616 | /**
|
27617 | * @license
|
27618 | * Copyright Google LLC All Rights Reserved.
|
27619 | *
|
27620 | * Use of this source code is governed by an MIT-style license that can be
|
27621 | * found in the LICENSE file at https://angular.io/license
|
27622 | */
|
27623 | const NG_COMP_DEF = getClosureSafeProperty({ ɵcmp: getClosureSafeProperty });
|
27624 | const NG_DIR_DEF = getClosureSafeProperty({ ɵdir: getClosureSafeProperty });
|
27625 | const NG_PIPE_DEF = getClosureSafeProperty({ ɵpipe: getClosureSafeProperty });
|
27626 | const NG_MOD_DEF = getClosureSafeProperty({ ɵmod: getClosureSafeProperty });
|
27627 | const NG_LOC_ID_DEF = getClosureSafeProperty({ ɵloc: getClosureSafeProperty });
|
27628 | const NG_FACTORY_DEF = getClosureSafeProperty({ ɵfac: getClosureSafeProperty });
|
27629 | /**
|
27630 | * If a directive is diPublic, bloomAdd sets a property on the type with this constant as
|
27631 | * the key and the directive's unique ID as the value. This allows us to map directives to their
|
27632 | * bloom filter bit for DI.
|
27633 | */
|
27634 | // TODO(misko): This is wrong. The NG_ELEMENT_ID should never be minified.
|
27635 | const NG_ELEMENT_ID = getClosureSafeProperty({ __NG_ELEMENT_ID__: getClosureSafeProperty });
|
27636 |
|
27637 | /**
|
27638 | * @license
|
27639 | * Copyright Google LLC All Rights Reserved.
|
27640 | *
|
27641 | * Use of this source code is governed by an MIT-style license that can be
|
27642 | * found in the LICENSE file at https://angular.io/license
|
27643 | */
|
27644 | /**
|
27645 | * The following getter methods retrieve the definition from the type. Currently the retrieval
|
27646 | * honors inheritance, but in the future we may change the rule to require that definitions are
|
27647 | * explicit. This would require some sort of migration strategy.
|
27648 | */
|
27649 | function getComponentDef(type) {
|
27650 | return type[NG_COMP_DEF] || null;
|
27651 | }
|
27652 |
|
27653 | /**
|
27654 | * @license
|
27655 | * Copyright Google LLC All Rights Reserved.
|
27656 | *
|
27657 | * Use of this source code is governed by an MIT-style license that can be
|
27658 | * found in the LICENSE file at https://angular.io/license
|
27659 | */
|
27660 | // Below are constants for LView indices to help us look up LView members
|
27661 | // without having to remember the specific indices.
|
27662 | // Uglify will inline these when minifying so there shouldn't be a cost.
|
27663 | const HOST = 0;
|
27664 | const TVIEW = 1;
|
27665 | const FLAGS = 2;
|
27666 | const PARENT = 3;
|
27667 | const NEXT = 4;
|
27668 | const TRANSPLANTED_VIEWS_TO_REFRESH = 5;
|
27669 | const T_HOST = 6;
|
27670 | const CLEANUP = 7;
|
27671 | const CONTEXT = 8;
|
27672 | const INJECTOR = 9;
|
27673 | const RENDERER_FACTORY = 10;
|
27674 | const RENDERER = 11;
|
27675 | const SANITIZER = 12;
|
27676 | const CHILD_HEAD = 13;
|
27677 | const CHILD_TAIL = 14;
|
27678 | // FIXME(misko): Investigate if the three declarations aren't all same thing.
|
27679 | const DECLARATION_VIEW = 15;
|
27680 | const DECLARATION_COMPONENT_VIEW = 16;
|
27681 | const DECLARATION_LCONTAINER = 17;
|
27682 | const PREORDER_HOOK_FLAGS = 18;
|
27683 | const QUERIES = 19;
|
27684 | /**
|
27685 | * Size of LView's header. Necessary to adjust for it when setting slots.
|
27686 | *
|
27687 | * IMPORTANT: `HEADER_OFFSET` should only be referred to the in the `ɵɵ*` instructions to translate
|
27688 | * instruction index into `LView` index. All other indexes should be in the `LView` index space and
|
27689 | * there should be no need to refer to `HEADER_OFFSET` anywhere else.
|
27690 | */
|
27691 | const HEADER_OFFSET = 20;
|
27692 | /**
|
27693 | * Converts `TViewType` into human readable text.
|
27694 | * Make sure this matches with `TViewType`
|
27695 | */
|
27696 | const TViewTypeAsString = [
|
27697 | 'Root',
|
27698 | 'Component',
|
27699 | 'Embedded',
|
27700 | ];
|
27701 |
|
27702 | /**
|
27703 | * @license
|
27704 | * Copyright Google LLC All Rights Reserved.
|
27705 | *
|
27706 | * Use of this source code is governed by an MIT-style license that can be
|
27707 | * found in the LICENSE file at https://angular.io/license
|
27708 | */
|
27709 | /**
|
27710 | * Special location which allows easy identification of type. If we have an array which was
|
27711 | * retrieved from the `LView` and that array has `true` at `TYPE` location, we know it is
|
27712 | * `LContainer`.
|
27713 | */
|
27714 | const TYPE = 1;
|
27715 | /**
|
27716 | * Below are constants for LContainer indices to help us look up LContainer members
|
27717 | * without having to remember the specific indices.
|
27718 | * Uglify will inline these when minifying so there shouldn't be a cost.
|
27719 | */
|
27720 | /**
|
27721 | * Flag to signify that this `LContainer` may have transplanted views which need to be change
|
27722 | * detected. (see: `LView[DECLARATION_COMPONENT_VIEW])`.
|
27723 | *
|
27724 | * This flag, once set, is never unset for the `LContainer`. This means that when unset we can skip
|
27725 | * a lot of work in `refreshEmbeddedViews`. But when set we still need to verify
|
27726 | * that the `MOVED_VIEWS` are transplanted and on-push.
|
27727 | */
|
27728 | const HAS_TRANSPLANTED_VIEWS = 2;
|
27729 | // PARENT, NEXT, TRANSPLANTED_VIEWS_TO_REFRESH are indices 3, 4, and 5
|
27730 | // As we already have these constants in LView, we don't need to re-create them.
|
27731 | // T_HOST is index 6
|
27732 | // We already have this constants in LView, we don't need to re-create it.
|
27733 | const NATIVE = 7;
|
27734 | const VIEW_REFS = 8;
|
27735 | const MOVED_VIEWS = 9;
|
27736 | /**
|
27737 | * Size of LContainer's header. Represents the index after which all views in the
|
27738 | * container will be inserted. We need to keep a record of current views so we know
|
27739 | * which views are already in the DOM (and don't need to be re-added) and so we can
|
27740 | * remove views from the DOM when they are no longer required.
|
27741 | */
|
27742 | const CONTAINER_HEADER_OFFSET = 10;
|
27743 |
|
27744 | /**
|
27745 | * @license
|
27746 | * Copyright Google LLC All Rights Reserved.
|
27747 | *
|
27748 | * Use of this source code is governed by an MIT-style license that can be
|
27749 | * found in the LICENSE file at https://angular.io/license
|
27750 | */
|
27751 | /**
|
27752 | * True if `value` is `LView`.
|
27753 | * @param value wrapped value of `RNode`, `LView`, `LContainer`
|
27754 | */
|
27755 | function isLView(value) {
|
27756 | return Array.isArray(value) && typeof value[TYPE] === 'object';
|
27757 | }
|
27758 | /**
|
27759 | * True if `value` is `LContainer`.
|
27760 | * @param value wrapped value of `RNode`, `LView`, `LContainer`
|
27761 | */
|
27762 | function isLContainer(value) {
|
27763 | return Array.isArray(value) && value[TYPE] === true;
|
27764 | }
|
27765 | function isComponentHost(tNode) {
|
27766 | return (tNode.flags & 2 /* isComponentHost */) === 2 /* isComponentHost */;
|
27767 | }
|
27768 | function isComponentDef(def) {
|
27769 | return def.template !== null;
|
27770 | }
|
27771 | function isRootView(target) {
|
27772 | return (target[FLAGS] & 512 /* IsRoot */) !== 0;
|
27773 | }
|
27774 |
|
27775 | /**
|
27776 | * @license
|
27777 | * Copyright Google LLC All Rights Reserved.
|
27778 | *
|
27779 | * Use of this source code is governed by an MIT-style license that can be
|
27780 | * found in the LICENSE file at https://angular.io/license
|
27781 | */
|
27782 | // [Assert functions do not constraint type when they are guarded by a truthy
|
27783 | // expression.](https://github.com/microsoft/TypeScript/issues/37295)
|
27784 | function assertTNodeForLView(tNode, lView) {
|
27785 | assertTNodeForTView(tNode, lView[TVIEW]);
|
27786 | }
|
27787 | function assertTNodeForTView(tNode, tView) {
|
27788 | assertTNode(tNode);
|
27789 | tNode.hasOwnProperty('tView_') &&
|
27790 | assertEqual(tNode.tView_, tView, 'This TNode does not belong to this TView.');
|
27791 | }
|
27792 | function assertTNode(tNode) {
|
27793 | assertDefined(tNode, 'TNode must be defined');
|
27794 | if (!(tNode && typeof tNode === 'object' && tNode.hasOwnProperty('directiveStylingLast'))) {
|
27795 | throwError('Not of type TNode, got: ' + tNode);
|
27796 | }
|
27797 | }
|
27798 | function assertComponentType(actual, msg = 'Type passed in is not ComponentType, it does not have \'ɵcmp\' property.') {
|
27799 | if (!getComponentDef(actual)) {
|
27800 | throwError(msg);
|
27801 | }
|
27802 | }
|
27803 | function assertLContainer(value) {
|
27804 | assertDefined(value, 'LContainer must be defined');
|
27805 | assertEqual(isLContainer(value), true, 'Expecting LContainer');
|
27806 | }
|
27807 | function assertLViewOrUndefined(value) {
|
27808 | value && assertEqual(isLView(value), true, 'Expecting LView or undefined or null');
|
27809 | }
|
27810 | function assertLView(value) {
|
27811 | assertDefined(value, 'LView must be defined');
|
27812 | assertEqual(isLView(value), true, 'Expecting LView');
|
27813 | }
|
27814 | function assertFirstCreatePass(tView, errMessage) {
|
27815 | assertEqual(tView.firstCreatePass, true, errMessage || 'Should only be called in first create pass.');
|
27816 | }
|
27817 | function assertFirstUpdatePass(tView, errMessage) {
|
27818 | assertEqual(tView.firstUpdatePass, true, errMessage || 'Should only be called in first update pass.');
|
27819 | }
|
27820 | /**
|
27821 | * This is a basic sanity check that an object is probably a directive def. DirectiveDef is
|
27822 | * an interface, so we can't do a direct instanceof check.
|
27823 | */
|
27824 | function assertDirectiveDef(obj) {
|
27825 | if (obj.type === undefined || obj.selectors == undefined || obj.inputs === undefined) {
|
27826 | throwError(`Expected a DirectiveDef/ComponentDef and this object does not seem to have the expected shape.`);
|
27827 | }
|
27828 | }
|
27829 | function assertIndexInDeclRange(lView, index) {
|
27830 | const tView = lView[1];
|
27831 | assertBetween(HEADER_OFFSET, tView.bindingStartIndex, index);
|
27832 | }
|
27833 | function assertIndexInExpandoRange(lView, index) {
|
27834 | const tView = lView[1];
|
27835 | assertBetween(tView.expandoStartIndex, lView.length, index);
|
27836 | }
|
27837 | function assertBetween(lower, upper, index) {
|
27838 | if (!(lower <= index && index < upper)) {
|
27839 | throwError(`Index out of range (expecting ${lower} <= ${index} < ${upper})`);
|
27840 | }
|
27841 | }
|
27842 | function assertProjectionSlots(lView, errMessage) {
|
27843 | assertDefined(lView[DECLARATION_COMPONENT_VIEW], 'Component views should exist.');
|
27844 | assertDefined(lView[DECLARATION_COMPONENT_VIEW][T_HOST].projection, errMessage ||
|
27845 | 'Components with projection nodes (<ng-content>) must have projection slots defined.');
|
27846 | }
|
27847 | function assertParentView(lView, errMessage) {
|
27848 | assertDefined(lView, errMessage || 'Component views should always have a parent view (component\'s host view)');
|
27849 | }
|
27850 | /**
|
27851 | * This is a basic sanity check that the `injectorIndex` seems to point to what looks like a
|
27852 | * NodeInjector data structure.
|
27853 | *
|
27854 | * @param lView `LView` which should be checked.
|
27855 | * @param injectorIndex index into the `LView` where the `NodeInjector` is expected.
|
27856 | */
|
27857 | function assertNodeInjector(lView, injectorIndex) {
|
27858 | assertIndexInExpandoRange(lView, injectorIndex);
|
27859 | assertIndexInExpandoRange(lView, injectorIndex + 8 /* PARENT */);
|
27860 | assertNumber(lView[injectorIndex + 0], 'injectorIndex should point to a bloom filter');
|
27861 | assertNumber(lView[injectorIndex + 1], 'injectorIndex should point to a bloom filter');
|
27862 | assertNumber(lView[injectorIndex + 2], 'injectorIndex should point to a bloom filter');
|
27863 | assertNumber(lView[injectorIndex + 3], 'injectorIndex should point to a bloom filter');
|
27864 | assertNumber(lView[injectorIndex + 4], 'injectorIndex should point to a bloom filter');
|
27865 | assertNumber(lView[injectorIndex + 5], 'injectorIndex should point to a bloom filter');
|
27866 | assertNumber(lView[injectorIndex + 6], 'injectorIndex should point to a bloom filter');
|
27867 | assertNumber(lView[injectorIndex + 7], 'injectorIndex should point to a bloom filter');
|
27868 | assertNumber(lView[injectorIndex + 8 /* PARENT */], 'injectorIndex should point to parent injector');
|
27869 | }
|
27870 |
|
27871 | /**
|
27872 | * @license
|
27873 | * Copyright Google LLC All Rights Reserved.
|
27874 | *
|
27875 | * Use of this source code is governed by an MIT-style license that can be
|
27876 | * found in the LICENSE file at https://angular.io/license
|
27877 | */
|
27878 | function getFactoryDef(type, throwNotFound) {
|
27879 | const hasFactoryDef = type.hasOwnProperty(NG_FACTORY_DEF);
|
27880 | if (!hasFactoryDef && throwNotFound === true && ngDevMode) {
|
27881 | throw new Error(`Type ${stringify$1(type)} does not have 'ɵfac' property.`);
|
27882 | }
|
27883 | return hasFactoryDef ? type[NG_FACTORY_DEF] : null;
|
27884 | }
|
27885 |
|
27886 | /**
|
27887 | * @license
|
27888 | * Copyright Google LLC All Rights Reserved.
|
27889 | *
|
27890 | * Use of this source code is governed by an MIT-style license that can be
|
27891 | * found in the LICENSE file at https://angular.io/license
|
27892 | */
|
27893 | /**
|
27894 | * Represents a basic change from a previous to a new value for a single
|
27895 | * property on a directive instance. Passed as a value in a
|
27896 | * {@link SimpleChanges} object to the `ngOnChanges` hook.
|
27897 | *
|
27898 | * @see `OnChanges`
|
27899 | *
|
27900 | * @publicApi
|
27901 | */
|
27902 | class SimpleChange {
|
27903 | constructor(previousValue, currentValue, firstChange) {
|
27904 | this.previousValue = previousValue;
|
27905 | this.currentValue = currentValue;
|
27906 | this.firstChange = firstChange;
|
27907 | }
|
27908 | /**
|
27909 | * Check whether the new value is the first value assigned.
|
27910 | */
|
27911 | isFirstChange() {
|
27912 | return this.firstChange;
|
27913 | }
|
27914 | }
|
27915 |
|
27916 | /**
|
27917 | * @license
|
27918 | * Copyright Google LLC All Rights Reserved.
|
27919 | *
|
27920 | * Use of this source code is governed by an MIT-style license that can be
|
27921 | * found in the LICENSE file at https://angular.io/license
|
27922 | */
|
27923 | function NgOnChangesFeatureImpl(definition) {
|
27924 | if (definition.type.prototype.ngOnChanges) {
|
27925 | definition.setInput = ngOnChangesSetInput;
|
27926 | }
|
27927 | return rememberChangeHistoryAndInvokeOnChangesHook;
|
27928 | }
|
27929 | /**
|
27930 | * This is a synthetic lifecycle hook which gets inserted into `TView.preOrderHooks` to simulate
|
27931 | * `ngOnChanges`.
|
27932 | *
|
27933 | * The hook reads the `NgSimpleChangesStore` data from the component instance and if changes are
|
27934 | * found it invokes `ngOnChanges` on the component instance.
|
27935 | *
|
27936 | * @param this Component instance. Because this function gets inserted into `TView.preOrderHooks`,
|
27937 | * it is guaranteed to be called with component instance.
|
27938 | */
|
27939 | function rememberChangeHistoryAndInvokeOnChangesHook() {
|
27940 | const simpleChangesStore = getSimpleChangesStore(this);
|
27941 | const current = simpleChangesStore === null || simpleChangesStore === void 0 ? void 0 : simpleChangesStore.current;
|
27942 | if (current) {
|
27943 | const previous = simpleChangesStore.previous;
|
27944 | if (previous === EMPTY_OBJ) {
|
27945 | simpleChangesStore.previous = current;
|
27946 | }
|
27947 | else {
|
27948 | // New changes are copied to the previous store, so that we don't lose history for inputs
|
27949 | // which were not changed this time
|
27950 | for (let key in current) {
|
27951 | previous[key] = current[key];
|
27952 | }
|
27953 | }
|
27954 | simpleChangesStore.current = null;
|
27955 | this.ngOnChanges(current);
|
27956 | }
|
27957 | }
|
27958 | function ngOnChangesSetInput(instance, value, publicName, privateName) {
|
27959 | const simpleChangesStore = getSimpleChangesStore(instance) ||
|
27960 | setSimpleChangesStore(instance, { previous: EMPTY_OBJ, current: null });
|
27961 | const current = simpleChangesStore.current || (simpleChangesStore.current = {});
|
27962 | const previous = simpleChangesStore.previous;
|
27963 | const declaredName = this.declaredInputs[publicName];
|
27964 | const previousChange = previous[declaredName];
|
27965 | current[declaredName] = new SimpleChange(previousChange && previousChange.currentValue, value, previous === EMPTY_OBJ);
|
27966 | instance[privateName] = value;
|
27967 | }
|
27968 | const SIMPLE_CHANGES_STORE = '__ngSimpleChanges__';
|
27969 | function getSimpleChangesStore(instance) {
|
27970 | return instance[SIMPLE_CHANGES_STORE] || null;
|
27971 | }
|
27972 | function setSimpleChangesStore(instance, store) {
|
27973 | return instance[SIMPLE_CHANGES_STORE] = store;
|
27974 | }
|
27975 |
|
27976 | /**
|
27977 | * @license
|
27978 | * Copyright Google LLC All Rights Reserved.
|
27979 | *
|
27980 | * Use of this source code is governed by an MIT-style license that can be
|
27981 | * found in the LICENSE file at https://angular.io/license
|
27982 | */
|
27983 | const SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
|
27984 | const MATH_ML_NAMESPACE = 'http://www.w3.org/1998/MathML/';
|
27985 |
|
27986 | /**
|
27987 | * @license
|
27988 | * Copyright Google LLC All Rights Reserved.
|
27989 | *
|
27990 | * Use of this source code is governed by an MIT-style license that can be
|
27991 | * found in the LICENSE file at https://angular.io/license
|
27992 | */
|
27993 | /**
|
27994 | * This property will be monkey-patched on elements, components and directives
|
27995 | */
|
27996 | const MONKEY_PATCH_KEY_NAME = '__ngContext__';
|
27997 |
|
27998 | /**
|
27999 | * @license
|
28000 | * Copyright Google LLC All Rights Reserved.
|
28001 | *
|
28002 | * Use of this source code is governed by an MIT-style license that can be
|
28003 | * found in the LICENSE file at https://angular.io/license
|
28004 | */
|
28005 | /**
|
28006 | * Access the object that represents the `document` for this platform.
|
28007 | *
|
28008 | * Ivy calls this whenever it needs to access the `document` object.
|
28009 | * For example to create the renderer or to do sanitization.
|
28010 | */
|
28011 | function getDocument() {
|
28012 | if (typeof document !== 'undefined') {
|
28013 | return document;
|
28014 | }
|
28015 | // No "document" can be found. This should only happen if we are running ivy outside Angular and
|
28016 | // the current platform is not a browser. Since this is not a supported scenario at the moment
|
28017 | // this should not happen in Angular apps.
|
28018 | // Once we support running ivy outside of Angular we will need to publish `setDocument()` as a
|
28019 | // public API. Meanwhile we just return `undefined` and let the application fail.
|
28020 | return undefined;
|
28021 | }
|
28022 |
|
28023 | /**
|
28024 | * @license
|
28025 | * Copyright Google LLC All Rights Reserved.
|
28026 | *
|
28027 | * Use of this source code is governed by an MIT-style license that can be
|
28028 | * found in the LICENSE file at https://angular.io/license
|
28029 | */
|
28030 | // TODO: cleanup once the code is merged in angular/angular
|
28031 | var RendererStyleFlags3;
|
28032 | (function (RendererStyleFlags3) {
|
28033 | RendererStyleFlags3[RendererStyleFlags3["Important"] = 1] = "Important";
|
28034 | RendererStyleFlags3[RendererStyleFlags3["DashCase"] = 2] = "DashCase";
|
28035 | })(RendererStyleFlags3 || (RendererStyleFlags3 = {}));
|
28036 | /** Returns whether the `renderer` is a `ProceduralRenderer3` */
|
28037 | function isProceduralRenderer(renderer) {
|
28038 | return !!(renderer.listen);
|
28039 | }
|
28040 | const ɵ0 = (hostElement, rendererType) => {
|
28041 | return getDocument();
|
28042 | };
|
28043 | const domRendererFactory3 = {
|
28044 | createRenderer: ɵ0
|
28045 | };
|
28046 |
|
28047 | /**
|
28048 | * @license
|
28049 | * Copyright Google LLC All Rights Reserved.
|
28050 | *
|
28051 | * Use of this source code is governed by an MIT-style license that can be
|
28052 | * found in the LICENSE file at https://angular.io/license
|
28053 | */
|
28054 | /**
|
28055 | * For efficiency reasons we often put several different data types (`RNode`, `LView`, `LContainer`)
|
28056 | * in same location in `LView`. This is because we don't want to pre-allocate space for it
|
28057 | * because the storage is sparse. This file contains utilities for dealing with such data types.
|
28058 | *
|
28059 | * How do we know what is stored at a given location in `LView`.
|
28060 | * - `Array.isArray(value) === false` => `RNode` (The normal storage value)
|
28061 | * - `Array.isArray(value) === true` => then the `value[0]` represents the wrapped value.
|
28062 | * - `typeof value[TYPE] === 'object'` => `LView`
|
28063 | * - This happens when we have a component at a given location
|
28064 | * - `typeof value[TYPE] === true` => `LContainer`
|
28065 | * - This happens when we have `LContainer` binding at a given location.
|
28066 | *
|
28067 | *
|
28068 | * NOTE: it is assumed that `Array.isArray` and `typeof` operations are very efficient.
|
28069 | */
|
28070 | /**
|
28071 | * Returns `RNode`.
|
28072 | * @param value wrapped value of `RNode`, `LView`, `LContainer`
|
28073 | */
|
28074 | function unwrapRNode(value) {
|
28075 | while (Array.isArray(value)) {
|
28076 | value = value[HOST];
|
28077 | }
|
28078 | return value;
|
28079 | }
|
28080 | /**
|
28081 | * Retrieve an `RNode` for a given `TNode` and `LView`.
|
28082 | *
|
28083 | * This function guarantees in dev mode to retrieve a non-null `RNode`.
|
28084 | *
|
28085 | * @param tNode
|
28086 | * @param lView
|
28087 | */
|
28088 | function getNativeByTNode(tNode, lView) {
|
28089 | ngDevMode && assertTNodeForLView(tNode, lView);
|
28090 | ngDevMode && assertIndexInRange(lView, tNode.index);
|
28091 | const node = unwrapRNode(lView[tNode.index]);
|
28092 | ngDevMode && !isProceduralRenderer(lView[RENDERER]) && assertDomNode(node);
|
28093 | return node;
|
28094 | }
|
28095 | // fixme(misko): The return Type should be `TNode|null`
|
28096 | function getTNode(tView, index) {
|
28097 | ngDevMode && assertGreaterThan(index, -1, 'wrong index for TNode');
|
28098 | ngDevMode && assertLessThan(index, tView.data.length, 'wrong index for TNode');
|
28099 | const tNode = tView.data[index];
|
28100 | ngDevMode && tNode !== null && assertTNode(tNode);
|
28101 | return tNode;
|
28102 | }
|
28103 | function getComponentLViewByIndex(nodeIndex, hostView) {
|
28104 | // Could be an LView or an LContainer. If LContainer, unwrap to find LView.
|
28105 | ngDevMode && assertIndexInRange(hostView, nodeIndex);
|
28106 | const slotValue = hostView[nodeIndex];
|
28107 | const lView = isLView(slotValue) ? slotValue : slotValue[HOST];
|
28108 | return lView;
|
28109 | }
|
28110 | /**
|
28111 | * Returns the monkey-patch value data present on the target (which could be
|
28112 | * a component, directive or a DOM node).
|
28113 | */
|
28114 | function readPatchedData(target) {
|
28115 | ngDevMode && assertDefined(target, 'Target expected');
|
28116 | return target[MONKEY_PATCH_KEY_NAME] || null;
|
28117 | }
|
28118 | function readPatchedLView(target) {
|
28119 | const value = readPatchedData(target);
|
28120 | if (value) {
|
28121 | return Array.isArray(value) ? value : value.lView;
|
28122 | }
|
28123 | return null;
|
28124 | }
|
28125 | /** Checks whether a given view is in creation mode */
|
28126 | function isCreationMode(view) {
|
28127 | return (view[FLAGS] & 4 /* CreationMode */) === 4 /* CreationMode */;
|
28128 | }
|
28129 | /**
|
28130 | * Returns a boolean for whether the view is attached to the change detection tree.
|
28131 | *
|
28132 | * Note: This determines whether a view should be checked, not whether it's inserted
|
28133 | * into a container. For that, you'll want `viewAttachedToContainer` below.
|
28134 | */
|
28135 | function viewAttachedToChangeDetector(view) {
|
28136 | return (view[FLAGS] & 128 /* Attached */) === 128 /* Attached */;
|
28137 | }
|
28138 | /**
|
28139 | * Resets the pre-order hook flags of the view.
|
28140 | * @param lView the LView on which the flags are reset
|
28141 | */
|
28142 | function resetPreOrderHookFlags(lView) {
|
28143 | lView[PREORDER_HOOK_FLAGS] = 0;
|
28144 | }
|
28145 | /**
|
28146 | * Updates the `TRANSPLANTED_VIEWS_TO_REFRESH` counter on the `LContainer` as well as the parents
|
28147 | * whose
|
28148 | * 1. counter goes from 0 to 1, indicating that there is a new child that has a view to refresh
|
28149 | * or
|
28150 | * 2. counter goes from 1 to 0, indicating there are no more descendant views to refresh
|
28151 | */
|
28152 | function updateTransplantedViewCount(lContainer, amount) {
|
28153 | lContainer[TRANSPLANTED_VIEWS_TO_REFRESH] += amount;
|
28154 | let viewOrContainer = lContainer;
|
28155 | let parent = lContainer[PARENT];
|
28156 | while (parent !== null &&
|
28157 | ((amount === 1 && viewOrContainer[TRANSPLANTED_VIEWS_TO_REFRESH] === 1) ||
|
28158 | (amount === -1 && viewOrContainer[TRANSPLANTED_VIEWS_TO_REFRESH] === 0))) {
|
28159 | parent[TRANSPLANTED_VIEWS_TO_REFRESH] += amount;
|
28160 | viewOrContainer = parent;
|
28161 | parent = parent[PARENT];
|
28162 | }
|
28163 | }
|
28164 |
|
28165 | /**
|
28166 | * @license
|
28167 | * Copyright Google LLC All Rights Reserved.
|
28168 | *
|
28169 | * Use of this source code is governed by an MIT-style license that can be
|
28170 | * found in the LICENSE file at https://angular.io/license
|
28171 | */
|
28172 | const instructionState = {
|
28173 | lFrame: createLFrame(null),
|
28174 | bindingsEnabled: true,
|
28175 | isInCheckNoChangesMode: false,
|
28176 | };
|
28177 | /**
|
28178 | * Return the current `LView`.
|
28179 | */
|
28180 | function getLView() {
|
28181 | return instructionState.lFrame.lView;
|
28182 | }
|
28183 | /**
|
28184 | * Return the current `TView`.
|
28185 | */
|
28186 | function getTView() {
|
28187 | return instructionState.lFrame.tView;
|
28188 | }
|
28189 | function getCurrentTNode() {
|
28190 | let currentTNode = getCurrentTNodePlaceholderOk();
|
28191 | while (currentTNode !== null && currentTNode.type === 64 /* Placeholder */) {
|
28192 | currentTNode = currentTNode.parent;
|
28193 | }
|
28194 | return currentTNode;
|
28195 | }
|
28196 | function getCurrentTNodePlaceholderOk() {
|
28197 | return instructionState.lFrame.currentTNode;
|
28198 | }
|
28199 | function getCurrentParentTNode() {
|
28200 | const lFrame = instructionState.lFrame;
|
28201 | const currentTNode = lFrame.currentTNode;
|
28202 | return lFrame.isParent ? currentTNode : currentTNode.parent;
|
28203 | }
|
28204 | function setCurrentTNode(tNode, isParent) {
|
28205 | ngDevMode && tNode && assertTNodeForTView(tNode, instructionState.lFrame.tView);
|
28206 | const lFrame = instructionState.lFrame;
|
28207 | lFrame.currentTNode = tNode;
|
28208 | lFrame.isParent = isParent;
|
28209 | }
|
28210 | function isCurrentTNodeParent() {
|
28211 | return instructionState.lFrame.isParent;
|
28212 | }
|
28213 | function isInCheckNoChangesMode() {
|
28214 | // TODO(misko): remove this from the LView since it is ngDevMode=true mode only.
|
28215 | return instructionState.isInCheckNoChangesMode;
|
28216 | }
|
28217 | function setIsInCheckNoChangesMode(mode) {
|
28218 | instructionState.isInCheckNoChangesMode = mode;
|
28219 | }
|
28220 | function setBindingIndex(value) {
|
28221 | return instructionState.lFrame.bindingIndex = value;
|
28222 | }
|
28223 | function isInI18nBlock() {
|
28224 | return instructionState.lFrame.inI18n;
|
28225 | }
|
28226 | /**
|
28227 | * Set a new binding root index so that host template functions can execute.
|
28228 | *
|
28229 | * Bindings inside the host template are 0 index. But because we don't know ahead of time
|
28230 | * how many host bindings we have we can't pre-compute them. For this reason they are all
|
28231 | * 0 index and we just shift the root so that they match next available location in the LView.
|
28232 | *
|
28233 | * @param bindingRootIndex Root index for `hostBindings`
|
28234 | * @param currentDirectiveIndex `TData[currentDirectiveIndex]` will point to the current directive
|
28235 | * whose `hostBindings` are being processed.
|
28236 | */
|
28237 | function setBindingRootForHostBindings(bindingRootIndex, currentDirectiveIndex) {
|
28238 | const lFrame = instructionState.lFrame;
|
28239 | lFrame.bindingIndex = lFrame.bindingRootIndex = bindingRootIndex;
|
28240 | setCurrentDirectiveIndex(currentDirectiveIndex);
|
28241 | }
|
28242 | /**
|
28243 | * Sets an index of a directive whose `hostBindings` are being processed.
|
28244 | *
|
28245 | * @param currentDirectiveIndex `TData` index where current directive instance can be found.
|
28246 | */
|
28247 | function setCurrentDirectiveIndex(currentDirectiveIndex) {
|
28248 | instructionState.lFrame.currentDirectiveIndex = currentDirectiveIndex;
|
28249 | }
|
28250 | function setCurrentQueryIndex(value) {
|
28251 | instructionState.lFrame.currentQueryIndex = value;
|
28252 | }
|
28253 | /**
|
28254 | * Returns a `TNode` of the location where the current `LView` is declared at.
|
28255 | *
|
28256 | * @param lView an `LView` that we want to find parent `TNode` for.
|
28257 | */
|
28258 | function getDeclarationTNode(lView) {
|
28259 | const tView = lView[TVIEW];
|
28260 | // Return the declaration parent for embedded views
|
28261 | if (tView.type === 2 /* Embedded */) {
|
28262 | ngDevMode && assertDefined(tView.declTNode, 'Embedded TNodes should have declaration parents.');
|
28263 | return tView.declTNode;
|
28264 | }
|
28265 | // Components don't have `TView.declTNode` because each instance of component could be
|
28266 | // inserted in different location, hence `TView.declTNode` is meaningless.
|
28267 | // Falling back to `T_HOST` in case we cross component boundary.
|
28268 | if (tView.type === 1 /* Component */) {
|
28269 | return lView[T_HOST];
|
28270 | }
|
28271 | // Remaining TNode type is `TViewType.Root` which doesn't have a parent TNode.
|
28272 | return null;
|
28273 | }
|
28274 | /**
|
28275 | * This is a light weight version of the `enterView` which is needed by the DI system.
|
28276 | *
|
28277 | * @param lView `LView` location of the DI context.
|
28278 | * @param tNode `TNode` for DI context
|
28279 | * @param flags DI context flags. if `SkipSelf` flag is set than we walk up the declaration
|
28280 | * tree from `tNode` until we find parent declared `TElementNode`.
|
28281 | * @returns `true` if we have successfully entered DI associated with `tNode` (or with declared
|
28282 | * `TNode` if `flags` has `SkipSelf`). Failing to enter DI implies that no associated
|
28283 | * `NodeInjector` can be found and we should instead use `ModuleInjector`.
|
28284 | * - If `true` than this call must be fallowed by `leaveDI`
|
28285 | * - If `false` than this call failed and we should NOT call `leaveDI`
|
28286 | */
|
28287 | function enterDI(lView, tNode, flags) {
|
28288 | ngDevMode && assertLViewOrUndefined(lView);
|
28289 | if (flags & InjectFlags.SkipSelf) {
|
28290 | ngDevMode && assertTNodeForTView(tNode, lView[TVIEW]);
|
28291 | let parentTNode = tNode;
|
28292 | let parentLView = lView;
|
28293 | while (true) {
|
28294 | ngDevMode && assertDefined(parentTNode, 'Parent TNode should be defined');
|
28295 | parentTNode = parentTNode.parent;
|
28296 | if (parentTNode === null && !(flags & InjectFlags.Host)) {
|
28297 | parentTNode = getDeclarationTNode(parentLView);
|
28298 | if (parentTNode === null)
|
28299 | break;
|
28300 | // In this case, a parent exists and is definitely an element. So it will definitely
|
28301 | // have an existing lView as the declaration view, which is why we can assume it's defined.
|
28302 | ngDevMode && assertDefined(parentLView, 'Parent LView should be defined');
|
28303 | parentLView = parentLView[DECLARATION_VIEW];
|
28304 | // In Ivy there are Comment nodes that correspond to ngIf and NgFor embedded directives
|
28305 | // We want to skip those and look only at Elements and ElementContainers to ensure
|
28306 | // we're looking at true parent nodes, and not content or other types.
|
28307 | if (parentTNode.type & (2 /* Element */ | 8 /* ElementContainer */)) {
|
28308 | break;
|
28309 | }
|
28310 | }
|
28311 | else {
|
28312 | break;
|
28313 | }
|
28314 | }
|
28315 | if (parentTNode === null) {
|
28316 | // If we failed to find a parent TNode this means that we should use module injector.
|
28317 | return false;
|
28318 | }
|
28319 | else {
|
28320 | tNode = parentTNode;
|
28321 | lView = parentLView;
|
28322 | }
|
28323 | }
|
28324 | ngDevMode && assertTNodeForLView(tNode, lView);
|
28325 | const lFrame = instructionState.lFrame = allocLFrame();
|
28326 | lFrame.currentTNode = tNode;
|
28327 | lFrame.lView = lView;
|
28328 | return true;
|
28329 | }
|
28330 | /**
|
28331 | * Swap the current lView with a new lView.
|
28332 | *
|
28333 | * For performance reasons we store the lView in the top level of the module.
|
28334 | * This way we minimize the number of properties to read. Whenever a new view
|
28335 | * is entered we have to store the lView for later, and when the view is
|
28336 | * exited the state has to be restored
|
28337 | *
|
28338 | * @param newView New lView to become active
|
28339 | * @returns the previously active lView;
|
28340 | */
|
28341 | function enterView(newView) {
|
28342 | ngDevMode && assertNotEqual(newView[0], newView[1], '????');
|
28343 | ngDevMode && assertLViewOrUndefined(newView);
|
28344 | const newLFrame = allocLFrame();
|
28345 | if (ngDevMode) {
|
28346 | assertEqual(newLFrame.isParent, true, 'Expected clean LFrame');
|
28347 | assertEqual(newLFrame.lView, null, 'Expected clean LFrame');
|
28348 | assertEqual(newLFrame.tView, null, 'Expected clean LFrame');
|
28349 | assertEqual(newLFrame.selectedIndex, -1, 'Expected clean LFrame');
|
28350 | assertEqual(newLFrame.elementDepthCount, 0, 'Expected clean LFrame');
|
28351 | assertEqual(newLFrame.currentDirectiveIndex, -1, 'Expected clean LFrame');
|
28352 | assertEqual(newLFrame.currentNamespace, null, 'Expected clean LFrame');
|
28353 | assertEqual(newLFrame.bindingRootIndex, -1, 'Expected clean LFrame');
|
28354 | assertEqual(newLFrame.currentQueryIndex, 0, 'Expected clean LFrame');
|
28355 | }
|
28356 | const tView = newView[TVIEW];
|
28357 | instructionState.lFrame = newLFrame;
|
28358 | ngDevMode && tView.firstChild && assertTNodeForTView(tView.firstChild, tView);
|
28359 | newLFrame.currentTNode = tView.firstChild;
|
28360 | newLFrame.lView = newView;
|
28361 | newLFrame.tView = tView;
|
28362 | newLFrame.contextLView = newView;
|
28363 | newLFrame.bindingIndex = tView.bindingStartIndex;
|
28364 | newLFrame.inI18n = false;
|
28365 | }
|
28366 | /**
|
28367 | * Allocates next free LFrame. This function tries to reuse the `LFrame`s to lower memory pressure.
|
28368 | */
|
28369 | function allocLFrame() {
|
28370 | const currentLFrame = instructionState.lFrame;
|
28371 | const childLFrame = currentLFrame === null ? null : currentLFrame.child;
|
28372 | const newLFrame = childLFrame === null ? createLFrame(currentLFrame) : childLFrame;
|
28373 | return newLFrame;
|
28374 | }
|
28375 | function createLFrame(parent) {
|
28376 | const lFrame = {
|
28377 | currentTNode: null,
|
28378 | isParent: true,
|
28379 | lView: null,
|
28380 | tView: null,
|
28381 | selectedIndex: -1,
|
28382 | contextLView: null,
|
28383 | elementDepthCount: 0,
|
28384 | currentNamespace: null,
|
28385 | currentDirectiveIndex: -1,
|
28386 | bindingRootIndex: -1,
|
28387 | bindingIndex: -1,
|
28388 | currentQueryIndex: 0,
|
28389 | parent: parent,
|
28390 | child: null,
|
28391 | inI18n: false,
|
28392 | };
|
28393 | parent !== null && (parent.child = lFrame); // link the new LFrame for reuse.
|
28394 | return lFrame;
|
28395 | }
|
28396 | /**
|
28397 | * A lightweight version of leave which is used with DI.
|
28398 | *
|
28399 | * This function only resets `currentTNode` and `LView` as those are the only properties
|
28400 | * used with DI (`enterDI()`).
|
28401 | *
|
28402 | * NOTE: This function is reexported as `leaveDI`. However `leaveDI` has return type of `void` where
|
28403 | * as `leaveViewLight` has `LFrame`. This is so that `leaveViewLight` can be used in `leaveView`.
|
28404 | */
|
28405 | function leaveViewLight() {
|
28406 | const oldLFrame = instructionState.lFrame;
|
28407 | instructionState.lFrame = oldLFrame.parent;
|
28408 | oldLFrame.currentTNode = null;
|
28409 | oldLFrame.lView = null;
|
28410 | return oldLFrame;
|
28411 | }
|
28412 | /**
|
28413 | * This is a lightweight version of the `leaveView` which is needed by the DI system.
|
28414 | *
|
28415 | * NOTE: this function is an alias so that we can change the type of the function to have `void`
|
28416 | * return type.
|
28417 | */
|
28418 | const leaveDI = leaveViewLight;
|
28419 | /**
|
28420 | * Leave the current `LView`
|
28421 | *
|
28422 | * This pops the `LFrame` with the associated `LView` from the stack.
|
28423 | *
|
28424 | * IMPORTANT: We must zero out the `LFrame` values here otherwise they will be retained. This is
|
28425 | * because for performance reasons we don't release `LFrame` but rather keep it for next use.
|
28426 | */
|
28427 | function leaveView() {
|
28428 | const oldLFrame = leaveViewLight();
|
28429 | oldLFrame.isParent = true;
|
28430 | oldLFrame.tView = null;
|
28431 | oldLFrame.selectedIndex = -1;
|
28432 | oldLFrame.contextLView = null;
|
28433 | oldLFrame.elementDepthCount = 0;
|
28434 | oldLFrame.currentDirectiveIndex = -1;
|
28435 | oldLFrame.currentNamespace = null;
|
28436 | oldLFrame.bindingRootIndex = -1;
|
28437 | oldLFrame.bindingIndex = -1;
|
28438 | oldLFrame.currentQueryIndex = 0;
|
28439 | }
|
28440 | /**
|
28441 | * Gets the currently selected element index.
|
28442 | *
|
28443 | * Used with {@link property} instruction (and more in the future) to identify the index in the
|
28444 | * current `LView` to act on.
|
28445 | */
|
28446 | function getSelectedIndex() {
|
28447 | return instructionState.lFrame.selectedIndex;
|
28448 | }
|
28449 | /**
|
28450 | * Sets the most recent index passed to {@link select}
|
28451 | *
|
28452 | * Used with {@link property} instruction (and more in the future) to identify the index in the
|
28453 | * current `LView` to act on.
|
28454 | *
|
28455 | * (Note that if an "exit function" was set earlier (via `setElementExitFn()`) then that will be
|
28456 | * run if and when the provided `index` value is different from the current selected index value.)
|
28457 | */
|
28458 | function setSelectedIndex(index) {
|
28459 | ngDevMode && index !== -1 &&
|
28460 | assertGreaterThanOrEqual(index, HEADER_OFFSET, 'Index must be past HEADER_OFFSET (or -1).');
|
28461 | ngDevMode &&
|
28462 | assertLessThan(index, instructionState.lFrame.lView.length, 'Can\'t set index passed end of LView');
|
28463 | instructionState.lFrame.selectedIndex = index;
|
28464 | }
|
28465 |
|
28466 | /**
|
28467 | * @license
|
28468 | * Copyright Google LLC All Rights Reserved.
|
28469 | *
|
28470 | * Use of this source code is governed by an MIT-style license that can be
|
28471 | * found in the LICENSE file at https://angular.io/license
|
28472 | */
|
28473 | /**
|
28474 | * Adds all directive lifecycle hooks from the given `DirectiveDef` to the given `TView`.
|
28475 | *
|
28476 | * Must be run *only* on the first template pass.
|
28477 | *
|
28478 | * Sets up the pre-order hooks on the provided `tView`,
|
28479 | * see {@link HookData} for details about the data structure.
|
28480 | *
|
28481 | * @param directiveIndex The index of the directive in LView
|
28482 | * @param directiveDef The definition containing the hooks to setup in tView
|
28483 | * @param tView The current TView
|
28484 | */
|
28485 | function registerPreOrderHooks(directiveIndex, directiveDef, tView) {
|
28486 | ngDevMode && assertFirstCreatePass(tView);
|
28487 | const { ngOnChanges, ngOnInit, ngDoCheck } = directiveDef.type.prototype;
|
28488 | if (ngOnChanges) {
|
28489 | const wrappedOnChanges = NgOnChangesFeatureImpl(directiveDef);
|
28490 | (tView.preOrderHooks || (tView.preOrderHooks = [])).push(directiveIndex, wrappedOnChanges);
|
28491 | (tView.preOrderCheckHooks || (tView.preOrderCheckHooks = []))
|
28492 | .push(directiveIndex, wrappedOnChanges);
|
28493 | }
|
28494 | if (ngOnInit) {
|
28495 | (tView.preOrderHooks || (tView.preOrderHooks = [])).push(0 - directiveIndex, ngOnInit);
|
28496 | }
|
28497 | if (ngDoCheck) {
|
28498 | (tView.preOrderHooks || (tView.preOrderHooks = [])).push(directiveIndex, ngDoCheck);
|
28499 | (tView.preOrderCheckHooks || (tView.preOrderCheckHooks = [])).push(directiveIndex, ngDoCheck);
|
28500 | }
|
28501 | }
|
28502 | /**
|
28503 | *
|
28504 | * Loops through the directives on the provided `tNode` and queues hooks to be
|
28505 | * run that are not initialization hooks.
|
28506 | *
|
28507 | * Should be executed during `elementEnd()` and similar to
|
28508 | * preserve hook execution order. Content, view, and destroy hooks for projected
|
28509 | * components and directives must be called *before* their hosts.
|
28510 | *
|
28511 | * Sets up the content, view, and destroy hooks on the provided `tView`,
|
28512 | * see {@link HookData} for details about the data structure.
|
28513 | *
|
28514 | * NOTE: This does not set up `onChanges`, `onInit` or `doCheck`, those are set up
|
28515 | * separately at `elementStart`.
|
28516 | *
|
28517 | * @param tView The current TView
|
28518 | * @param tNode The TNode whose directives are to be searched for hooks to queue
|
28519 | */
|
28520 | function registerPostOrderHooks(tView, tNode) {
|
28521 | ngDevMode && assertFirstCreatePass(tView);
|
28522 | // It's necessary to loop through the directives at elementEnd() (rather than processing in
|
28523 | // directiveCreate) so we can preserve the current hook order. Content, view, and destroy
|
28524 | // hooks for projected components and directives must be called *before* their hosts.
|
28525 | for (let i = tNode.directiveStart, end = tNode.directiveEnd; i < end; i++) {
|
28526 | const directiveDef = tView.data[i];
|
28527 | ngDevMode && assertDefined(directiveDef, 'Expecting DirectiveDef');
|
28528 | const lifecycleHooks = directiveDef.type.prototype;
|
28529 | const { ngAfterContentInit, ngAfterContentChecked, ngAfterViewInit, ngAfterViewChecked, ngOnDestroy } = lifecycleHooks;
|
28530 | if (ngAfterContentInit) {
|
28531 | (tView.contentHooks || (tView.contentHooks = [])).push(-i, ngAfterContentInit);
|
28532 | }
|
28533 | if (ngAfterContentChecked) {
|
28534 | (tView.contentHooks || (tView.contentHooks = [])).push(i, ngAfterContentChecked);
|
28535 | (tView.contentCheckHooks || (tView.contentCheckHooks = [])).push(i, ngAfterContentChecked);
|
28536 | }
|
28537 | if (ngAfterViewInit) {
|
28538 | (tView.viewHooks || (tView.viewHooks = [])).push(-i, ngAfterViewInit);
|
28539 | }
|
28540 | if (ngAfterViewChecked) {
|
28541 | (tView.viewHooks || (tView.viewHooks = [])).push(i, ngAfterViewChecked);
|
28542 | (tView.viewCheckHooks || (tView.viewCheckHooks = [])).push(i, ngAfterViewChecked);
|
28543 | }
|
28544 | if (ngOnDestroy != null) {
|
28545 | (tView.destroyHooks || (tView.destroyHooks = [])).push(i, ngOnDestroy);
|
28546 | }
|
28547 | }
|
28548 | }
|
28549 | /**
|
28550 | * Executing hooks requires complex logic as we need to deal with 2 constraints.
|
28551 | *
|
28552 | * 1. Init hooks (ngOnInit, ngAfterContentInit, ngAfterViewInit) must all be executed once and only
|
28553 | * once, across many change detection cycles. This must be true even if some hooks throw, or if
|
28554 | * some recursively trigger a change detection cycle.
|
28555 | * To solve that, it is required to track the state of the execution of these init hooks.
|
28556 | * This is done by storing and maintaining flags in the view: the {@link InitPhaseState},
|
28557 | * and the index within that phase. They can be seen as a cursor in the following structure:
|
28558 | * [[onInit1, onInit2], [afterContentInit1], [afterViewInit1, afterViewInit2, afterViewInit3]]
|
28559 | * They are are stored as flags in LView[FLAGS].
|
28560 | *
|
28561 | * 2. Pre-order hooks can be executed in batches, because of the select instruction.
|
28562 | * To be able to pause and resume their execution, we also need some state about the hook's array
|
28563 | * that is being processed:
|
28564 | * - the index of the next hook to be executed
|
28565 | * - the number of init hooks already found in the processed part of the array
|
28566 | * They are are stored as flags in LView[PREORDER_HOOK_FLAGS].
|
28567 | */
|
28568 | /**
|
28569 | * Executes pre-order check hooks ( OnChanges, DoChanges) given a view where all the init hooks were
|
28570 | * executed once. This is a light version of executeInitAndCheckPreOrderHooks where we can skip read
|
28571 | * / write of the init-hooks related flags.
|
28572 | * @param lView The LView where hooks are defined
|
28573 | * @param hooks Hooks to be run
|
28574 | * @param nodeIndex 3 cases depending on the value:
|
28575 | * - undefined: all hooks from the array should be executed (post-order case)
|
28576 | * - null: execute hooks only from the saved index until the end of the array (pre-order case, when
|
28577 | * flushing the remaining hooks)
|
28578 | * - number: execute hooks only from the saved index until that node index exclusive (pre-order
|
28579 | * case, when executing select(number))
|
28580 | */
|
28581 | function executeCheckHooks(lView, hooks, nodeIndex) {
|
28582 | callHooks(lView, hooks, 3 /* InitPhaseCompleted */, nodeIndex);
|
28583 | }
|
28584 | /**
|
28585 | * Executes post-order init and check hooks (one of AfterContentInit, AfterContentChecked,
|
28586 | * AfterViewInit, AfterViewChecked) given a view where there are pending init hooks to be executed.
|
28587 | * @param lView The LView where hooks are defined
|
28588 | * @param hooks Hooks to be run
|
28589 | * @param initPhase A phase for which hooks should be run
|
28590 | * @param nodeIndex 3 cases depending on the value:
|
28591 | * - undefined: all hooks from the array should be executed (post-order case)
|
28592 | * - null: execute hooks only from the saved index until the end of the array (pre-order case, when
|
28593 | * flushing the remaining hooks)
|
28594 | * - number: execute hooks only from the saved index until that node index exclusive (pre-order
|
28595 | * case, when executing select(number))
|
28596 | */
|
28597 | function executeInitAndCheckHooks(lView, hooks, initPhase, nodeIndex) {
|
28598 | ngDevMode &&
|
28599 | assertNotEqual(initPhase, 3 /* InitPhaseCompleted */, 'Init pre-order hooks should not be called more than once');
|
28600 | if ((lView[FLAGS] & 3 /* InitPhaseStateMask */) === initPhase) {
|
28601 | callHooks(lView, hooks, initPhase, nodeIndex);
|
28602 | }
|
28603 | }
|
28604 | function incrementInitPhaseFlags(lView, initPhase) {
|
28605 | ngDevMode &&
|
28606 | assertNotEqual(initPhase, 3 /* InitPhaseCompleted */, 'Init hooks phase should not be incremented after all init hooks have been run.');
|
28607 | let flags = lView[FLAGS];
|
28608 | if ((flags & 3 /* InitPhaseStateMask */) === initPhase) {
|
28609 | flags &= 2047 /* IndexWithinInitPhaseReset */;
|
28610 | flags += 1 /* InitPhaseStateIncrementer */;
|
28611 | lView[FLAGS] = flags;
|
28612 | }
|
28613 | }
|
28614 | /**
|
28615 | * Calls lifecycle hooks with their contexts, skipping init hooks if it's not
|
28616 | * the first LView pass
|
28617 | *
|
28618 | * @param currentView The current view
|
28619 | * @param arr The array in which the hooks are found
|
28620 | * @param initPhaseState the current state of the init phase
|
28621 | * @param currentNodeIndex 3 cases depending on the value:
|
28622 | * - undefined: all hooks from the array should be executed (post-order case)
|
28623 | * - null: execute hooks only from the saved index until the end of the array (pre-order case, when
|
28624 | * flushing the remaining hooks)
|
28625 | * - number: execute hooks only from the saved index until that node index exclusive (pre-order
|
28626 | * case, when executing select(number))
|
28627 | */
|
28628 | function callHooks(currentView, arr, initPhase, currentNodeIndex) {
|
28629 | ngDevMode &&
|
28630 | assertEqual(isInCheckNoChangesMode(), false, 'Hooks should never be run when in check no changes mode.');
|
28631 | const startIndex = currentNodeIndex !== undefined ?
|
28632 | (currentView[PREORDER_HOOK_FLAGS] & 65535 /* IndexOfTheNextPreOrderHookMaskMask */) :
|
28633 | 0;
|
28634 | const nodeIndexLimit = currentNodeIndex != null ? currentNodeIndex : -1;
|
28635 | const max = arr.length - 1; // Stop the loop at length - 1, because we look for the hook at i + 1
|
28636 | let lastNodeIndexFound = 0;
|
28637 | for (let i = startIndex; i < max; i++) {
|
28638 | const hook = arr[i + 1];
|
28639 | if (typeof hook === 'number') {
|
28640 | lastNodeIndexFound = arr[i];
|
28641 | if (currentNodeIndex != null && lastNodeIndexFound >= currentNodeIndex) {
|
28642 | break;
|
28643 | }
|
28644 | }
|
28645 | else {
|
28646 | const isInitHook = arr[i] < 0;
|
28647 | if (isInitHook)
|
28648 | currentView[PREORDER_HOOK_FLAGS] += 65536 /* NumberOfInitHooksCalledIncrementer */;
|
28649 | if (lastNodeIndexFound < nodeIndexLimit || nodeIndexLimit == -1) {
|
28650 | callHook(currentView, initPhase, arr, i);
|
28651 | currentView[PREORDER_HOOK_FLAGS] =
|
28652 | (currentView[PREORDER_HOOK_FLAGS] & 4294901760 /* NumberOfInitHooksCalledMask */) + i +
|
28653 | 2;
|
28654 | }
|
28655 | i++;
|
28656 | }
|
28657 | }
|
28658 | }
|
28659 | /**
|
28660 | * Execute one hook against the current `LView`.
|
28661 | *
|
28662 | * @param currentView The current view
|
28663 | * @param initPhaseState the current state of the init phase
|
28664 | * @param arr The array in which the hooks are found
|
28665 | * @param i The current index within the hook data array
|
28666 | */
|
28667 | function callHook(currentView, initPhase, arr, i) {
|
28668 | const isInitHook = arr[i] < 0;
|
28669 | const hook = arr[i + 1];
|
28670 | const directiveIndex = isInitHook ? -arr[i] : arr[i];
|
28671 | const directive = currentView[directiveIndex];
|
28672 | if (isInitHook) {
|
28673 | const indexWithintInitPhase = currentView[FLAGS] >> 11 /* IndexWithinInitPhaseShift */;
|
28674 | // The init phase state must be always checked here as it may have been recursively updated.
|
28675 | if (indexWithintInitPhase <
|
28676 | (currentView[PREORDER_HOOK_FLAGS] >> 16 /* NumberOfInitHooksCalledShift */) &&
|
28677 | (currentView[FLAGS] & 3 /* InitPhaseStateMask */) === initPhase) {
|
28678 | currentView[FLAGS] += 2048 /* IndexWithinInitPhaseIncrementer */;
|
28679 | hook.call(directive);
|
28680 | }
|
28681 | }
|
28682 | else {
|
28683 | hook.call(directive);
|
28684 | }
|
28685 | }
|
28686 |
|
28687 | /**
|
28688 | * @license
|
28689 | * Copyright Google LLC All Rights Reserved.
|
28690 | *
|
28691 | * Use of this source code is governed by an MIT-style license that can be
|
28692 | * found in the LICENSE file at https://angular.io/license
|
28693 | */
|
28694 | const NO_PARENT_INJECTOR = -1;
|
28695 | /**
|
28696 | * Each injector is saved in 9 contiguous slots in `LView` and 9 contiguous slots in
|
28697 | * `TView.data`. This allows us to store information about the current node's tokens (which
|
28698 | * can be shared in `TView`) as well as the tokens of its ancestor nodes (which cannot be
|
28699 | * shared, so they live in `LView`).
|
28700 | *
|
28701 | * Each of these slots (aside from the last slot) contains a bloom filter. This bloom filter
|
28702 | * determines whether a directive is available on the associated node or not. This prevents us
|
28703 | * from searching the directives array at this level unless it's probable the directive is in it.
|
28704 | *
|
28705 | * See: https://en.wikipedia.org/wiki/Bloom_filter for more about bloom filters.
|
28706 | *
|
28707 | * Because all injectors have been flattened into `LView` and `TViewData`, they cannot typed
|
28708 | * using interfaces as they were previously. The start index of each `LInjector` and `TInjector`
|
28709 | * will differ based on where it is flattened into the main array, so it's not possible to know
|
28710 | * the indices ahead of time and save their types here. The interfaces are still included here
|
28711 | * for documentation purposes.
|
28712 | *
|
28713 | * export interface LInjector extends Array<any> {
|
28714 | *
|
28715 | * // Cumulative bloom for directive IDs 0-31 (IDs are % BLOOM_SIZE)
|
28716 | * [0]: number;
|
28717 | *
|
28718 | * // Cumulative bloom for directive IDs 32-63
|
28719 | * [1]: number;
|
28720 | *
|
28721 | * // Cumulative bloom for directive IDs 64-95
|
28722 | * [2]: number;
|
28723 | *
|
28724 | * // Cumulative bloom for directive IDs 96-127
|
28725 | * [3]: number;
|
28726 | *
|
28727 | * // Cumulative bloom for directive IDs 128-159
|
28728 | * [4]: number;
|
28729 | *
|
28730 | * // Cumulative bloom for directive IDs 160 - 191
|
28731 | * [5]: number;
|
28732 | *
|
28733 | * // Cumulative bloom for directive IDs 192 - 223
|
28734 | * [6]: number;
|
28735 | *
|
28736 | * // Cumulative bloom for directive IDs 224 - 255
|
28737 | * [7]: number;
|
28738 | *
|
28739 | * // We need to store a reference to the injector's parent so DI can keep looking up
|
28740 | * // the injector tree until it finds the dependency it's looking for.
|
28741 | * [PARENT_INJECTOR]: number;
|
28742 | * }
|
28743 | *
|
28744 | * export interface TInjector extends Array<any> {
|
28745 | *
|
28746 | * // Shared node bloom for directive IDs 0-31 (IDs are % BLOOM_SIZE)
|
28747 | * [0]: number;
|
28748 | *
|
28749 | * // Shared node bloom for directive IDs 32-63
|
28750 | * [1]: number;
|
28751 | *
|
28752 | * // Shared node bloom for directive IDs 64-95
|
28753 | * [2]: number;
|
28754 | *
|
28755 | * // Shared node bloom for directive IDs 96-127
|
28756 | * [3]: number;
|
28757 | *
|
28758 | * // Shared node bloom for directive IDs 128-159
|
28759 | * [4]: number;
|
28760 | *
|
28761 | * // Shared node bloom for directive IDs 160 - 191
|
28762 | * [5]: number;
|
28763 | *
|
28764 | * // Shared node bloom for directive IDs 192 - 223
|
28765 | * [6]: number;
|
28766 | *
|
28767 | * // Shared node bloom for directive IDs 224 - 255
|
28768 | * [7]: number;
|
28769 | *
|
28770 | * // Necessary to find directive indices for a particular node.
|
28771 | * [TNODE]: TElementNode|TElementContainerNode|TContainerNode;
|
28772 | * }
|
28773 | */
|
28774 | /**
|
28775 | * Factory for creating instances of injectors in the NodeInjector.
|
28776 | *
|
28777 | * This factory is complicated by the fact that it can resolve `multi` factories as well.
|
28778 | *
|
28779 | * NOTE: Some of the fields are optional which means that this class has two hidden classes.
|
28780 | * - One without `multi` support (most common)
|
28781 | * - One with `multi` values, (rare).
|
28782 | *
|
28783 | * Since VMs can cache up to 4 inline hidden classes this is OK.
|
28784 | *
|
28785 | * - Single factory: Only `resolving` and `factory` is defined.
|
28786 | * - `providers` factory: `componentProviders` is a number and `index = -1`.
|
28787 | * - `viewProviders` factory: `componentProviders` is a number and `index` points to `providers`.
|
28788 | */
|
28789 | class NodeInjectorFactory {
|
28790 | constructor(
|
28791 | /**
|
28792 | * Factory to invoke in order to create a new instance.
|
28793 | */
|
28794 | factory,
|
28795 | /**
|
28796 | * Set to `true` if the token is declared in `viewProviders` (or if it is component).
|
28797 | */
|
28798 | isViewProvider, injectImplementation) {
|
28799 | this.factory = factory;
|
28800 | /**
|
28801 | * Marker set to true during factory invocation to see if we get into recursive loop.
|
28802 | * Recursive loop causes an error to be displayed.
|
28803 | */
|
28804 | this.resolving = false;
|
28805 | ngDevMode && assertDefined(factory, 'Factory not specified');
|
28806 | ngDevMode && assertEqual(typeof factory, 'function', 'Expected factory function.');
|
28807 | this.canSeeViewProviders = isViewProvider;
|
28808 | this.injectImpl = injectImplementation;
|
28809 | }
|
28810 | }
|
28811 | function isFactory(obj) {
|
28812 | return obj instanceof NodeInjectorFactory;
|
28813 | }
|
28814 |
|
28815 | /**
|
28816 | * Converts `TNodeType` into human readable text.
|
28817 | * Make sure this matches with `TNodeType`
|
28818 | */
|
28819 | function toTNodeTypeAsString(tNodeType) {
|
28820 | let text = '';
|
28821 | (tNodeType & 1 /* Text */) && (text += '|Text');
|
28822 | (tNodeType & 2 /* Element */) && (text += '|Element');
|
28823 | (tNodeType & 4 /* Container */) && (text += '|Container');
|
28824 | (tNodeType & 8 /* ElementContainer */) && (text += '|ElementContainer');
|
28825 | (tNodeType & 16 /* Projection */) && (text += '|Projection');
|
28826 | (tNodeType & 32 /* Icu */) && (text += '|IcuContainer');
|
28827 | (tNodeType & 64 /* Placeholder */) && (text += '|Placeholder');
|
28828 | return text.length > 0 ? text.substring(1) : text;
|
28829 | }
|
28830 |
|
28831 | /**
|
28832 | * @license
|
28833 | * Copyright Google LLC All Rights Reserved.
|
28834 | *
|
28835 | * Use of this source code is governed by an MIT-style license that can be
|
28836 | * found in the LICENSE file at https://angular.io/license
|
28837 | */
|
28838 | function assertTNodeType(tNode, expectedTypes, message) {
|
28839 | assertDefined(tNode, 'should be called with a TNode');
|
28840 | if ((tNode.type & expectedTypes) === 0) {
|
28841 | throwError(message ||
|
28842 | `Expected [${toTNodeTypeAsString(expectedTypes)}] but got ${toTNodeTypeAsString(tNode.type)}.`);
|
28843 | }
|
28844 | }
|
28845 | function assertPureTNodeType(type) {
|
28846 | if (!(type === 2 /* Element */ || //
|
28847 | type === 1 /* Text */ || //
|
28848 | type === 4 /* Container */ || //
|
28849 | type === 8 /* ElementContainer */ || //
|
28850 | type === 32 /* Icu */ || //
|
28851 | type === 16 /* Projection */ || //
|
28852 | type === 64 /* Placeholder */)) {
|
28853 | throwError(`Expected TNodeType to have only a single type selected, but got ${toTNodeTypeAsString(type)}.`);
|
28854 | }
|
28855 | }
|
28856 |
|
28857 | /**
|
28858 | * Assigns all attribute values to the provided element via the inferred renderer.
|
28859 | *
|
28860 | * This function accepts two forms of attribute entries:
|
28861 | *
|
28862 | * default: (key, value):
|
28863 | * attrs = [key1, value1, key2, value2]
|
28864 | *
|
28865 | * namespaced: (NAMESPACE_MARKER, uri, name, value)
|
28866 | * attrs = [NAMESPACE_MARKER, uri, name, value, NAMESPACE_MARKER, uri, name, value]
|
28867 | *
|
28868 | * The `attrs` array can contain a mix of both the default and namespaced entries.
|
28869 | * The "default" values are set without a marker, but if the function comes across
|
28870 | * a marker value then it will attempt to set a namespaced value. If the marker is
|
28871 | * not of a namespaced value then the function will quit and return the index value
|
28872 | * where it stopped during the iteration of the attrs array.
|
28873 | *
|
28874 | * See [AttributeMarker] to understand what the namespace marker value is.
|
28875 | *
|
28876 | * Note that this instruction does not support assigning style and class values to
|
28877 | * an element. See `elementStart` and `elementHostAttrs` to learn how styling values
|
28878 | * are applied to an element.
|
28879 | * @param renderer The renderer to be used
|
28880 | * @param native The element that the attributes will be assigned to
|
28881 | * @param attrs The attribute array of values that will be assigned to the element
|
28882 | * @returns the index value that was last accessed in the attributes array
|
28883 | */
|
28884 | function setUpAttributes(renderer, native, attrs) {
|
28885 | const isProc = isProceduralRenderer(renderer);
|
28886 | let i = 0;
|
28887 | while (i < attrs.length) {
|
28888 | const value = attrs[i];
|
28889 | if (typeof value === 'number') {
|
28890 | // only namespaces are supported. Other value types (such as style/class
|
28891 | // entries) are not supported in this function.
|
28892 | if (value !== 0 /* NamespaceURI */) {
|
28893 | break;
|
28894 | }
|
28895 | // we just landed on the marker value ... therefore
|
28896 | // we should skip to the next entry
|
28897 | i++;
|
28898 | const namespaceURI = attrs[i++];
|
28899 | const attrName = attrs[i++];
|
28900 | const attrVal = attrs[i++];
|
28901 | ngDevMode && ngDevMode.rendererSetAttribute++;
|
28902 | isProc ?
|
28903 | renderer.setAttribute(native, attrName, attrVal, namespaceURI) :
|
28904 | native.setAttributeNS(namespaceURI, attrName, attrVal);
|
28905 | }
|
28906 | else {
|
28907 | // attrName is string;
|
28908 | const attrName = value;
|
28909 | const attrVal = attrs[++i];
|
28910 | // Standard attributes
|
28911 | ngDevMode && ngDevMode.rendererSetAttribute++;
|
28912 | if (isAnimationProp(attrName)) {
|
28913 | if (isProc) {
|
28914 | renderer.setProperty(native, attrName, attrVal);
|
28915 | }
|
28916 | }
|
28917 | else {
|
28918 | isProc ?
|
28919 | renderer.setAttribute(native, attrName, attrVal) :
|
28920 | native.setAttribute(attrName, attrVal);
|
28921 | }
|
28922 | i++;
|
28923 | }
|
28924 | }
|
28925 | // another piece of code may iterate over the same attributes array. Therefore
|
28926 | // it may be helpful to return the exact spot where the attributes array exited
|
28927 | // whether by running into an unsupported marker or if all the static values were
|
28928 | // iterated over.
|
28929 | return i;
|
28930 | }
|
28931 | function isAnimationProp(name) {
|
28932 | // Perf note: accessing charCodeAt to check for the first character of a string is faster as
|
28933 | // compared to accessing a character at index 0 (ex. name[0]). The main reason for this is that
|
28934 | // charCodeAt doesn't allocate memory to return a substring.
|
28935 | return name.charCodeAt(0) === 64 /* AT_SIGN */;
|
28936 | }
|
28937 |
|
28938 | /**
|
28939 | * @license
|
28940 | * Copyright Google LLC All Rights Reserved.
|
28941 | *
|
28942 | * Use of this source code is governed by an MIT-style license that can be
|
28943 | * found in the LICENSE file at https://angular.io/license
|
28944 | */
|
28945 | /// Parent Injector Utils ///////////////////////////////////////////////////////////////
|
28946 | function hasParentInjector(parentLocation) {
|
28947 | return parentLocation !== NO_PARENT_INJECTOR;
|
28948 | }
|
28949 | function getParentInjectorIndex(parentLocation) {
|
28950 | ngDevMode && assertNumber(parentLocation, 'Number expected');
|
28951 | ngDevMode && assertNotEqual(parentLocation, -1, 'Not a valid state.');
|
28952 | const parentInjectorIndex = parentLocation & 32767 /* InjectorIndexMask */;
|
28953 | ngDevMode &&
|
28954 | assertGreaterThan(parentInjectorIndex, HEADER_OFFSET, 'Parent injector must be pointing past HEADER_OFFSET.');
|
28955 | return parentLocation & 32767 /* InjectorIndexMask */;
|
28956 | }
|
28957 | function getParentInjectorViewOffset(parentLocation) {
|
28958 | return parentLocation >> 16 /* ViewOffsetShift */;
|
28959 | }
|
28960 | /**
|
28961 | * Unwraps a parent injector location number to find the view offset from the current injector,
|
28962 | * then walks up the declaration view tree until the view is found that contains the parent
|
28963 | * injector.
|
28964 | *
|
28965 | * @param location The location of the parent injector, which contains the view offset
|
28966 | * @param startView The LView instance from which to start walking up the view tree
|
28967 | * @returns The LView instance that contains the parent injector
|
28968 | */
|
28969 | function getParentInjectorView(location, startView) {
|
28970 | let viewOffset = getParentInjectorViewOffset(location);
|
28971 | let parentView = startView;
|
28972 | // For most cases, the parent injector can be found on the host node (e.g. for component
|
28973 | // or container), but we must keep the loop here to support the rarer case of deeply nested
|
28974 | // <ng-template> tags or inline views, where the parent injector might live many views
|
28975 | // above the child injector.
|
28976 | while (viewOffset > 0) {
|
28977 | parentView = parentView[DECLARATION_VIEW];
|
28978 | viewOffset--;
|
28979 | }
|
28980 | return parentView;
|
28981 | }
|
28982 |
|
28983 | /**
|
28984 | * @license
|
28985 | * Copyright Google LLC All Rights Reserved.
|
28986 | *
|
28987 | * Use of this source code is governed by an MIT-style license that can be
|
28988 | * found in the LICENSE file at https://angular.io/license
|
28989 | */
|
28990 | /**
|
28991 | * Defines if the call to `inject` should include `viewProviders` in its resolution.
|
28992 | *
|
28993 | * This is set to true when we try to instantiate a component. This value is reset in
|
28994 | * `getNodeInjectable` to a value which matches the declaration location of the token about to be
|
28995 | * instantiated. This is done so that if we are injecting a token which was declared outside of
|
28996 | * `viewProviders` we don't accidentally pull `viewProviders` in.
|
28997 | *
|
28998 | * Example:
|
28999 | *
|
29000 | * ```
|
29001 | * @Injectable()
|
29002 | * class MyService {
|
29003 | * constructor(public value: String) {}
|
29004 | * }
|
29005 | *
|
29006 | * @Component({
|
29007 | * providers: [
|
29008 | * MyService,
|
29009 | * {provide: String, value: 'providers' }
|
29010 | * ]
|
29011 | * viewProviders: [
|
29012 | * {provide: String, value: 'viewProviders'}
|
29013 | * ]
|
29014 | * })
|
29015 | * class MyComponent {
|
29016 | * constructor(myService: MyService, value: String) {
|
29017 | * // We expect that Component can see into `viewProviders`.
|
29018 | * expect(value).toEqual('viewProviders');
|
29019 | * // `MyService` was not declared in `viewProviders` hence it can't see it.
|
29020 | * expect(myService.value).toEqual('providers');
|
29021 | * }
|
29022 | * }
|
29023 | *
|
29024 | * ```
|
29025 | */
|
29026 | let includeViewProviders = true;
|
29027 | function setIncludeViewProviders(v) {
|
29028 | const oldValue = includeViewProviders;
|
29029 | includeViewProviders = v;
|
29030 | return oldValue;
|
29031 | }
|
29032 | /**
|
29033 | * The number of slots in each bloom filter (used by DI). The larger this number, the fewer
|
29034 | * directives that will share slots, and thus, the fewer false positives when checking for
|
29035 | * the existence of a directive.
|
29036 | */
|
29037 | const BLOOM_SIZE = 256;
|
29038 | const BLOOM_MASK = BLOOM_SIZE - 1;
|
29039 | /**
|
29040 | * The number of bits that is represented by a single bloom bucket. JS bit operations are 32 bits,
|
29041 | * so each bucket represents 32 distinct tokens which accounts for log2(32) = 5 bits of a bloom hash
|
29042 | * number.
|
29043 | */
|
29044 | const BLOOM_BUCKET_BITS = 5;
|
29045 | /** Counter used to generate unique IDs for directives. */
|
29046 | let nextNgElementId = 0;
|
29047 | /**
|
29048 | * Registers this directive as present in its node's injector by flipping the directive's
|
29049 | * corresponding bit in the injector's bloom filter.
|
29050 | *
|
29051 | * @param injectorIndex The index of the node injector where this token should be registered
|
29052 | * @param tView The TView for the injector's bloom filters
|
29053 | * @param type The directive token to register
|
29054 | */
|
29055 | function bloomAdd(injectorIndex, tView, type) {
|
29056 | ngDevMode && assertEqual(tView.firstCreatePass, true, 'expected firstCreatePass to be true');
|
29057 | let id;
|
29058 | if (typeof type === 'string') {
|
29059 | id = type.charCodeAt(0) || 0;
|
29060 | }
|
29061 | else if (type.hasOwnProperty(NG_ELEMENT_ID)) {
|
29062 | id = type[NG_ELEMENT_ID];
|
29063 | }
|
29064 | // Set a unique ID on the directive type, so if something tries to inject the directive,
|
29065 | // we can easily retrieve the ID and hash it into the bloom bit that should be checked.
|
29066 | if (id == null) {
|
29067 | id = type[NG_ELEMENT_ID] = nextNgElementId++;
|
29068 | }
|
29069 | // We only have BLOOM_SIZE (256) slots in our bloom filter (8 buckets * 32 bits each),
|
29070 | // so all unique IDs must be modulo-ed into a number from 0 - 255 to fit into the filter.
|
29071 | const bloomHash = id & BLOOM_MASK;
|
29072 | // Create a mask that targets the specific bit associated with the directive.
|
29073 | // JS bit operations are 32 bits, so this will be a number between 2^0 and 2^31, corresponding
|
29074 | // to bit positions 0 - 31 in a 32 bit integer.
|
29075 | const mask = 1 << bloomHash;
|
29076 | // Each bloom bucket in `tData` represents `BLOOM_BUCKET_BITS` number of bits of `bloomHash`.
|
29077 | // Any bits in `bloomHash` beyond `BLOOM_BUCKET_BITS` indicate the bucket offset that the mask
|
29078 | // should be written to.
|
29079 | tView.data[injectorIndex + (bloomHash >> BLOOM_BUCKET_BITS)] |= mask;
|
29080 | }
|
29081 | /**
|
29082 | * Creates (or gets an existing) injector for a given element or container.
|
29083 | *
|
29084 | * @param tNode for which an injector should be retrieved / created.
|
29085 | * @param lView View where the node is stored
|
29086 | * @returns Node injector
|
29087 | */
|
29088 | function getOrCreateNodeInjectorForNode(tNode, lView) {
|
29089 | const existingInjectorIndex = getInjectorIndex(tNode, lView);
|
29090 | if (existingInjectorIndex !== -1) {
|
29091 | return existingInjectorIndex;
|
29092 | }
|
29093 | const tView = lView[TVIEW];
|
29094 | if (tView.firstCreatePass) {
|
29095 | tNode.injectorIndex = lView.length;
|
29096 | insertBloom(tView.data, tNode); // foundation for node bloom
|
29097 | insertBloom(lView, null); // foundation for cumulative bloom
|
29098 | insertBloom(tView.blueprint, null);
|
29099 | }
|
29100 | const parentLoc = getParentInjectorLocation(tNode, lView);
|
29101 | const injectorIndex = tNode.injectorIndex;
|
29102 | // If a parent injector can't be found, its location is set to -1.
|
29103 | // In that case, we don't need to set up a cumulative bloom
|
29104 | if (hasParentInjector(parentLoc)) {
|
29105 | const parentIndex = getParentInjectorIndex(parentLoc);
|
29106 | const parentLView = getParentInjectorView(parentLoc, lView);
|
29107 | const parentData = parentLView[TVIEW].data;
|
29108 | // Creates a cumulative bloom filter that merges the parent's bloom filter
|
29109 | // and its own cumulative bloom (which contains tokens for all ancestors)
|
29110 | for (let i = 0; i < 8 /* BLOOM_SIZE */; i++) {
|
29111 | lView[injectorIndex + i] = parentLView[parentIndex + i] | parentData[parentIndex + i];
|
29112 | }
|
29113 | }
|
29114 | lView[injectorIndex + 8 /* PARENT */] = parentLoc;
|
29115 | return injectorIndex;
|
29116 | }
|
29117 | function insertBloom(arr, footer) {
|
29118 | arr.push(0, 0, 0, 0, 0, 0, 0, 0, footer);
|
29119 | }
|
29120 | function getInjectorIndex(tNode, lView) {
|
29121 | if (tNode.injectorIndex === -1 ||
|
29122 | // If the injector index is the same as its parent's injector index, then the index has been
|
29123 | // copied down from the parent node. No injector has been created yet on this node.
|
29124 | (tNode.parent && tNode.parent.injectorIndex === tNode.injectorIndex) ||
|
29125 | // After the first template pass, the injector index might exist but the parent values
|
29126 | // might not have been calculated yet for this instance
|
29127 | lView[tNode.injectorIndex + 8 /* PARENT */] === null) {
|
29128 | return -1;
|
29129 | }
|
29130 | else {
|
29131 | ngDevMode && assertIndexInRange(lView, tNode.injectorIndex);
|
29132 | return tNode.injectorIndex;
|
29133 | }
|
29134 | }
|
29135 | /**
|
29136 | * Finds the index of the parent injector, with a view offset if applicable. Used to set the
|
29137 | * parent injector initially.
|
29138 | *
|
29139 | * @returns Returns a number that is the combination of the number of LViews that we have to go up
|
29140 | * to find the LView containing the parent inject AND the index of the injector within that LView.
|
29141 | */
|
29142 | function getParentInjectorLocation(tNode, lView) {
|
29143 | if (tNode.parent && tNode.parent.injectorIndex !== -1) {
|
29144 | // If we have a parent `TNode` and there is an injector associated with it we are done, because
|
29145 | // the parent injector is within the current `LView`.
|
29146 | return tNode.parent.injectorIndex; // ViewOffset is 0
|
29147 | }
|
29148 | // When parent injector location is computed it may be outside of the current view. (ie it could
|
29149 | // be pointing to a declared parent location). This variable stores number of declaration parents
|
29150 | // we need to walk up in order to find the parent injector location.
|
29151 | let declarationViewOffset = 0;
|
29152 | let parentTNode = null;
|
29153 | let lViewCursor = lView;
|
29154 | // The parent injector is not in the current `LView`. We will have to walk the declared parent
|
29155 | // `LView` hierarchy and look for it. If we walk of the top, that means that there is no parent
|
29156 | // `NodeInjector`.
|
29157 | while (lViewCursor !== null) {
|
29158 | // First determine the `parentTNode` location. The parent pointer differs based on `TView.type`.
|
29159 | const tView = lViewCursor[TVIEW];
|
29160 | const tViewType = tView.type;
|
29161 | if (tViewType === 2 /* Embedded */) {
|
29162 | ngDevMode &&
|
29163 | assertDefined(tView.declTNode, 'Embedded TNodes should have declaration parents.');
|
29164 | parentTNode = tView.declTNode;
|
29165 | }
|
29166 | else if (tViewType === 1 /* Component */) {
|
29167 | // Components don't have `TView.declTNode` because each instance of component could be
|
29168 | // inserted in different location, hence `TView.declTNode` is meaningless.
|
29169 | parentTNode = lViewCursor[T_HOST];
|
29170 | }
|
29171 | else {
|
29172 | ngDevMode && assertEqual(tView.type, 0 /* Root */, 'Root type expected');
|
29173 | parentTNode = null;
|
29174 | }
|
29175 | if (parentTNode === null) {
|
29176 | // If we have no parent, than we are done.
|
29177 | return NO_PARENT_INJECTOR;
|
29178 | }
|
29179 | ngDevMode && parentTNode && assertTNodeForLView(parentTNode, lViewCursor[DECLARATION_VIEW]);
|
29180 | // Every iteration of the loop requires that we go to the declared parent.
|
29181 | declarationViewOffset++;
|
29182 | lViewCursor = lViewCursor[DECLARATION_VIEW];
|
29183 | if (parentTNode.injectorIndex !== -1) {
|
29184 | // We found a NodeInjector which points to something.
|
29185 | return (parentTNode.injectorIndex |
|
29186 | (declarationViewOffset << 16 /* ViewOffsetShift */));
|
29187 | }
|
29188 | }
|
29189 | return NO_PARENT_INJECTOR;
|
29190 | }
|
29191 | /**
|
29192 | * Makes a type or an injection token public to the DI system by adding it to an
|
29193 | * injector's bloom filter.
|
29194 | *
|
29195 | * @param di The node injector in which a directive will be added
|
29196 | * @param token The type or the injection token to be made public
|
29197 | */
|
29198 | function diPublicInInjector(injectorIndex, tView, token) {
|
29199 | bloomAdd(injectorIndex, tView, token);
|
29200 | }
|
29201 | function notFoundValueOrThrow(notFoundValue, token, flags) {
|
29202 | if (flags & InjectFlags.Optional) {
|
29203 | return notFoundValue;
|
29204 | }
|
29205 | else {
|
29206 | throwProviderNotFoundError(token, 'NodeInjector');
|
29207 | }
|
29208 | }
|
29209 | /**
|
29210 | * Returns the value associated to the given token from the ModuleInjector or throws exception
|
29211 | *
|
29212 | * @param lView The `LView` that contains the `tNode`
|
29213 | * @param token The token to look for
|
29214 | * @param flags Injection flags
|
29215 | * @param notFoundValue The value to return when the injection flags is `InjectFlags.Optional`
|
29216 | * @returns the value from the injector or throws an exception
|
29217 | */
|
29218 | function lookupTokenUsingModuleInjector(lView, token, flags, notFoundValue) {
|
29219 | if (flags & InjectFlags.Optional && notFoundValue === undefined) {
|
29220 | // This must be set or the NullInjector will throw for optional deps
|
29221 | notFoundValue = null;
|
29222 | }
|
29223 | if ((flags & (InjectFlags.Self | InjectFlags.Host)) === 0) {
|
29224 | const moduleInjector = lView[INJECTOR];
|
29225 | // switch to `injectInjectorOnly` implementation for module injector, since module injector
|
29226 | // should not have access to Component/Directive DI scope (that may happen through
|
29227 | // `directiveInject` implementation)
|
29228 | const previousInjectImplementation = setInjectImplementation(undefined);
|
29229 | try {
|
29230 | if (moduleInjector) {
|
29231 | return moduleInjector.get(token, notFoundValue, flags & InjectFlags.Optional);
|
29232 | }
|
29233 | else {
|
29234 | return injectRootLimpMode(token, notFoundValue, flags & InjectFlags.Optional);
|
29235 | }
|
29236 | }
|
29237 | finally {
|
29238 | setInjectImplementation(previousInjectImplementation);
|
29239 | }
|
29240 | }
|
29241 | return notFoundValueOrThrow(notFoundValue, token, flags);
|
29242 | }
|
29243 | /**
|
29244 | * Returns the value associated to the given token from the NodeInjectors => ModuleInjector.
|
29245 | *
|
29246 | * Look for the injector providing the token by walking up the node injector tree and then
|
29247 | * the module injector tree.
|
29248 | *
|
29249 | * This function patches `token` with `__NG_ELEMENT_ID__` which contains the id for the bloom
|
29250 | * filter. `-1` is reserved for injecting `Injector` (implemented by `NodeInjector`)
|
29251 | *
|
29252 | * @param tNode The Node where the search for the injector should start
|
29253 | * @param lView The `LView` that contains the `tNode`
|
29254 | * @param token The token to look for
|
29255 | * @param flags Injection flags
|
29256 | * @param notFoundValue The value to return when the injection flags is `InjectFlags.Optional`
|
29257 | * @returns the value from the injector, `null` when not found, or `notFoundValue` if provided
|
29258 | */
|
29259 | function getOrCreateInjectable(tNode, lView, token, flags = InjectFlags.Default, notFoundValue) {
|
29260 | if (tNode !== null) {
|
29261 | const bloomHash = bloomHashBitOrFactory(token);
|
29262 | // If the ID stored here is a function, this is a special object like ElementRef or TemplateRef
|
29263 | // so just call the factory function to create it.
|
29264 | if (typeof bloomHash === 'function') {
|
29265 | if (!enterDI(lView, tNode, flags)) {
|
29266 | // Failed to enter DI, try module injector instead. If a token is injected with the @Host
|
29267 | // flag, the module injector is not searched for that token in Ivy.
|
29268 | return (flags & InjectFlags.Host) ?
|
29269 | notFoundValueOrThrow(notFoundValue, token, flags) :
|
29270 | lookupTokenUsingModuleInjector(lView, token, flags, notFoundValue);
|
29271 | }
|
29272 | try {
|
29273 | const value = bloomHash();
|
29274 | if (value == null && !(flags & InjectFlags.Optional)) {
|
29275 | throwProviderNotFoundError(token);
|
29276 | }
|
29277 | else {
|
29278 | return value;
|
29279 | }
|
29280 | }
|
29281 | finally {
|
29282 | leaveDI();
|
29283 | }
|
29284 | }
|
29285 | else if (typeof bloomHash === 'number') {
|
29286 | // A reference to the previous injector TView that was found while climbing the element
|
29287 | // injector tree. This is used to know if viewProviders can be accessed on the current
|
29288 | // injector.
|
29289 | let previousTView = null;
|
29290 | let injectorIndex = getInjectorIndex(tNode, lView);
|
29291 | let parentLocation = NO_PARENT_INJECTOR;
|
29292 | let hostTElementNode = flags & InjectFlags.Host ? lView[DECLARATION_COMPONENT_VIEW][T_HOST] : null;
|
29293 | // If we should skip this injector, or if there is no injector on this node, start by
|
29294 | // searching the parent injector.
|
29295 | if (injectorIndex === -1 || flags & InjectFlags.SkipSelf) {
|
29296 | parentLocation = injectorIndex === -1 ? getParentInjectorLocation(tNode, lView) :
|
29297 | lView[injectorIndex + 8 /* PARENT */];
|
29298 | if (parentLocation === NO_PARENT_INJECTOR || !shouldSearchParent(flags, false)) {
|
29299 | injectorIndex = -1;
|
29300 | }
|
29301 | else {
|
29302 | previousTView = lView[TVIEW];
|
29303 | injectorIndex = getParentInjectorIndex(parentLocation);
|
29304 | lView = getParentInjectorView(parentLocation, lView);
|
29305 | }
|
29306 | }
|
29307 | // Traverse up the injector tree until we find a potential match or until we know there
|
29308 | // *isn't* a match.
|
29309 | while (injectorIndex !== -1) {
|
29310 | ngDevMode && assertNodeInjector(lView, injectorIndex);
|
29311 | // Check the current injector. If it matches, see if it contains token.
|
29312 | const tView = lView[TVIEW];
|
29313 | ngDevMode &&
|
29314 | assertTNodeForLView(tView.data[injectorIndex + 8 /* TNODE */], lView);
|
29315 | if (bloomHasToken(bloomHash, injectorIndex, tView.data)) {
|
29316 | // At this point, we have an injector which *may* contain the token, so we step through
|
29317 | // the providers and directives associated with the injector's corresponding node to get
|
29318 | // the instance.
|
29319 | const instance = searchTokensOnInjector(injectorIndex, lView, token, previousTView, flags, hostTElementNode);
|
29320 | if (instance !== NOT_FOUND) {
|
29321 | return instance;
|
29322 | }
|
29323 | }
|
29324 | parentLocation = lView[injectorIndex + 8 /* PARENT */];
|
29325 | if (parentLocation !== NO_PARENT_INJECTOR &&
|
29326 | shouldSearchParent(flags, lView[TVIEW].data[injectorIndex + 8 /* TNODE */] === hostTElementNode) &&
|
29327 | bloomHasToken(bloomHash, injectorIndex, lView)) {
|
29328 | // The def wasn't found anywhere on this node, so it was a false positive.
|
29329 | // Traverse up the tree and continue searching.
|
29330 | previousTView = tView;
|
29331 | injectorIndex = getParentInjectorIndex(parentLocation);
|
29332 | lView = getParentInjectorView(parentLocation, lView);
|
29333 | }
|
29334 | else {
|
29335 | // If we should not search parent OR If the ancestor bloom filter value does not have the
|
29336 | // bit corresponding to the directive we can give up on traversing up to find the specific
|
29337 | // injector.
|
29338 | injectorIndex = -1;
|
29339 | }
|
29340 | }
|
29341 | }
|
29342 | }
|
29343 | return lookupTokenUsingModuleInjector(lView, token, flags, notFoundValue);
|
29344 | }
|
29345 | const NOT_FOUND = {};
|
29346 | function createNodeInjector() {
|
29347 | return new NodeInjector(getCurrentTNode(), getLView());
|
29348 | }
|
29349 | function searchTokensOnInjector(injectorIndex, lView, token, previousTView, flags, hostTElementNode) {
|
29350 | const currentTView = lView[TVIEW];
|
29351 | const tNode = currentTView.data[injectorIndex + 8 /* TNODE */];
|
29352 | // First, we need to determine if view providers can be accessed by the starting element.
|
29353 | // There are two possibilities
|
29354 | const canAccessViewProviders = previousTView == null ?
|
29355 | // 1) This is the first invocation `previousTView == null` which means that we are at the
|
29356 | // `TNode` of where injector is starting to look. In such a case the only time we are allowed
|
29357 | // to look into the ViewProviders is if:
|
29358 | // - we are on a component
|
29359 | // - AND the injector set `includeViewProviders` to true (implying that the token can see
|
29360 | // ViewProviders because it is the Component or a Service which itself was declared in
|
29361 | // ViewProviders)
|
29362 | (isComponentHost(tNode) && includeViewProviders) :
|
29363 | // 2) `previousTView != null` which means that we are now walking across the parent nodes.
|
29364 | // In such a case we are only allowed to look into the ViewProviders if:
|
29365 | // - We just crossed from child View to Parent View `previousTView != currentTView`
|
29366 | // - AND the parent TNode is an Element.
|
29367 | // This means that we just came from the Component's View and therefore are allowed to see
|
29368 | // into the ViewProviders.
|
29369 | (previousTView != currentTView && ((tNode.type & 3 /* AnyRNode */) !== 0));
|
29370 | // This special case happens when there is a @host on the inject and when we are searching
|
29371 | // on the host element node.
|
29372 | const isHostSpecialCase = (flags & InjectFlags.Host) && hostTElementNode === tNode;
|
29373 | const injectableIdx = locateDirectiveOrProvider(tNode, currentTView, token, canAccessViewProviders, isHostSpecialCase);
|
29374 | if (injectableIdx !== null) {
|
29375 | return getNodeInjectable(lView, currentTView, injectableIdx, tNode);
|
29376 | }
|
29377 | else {
|
29378 | return NOT_FOUND;
|
29379 | }
|
29380 | }
|
29381 | /**
|
29382 | * Searches for the given token among the node's directives and providers.
|
29383 | *
|
29384 | * @param tNode TNode on which directives are present.
|
29385 | * @param tView The tView we are currently processing
|
29386 | * @param token Provider token or type of a directive to look for.
|
29387 | * @param canAccessViewProviders Whether view providers should be considered.
|
29388 | * @param isHostSpecialCase Whether the host special case applies.
|
29389 | * @returns Index of a found directive or provider, or null when none found.
|
29390 | */
|
29391 | function locateDirectiveOrProvider(tNode, tView, token, canAccessViewProviders, isHostSpecialCase) {
|
29392 | const nodeProviderIndexes = tNode.providerIndexes;
|
29393 | const tInjectables = tView.data;
|
29394 | const injectablesStart = nodeProviderIndexes & 1048575 /* ProvidersStartIndexMask */;
|
29395 | const directivesStart = tNode.directiveStart;
|
29396 | const directiveEnd = tNode.directiveEnd;
|
29397 | const cptViewProvidersCount = nodeProviderIndexes >> 20 /* CptViewProvidersCountShift */;
|
29398 | const startingIndex = canAccessViewProviders ? injectablesStart : injectablesStart + cptViewProvidersCount;
|
29399 | // When the host special case applies, only the viewProviders and the component are visible
|
29400 | const endIndex = isHostSpecialCase ? injectablesStart + cptViewProvidersCount : directiveEnd;
|
29401 | for (let i = startingIndex; i < endIndex; i++) {
|
29402 | const providerTokenOrDef = tInjectables[i];
|
29403 | if (i < directivesStart && token === providerTokenOrDef ||
|
29404 | i >= directivesStart && providerTokenOrDef.type === token) {
|
29405 | return i;
|
29406 | }
|
29407 | }
|
29408 | if (isHostSpecialCase) {
|
29409 | const dirDef = tInjectables[directivesStart];
|
29410 | if (dirDef && isComponentDef(dirDef) && dirDef.type === token) {
|
29411 | return directivesStart;
|
29412 | }
|
29413 | }
|
29414 | return null;
|
29415 | }
|
29416 | /**
|
29417 | * Retrieve or instantiate the injectable from the `LView` at particular `index`.
|
29418 | *
|
29419 | * This function checks to see if the value has already been instantiated and if so returns the
|
29420 | * cached `injectable`. Otherwise if it detects that the value is still a factory it
|
29421 | * instantiates the `injectable` and caches the value.
|
29422 | */
|
29423 | function getNodeInjectable(lView, tView, index, tNode) {
|
29424 | let value = lView[index];
|
29425 | const tData = tView.data;
|
29426 | if (isFactory(value)) {
|
29427 | const factory = value;
|
29428 | if (factory.resolving) {
|
29429 | throwCyclicDependencyError(stringifyForError(tData[index]));
|
29430 | }
|
29431 | const previousIncludeViewProviders = setIncludeViewProviders(factory.canSeeViewProviders);
|
29432 | factory.resolving = true;
|
29433 | const previousInjectImplementation = factory.injectImpl ? setInjectImplementation(factory.injectImpl) : null;
|
29434 | const success = enterDI(lView, tNode, InjectFlags.Default);
|
29435 | ngDevMode &&
|
29436 | assertEqual(success, true, 'Because flags do not contain \`SkipSelf\' we expect this to always succeed.');
|
29437 | try {
|
29438 | value = lView[index] = factory.factory(undefined, tData, lView, tNode);
|
29439 | // This code path is hit for both directives and providers.
|
29440 | // For perf reasons, we want to avoid searching for hooks on providers.
|
29441 | // It does no harm to try (the hooks just won't exist), but the extra
|
29442 | // checks are unnecessary and this is a hot path. So we check to see
|
29443 | // if the index of the dependency is in the directive range for this
|
29444 | // tNode. If it's not, we know it's a provider and skip hook registration.
|
29445 | if (tView.firstCreatePass && index >= tNode.directiveStart) {
|
29446 | ngDevMode && assertDirectiveDef(tData[index]);
|
29447 | registerPreOrderHooks(index, tData[index], tView);
|
29448 | }
|
29449 | }
|
29450 | finally {
|
29451 | previousInjectImplementation !== null &&
|
29452 | setInjectImplementation(previousInjectImplementation);
|
29453 | setIncludeViewProviders(previousIncludeViewProviders);
|
29454 | factory.resolving = false;
|
29455 | leaveDI();
|
29456 | }
|
29457 | }
|
29458 | return value;
|
29459 | }
|
29460 | /**
|
29461 | * Returns the bit in an injector's bloom filter that should be used to determine whether or not
|
29462 | * the directive might be provided by the injector.
|
29463 | *
|
29464 | * When a directive is public, it is added to the bloom filter and given a unique ID that can be
|
29465 | * retrieved on the Type. When the directive isn't public or the token is not a directive `null`
|
29466 | * is returned as the node injector can not possibly provide that token.
|
29467 | *
|
29468 | * @param token the injection token
|
29469 | * @returns the matching bit to check in the bloom filter or `null` if the token is not known.
|
29470 | * When the returned value is negative then it represents special values such as `Injector`.
|
29471 | */
|
29472 | function bloomHashBitOrFactory(token) {
|
29473 | ngDevMode && assertDefined(token, 'token must be defined');
|
29474 | if (typeof token === 'string') {
|
29475 | return token.charCodeAt(0) || 0;
|
29476 | }
|
29477 | const tokenId =
|
29478 | // First check with `hasOwnProperty` so we don't get an inherited ID.
|
29479 | token.hasOwnProperty(NG_ELEMENT_ID) ? token[NG_ELEMENT_ID] : undefined;
|
29480 | // Negative token IDs are used for special objects such as `Injector`
|
29481 | if (typeof tokenId === 'number') {
|
29482 | if (tokenId >= 0) {
|
29483 | return tokenId & BLOOM_MASK;
|
29484 | }
|
29485 | else {
|
29486 | ngDevMode &&
|
29487 | assertEqual(tokenId, -1 /* Injector */, 'Expecting to get Special Injector Id');
|
29488 | return createNodeInjector;
|
29489 | }
|
29490 | }
|
29491 | else {
|
29492 | return tokenId;
|
29493 | }
|
29494 | }
|
29495 | function bloomHasToken(bloomHash, injectorIndex, injectorView) {
|
29496 | // Create a mask that targets the specific bit associated with the directive we're looking for.
|
29497 | // JS bit operations are 32 bits, so this will be a number between 2^0 and 2^31, corresponding
|
29498 | // to bit positions 0 - 31 in a 32 bit integer.
|
29499 | const mask = 1 << bloomHash;
|
29500 | // Each bloom bucket in `injectorView` represents `BLOOM_BUCKET_BITS` number of bits of
|
29501 | // `bloomHash`. Any bits in `bloomHash` beyond `BLOOM_BUCKET_BITS` indicate the bucket offset
|
29502 | // that should be used.
|
29503 | const value = injectorView[injectorIndex + (bloomHash >> BLOOM_BUCKET_BITS)];
|
29504 | // If the bloom filter value has the bit corresponding to the directive's bloomBit flipped on,
|
29505 | // this injector is a potential match.
|
29506 | return !!(value & mask);
|
29507 | }
|
29508 | /** Returns true if flags prevent parent injector from being searched for tokens */
|
29509 | function shouldSearchParent(flags, isFirstHostTNode) {
|
29510 | return !(flags & InjectFlags.Self) && !(flags & InjectFlags.Host && isFirstHostTNode);
|
29511 | }
|
29512 | class NodeInjector {
|
29513 | constructor(_tNode, _lView) {
|
29514 | this._tNode = _tNode;
|
29515 | this._lView = _lView;
|
29516 | }
|
29517 | get(token, notFoundValue) {
|
29518 | return getOrCreateInjectable(this._tNode, this._lView, token, undefined, notFoundValue);
|
29519 | }
|
29520 | }
|
29521 |
|
29522 | /**
|
29523 | * @license
|
29524 | * Copyright Google LLC All Rights Reserved.
|
29525 | *
|
29526 | * Use of this source code is governed by an MIT-style license that can be
|
29527 | * found in the LICENSE file at https://angular.io/license
|
29528 | */
|
29529 | const ANNOTATIONS = '__annotations__';
|
29530 | const PARAMETERS = '__parameters__';
|
29531 | const PROP_METADATA = '__prop__metadata__';
|
29532 | /**
|
29533 | * @suppress {globalThis}
|
29534 | */
|
29535 | function makeDecorator(name, props, parentClass, additionalProcessing, typeFn) {
|
29536 | return noSideEffects(() => {
|
29537 | const metaCtor = makeMetadataCtor(props);
|
29538 | function DecoratorFactory(...args) {
|
29539 | if (this instanceof DecoratorFactory) {
|
29540 | metaCtor.call(this, ...args);
|
29541 | return this;
|
29542 | }
|
29543 | const annotationInstance = new DecoratorFactory(...args);
|
29544 | return function TypeDecorator(cls) {
|
29545 | if (typeFn)
|
29546 | typeFn(cls, ...args);
|
29547 | // Use of Object.defineProperty is important since it creates non-enumerable property which
|
29548 | // prevents the property is copied during subclassing.
|
29549 | const annotations = cls.hasOwnProperty(ANNOTATIONS) ?
|
29550 | cls[ANNOTATIONS] :
|
29551 | Object.defineProperty(cls, ANNOTATIONS, { value: [] })[ANNOTATIONS];
|
29552 | annotations.push(annotationInstance);
|
29553 | if (additionalProcessing)
|
29554 | additionalProcessing(cls);
|
29555 | return cls;
|
29556 | };
|
29557 | }
|
29558 | if (parentClass) {
|
29559 | DecoratorFactory.prototype = Object.create(parentClass.prototype);
|
29560 | }
|
29561 | DecoratorFactory.prototype.ngMetadataName = name;
|
29562 | DecoratorFactory.annotationCls = DecoratorFactory;
|
29563 | return DecoratorFactory;
|
29564 | });
|
29565 | }
|
29566 | function makeMetadataCtor(props) {
|
29567 | return function ctor(...args) {
|
29568 | if (props) {
|
29569 | const values = props(...args);
|
29570 | for (const propName in values) {
|
29571 | this[propName] = values[propName];
|
29572 | }
|
29573 | }
|
29574 | };
|
29575 | }
|
29576 | function makeParamDecorator(name, props, parentClass) {
|
29577 | return noSideEffects(() => {
|
29578 | const metaCtor = makeMetadataCtor(props);
|
29579 | function ParamDecoratorFactory(...args) {
|
29580 | if (this instanceof ParamDecoratorFactory) {
|
29581 | metaCtor.apply(this, args);
|
29582 | return this;
|
29583 | }
|
29584 | const annotationInstance = new ParamDecoratorFactory(...args);
|
29585 | ParamDecorator.annotation = annotationInstance;
|
29586 | return ParamDecorator;
|
29587 | function ParamDecorator(cls, unusedKey, index) {
|
29588 | // Use of Object.defineProperty is important since it creates non-enumerable property which
|
29589 | // prevents the property is copied during subclassing.
|
29590 | const parameters = cls.hasOwnProperty(PARAMETERS) ?
|
29591 | cls[PARAMETERS] :
|
29592 | Object.defineProperty(cls, PARAMETERS, { value: [] })[PARAMETERS];
|
29593 | // there might be gaps if some in between parameters do not have annotations.
|
29594 | // we pad with nulls.
|
29595 | while (parameters.length <= index) {
|
29596 | parameters.push(null);
|
29597 | }
|
29598 | (parameters[index] = parameters[index] || []).push(annotationInstance);
|
29599 | return cls;
|
29600 | }
|
29601 | }
|
29602 | if (parentClass) {
|
29603 | ParamDecoratorFactory.prototype = Object.create(parentClass.prototype);
|
29604 | }
|
29605 | ParamDecoratorFactory.prototype.ngMetadataName = name;
|
29606 | ParamDecoratorFactory.annotationCls = ParamDecoratorFactory;
|
29607 | return ParamDecoratorFactory;
|
29608 | });
|
29609 | }
|
29610 | function makePropDecorator(name, props, parentClass, additionalProcessing) {
|
29611 | return noSideEffects(() => {
|
29612 | const metaCtor = makeMetadataCtor(props);
|
29613 | function PropDecoratorFactory(...args) {
|
29614 | if (this instanceof PropDecoratorFactory) {
|
29615 | metaCtor.apply(this, args);
|
29616 | return this;
|
29617 | }
|
29618 | const decoratorInstance = new PropDecoratorFactory(...args);
|
29619 | function PropDecorator(target, name) {
|
29620 | const constructor = target.constructor;
|
29621 | // Use of Object.defineProperty is important because it creates a non-enumerable property
|
29622 | // which prevents the property from being copied during subclassing.
|
29623 | const meta = constructor.hasOwnProperty(PROP_METADATA) ?
|
29624 | constructor[PROP_METADATA] :
|
29625 | Object.defineProperty(constructor, PROP_METADATA, { value: {} })[PROP_METADATA];
|
29626 | meta[name] = meta.hasOwnProperty(name) && meta[name] || [];
|
29627 | meta[name].unshift(decoratorInstance);
|
29628 | if (additionalProcessing)
|
29629 | additionalProcessing(target, name, ...args);
|
29630 | }
|
29631 | return PropDecorator;
|
29632 | }
|
29633 | if (parentClass) {
|
29634 | PropDecoratorFactory.prototype = Object.create(parentClass.prototype);
|
29635 | }
|
29636 | PropDecoratorFactory.prototype.ngMetadataName = name;
|
29637 | PropDecoratorFactory.annotationCls = PropDecoratorFactory;
|
29638 | return PropDecoratorFactory;
|
29639 | });
|
29640 | }
|
29641 |
|
29642 | /**
|
29643 | * @license
|
29644 | * Copyright Google LLC All Rights Reserved.
|
29645 | *
|
29646 | * Use of this source code is governed by an MIT-style license that can be
|
29647 | * found in the LICENSE file at https://angular.io/license
|
29648 | */
|
29649 | function CREATE_ATTRIBUTE_DECORATOR__PRE_R3__() {
|
29650 | return makeParamDecorator('Attribute', (attributeName) => ({ attributeName }));
|
29651 | }
|
29652 | const CREATE_ATTRIBUTE_DECORATOR_IMPL = CREATE_ATTRIBUTE_DECORATOR__PRE_R3__;
|
29653 | /**
|
29654 | * Attribute decorator and metadata.
|
29655 | *
|
29656 | * @Annotation
|
29657 | * @publicApi
|
29658 | */
|
29659 | const Attribute$1 = CREATE_ATTRIBUTE_DECORATOR_IMPL();
|
29660 |
|
29661 | /**
|
29662 | * @license
|
29663 | * Copyright Google LLC All Rights Reserved.
|
29664 | *
|
29665 | * Use of this source code is governed by an MIT-style license that can be
|
29666 | * found in the LICENSE file at https://angular.io/license
|
29667 | */
|
29668 | /**
|
29669 | * Creates a token that can be used in a DI Provider.
|
29670 | *
|
29671 | * Use an `InjectionToken` whenever the type you are injecting is not reified (does not have a
|
29672 | * runtime representation) such as when injecting an interface, callable type, array or
|
29673 | * parameterized type.
|
29674 | *
|
29675 | * `InjectionToken` is parameterized on `T` which is the type of object which will be returned by
|
29676 | * the `Injector`. This provides additional level of type safety.
|
29677 | *
|
29678 | * ```
|
29679 | * interface MyInterface {...}
|
29680 | * var myInterface = injector.get(new InjectionToken<MyInterface>('SomeToken'));
|
29681 | * // myInterface is inferred to be MyInterface.
|
29682 | * ```
|
29683 | *
|
29684 | * When creating an `InjectionToken`, you can optionally specify a factory function which returns
|
29685 | * (possibly by creating) a default value of the parameterized type `T`. This sets up the
|
29686 | * `InjectionToken` using this factory as a provider as if it was defined explicitly in the
|
29687 | * application's root injector. If the factory function, which takes zero arguments, needs to inject
|
29688 | * dependencies, it can do so using the `inject` function. See below for an example.
|
29689 | *
|
29690 | * Additionally, if a `factory` is specified you can also specify the `providedIn` option, which
|
29691 | * overrides the above behavior and marks the token as belonging to a particular `@NgModule`. As
|
29692 | * mentioned above, `'root'` is the default value for `providedIn`.
|
29693 | *
|
29694 | * @usageNotes
|
29695 | * ### Basic Example
|
29696 | *
|
29697 | * ### Plain InjectionToken
|
29698 | *
|
29699 | * {@example core/di/ts/injector_spec.ts region='InjectionToken'}
|
29700 | *
|
29701 | * ### Tree-shakable InjectionToken
|
29702 | *
|
29703 | * {@example core/di/ts/injector_spec.ts region='ShakableInjectionToken'}
|
29704 | *
|
29705 | *
|
29706 | * @publicApi
|
29707 | */
|
29708 | class InjectionToken {
|
29709 | constructor(_desc, options) {
|
29710 | this._desc = _desc;
|
29711 | /** @internal */
|
29712 | this.ngMetadataName = 'InjectionToken';
|
29713 | this.ɵprov = undefined;
|
29714 | if (typeof options == 'number') {
|
29715 | (typeof ngDevMode === 'undefined' || ngDevMode) &&
|
29716 | assertLessThan(options, 0, 'Only negative numbers are supported here');
|
29717 | // This is a special hack to assign __NG_ELEMENT_ID__ to this instance.
|
29718 | // See `InjectorMarkers`
|
29719 | this.__NG_ELEMENT_ID__ = options;
|
29720 | }
|
29721 | else if (options !== undefined) {
|
29722 | this.ɵprov = ɵɵdefineInjectable({
|
29723 | token: this,
|
29724 | providedIn: options.providedIn || 'root',
|
29725 | factory: options.factory,
|
29726 | });
|
29727 | }
|
29728 | }
|
29729 | toString() {
|
29730 | return `InjectionToken ${this._desc}`;
|
29731 | }
|
29732 | }
|
29733 |
|
29734 | /**
|
29735 | * @license
|
29736 | * Copyright Google LLC All Rights Reserved.
|
29737 | *
|
29738 | * Use of this source code is governed by an MIT-style license that can be
|
29739 | * found in the LICENSE file at https://angular.io/license
|
29740 | */
|
29741 | /**
|
29742 | * A DI token that you can use to create a virtual [provider](guide/glossary#provider)
|
29743 | * that will populate the `entryComponents` field of components and NgModules
|
29744 | * based on its `useValue` property value.
|
29745 | * All components that are referenced in the `useValue` value (either directly
|
29746 | * or in a nested array or map) are added to the `entryComponents` property.
|
29747 | *
|
29748 | * @usageNotes
|
29749 | *
|
29750 | * The following example shows how the router can populate the `entryComponents`
|
29751 | * field of an NgModule based on a router configuration that refers
|
29752 | * to components.
|
29753 | *
|
29754 | * ```typescript
|
29755 | * // helper function inside the router
|
29756 | * function provideRoutes(routes) {
|
29757 | * return [
|
29758 | * {provide: ROUTES, useValue: routes},
|
29759 | * {provide: ANALYZE_FOR_ENTRY_COMPONENTS, useValue: routes, multi: true}
|
29760 | * ];
|
29761 | * }
|
29762 | *
|
29763 | * // user code
|
29764 | * let routes = [
|
29765 | * {path: '/root', component: RootComp},
|
29766 | * {path: '/teams', component: TeamsComp}
|
29767 | * ];
|
29768 | *
|
29769 | * @NgModule({
|
29770 | * providers: [provideRoutes(routes)]
|
29771 | * })
|
29772 | * class ModuleWithRoutes {}
|
29773 | * ```
|
29774 | *
|
29775 | * @publicApi
|
29776 | * @deprecated Since 9.0.0. With Ivy, this property is no longer necessary.
|
29777 | */
|
29778 | const ANALYZE_FOR_ENTRY_COMPONENTS = new InjectionToken('AnalyzeForEntryComponents');
|
29779 | // Stores the default value of `emitDistinctChangesOnly` when the `emitDistinctChangesOnly` is not
|
29780 | // explicitly set. This value will be changed to `true` in v12.
|
29781 | // TODO(misko): switch the default in v12 to `true`. See: packages/compiler/src/core.ts
|
29782 | const emitDistinctChangesOnlyDefaultValue$1 = false;
|
29783 | /**
|
29784 | * Base class for query metadata.
|
29785 | *
|
29786 | * @see `ContentChildren`.
|
29787 | * @see `ContentChild`.
|
29788 | * @see `ViewChildren`.
|
29789 | * @see `ViewChild`.
|
29790 | *
|
29791 | * @publicApi
|
29792 | */
|
29793 | class Query {
|
29794 | }
|
29795 | const ɵ0$1 = (selector, data = {}) => (Object.assign({ selector, first: false, isViewQuery: false, descendants: false, emitDistinctChangesOnly: emitDistinctChangesOnlyDefaultValue$1 }, data));
|
29796 | /**
|
29797 | * ContentChildren decorator and metadata.
|
29798 | *
|
29799 | *
|
29800 | * @Annotation
|
29801 | * @publicApi
|
29802 | */
|
29803 | const ContentChildren = makePropDecorator('ContentChildren', ɵ0$1, Query);
|
29804 | const ɵ1 = (selector, data = {}) => (Object.assign({ selector, first: true, isViewQuery: false, descendants: true }, data));
|
29805 | /**
|
29806 | * ContentChild decorator and metadata.
|
29807 | *
|
29808 | *
|
29809 | * @Annotation
|
29810 | *
|
29811 | * @publicApi
|
29812 | */
|
29813 | const ContentChild = makePropDecorator('ContentChild', ɵ1, Query);
|
29814 | const ɵ2 = (selector, data = {}) => (Object.assign({ selector, first: false, isViewQuery: true, descendants: true, emitDistinctChangesOnly: emitDistinctChangesOnlyDefaultValue$1 }, data));
|
29815 | /**
|
29816 | * ViewChildren decorator and metadata.
|
29817 | *
|
29818 | * @Annotation
|
29819 | * @publicApi
|
29820 | */
|
29821 | const ViewChildren = makePropDecorator('ViewChildren', ɵ2, Query);
|
29822 | const ɵ3 = (selector, data) => (Object.assign({ selector, first: true, isViewQuery: true, descendants: true }, data));
|
29823 | /**
|
29824 | * ViewChild decorator and metadata.
|
29825 | *
|
29826 | * @Annotation
|
29827 | * @publicApi
|
29828 | */
|
29829 | const ViewChild = makePropDecorator('ViewChild', ɵ3, Query);
|
29830 |
|
29831 | /**
|
29832 | * @license
|
29833 | * Copyright Google LLC All Rights Reserved.
|
29834 | *
|
29835 | * Use of this source code is governed by an MIT-style license that can be
|
29836 | * found in the LICENSE file at https://angular.io/license
|
29837 | */
|
29838 | var R3ResolvedDependencyType$1;
|
29839 | (function (R3ResolvedDependencyType) {
|
29840 | R3ResolvedDependencyType[R3ResolvedDependencyType["Token"] = 0] = "Token";
|
29841 | R3ResolvedDependencyType[R3ResolvedDependencyType["Attribute"] = 1] = "Attribute";
|
29842 | R3ResolvedDependencyType[R3ResolvedDependencyType["ChangeDetectorRef"] = 2] = "ChangeDetectorRef";
|
29843 | R3ResolvedDependencyType[R3ResolvedDependencyType["Invalid"] = 3] = "Invalid";
|
29844 | })(R3ResolvedDependencyType$1 || (R3ResolvedDependencyType$1 = {}));
|
29845 | var R3FactoryTarget$1;
|
29846 | (function (R3FactoryTarget) {
|
29847 | R3FactoryTarget[R3FactoryTarget["Directive"] = 0] = "Directive";
|
29848 | R3FactoryTarget[R3FactoryTarget["Component"] = 1] = "Component";
|
29849 | R3FactoryTarget[R3FactoryTarget["Injectable"] = 2] = "Injectable";
|
29850 | R3FactoryTarget[R3FactoryTarget["Pipe"] = 3] = "Pipe";
|
29851 | R3FactoryTarget[R3FactoryTarget["NgModule"] = 4] = "NgModule";
|
29852 | })(R3FactoryTarget$1 || (R3FactoryTarget$1 = {}));
|
29853 | var ViewEncapsulation$2;
|
29854 | (function (ViewEncapsulation) {
|
29855 | ViewEncapsulation[ViewEncapsulation["Emulated"] = 0] = "Emulated";
|
29856 | // Historically the 1 value was for `Native` encapsulation which has been removed as of v11.
|
29857 | ViewEncapsulation[ViewEncapsulation["None"] = 2] = "None";
|
29858 | ViewEncapsulation[ViewEncapsulation["ShadowDom"] = 3] = "ShadowDom";
|
29859 | })(ViewEncapsulation$2 || (ViewEncapsulation$2 = {}));
|
29860 |
|
29861 | /**
|
29862 | * @license
|
29863 | * Copyright Google LLC All Rights Reserved.
|
29864 | *
|
29865 | * Use of this source code is governed by an MIT-style license that can be
|
29866 | * found in the LICENSE file at https://angular.io/license
|
29867 | */
|
29868 | /**
|
29869 | * @description
|
29870 | *
|
29871 | * Represents a type that a Component or other object is instances of.
|
29872 | *
|
29873 | * An example of a `Type` is `MyCustomComponent` class, which in JavaScript is represented by
|
29874 | * the `MyCustomComponent` constructor function.
|
29875 | *
|
29876 | * @publicApi
|
29877 | */
|
29878 | const Type$2 = Function;
|
29879 | function isType(v) {
|
29880 | return typeof v === 'function';
|
29881 | }
|
29882 |
|
29883 | /**
|
29884 | * @license
|
29885 | * Copyright Google LLC All Rights Reserved.
|
29886 | *
|
29887 | * Use of this source code is governed by an MIT-style license that can be
|
29888 | * found in the LICENSE file at https://angular.io/license
|
29889 | */
|
29890 | function removeFromArray(arr, index) {
|
29891 | // perf: array.pop is faster than array.splice!
|
29892 | if (index >= arr.length - 1) {
|
29893 | return arr.pop();
|
29894 | }
|
29895 | else {
|
29896 | return arr.splice(index, 1)[0];
|
29897 | }
|
29898 | }
|
29899 | function newArray$1(size, value) {
|
29900 | const list = [];
|
29901 | for (let i = 0; i < size; i++) {
|
29902 | list.push(value);
|
29903 | }
|
29904 | return list;
|
29905 | }
|
29906 |
|
29907 | /**
|
29908 | * @license
|
29909 | * Copyright Google LLC All Rights Reserved.
|
29910 | *
|
29911 | * Use of this source code is governed by an MIT-style license that can be
|
29912 | * found in the LICENSE file at https://angular.io/license
|
29913 | */
|
29914 | /*
|
29915 | * #########################
|
29916 | * Attention: These Regular expressions have to hold even if the code is minified!
|
29917 | * ##########################
|
29918 | */
|
29919 | /**
|
29920 | * Regular expression that detects pass-through constructors for ES5 output. This Regex
|
29921 | * intends to capture the common delegation pattern emitted by TypeScript and Babel. Also
|
29922 | * it intends to capture the pattern where existing constructors have been downleveled from
|
29923 | * ES2015 to ES5 using TypeScript w/ downlevel iteration. e.g.
|
29924 | *
|
29925 | * ```
|
29926 | * function MyClass() {
|
29927 | * var _this = _super.apply(this, arguments) || this;
|
29928 | * ```
|
29929 | *
|
29930 | * ```
|
29931 | * function MyClass() {
|
29932 | * var _this = _super.apply(this, __spread(arguments)) || this;
|
29933 | * ```
|
29934 | *
|
29935 | * More details can be found in: https://github.com/angular/angular/issues/38453.
|
29936 | */
|
29937 | const ES5_DELEGATE_CTOR = /^function\s+\S+\(\)\s*{[\s\S]+\.apply\(this,\s*(arguments|[^()]+\(arguments\))\)/;
|
29938 | /** Regular expression that detects ES2015 classes which extend from other classes. */
|
29939 | const ES2015_INHERITED_CLASS = /^class\s+[A-Za-z\d$_]*\s*extends\s+[^{]+{/;
|
29940 | /**
|
29941 | * Regular expression that detects ES2015 classes which extend from other classes and
|
29942 | * have an explicit constructor defined.
|
29943 | */
|
29944 | const ES2015_INHERITED_CLASS_WITH_CTOR = /^class\s+[A-Za-z\d$_]*\s*extends\s+[^{]+{[\s\S]*constructor\s*\(/;
|
29945 | /**
|
29946 | * Regular expression that detects ES2015 classes which extend from other classes
|
29947 | * and inherit a constructor.
|
29948 | */
|
29949 | const ES2015_INHERITED_CLASS_WITH_DELEGATE_CTOR = /^class\s+[A-Za-z\d$_]*\s*extends\s+[^{]+{[\s\S]*constructor\s*\(\)\s*{\s*super\(\.\.\.arguments\)/;
|
29950 | /**
|
29951 | * Determine whether a stringified type is a class which delegates its constructor
|
29952 | * to its parent.
|
29953 | *
|
29954 | * This is not trivial since compiled code can actually contain a constructor function
|
29955 | * even if the original source code did not. For instance, when the child class contains
|
29956 | * an initialized instance property.
|
29957 | */
|
29958 | function isDelegateCtor(typeStr) {
|
29959 | return ES5_DELEGATE_CTOR.test(typeStr) ||
|
29960 | ES2015_INHERITED_CLASS_WITH_DELEGATE_CTOR.test(typeStr) ||
|
29961 | (ES2015_INHERITED_CLASS.test(typeStr) && !ES2015_INHERITED_CLASS_WITH_CTOR.test(typeStr));
|
29962 | }
|
29963 | class ReflectionCapabilities {
|
29964 | constructor(reflect) {
|
29965 | this._reflect = reflect || _global$1['Reflect'];
|
29966 | }
|
29967 | isReflectionEnabled() {
|
29968 | return true;
|
29969 | }
|
29970 | factory(t) {
|
29971 | return (...args) => new t(...args);
|
29972 | }
|
29973 | /** @internal */
|
29974 | _zipTypesAndAnnotations(paramTypes, paramAnnotations) {
|
29975 | let result;
|
29976 | if (typeof paramTypes === 'undefined') {
|
29977 | result = newArray$1(paramAnnotations.length);
|
29978 | }
|
29979 | else {
|
29980 | result = newArray$1(paramTypes.length);
|
29981 | }
|
29982 | for (let i = 0; i < result.length; i++) {
|
29983 | // TS outputs Object for parameters without types, while Traceur omits
|
29984 | // the annotations. For now we preserve the Traceur behavior to aid
|
29985 | // migration, but this can be revisited.
|
29986 | if (typeof paramTypes === 'undefined') {
|
29987 | result[i] = [];
|
29988 | }
|
29989 | else if (paramTypes[i] && paramTypes[i] != Object) {
|
29990 | result[i] = [paramTypes[i]];
|
29991 | }
|
29992 | else {
|
29993 | result[i] = [];
|
29994 | }
|
29995 | if (paramAnnotations && paramAnnotations[i] != null) {
|
29996 | result[i] = result[i].concat(paramAnnotations[i]);
|
29997 | }
|
29998 | }
|
29999 | return result;
|
30000 | }
|
30001 | _ownParameters(type, parentCtor) {
|
30002 | const typeStr = type.toString();
|
30003 | // If we have no decorators, we only have function.length as metadata.
|
30004 | // In that case, to detect whether a child class declared an own constructor or not,
|
30005 | // we need to look inside of that constructor to check whether it is
|
30006 | // just calling the parent.
|
30007 | // This also helps to work around for https://github.com/Microsoft/TypeScript/issues/12439
|
30008 | // that sets 'design:paramtypes' to []
|
30009 | // if a class inherits from another class but has no ctor declared itself.
|
30010 | if (isDelegateCtor(typeStr)) {
|
30011 | return null;
|
30012 | }
|
30013 | // Prefer the direct API.
|
30014 | if (type.parameters && type.parameters !== parentCtor.parameters) {
|
30015 | return type.parameters;
|
30016 | }
|
30017 | // API of tsickle for lowering decorators to properties on the class.
|
30018 | const tsickleCtorParams = type.ctorParameters;
|
30019 | if (tsickleCtorParams && tsickleCtorParams !== parentCtor.ctorParameters) {
|
30020 | // Newer tsickle uses a function closure
|
30021 | // Retain the non-function case for compatibility with older tsickle
|
30022 | const ctorParameters = typeof tsickleCtorParams === 'function' ? tsickleCtorParams() : tsickleCtorParams;
|
30023 | const paramTypes = ctorParameters.map((ctorParam) => ctorParam && ctorParam.type);
|
30024 | const paramAnnotations = ctorParameters.map((ctorParam) => ctorParam && convertTsickleDecoratorIntoMetadata(ctorParam.decorators));
|
30025 | return this._zipTypesAndAnnotations(paramTypes, paramAnnotations);
|
30026 | }
|
30027 | // API for metadata created by invoking the decorators.
|
30028 | const paramAnnotations = type.hasOwnProperty(PARAMETERS) && type[PARAMETERS];
|
30029 | const paramTypes = this._reflect && this._reflect.getOwnMetadata &&
|
30030 | this._reflect.getOwnMetadata('design:paramtypes', type);
|
30031 | if (paramTypes || paramAnnotations) {
|
30032 | return this._zipTypesAndAnnotations(paramTypes, paramAnnotations);
|
30033 | }
|
30034 | // If a class has no decorators, at least create metadata
|
30035 | // based on function.length.
|
30036 | // Note: We know that this is a real constructor as we checked
|
30037 | // the content of the constructor above.
|
30038 | return newArray$1(type.length);
|
30039 | }
|
30040 | parameters(type) {
|
30041 | // Note: only report metadata if we have at least one class decorator
|
30042 | // to stay in sync with the static reflector.
|
30043 | if (!isType(type)) {
|
30044 | return [];
|
30045 | }
|
30046 | const parentCtor = getParentCtor(type);
|
30047 | let parameters = this._ownParameters(type, parentCtor);
|
30048 | if (!parameters && parentCtor !== Object) {
|
30049 | parameters = this.parameters(parentCtor);
|
30050 | }
|
30051 | return parameters || [];
|
30052 | }
|
30053 | _ownAnnotations(typeOrFunc, parentCtor) {
|
30054 | // Prefer the direct API.
|
30055 | if (typeOrFunc.annotations && typeOrFunc.annotations !== parentCtor.annotations) {
|
30056 | let annotations = typeOrFunc.annotations;
|
30057 | if (typeof annotations === 'function' && annotations.annotations) {
|
30058 | annotations = annotations.annotations;
|
30059 | }
|
30060 | return annotations;
|
30061 | }
|
30062 | // API of tsickle for lowering decorators to properties on the class.
|
30063 | if (typeOrFunc.decorators && typeOrFunc.decorators !== parentCtor.decorators) {
|
30064 | return convertTsickleDecoratorIntoMetadata(typeOrFunc.decorators);
|
30065 | }
|
30066 | // API for metadata created by invoking the decorators.
|
30067 | if (typeOrFunc.hasOwnProperty(ANNOTATIONS)) {
|
30068 | return typeOrFunc[ANNOTATIONS];
|
30069 | }
|
30070 | return null;
|
30071 | }
|
30072 | annotations(typeOrFunc) {
|
30073 | if (!isType(typeOrFunc)) {
|
30074 | return [];
|
30075 | }
|
30076 | const parentCtor = getParentCtor(typeOrFunc);
|
30077 | const ownAnnotations = this._ownAnnotations(typeOrFunc, parentCtor) || [];
|
30078 | const parentAnnotations = parentCtor !== Object ? this.annotations(parentCtor) : [];
|
30079 | return parentAnnotations.concat(ownAnnotations);
|
30080 | }
|
30081 | _ownPropMetadata(typeOrFunc, parentCtor) {
|
30082 | // Prefer the direct API.
|
30083 | if (typeOrFunc.propMetadata &&
|
30084 | typeOrFunc.propMetadata !== parentCtor.propMetadata) {
|
30085 | let propMetadata = typeOrFunc.propMetadata;
|
30086 | if (typeof propMetadata === 'function' && propMetadata.propMetadata) {
|
30087 | propMetadata = propMetadata.propMetadata;
|
30088 | }
|
30089 | return propMetadata;
|
30090 | }
|
30091 | // API of tsickle for lowering decorators to properties on the class.
|
30092 | if (typeOrFunc.propDecorators &&
|
30093 | typeOrFunc.propDecorators !== parentCtor.propDecorators) {
|
30094 | const propDecorators = typeOrFunc.propDecorators;
|
30095 | const propMetadata = {};
|
30096 | Object.keys(propDecorators).forEach(prop => {
|
30097 | propMetadata[prop] = convertTsickleDecoratorIntoMetadata(propDecorators[prop]);
|
30098 | });
|
30099 | return propMetadata;
|
30100 | }
|
30101 | // API for metadata created by invoking the decorators.
|
30102 | if (typeOrFunc.hasOwnProperty(PROP_METADATA)) {
|
30103 | return typeOrFunc[PROP_METADATA];
|
30104 | }
|
30105 | return null;
|
30106 | }
|
30107 | propMetadata(typeOrFunc) {
|
30108 | if (!isType(typeOrFunc)) {
|
30109 | return {};
|
30110 | }
|
30111 | const parentCtor = getParentCtor(typeOrFunc);
|
30112 | const propMetadata = {};
|
30113 | if (parentCtor !== Object) {
|
30114 | const parentPropMetadata = this.propMetadata(parentCtor);
|
30115 | Object.keys(parentPropMetadata).forEach((propName) => {
|
30116 | propMetadata[propName] = parentPropMetadata[propName];
|
30117 | });
|
30118 | }
|
30119 | const ownPropMetadata = this._ownPropMetadata(typeOrFunc, parentCtor);
|
30120 | if (ownPropMetadata) {
|
30121 | Object.keys(ownPropMetadata).forEach((propName) => {
|
30122 | const decorators = [];
|
30123 | if (propMetadata.hasOwnProperty(propName)) {
|
30124 | decorators.push(...propMetadata[propName]);
|
30125 | }
|
30126 | decorators.push(...ownPropMetadata[propName]);
|
30127 | propMetadata[propName] = decorators;
|
30128 | });
|
30129 | }
|
30130 | return propMetadata;
|
30131 | }
|
30132 | ownPropMetadata(typeOrFunc) {
|
30133 | if (!isType(typeOrFunc)) {
|
30134 | return {};
|
30135 | }
|
30136 | return this._ownPropMetadata(typeOrFunc, getParentCtor(typeOrFunc)) || {};
|
30137 | }
|
30138 | hasLifecycleHook(type, lcProperty) {
|
30139 | return type instanceof Type$2 && lcProperty in type.prototype;
|
30140 | }
|
30141 | guards(type) {
|
30142 | return {};
|
30143 | }
|
30144 | getter(name) {
|
30145 | return new Function('o', 'return o.' + name + ';');
|
30146 | }
|
30147 | setter(name) {
|
30148 | return new Function('o', 'v', 'return o.' + name + ' = v;');
|
30149 | }
|
30150 | method(name) {
|
30151 | const functionBody = `if (!o.${name}) throw new Error('"${name}" is undefined');
|
30152 | return o.${name}.apply(o, args);`;
|
30153 | return new Function('o', 'args', functionBody);
|
30154 | }
|
30155 | // There is not a concept of import uri in Js, but this is useful in developing Dart applications.
|
30156 | importUri(type) {
|
30157 | // StaticSymbol
|
30158 | if (typeof type === 'object' && type['filePath']) {
|
30159 | return type['filePath'];
|
30160 | }
|
30161 | // Runtime type
|
30162 | return `./${stringify$1(type)}`;
|
30163 | }
|
30164 | resourceUri(type) {
|
30165 | return `./${stringify$1(type)}`;
|
30166 | }
|
30167 | resolveIdentifier(name, moduleUrl, members, runtime) {
|
30168 | return runtime;
|
30169 | }
|
30170 | resolveEnum(enumIdentifier, name) {
|
30171 | return enumIdentifier[name];
|
30172 | }
|
30173 | }
|
30174 | function convertTsickleDecoratorIntoMetadata(decoratorInvocations) {
|
30175 | if (!decoratorInvocations) {
|
30176 | return [];
|
30177 | }
|
30178 | return decoratorInvocations.map(decoratorInvocation => {
|
30179 | const decoratorType = decoratorInvocation.type;
|
30180 | const annotationCls = decoratorType.annotationCls;
|
30181 | const annotationArgs = decoratorInvocation.args ? decoratorInvocation.args : [];
|
30182 | return new annotationCls(...annotationArgs);
|
30183 | });
|
30184 | }
|
30185 | function getParentCtor(ctor) {
|
30186 | const parentProto = ctor.prototype ? Object.getPrototypeOf(ctor.prototype) : null;
|
30187 | const parentCtor = parentProto ? parentProto.constructor : null;
|
30188 | // Note: We always use `Object` as the null value
|
30189 | // to simplify checking later on.
|
30190 | return parentCtor || Object;
|
30191 | }
|
30192 |
|
30193 | /**
|
30194 | * @license
|
30195 | * Copyright Google LLC All Rights Reserved.
|
30196 | *
|
30197 | * Use of this source code is governed by an MIT-style license that can be
|
30198 | * found in the LICENSE file at https://angular.io/license
|
30199 | */
|
30200 | const _THROW_IF_NOT_FOUND = {};
|
30201 | const THROW_IF_NOT_FOUND = _THROW_IF_NOT_FOUND;
|
30202 | /*
|
30203 | * Name of a property (that we patch onto DI decorator), which is used as an annotation of which
|
30204 | * InjectFlag this decorator represents. This allows to avoid direct references to the DI decorators
|
30205 | * in the code, thus making them tree-shakable.
|
30206 | */
|
30207 | const DI_DECORATOR_FLAG = '__NG_DI_FLAG__';
|
30208 | const NG_TEMP_TOKEN_PATH = 'ngTempTokenPath';
|
30209 | const NG_TOKEN_PATH = 'ngTokenPath';
|
30210 | const NEW_LINE = /\n/gm;
|
30211 | const NO_NEW_LINE = 'ɵ';
|
30212 | const SOURCE = '__source';
|
30213 | const ɵ0$2 = getClosureSafeProperty;
|
30214 | const USE_VALUE$2 = getClosureSafeProperty({ provide: String, useValue: ɵ0$2 });
|
30215 | /**
|
30216 | * Current injector value used by `inject`.
|
30217 | * - `undefined`: it is an error to call `inject`
|
30218 | * - `null`: `inject` can be called but there is no injector (limp-mode).
|
30219 | * - Injector instance: Use the injector for resolution.
|
30220 | */
|
30221 | let _currentInjector = undefined;
|
30222 | function setCurrentInjector(injector) {
|
30223 | const former = _currentInjector;
|
30224 | _currentInjector = injector;
|
30225 | return former;
|
30226 | }
|
30227 | function injectInjectorOnly(token, flags = InjectFlags.Default) {
|
30228 | if (_currentInjector === undefined) {
|
30229 | throw new Error(`inject() must be called from an injection context`);
|
30230 | }
|
30231 | else if (_currentInjector === null) {
|
30232 | return injectRootLimpMode(token, undefined, flags);
|
30233 | }
|
30234 | else {
|
30235 | return _currentInjector.get(token, flags & InjectFlags.Optional ? null : undefined, flags);
|
30236 | }
|
30237 | }
|
30238 | function ɵɵinject(token, flags = InjectFlags.Default) {
|
30239 | return (getInjectImplementation() || injectInjectorOnly)(resolveForwardRef$1(token), flags);
|
30240 | }
|
30241 | function injectArgs(types) {
|
30242 | const args = [];
|
30243 | for (let i = 0; i < types.length; i++) {
|
30244 | const arg = resolveForwardRef$1(types[i]);
|
30245 | if (Array.isArray(arg)) {
|
30246 | if (arg.length === 0) {
|
30247 | throw new Error('Arguments array must have arguments.');
|
30248 | }
|
30249 | let type = undefined;
|
30250 | let flags = InjectFlags.Default;
|
30251 | for (let j = 0; j < arg.length; j++) {
|
30252 | const meta = arg[j];
|
30253 | const flag = getInjectFlag(meta);
|
30254 | if (typeof flag === 'number') {
|
30255 | // Special case when we handle @Inject decorator.
|
30256 | if (flag === -1 /* Inject */) {
|
30257 | type = meta.token;
|
30258 | }
|
30259 | else {
|
30260 | flags |= flag;
|
30261 | }
|
30262 | }
|
30263 | else {
|
30264 | type = meta;
|
30265 | }
|
30266 | }
|
30267 | args.push(ɵɵinject(type, flags));
|
30268 | }
|
30269 | else {
|
30270 | args.push(ɵɵinject(arg));
|
30271 | }
|
30272 | }
|
30273 | return args;
|
30274 | }
|
30275 | /**
|
30276 | * Attaches a given InjectFlag to a given decorator using monkey-patching.
|
30277 | * Since DI decorators can be used in providers `deps` array (when provider is configured using
|
30278 | * `useFactory`) without initialization (e.g. `Host`) and as an instance (e.g. `new Host()`), we
|
30279 | * attach the flag to make it available both as a static property and as a field on decorator
|
30280 | * instance.
|
30281 | *
|
30282 | * @param decorator Provided DI decorator.
|
30283 | * @param flag InjectFlag that should be applied.
|
30284 | */
|
30285 | function attachInjectFlag(decorator, flag) {
|
30286 | decorator[DI_DECORATOR_FLAG] = flag;
|
30287 | decorator.prototype[DI_DECORATOR_FLAG] = flag;
|
30288 | return decorator;
|
30289 | }
|
30290 | /**
|
30291 | * Reads monkey-patched property that contains InjectFlag attached to a decorator.
|
30292 | *
|
30293 | * @param token Token that may contain monkey-patched DI flags property.
|
30294 | */
|
30295 | function getInjectFlag(token) {
|
30296 | return token[DI_DECORATOR_FLAG];
|
30297 | }
|
30298 | function catchInjectorError(e, token, injectorErrorName, source) {
|
30299 | const tokenPath = e[NG_TEMP_TOKEN_PATH];
|
30300 | if (token[SOURCE]) {
|
30301 | tokenPath.unshift(token[SOURCE]);
|
30302 | }
|
30303 | e.message = formatError('\n' + e.message, tokenPath, injectorErrorName, source);
|
30304 | e[NG_TOKEN_PATH] = tokenPath;
|
30305 | e[NG_TEMP_TOKEN_PATH] = null;
|
30306 | throw e;
|
30307 | }
|
30308 | function formatError(text, obj, injectorErrorName, source = null) {
|
30309 | text = text && text.charAt(0) === '\n' && text.charAt(1) == NO_NEW_LINE ? text.substr(2) : text;
|
30310 | let context = stringify$1(obj);
|
30311 | if (Array.isArray(obj)) {
|
30312 | context = obj.map(stringify$1).join(' -> ');
|
30313 | }
|
30314 | else if (typeof obj === 'object') {
|
30315 | let parts = [];
|
30316 | for (let key in obj) {
|
30317 | if (obj.hasOwnProperty(key)) {
|
30318 | let value = obj[key];
|
30319 | parts.push(key + ':' + (typeof value === 'string' ? JSON.stringify(value) : stringify$1(value)));
|
30320 | }
|
30321 | }
|
30322 | context = `{${parts.join(', ')}}`;
|
30323 | }
|
30324 | return `${injectorErrorName}${source ? '(' + source + ')' : ''}[${context}]: ${text.replace(NEW_LINE, '\n ')}`;
|
30325 | }
|
30326 |
|
30327 | /**
|
30328 | * @license
|
30329 | * Copyright Google LLC All Rights Reserved.
|
30330 | *
|
30331 | * Use of this source code is governed by an MIT-style license that can be
|
30332 | * found in the LICENSE file at https://angular.io/license
|
30333 | */
|
30334 | const ɵ0$3 = (token) => ({ token });
|
30335 | /**
|
30336 | * Inject decorator and metadata.
|
30337 | *
|
30338 | * @Annotation
|
30339 | * @publicApi
|
30340 | */
|
30341 | const Inject = attachInjectFlag(
|
30342 | // Disable tslint because `DecoratorFlags` is a const enum which gets inlined.
|
30343 | // tslint:disable-next-line: no-toplevel-property-access
|
30344 | makeParamDecorator('Inject', ɵ0$3), -1 /* Inject */);
|
30345 | /**
|
30346 | * Optional decorator and metadata.
|
30347 | *
|
30348 | * @Annotation
|
30349 | * @publicApi
|
30350 | */
|
30351 | const Optional =
|
30352 | // Disable tslint because `InternalInjectFlags` is a const enum which gets inlined.
|
30353 | // tslint:disable-next-line: no-toplevel-property-access
|
30354 | attachInjectFlag(makeParamDecorator('Optional'), 8 /* Optional */);
|
30355 | /**
|
30356 | * Self decorator and metadata.
|
30357 | *
|
30358 | * @Annotation
|
30359 | * @publicApi
|
30360 | */
|
30361 | const Self =
|
30362 | // Disable tslint because `InternalInjectFlags` is a const enum which gets inlined.
|
30363 | // tslint:disable-next-line: no-toplevel-property-access
|
30364 | attachInjectFlag(makeParamDecorator('Self'), 2 /* Self */);
|
30365 | /**
|
30366 | * `SkipSelf` decorator and metadata.
|
30367 | *
|
30368 | * @Annotation
|
30369 | * @publicApi
|
30370 | */
|
30371 | const SkipSelf =
|
30372 | // Disable tslint because `InternalInjectFlags` is a const enum which gets inlined.
|
30373 | // tslint:disable-next-line: no-toplevel-property-access
|
30374 | attachInjectFlag(makeParamDecorator('SkipSelf'), 4 /* SkipSelf */);
|
30375 | /**
|
30376 | * Host decorator and metadata.
|
30377 | *
|
30378 | * @Annotation
|
30379 | * @publicApi
|
30380 | */
|
30381 | const Host =
|
30382 | // Disable tslint because `InternalInjectFlags` is a const enum which gets inlined.
|
30383 | // tslint:disable-next-line: no-toplevel-property-access
|
30384 | attachInjectFlag(makeParamDecorator('Host'), 1 /* Host */);
|
30385 |
|
30386 | /**
|
30387 | * @license
|
30388 | * Copyright Google LLC All Rights Reserved.
|
30389 | *
|
30390 | * Use of this source code is governed by an MIT-style license that can be
|
30391 | * found in the LICENSE file at https://angular.io/license
|
30392 | */
|
30393 | /**
|
30394 | * The Trusted Types policy, or null if Trusted Types are not
|
30395 | * enabled/supported, or undefined if the policy has not been created yet.
|
30396 | */
|
30397 | let policy$1;
|
30398 | /**
|
30399 | * Returns the Trusted Types policy, or null if Trusted Types are not
|
30400 | * enabled/supported. The first call to this function will create the policy.
|
30401 | */
|
30402 | function getPolicy$1() {
|
30403 | if (policy$1 === undefined) {
|
30404 | policy$1 = null;
|
30405 | if (_global$1.trustedTypes) {
|
30406 | try {
|
30407 | policy$1 = _global$1.trustedTypes.createPolicy('angular', {
|
30408 | createHTML: (s) => s,
|
30409 | createScript: (s) => s,
|
30410 | createScriptURL: (s) => s,
|
30411 | });
|
30412 | }
|
30413 | catch (_a) {
|
30414 | // trustedTypes.createPolicy throws if called with a name that is
|
30415 | // already registered, even in report-only mode. Until the API changes,
|
30416 | // catch the error not to break the applications functionally. In such
|
30417 | // cases, the code will fall back to using strings.
|
30418 | }
|
30419 | }
|
30420 | }
|
30421 | return policy$1;
|
30422 | }
|
30423 | /**
|
30424 | * Unsafely promote a string to a TrustedScript, falling back to strings when
|
30425 | * Trusted Types are not available.
|
30426 | * @security In particular, it must be assured that the provided string will
|
30427 | * never cause an XSS vulnerability if used in a context that will be
|
30428 | * interpreted and executed as a script by a browser, e.g. when calling eval.
|
30429 | */
|
30430 | function trustedScriptFromString$1(script) {
|
30431 | var _a;
|
30432 | return ((_a = getPolicy$1()) === null || _a === void 0 ? void 0 : _a.createScript(script)) || script;
|
30433 | }
|
30434 | /**
|
30435 | * Unsafely call the Function constructor with the given string arguments. It
|
30436 | * is only available in development mode, and should be stripped out of
|
30437 | * production code.
|
30438 | * @security This is a security-sensitive function; any use of this function
|
30439 | * must go through security review. In particular, it must be assured that it
|
30440 | * is only called from development code, as use in production code can lead to
|
30441 | * XSS vulnerabilities.
|
30442 | */
|
30443 | function newTrustedFunctionForDev(...args) {
|
30444 | if (typeof ngDevMode === 'undefined') {
|
30445 | throw new Error('newTrustedFunctionForDev should never be called in production');
|
30446 | }
|
30447 | if (!_global$1.trustedTypes) {
|
30448 | // In environments that don't support Trusted Types, fall back to the most
|
30449 | // straightforward implementation:
|
30450 | return new Function(...args);
|
30451 | }
|
30452 | // Chrome currently does not support passing TrustedScript to the Function
|
30453 | // constructor. The following implements the workaround proposed on the page
|
30454 | // below, where the Chromium bug is also referenced:
|
30455 | // https://github.com/w3c/webappsec-trusted-types/wiki/Trusted-Types-for-function-constructor
|
30456 | const fnArgs = args.slice(0, -1).join(',');
|
30457 | const fnBody = args[args.length - 1];
|
30458 | const body = `(function anonymous(${fnArgs}
|
30459 | ) { ${fnBody}
|
30460 | })`;
|
30461 | // Using eval directly confuses the compiler and prevents this module from
|
30462 | // being stripped out of JS binaries even if not used. The global['eval']
|
30463 | // indirection fixes that.
|
30464 | const fn = _global$1['eval'](trustedScriptFromString$1(body));
|
30465 | if (fn.bind === undefined) {
|
30466 | // Workaround for a browser bug that only exists in Chrome 83, where passing
|
30467 | // a TrustedScript to eval just returns the TrustedScript back without
|
30468 | // evaluating it. In that case, fall back to the most straightforward
|
30469 | // implementation:
|
30470 | return new Function(...args);
|
30471 | }
|
30472 | // To completely mimic the behavior of calling "new Function", two more
|
30473 | // things need to happen:
|
30474 | // 1. Stringifying the resulting function should return its source code
|
30475 | fn.toString = () => body;
|
30476 | // 2. When calling the resulting function, `this` should refer to `global`
|
30477 | return fn.bind(_global$1);
|
30478 | // When Trusted Types support in Function constructors is widely available,
|
30479 | // the implementation of this function can be simplified to:
|
30480 | // return new Function(...args.map(a => trustedScriptFromString(a)));
|
30481 | }
|
30482 |
|
30483 | /**
|
30484 | * @license
|
30485 | * Copyright Google LLC All Rights Reserved.
|
30486 | *
|
30487 | * Use of this source code is governed by an MIT-style license that can be
|
30488 | * found in the LICENSE file at https://angular.io/license
|
30489 | */
|
30490 | function tagSet(tags) {
|
30491 | const res = {};
|
30492 | for (const t of tags.split(','))
|
30493 | res[t] = true;
|
30494 | return res;
|
30495 | }
|
30496 | function merge(...sets) {
|
30497 | const res = {};
|
30498 | for (const s of sets) {
|
30499 | for (const v in s) {
|
30500 | if (s.hasOwnProperty(v))
|
30501 | res[v] = true;
|
30502 | }
|
30503 | }
|
30504 | return res;
|
30505 | }
|
30506 | // Good source of info about elements and attributes
|
30507 | // https://html.spec.whatwg.org/#semantics
|
30508 | // https://simon.html5.org/html-elements
|
30509 | // Safe Void Elements - HTML5
|
30510 | // https://html.spec.whatwg.org/#void-elements
|
30511 | const VOID_ELEMENTS = tagSet('area,br,col,hr,img,wbr');
|
30512 | // Elements that you can, intentionally, leave open (and which close themselves)
|
30513 | // https://html.spec.whatwg.org/#optional-tags
|
30514 | const OPTIONAL_END_TAG_BLOCK_ELEMENTS = tagSet('colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr');
|
30515 | const OPTIONAL_END_TAG_INLINE_ELEMENTS = tagSet('rp,rt');
|
30516 | const OPTIONAL_END_TAG_ELEMENTS = merge(OPTIONAL_END_TAG_INLINE_ELEMENTS, OPTIONAL_END_TAG_BLOCK_ELEMENTS);
|
30517 | // Safe Block Elements - HTML5
|
30518 | const BLOCK_ELEMENTS = merge(OPTIONAL_END_TAG_BLOCK_ELEMENTS, tagSet('address,article,' +
|
30519 | 'aside,blockquote,caption,center,del,details,dialog,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5,' +
|
30520 | 'h6,header,hgroup,hr,ins,main,map,menu,nav,ol,pre,section,summary,table,ul'));
|
30521 | // Inline Elements - HTML5
|
30522 | const INLINE_ELEMENTS = merge(OPTIONAL_END_TAG_INLINE_ELEMENTS, tagSet('a,abbr,acronym,audio,b,' +
|
30523 | 'bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,picture,q,ruby,rp,rt,s,' +
|
30524 | 'samp,small,source,span,strike,strong,sub,sup,time,track,tt,u,var,video'));
|
30525 | const VALID_ELEMENTS = merge(VOID_ELEMENTS, BLOCK_ELEMENTS, INLINE_ELEMENTS, OPTIONAL_END_TAG_ELEMENTS);
|
30526 | // Attributes that have href and hence need to be sanitized
|
30527 | const URI_ATTRS = tagSet('background,cite,href,itemtype,longdesc,poster,src,xlink:href');
|
30528 | // Attributes that have special href set hence need to be sanitized
|
30529 | const SRCSET_ATTRS = tagSet('srcset');
|
30530 | const HTML_ATTRS = tagSet('abbr,accesskey,align,alt,autoplay,axis,bgcolor,border,cellpadding,cellspacing,class,clear,color,cols,colspan,' +
|
30531 | 'compact,controls,coords,datetime,default,dir,download,face,headers,height,hidden,hreflang,hspace,' +
|
30532 | 'ismap,itemscope,itemprop,kind,label,lang,language,loop,media,muted,nohref,nowrap,open,preload,rel,rev,role,rows,rowspan,rules,' +
|
30533 | 'scope,scrolling,shape,size,sizes,span,srclang,start,summary,tabindex,target,title,translate,type,usemap,' +
|
30534 | 'valign,value,vspace,width');
|
30535 | // Accessibility attributes as per WAI-ARIA 1.1 (W3C Working Draft 14 December 2018)
|
30536 | const ARIA_ATTRS = tagSet('aria-activedescendant,aria-atomic,aria-autocomplete,aria-busy,aria-checked,aria-colcount,aria-colindex,' +
|
30537 | 'aria-colspan,aria-controls,aria-current,aria-describedby,aria-details,aria-disabled,aria-dropeffect,' +
|
30538 | 'aria-errormessage,aria-expanded,aria-flowto,aria-grabbed,aria-haspopup,aria-hidden,aria-invalid,' +
|
30539 | 'aria-keyshortcuts,aria-label,aria-labelledby,aria-level,aria-live,aria-modal,aria-multiline,' +
|
30540 | 'aria-multiselectable,aria-orientation,aria-owns,aria-placeholder,aria-posinset,aria-pressed,aria-readonly,' +
|
30541 | 'aria-relevant,aria-required,aria-roledescription,aria-rowcount,aria-rowindex,aria-rowspan,aria-selected,' +
|
30542 | 'aria-setsize,aria-sort,aria-valuemax,aria-valuemin,aria-valuenow,aria-valuetext');
|
30543 | // NB: This currently consciously doesn't support SVG. SVG sanitization has had several security
|
30544 | // issues in the past, so it seems safer to leave it out if possible. If support for binding SVG via
|
30545 | // innerHTML is required, SVG attributes should be added here.
|
30546 | // NB: Sanitization does not allow <form> elements or other active elements (<button> etc). Those
|
30547 | // can be sanitized, but they increase security surface area without a legitimate use case, so they
|
30548 | // are left out here.
|
30549 | const VALID_ATTRS = merge(URI_ATTRS, SRCSET_ATTRS, HTML_ATTRS, ARIA_ATTRS);
|
30550 | // Elements whose content should not be traversed/preserved, if the elements themselves are invalid.
|
30551 | //
|
30552 | // Typically, `<invalid>Some content</invalid>` would traverse (and in this case preserve)
|
30553 | // `Some content`, but strip `invalid-element` opening/closing tags. For some elements, though, we
|
30554 | // don't want to preserve the content, if the elements themselves are going to be removed.
|
30555 | const SKIP_TRAVERSING_CONTENT_IF_INVALID_ELEMENTS = tagSet('script,style,template');
|
30556 |
|
30557 | /**
|
30558 | * @license
|
30559 | * Copyright Google LLC All Rights Reserved.
|
30560 | *
|
30561 | * Use of this source code is governed by an MIT-style license that can be
|
30562 | * found in the LICENSE file at https://angular.io/license
|
30563 | */
|
30564 | /**
|
30565 | * A SecurityContext marks a location that has dangerous security implications, e.g. a DOM property
|
30566 | * like `innerHTML` that could cause Cross Site Scripting (XSS) security bugs when improperly
|
30567 | * handled.
|
30568 | *
|
30569 | * See DomSanitizer for more details on security in Angular applications.
|
30570 | *
|
30571 | * @publicApi
|
30572 | */
|
30573 | var SecurityContext$1;
|
30574 | (function (SecurityContext) {
|
30575 | SecurityContext[SecurityContext["NONE"] = 0] = "NONE";
|
30576 | SecurityContext[SecurityContext["HTML"] = 1] = "HTML";
|
30577 | SecurityContext[SecurityContext["STYLE"] = 2] = "STYLE";
|
30578 | SecurityContext[SecurityContext["SCRIPT"] = 3] = "SCRIPT";
|
30579 | SecurityContext[SecurityContext["URL"] = 4] = "URL";
|
30580 | SecurityContext[SecurityContext["RESOURCE_URL"] = 5] = "RESOURCE_URL";
|
30581 | })(SecurityContext$1 || (SecurityContext$1 = {}));
|
30582 |
|
30583 | /**
|
30584 | * @license
|
30585 | * Copyright Google LLC All Rights Reserved.
|
30586 | *
|
30587 | * Use of this source code is governed by an MIT-style license that can be
|
30588 | * found in the LICENSE file at https://angular.io/license
|
30589 | */
|
30590 | const ERROR_DEBUG_CONTEXT = 'ngDebugContext';
|
30591 | const ERROR_ORIGINAL_ERROR = 'ngOriginalError';
|
30592 | const ERROR_LOGGER = 'ngErrorLogger';
|
30593 | function wrappedError(message, originalError) {
|
30594 | const msg = `${message} caused by: ${originalError instanceof Error ? originalError.message : originalError}`;
|
30595 | const error = Error(msg);
|
30596 | error[ERROR_ORIGINAL_ERROR] = originalError;
|
30597 | return error;
|
30598 | }
|
30599 |
|
30600 | /**
|
30601 | * @license
|
30602 | * Copyright Google LLC All Rights Reserved.
|
30603 | *
|
30604 | * Use of this source code is governed by an MIT-style license that can be
|
30605 | * found in the LICENSE file at https://angular.io/license
|
30606 | */
|
30607 | function getDebugContext(error) {
|
30608 | return error[ERROR_DEBUG_CONTEXT];
|
30609 | }
|
30610 | function getOriginalError(error) {
|
30611 | return error[ERROR_ORIGINAL_ERROR];
|
30612 | }
|
30613 | function getErrorLogger(error) {
|
30614 | return error[ERROR_LOGGER] || defaultErrorLogger;
|
30615 | }
|
30616 | function defaultErrorLogger(console, ...values) {
|
30617 | console.error(...values);
|
30618 | }
|
30619 |
|
30620 | /**
|
30621 | * @license
|
30622 | * Copyright Google LLC All Rights Reserved.
|
30623 | *
|
30624 | * Use of this source code is governed by an MIT-style license that can be
|
30625 | * found in the LICENSE file at https://angular.io/license
|
30626 | */
|
30627 | /**
|
30628 | * Provides a hook for centralized exception handling.
|
30629 | *
|
30630 | * The default implementation of `ErrorHandler` prints error messages to the `console`. To
|
30631 | * intercept error handling, write a custom exception handler that replaces this default as
|
30632 | * appropriate for your app.
|
30633 | *
|
30634 | * @usageNotes
|
30635 | * ### Example
|
30636 | *
|
30637 | * ```
|
30638 | * class MyErrorHandler implements ErrorHandler {
|
30639 | * handleError(error) {
|
30640 | * // do something with the exception
|
30641 | * }
|
30642 | * }
|
30643 | *
|
30644 | * @NgModule({
|
30645 | * providers: [{provide: ErrorHandler, useClass: MyErrorHandler}]
|
30646 | * })
|
30647 | * class MyModule {}
|
30648 | * ```
|
30649 | *
|
30650 | * @publicApi
|
30651 | */
|
30652 | class ErrorHandler {
|
30653 | constructor() {
|
30654 | /**
|
30655 | * @internal
|
30656 | */
|
30657 | this._console = console;
|
30658 | }
|
30659 | handleError(error) {
|
30660 | const originalError = this._findOriginalError(error);
|
30661 | const context = this._findContext(error);
|
30662 | // Note: Browser consoles show the place from where console.error was called.
|
30663 | // We can use this to give users additional information about the error.
|
30664 | const errorLogger = getErrorLogger(error);
|
30665 | errorLogger(this._console, `ERROR`, error);
|
30666 | if (originalError) {
|
30667 | errorLogger(this._console, `ORIGINAL ERROR`, originalError);
|
30668 | }
|
30669 | if (context) {
|
30670 | errorLogger(this._console, 'ERROR CONTEXT', context);
|
30671 | }
|
30672 | }
|
30673 | /** @internal */
|
30674 | _findContext(error) {
|
30675 | if (error) {
|
30676 | return getDebugContext(error) ? getDebugContext(error) :
|
30677 | this._findContext(getOriginalError(error));
|
30678 | }
|
30679 | return null;
|
30680 | }
|
30681 | /** @internal */
|
30682 | _findOriginalError(error) {
|
30683 | let e = getOriginalError(error);
|
30684 | while (e && getOriginalError(e)) {
|
30685 | e = getOriginalError(e);
|
30686 | }
|
30687 | return e;
|
30688 | }
|
30689 | }
|
30690 |
|
30691 | /**
|
30692 | * @license
|
30693 | * Copyright Google LLC All Rights Reserved.
|
30694 | *
|
30695 | * Use of this source code is governed by an MIT-style license that can be
|
30696 | * found in the LICENSE file at https://angular.io/license
|
30697 | */
|
30698 | /**
|
30699 | * THIS FILE CONTAINS CODE WHICH SHOULD BE TREE SHAKEN AND NEVER CALLED FROM PRODUCTION CODE!!!
|
30700 | */
|
30701 | /**
|
30702 | * Creates an `Array` construction with a given name. This is useful when
|
30703 | * looking for memory consumption to see what time of array it is.
|
30704 | *
|
30705 | *
|
30706 | * @param name Name to give to the constructor
|
30707 | * @returns A subclass of `Array` if possible. This can only be done in
|
30708 | * environments which support `class` construct.
|
30709 | */
|
30710 | function createNamedArrayType(name) {
|
30711 | // This should never be called in prod mode, so let's verify that is the case.
|
30712 | if (ngDevMode) {
|
30713 | try {
|
30714 | // If this function were compromised the following could lead to arbitrary
|
30715 | // script execution. We bless it with Trusted Types anyway since this
|
30716 | // function is stripped out of production binaries.
|
30717 | return (newTrustedFunctionForDev('Array', `return class ${name} extends Array{}`))(Array);
|
30718 | }
|
30719 | catch (e) {
|
30720 | // If it does not work just give up and fall back to regular Array.
|
30721 | return Array;
|
30722 | }
|
30723 | }
|
30724 | else {
|
30725 | throw new Error('Looks like we are in \'prod mode\', but we are creating a named Array type, which is wrong! Check your code');
|
30726 | }
|
30727 | }
|
30728 |
|
30729 | /**
|
30730 | * @license
|
30731 | * Copyright Google LLC All Rights Reserved.
|
30732 | *
|
30733 | * Use of this source code is governed by an MIT-style license that can be
|
30734 | * found in the LICENSE file at https://angular.io/license
|
30735 | */
|
30736 | /**
|
30737 | * Assigns the given data to the given target (which could be a component,
|
30738 | * directive or DOM node instance) using monkey-patching.
|
30739 | */
|
30740 | function attachPatchData(target, data) {
|
30741 | target[MONKEY_PATCH_KEY_NAME] = data;
|
30742 | }
|
30743 |
|
30744 | /**
|
30745 | * @license
|
30746 | * Copyright Google LLC All Rights Reserved.
|
30747 | *
|
30748 | * Use of this source code is governed by an MIT-style license that can be
|
30749 | * found in the LICENSE file at https://angular.io/license
|
30750 | */
|
30751 | const ɵ0$4 = () => (typeof requestAnimationFrame !== 'undefined' &&
|
30752 | requestAnimationFrame || // browser only
|
30753 | setTimeout // everything else
|
30754 | )
|
30755 | .bind(_global$1);
|
30756 | const defaultScheduler = (ɵ0$4)();
|
30757 |
|
30758 | /**
|
30759 | * @license
|
30760 | * Copyright Google LLC All Rights Reserved.
|
30761 | *
|
30762 | * Use of this source code is governed by an MIT-style license that can be
|
30763 | * found in the LICENSE file at https://angular.io/license
|
30764 | */
|
30765 | /**
|
30766 | * Flags for renderer-specific style modifiers.
|
30767 | * @publicApi
|
30768 | */
|
30769 | var RendererStyleFlags2;
|
30770 | (function (RendererStyleFlags2) {
|
30771 | // TODO(misko): This needs to be refactored into a separate file so that it can be imported from
|
30772 | // `node_manipulation.ts` Currently doing the import cause resolution order to change and fails
|
30773 | // the tests. The work around is to have hard coded value in `node_manipulation.ts` for now.
|
30774 | /**
|
30775 | * Marks a style as important.
|
30776 | */
|
30777 | RendererStyleFlags2[RendererStyleFlags2["Important"] = 1] = "Important";
|
30778 | /**
|
30779 | * Marks a style as using dash case naming (this-is-dash-case).
|
30780 | */
|
30781 | RendererStyleFlags2[RendererStyleFlags2["DashCase"] = 2] = "DashCase";
|
30782 | })(RendererStyleFlags2 || (RendererStyleFlags2 = {}));
|
30783 |
|
30784 | /**
|
30785 | * @license
|
30786 | * Copyright Google LLC All Rights Reserved.
|
30787 | *
|
30788 | * Use of this source code is governed by an MIT-style license that can be
|
30789 | * found in the LICENSE file at https://angular.io/license
|
30790 | */
|
30791 | let _icuContainerIterate;
|
30792 | /**
|
30793 | * Iterator which provides ability to visit all of the `TIcuContainerNode` root `RNode`s.
|
30794 | */
|
30795 | function icuContainerIterate(tIcuContainerNode, lView) {
|
30796 | return _icuContainerIterate();
|
30797 | }
|
30798 |
|
30799 | /**
|
30800 | * @license
|
30801 | * Copyright Google LLC All Rights Reserved.
|
30802 | *
|
30803 | * Use of this source code is governed by an MIT-style license that can be
|
30804 | * found in the LICENSE file at https://angular.io/license
|
30805 | */
|
30806 | /**
|
30807 | * Gets the parent LView of the passed LView, if the PARENT is an LContainer, will get the parent of
|
30808 | * that LContainer, which is an LView
|
30809 | * @param lView the lView whose parent to get
|
30810 | */
|
30811 | function getLViewParent(lView) {
|
30812 | ngDevMode && assertLView(lView);
|
30813 | const parent = lView[PARENT];
|
30814 | return isLContainer(parent) ? parent[PARENT] : parent;
|
30815 | }
|
30816 | /**
|
30817 | * Gets the first `LContainer` in the LView or `null` if none exists.
|
30818 | */
|
30819 | function getFirstLContainer(lView) {
|
30820 | return getNearestLContainer(lView[CHILD_HEAD]);
|
30821 | }
|
30822 | /**
|
30823 | * Gets the next `LContainer` that is a sibling of the given container.
|
30824 | */
|
30825 | function getNextLContainer(container) {
|
30826 | return getNearestLContainer(container[NEXT]);
|
30827 | }
|
30828 | function getNearestLContainer(viewOrContainer) {
|
30829 | while (viewOrContainer !== null && !isLContainer(viewOrContainer)) {
|
30830 | viewOrContainer = viewOrContainer[NEXT];
|
30831 | }
|
30832 | return viewOrContainer;
|
30833 | }
|
30834 |
|
30835 | /**
|
30836 | * @license
|
30837 | * Copyright Google LLC All Rights Reserved.
|
30838 | *
|
30839 | * Use of this source code is governed by an MIT-style license that can be
|
30840 | * found in the LICENSE file at https://angular.io/license
|
30841 | */
|
30842 | /**
|
30843 | * NOTE: for performance reasons, the possible actions are inlined within the function instead of
|
30844 | * being passed as an argument.
|
30845 | */
|
30846 | function applyToElementOrContainer(action, renderer, parent, lNodeToHandle, beforeNode) {
|
30847 | // If this slot was allocated for a text node dynamically created by i18n, the text node itself
|
30848 | // won't be created until i18nApply() in the update block, so this node should be skipped.
|
30849 | // For more info, see "ICU expressions should work inside an ngTemplateOutlet inside an ngFor"
|
30850 | // in `i18n_spec.ts`.
|
30851 | if (lNodeToHandle != null) {
|
30852 | let lContainer;
|
30853 | let isComponent = false;
|
30854 | // We are expecting an RNode, but in the case of a component or LContainer the `RNode` is
|
30855 | // wrapped in an array which needs to be unwrapped. We need to know if it is a component and if
|
30856 | // it has LContainer so that we can process all of those cases appropriately.
|
30857 | if (isLContainer(lNodeToHandle)) {
|
30858 | lContainer = lNodeToHandle;
|
30859 | }
|
30860 | else if (isLView(lNodeToHandle)) {
|
30861 | isComponent = true;
|
30862 | ngDevMode && assertDefined(lNodeToHandle[HOST], 'HOST must be defined for a component LView');
|
30863 | lNodeToHandle = lNodeToHandle[HOST];
|
30864 | }
|
30865 | const rNode = unwrapRNode(lNodeToHandle);
|
30866 | ngDevMode && !isProceduralRenderer(renderer) && assertDomNode(rNode);
|
30867 | if (action === 0 /* Create */ && parent !== null) {
|
30868 | if (beforeNode == null) {
|
30869 | nativeAppendChild(renderer, parent, rNode);
|
30870 | }
|
30871 | else {
|
30872 | nativeInsertBefore(renderer, parent, rNode, beforeNode || null, true);
|
30873 | }
|
30874 | }
|
30875 | else if (action === 1 /* Insert */ && parent !== null) {
|
30876 | nativeInsertBefore(renderer, parent, rNode, beforeNode || null, true);
|
30877 | }
|
30878 | else if (action === 2 /* Detach */) {
|
30879 | nativeRemoveNode(renderer, rNode, isComponent);
|
30880 | }
|
30881 | else if (action === 3 /* Destroy */) {
|
30882 | ngDevMode && ngDevMode.rendererDestroyNode++;
|
30883 | renderer.destroyNode(rNode);
|
30884 | }
|
30885 | if (lContainer != null) {
|
30886 | applyContainer(renderer, action, lContainer, parent, beforeNode);
|
30887 | }
|
30888 | }
|
30889 | }
|
30890 | /**
|
30891 | * Creates a native element from a tag name, using a renderer.
|
30892 | * @param renderer A renderer to use
|
30893 | * @param name the tag name
|
30894 | * @param namespace Optional namespace for element.
|
30895 | * @returns the element created
|
30896 | */
|
30897 | function createElementNode(renderer, name, namespace) {
|
30898 | ngDevMode && ngDevMode.rendererCreateElement++;
|
30899 | if (isProceduralRenderer(renderer)) {
|
30900 | return renderer.createElement(name, namespace);
|
30901 | }
|
30902 | else {
|
30903 | return namespace === null ? renderer.createElement(name) :
|
30904 | renderer.createElementNS(namespace, name);
|
30905 | }
|
30906 | }
|
30907 | /**
|
30908 | * Removes all DOM elements associated with a view.
|
30909 | *
|
30910 | * Because some root nodes of the view may be containers, we sometimes need
|
30911 | * to propagate deeply into the nested containers to remove all elements in the
|
30912 | * views beneath it.
|
30913 | *
|
30914 | * @param tView The `TView' of the `LView` from which elements should be added or removed
|
30915 | * @param lView The view from which elements should be added or removed
|
30916 | */
|
30917 | function removeViewFromContainer(tView, lView) {
|
30918 | const renderer = lView[RENDERER];
|
30919 | applyView(tView, lView, renderer, 2 /* Detach */, null, null);
|
30920 | lView[HOST] = null;
|
30921 | lView[T_HOST] = null;
|
30922 | }
|
30923 | /**
|
30924 | * Detach a `LView` from the DOM by detaching its nodes.
|
30925 | *
|
30926 | * @param tView The `TView' of the `LView` to be detached
|
30927 | * @param lView the `LView` to be detached.
|
30928 | */
|
30929 | function renderDetachView(tView, lView) {
|
30930 | applyView(tView, lView, lView[RENDERER], 2 /* Detach */, null, null);
|
30931 | }
|
30932 | /**
|
30933 | * Traverses down and up the tree of views and containers to remove listeners and
|
30934 | * call onDestroy callbacks.
|
30935 | *
|
30936 | * Notes:
|
30937 | * - Because it's used for onDestroy calls, it needs to be bottom-up.
|
30938 | * - Must process containers instead of their views to avoid splicing
|
30939 | * when views are destroyed and re-added.
|
30940 | * - Using a while loop because it's faster than recursion
|
30941 | * - Destroy only called on movement to sibling or movement to parent (laterally or up)
|
30942 | *
|
30943 | * @param rootView The view to destroy
|
30944 | */
|
30945 | function destroyViewTree(rootView) {
|
30946 | // If the view has no children, we can clean it up and return early.
|
30947 | let lViewOrLContainer = rootView[CHILD_HEAD];
|
30948 | if (!lViewOrLContainer) {
|
30949 | return cleanUpView(rootView[TVIEW], rootView);
|
30950 | }
|
30951 | while (lViewOrLContainer) {
|
30952 | let next = null;
|
30953 | if (isLView(lViewOrLContainer)) {
|
30954 | // If LView, traverse down to child.
|
30955 | next = lViewOrLContainer[CHILD_HEAD];
|
30956 | }
|
30957 | else {
|
30958 | ngDevMode && assertLContainer(lViewOrLContainer);
|
30959 | // If container, traverse down to its first LView.
|
30960 | const firstView = lViewOrLContainer[CONTAINER_HEADER_OFFSET];
|
30961 | if (firstView)
|
30962 | next = firstView;
|
30963 | }
|
30964 | if (!next) {
|
30965 | // Only clean up view when moving to the side or up, as destroy hooks
|
30966 | // should be called in order from the bottom up.
|
30967 | while (lViewOrLContainer && !lViewOrLContainer[NEXT] && lViewOrLContainer !== rootView) {
|
30968 | if (isLView(lViewOrLContainer)) {
|
30969 | cleanUpView(lViewOrLContainer[TVIEW], lViewOrLContainer);
|
30970 | }
|
30971 | lViewOrLContainer = lViewOrLContainer[PARENT];
|
30972 | }
|
30973 | if (lViewOrLContainer === null)
|
30974 | lViewOrLContainer = rootView;
|
30975 | if (isLView(lViewOrLContainer)) {
|
30976 | cleanUpView(lViewOrLContainer[TVIEW], lViewOrLContainer);
|
30977 | }
|
30978 | next = lViewOrLContainer && lViewOrLContainer[NEXT];
|
30979 | }
|
30980 | lViewOrLContainer = next;
|
30981 | }
|
30982 | }
|
30983 | function detachMovedView(declarationContainer, lView) {
|
30984 | ngDevMode && assertLContainer(declarationContainer);
|
30985 | ngDevMode &&
|
30986 | assertDefined(declarationContainer[MOVED_VIEWS], 'A projected view should belong to a non-empty projected views collection');
|
30987 | const movedViews = declarationContainer[MOVED_VIEWS];
|
30988 | const declarationViewIndex = movedViews.indexOf(lView);
|
30989 | const insertionLContainer = lView[PARENT];
|
30990 | ngDevMode && assertLContainer(insertionLContainer);
|
30991 | // If the view was marked for refresh but then detached before it was checked (where the flag
|
30992 | // would be cleared and the counter decremented), we need to decrement the view counter here
|
30993 | // instead.
|
30994 | if (lView[FLAGS] & 1024 /* RefreshTransplantedView */) {
|
30995 | lView[FLAGS] &= ~1024 /* RefreshTransplantedView */;
|
30996 | updateTransplantedViewCount(insertionLContainer, -1);
|
30997 | }
|
30998 | movedViews.splice(declarationViewIndex, 1);
|
30999 | }
|
31000 | /**
|
31001 | * Detaches a view from a container.
|
31002 | *
|
31003 | * This method removes the view from the container's array of active views. It also
|
31004 | * removes the view's elements from the DOM.
|
31005 | *
|
31006 | * @param lContainer The container from which to detach a view
|
31007 | * @param removeIndex The index of the view to detach
|
31008 | * @returns Detached LView instance.
|
31009 | */
|
31010 | function detachView(lContainer, removeIndex) {
|
31011 | if (lContainer.length <= CONTAINER_HEADER_OFFSET)
|
31012 | return;
|
31013 | const indexInContainer = CONTAINER_HEADER_OFFSET + removeIndex;
|
31014 | const viewToDetach = lContainer[indexInContainer];
|
31015 | if (viewToDetach) {
|
31016 | const declarationLContainer = viewToDetach[DECLARATION_LCONTAINER];
|
31017 | if (declarationLContainer !== null && declarationLContainer !== lContainer) {
|
31018 | detachMovedView(declarationLContainer, viewToDetach);
|
31019 | }
|
31020 | if (removeIndex > 0) {
|
31021 | lContainer[indexInContainer - 1][NEXT] = viewToDetach[NEXT];
|
31022 | }
|
31023 | const removedLView = removeFromArray(lContainer, CONTAINER_HEADER_OFFSET + removeIndex);
|
31024 | removeViewFromContainer(viewToDetach[TVIEW], viewToDetach);
|
31025 | // notify query that a view has been removed
|
31026 | const lQueries = removedLView[QUERIES];
|
31027 | if (lQueries !== null) {
|
31028 | lQueries.detachView(removedLView[TVIEW]);
|
31029 | }
|
31030 | viewToDetach[PARENT] = null;
|
31031 | viewToDetach[NEXT] = null;
|
31032 | // Unsets the attached flag
|
31033 | viewToDetach[FLAGS] &= ~128 /* Attached */;
|
31034 | }
|
31035 | return viewToDetach;
|
31036 | }
|
31037 | /**
|
31038 | * A standalone function which destroys an LView,
|
31039 | * conducting clean up (e.g. removing listeners, calling onDestroys).
|
31040 | *
|
31041 | * @param tView The `TView' of the `LView` to be destroyed
|
31042 | * @param lView The view to be destroyed.
|
31043 | */
|
31044 | function destroyLView(tView, lView) {
|
31045 | if (!(lView[FLAGS] & 256 /* Destroyed */)) {
|
31046 | const renderer = lView[RENDERER];
|
31047 | if (isProceduralRenderer(renderer) && renderer.destroyNode) {
|
31048 | applyView(tView, lView, renderer, 3 /* Destroy */, null, null);
|
31049 | }
|
31050 | destroyViewTree(lView);
|
31051 | }
|
31052 | }
|
31053 | /**
|
31054 | * Calls onDestroys hooks for all directives and pipes in a given view and then removes all
|
31055 | * listeners. Listeners are removed as the last step so events delivered in the onDestroys hooks
|
31056 | * can be propagated to @Output listeners.
|
31057 | *
|
31058 | * @param tView `TView` for the `LView` to clean up.
|
31059 | * @param lView The LView to clean up
|
31060 | */
|
31061 | function cleanUpView(tView, lView) {
|
31062 | if (!(lView[FLAGS] & 256 /* Destroyed */)) {
|
31063 | // Usually the Attached flag is removed when the view is detached from its parent, however
|
31064 | // if it's a root view, the flag won't be unset hence why we're also removing on destroy.
|
31065 | lView[FLAGS] &= ~128 /* Attached */;
|
31066 | // Mark the LView as destroyed *before* executing the onDestroy hooks. An onDestroy hook
|
31067 | // runs arbitrary user code, which could include its own `viewRef.destroy()` (or similar). If
|
31068 | // We don't flag the view as destroyed before the hooks, this could lead to an infinite loop.
|
31069 | // This also aligns with the ViewEngine behavior. It also means that the onDestroy hook is
|
31070 | // really more of an "afterDestroy" hook if you think about it.
|
31071 | lView[FLAGS] |= 256 /* Destroyed */;
|
31072 | executeOnDestroys(tView, lView);
|
31073 | processCleanups(tView, lView);
|
31074 | // For component views only, the local renderer is destroyed at clean up time.
|
31075 | if (lView[TVIEW].type === 1 /* Component */ && isProceduralRenderer(lView[RENDERER])) {
|
31076 | ngDevMode && ngDevMode.rendererDestroy++;
|
31077 | lView[RENDERER].destroy();
|
31078 | }
|
31079 | const declarationContainer = lView[DECLARATION_LCONTAINER];
|
31080 | // we are dealing with an embedded view that is still inserted into a container
|
31081 | if (declarationContainer !== null && isLContainer(lView[PARENT])) {
|
31082 | // and this is a projected view
|
31083 | if (declarationContainer !== lView[PARENT]) {
|
31084 | detachMovedView(declarationContainer, lView);
|
31085 | }
|
31086 | // For embedded views still attached to a container: remove query result from this view.
|
31087 | const lQueries = lView[QUERIES];
|
31088 | if (lQueries !== null) {
|
31089 | lQueries.detachView(tView);
|
31090 | }
|
31091 | }
|
31092 | }
|
31093 | }
|
31094 | /** Removes listeners and unsubscribes from output subscriptions */
|
31095 | function processCleanups(tView, lView) {
|
31096 | const tCleanup = tView.cleanup;
|
31097 | const lCleanup = lView[CLEANUP];
|
31098 | // `LCleanup` contains both share information with `TCleanup` as well as instance specific
|
31099 | // information appended at the end. We need to know where the end of the `TCleanup` information
|
31100 | // is, and we track this with `lastLCleanupIndex`.
|
31101 | let lastLCleanupIndex = -1;
|
31102 | if (tCleanup !== null) {
|
31103 | for (let i = 0; i < tCleanup.length - 1; i += 2) {
|
31104 | if (typeof tCleanup[i] === 'string') {
|
31105 | // This is a native DOM listener
|
31106 | const idxOrTargetGetter = tCleanup[i + 1];
|
31107 | const target = typeof idxOrTargetGetter === 'function' ?
|
31108 | idxOrTargetGetter(lView) :
|
31109 | unwrapRNode(lView[idxOrTargetGetter]);
|
31110 | const listener = lCleanup[lastLCleanupIndex = tCleanup[i + 2]];
|
31111 | const useCaptureOrSubIdx = tCleanup[i + 3];
|
31112 | if (typeof useCaptureOrSubIdx === 'boolean') {
|
31113 | // native DOM listener registered with Renderer3
|
31114 | target.removeEventListener(tCleanup[i], listener, useCaptureOrSubIdx);
|
31115 | }
|
31116 | else {
|
31117 | if (useCaptureOrSubIdx >= 0) {
|
31118 | // unregister
|
31119 | lCleanup[lastLCleanupIndex = useCaptureOrSubIdx]();
|
31120 | }
|
31121 | else {
|
31122 | // Subscription
|
31123 | lCleanup[lastLCleanupIndex = -useCaptureOrSubIdx].unsubscribe();
|
31124 | }
|
31125 | }
|
31126 | i += 2;
|
31127 | }
|
31128 | else {
|
31129 | // This is a cleanup function that is grouped with the index of its context
|
31130 | const context = lCleanup[lastLCleanupIndex = tCleanup[i + 1]];
|
31131 | tCleanup[i].call(context);
|
31132 | }
|
31133 | }
|
31134 | }
|
31135 | if (lCleanup !== null) {
|
31136 | for (let i = lastLCleanupIndex + 1; i < lCleanup.length; i++) {
|
31137 | const instanceCleanupFn = lCleanup[i];
|
31138 | ngDevMode && assertFunction(instanceCleanupFn, 'Expecting instance cleanup function.');
|
31139 | instanceCleanupFn();
|
31140 | }
|
31141 | lView[CLEANUP] = null;
|
31142 | }
|
31143 | }
|
31144 | /** Calls onDestroy hooks for this view */
|
31145 | function executeOnDestroys(tView, lView) {
|
31146 | let destroyHooks;
|
31147 | if (tView != null && (destroyHooks = tView.destroyHooks) != null) {
|
31148 | for (let i = 0; i < destroyHooks.length; i += 2) {
|
31149 | const context = lView[destroyHooks[i]];
|
31150 | // Only call the destroy hook if the context has been requested.
|
31151 | if (!(context instanceof NodeInjectorFactory)) {
|
31152 | const toCall = destroyHooks[i + 1];
|
31153 | if (Array.isArray(toCall)) {
|
31154 | for (let j = 0; j < toCall.length; j += 2) {
|
31155 | toCall[j + 1].call(context[toCall[j]]);
|
31156 | }
|
31157 | }
|
31158 | else {
|
31159 | toCall.call(context);
|
31160 | }
|
31161 | }
|
31162 | }
|
31163 | }
|
31164 | }
|
31165 | /**
|
31166 | * Inserts a native node before another native node for a given parent using {@link Renderer3}.
|
31167 | * This is a utility function that can be used when native nodes were determined - it abstracts an
|
31168 | * actual renderer being used.
|
31169 | */
|
31170 | function nativeInsertBefore(renderer, parent, child, beforeNode, isMove) {
|
31171 | ngDevMode && ngDevMode.rendererInsertBefore++;
|
31172 | if (isProceduralRenderer(renderer)) {
|
31173 | renderer.insertBefore(parent, child, beforeNode, isMove);
|
31174 | }
|
31175 | else {
|
31176 | parent.insertBefore(child, beforeNode, isMove);
|
31177 | }
|
31178 | }
|
31179 | function nativeAppendChild(renderer, parent, child) {
|
31180 | ngDevMode && ngDevMode.rendererAppendChild++;
|
31181 | ngDevMode && assertDefined(parent, 'parent node must be defined');
|
31182 | if (isProceduralRenderer(renderer)) {
|
31183 | renderer.appendChild(parent, child);
|
31184 | }
|
31185 | else {
|
31186 | parent.appendChild(child);
|
31187 | }
|
31188 | }
|
31189 | /** Removes a node from the DOM given its native parent. */
|
31190 | function nativeRemoveChild(renderer, parent, child, isHostElement) {
|
31191 | if (isProceduralRenderer(renderer)) {
|
31192 | renderer.removeChild(parent, child, isHostElement);
|
31193 | }
|
31194 | else {
|
31195 | parent.removeChild(child);
|
31196 | }
|
31197 | }
|
31198 | /**
|
31199 | * Returns a native parent of a given native node.
|
31200 | */
|
31201 | function nativeParentNode(renderer, node) {
|
31202 | return (isProceduralRenderer(renderer) ? renderer.parentNode(node) : node.parentNode);
|
31203 | }
|
31204 | function getProjectionNodes(lView, tNode) {
|
31205 | if (tNode !== null) {
|
31206 | const componentView = lView[DECLARATION_COMPONENT_VIEW];
|
31207 | const componentHost = componentView[T_HOST];
|
31208 | const slotIdx = tNode.projection;
|
31209 | ngDevMode && assertProjectionSlots(lView);
|
31210 | return componentHost.projection[slotIdx];
|
31211 | }
|
31212 | return null;
|
31213 | }
|
31214 | /**
|
31215 | * Removes a native node itself using a given renderer. To remove the node we are looking up its
|
31216 | * parent from the native tree as not all platforms / browsers support the equivalent of
|
31217 | * node.remove().
|
31218 | *
|
31219 | * @param renderer A renderer to be used
|
31220 | * @param rNode The native node that should be removed
|
31221 | * @param isHostElement A flag indicating if a node to be removed is a host of a component.
|
31222 | */
|
31223 | function nativeRemoveNode(renderer, rNode, isHostElement) {
|
31224 | ngDevMode && ngDevMode.rendererRemoveNode++;
|
31225 | const nativeParent = nativeParentNode(renderer, rNode);
|
31226 | if (nativeParent) {
|
31227 | nativeRemoveChild(renderer, nativeParent, rNode, isHostElement);
|
31228 | }
|
31229 | }
|
31230 | /**
|
31231 | * Performs the operation of `action` on the node. Typically this involves inserting or removing
|
31232 | * nodes on the LView or projection boundary.
|
31233 | */
|
31234 | function applyNodes(renderer, action, tNode, lView, parentRElement, beforeNode, isProjection) {
|
31235 | while (tNode != null) {
|
31236 | ngDevMode && assertTNodeForLView(tNode, lView);
|
31237 | ngDevMode &&
|
31238 | assertTNodeType(tNode, 3 /* AnyRNode */ | 12 /* AnyContainer */ | 16 /* Projection */ | 32 /* Icu */);
|
31239 | const rawSlotValue = lView[tNode.index];
|
31240 | const tNodeType = tNode.type;
|
31241 | if (isProjection) {
|
31242 | if (action === 0 /* Create */) {
|
31243 | rawSlotValue && attachPatchData(unwrapRNode(rawSlotValue), lView);
|
31244 | tNode.flags |= 4 /* isProjected */;
|
31245 | }
|
31246 | }
|
31247 | if ((tNode.flags & 64 /* isDetached */) !== 64 /* isDetached */) {
|
31248 | if (tNodeType & 8 /* ElementContainer */) {
|
31249 | applyNodes(renderer, action, tNode.child, lView, parentRElement, beforeNode, false);
|
31250 | applyToElementOrContainer(action, renderer, parentRElement, rawSlotValue, beforeNode);
|
31251 | }
|
31252 | else if (tNodeType & 32 /* Icu */) {
|
31253 | const nextRNode = icuContainerIterate();
|
31254 | let rNode;
|
31255 | while (rNode = nextRNode()) {
|
31256 | applyToElementOrContainer(action, renderer, parentRElement, rNode, beforeNode);
|
31257 | }
|
31258 | applyToElementOrContainer(action, renderer, parentRElement, rawSlotValue, beforeNode);
|
31259 | }
|
31260 | else if (tNodeType & 16 /* Projection */) {
|
31261 | applyProjectionRecursive(renderer, action, lView, tNode, parentRElement, beforeNode);
|
31262 | }
|
31263 | else {
|
31264 | ngDevMode && assertTNodeType(tNode, 3 /* AnyRNode */ | 4 /* Container */);
|
31265 | applyToElementOrContainer(action, renderer, parentRElement, rawSlotValue, beforeNode);
|
31266 | }
|
31267 | }
|
31268 | tNode = isProjection ? tNode.projectionNext : tNode.next;
|
31269 | }
|
31270 | }
|
31271 | function applyView(tView, lView, renderer, action, parentRElement, beforeNode) {
|
31272 | applyNodes(renderer, action, tView.firstChild, lView, parentRElement, beforeNode, false);
|
31273 | }
|
31274 | /**
|
31275 | * `applyProjectionRecursive` performs operation on the projection specified by `action` (insert,
|
31276 | * detach, destroy)
|
31277 | *
|
31278 | * Inserting a projection requires us to locate the projected nodes from the parent component. The
|
31279 | * complication is that those nodes themselves could be re-projected from their parent component.
|
31280 | *
|
31281 | * @param renderer Render to use
|
31282 | * @param action action to perform (insert, detach, destroy)
|
31283 | * @param lView The LView which needs to be inserted, detached, destroyed.
|
31284 | * @param tProjectionNode node to project
|
31285 | * @param parentRElement parent DOM element for insertion/removal.
|
31286 | * @param beforeNode Before which node the insertions should happen.
|
31287 | */
|
31288 | function applyProjectionRecursive(renderer, action, lView, tProjectionNode, parentRElement, beforeNode) {
|
31289 | const componentLView = lView[DECLARATION_COMPONENT_VIEW];
|
31290 | const componentNode = componentLView[T_HOST];
|
31291 | ngDevMode &&
|
31292 | assertEqual(typeof tProjectionNode.projection, 'number', 'expecting projection index');
|
31293 | const nodeToProjectOrRNodes = componentNode.projection[tProjectionNode.projection];
|
31294 | if (Array.isArray(nodeToProjectOrRNodes)) {
|
31295 | // This should not exist, it is a bit of a hack. When we bootstrap a top level node and we
|
31296 | // need to support passing projectable nodes, so we cheat and put them in the TNode
|
31297 | // of the Host TView. (Yes we put instance info at the T Level). We can get away with it
|
31298 | // because we know that that TView is not shared and therefore it will not be a problem.
|
31299 | // This should be refactored and cleaned up.
|
31300 | for (let i = 0; i < nodeToProjectOrRNodes.length; i++) {
|
31301 | const rNode = nodeToProjectOrRNodes[i];
|
31302 | applyToElementOrContainer(action, renderer, parentRElement, rNode, beforeNode);
|
31303 | }
|
31304 | }
|
31305 | else {
|
31306 | let nodeToProject = nodeToProjectOrRNodes;
|
31307 | const projectedComponentLView = componentLView[PARENT];
|
31308 | applyNodes(renderer, action, nodeToProject, projectedComponentLView, parentRElement, beforeNode, true);
|
31309 | }
|
31310 | }
|
31311 | /**
|
31312 | * `applyContainer` performs an operation on the container and its views as specified by
|
31313 | * `action` (insert, detach, destroy)
|
31314 | *
|
31315 | * Inserting a Container is complicated by the fact that the container may have Views which
|
31316 | * themselves have containers or projections.
|
31317 | *
|
31318 | * @param renderer Renderer to use
|
31319 | * @param action action to perform (insert, detach, destroy)
|
31320 | * @param lContainer The LContainer which needs to be inserted, detached, destroyed.
|
31321 | * @param parentRElement parent DOM element for insertion/removal.
|
31322 | * @param beforeNode Before which node the insertions should happen.
|
31323 | */
|
31324 | function applyContainer(renderer, action, lContainer, parentRElement, beforeNode) {
|
31325 | ngDevMode && assertLContainer(lContainer);
|
31326 | const anchor = lContainer[NATIVE]; // LContainer has its own before node.
|
31327 | const native = unwrapRNode(lContainer);
|
31328 | // An LContainer can be created dynamically on any node by injecting ViewContainerRef.
|
31329 | // Asking for a ViewContainerRef on an element will result in a creation of a separate anchor
|
31330 | // node (comment in the DOM) that will be different from the LContainer's host node. In this
|
31331 | // particular case we need to execute action on 2 nodes:
|
31332 | // - container's host node (this is done in the executeActionOnElementOrContainer)
|
31333 | // - container's host node (this is done here)
|
31334 | if (anchor !== native) {
|
31335 | // This is very strange to me (Misko). I would expect that the native is same as anchor. I
|
31336 | // don't see a reason why they should be different, but they are.
|
31337 | //
|
31338 | // If they are we need to process the second anchor as well.
|
31339 | applyToElementOrContainer(action, renderer, parentRElement, anchor, beforeNode);
|
31340 | }
|
31341 | for (let i = CONTAINER_HEADER_OFFSET; i < lContainer.length; i++) {
|
31342 | const lView = lContainer[i];
|
31343 | applyView(lView[TVIEW], lView, renderer, action, parentRElement, anchor);
|
31344 | }
|
31345 | }
|
31346 | /**
|
31347 | * Write `cssText` to `RElement`.
|
31348 | *
|
31349 | * This function does direct write without any reconciliation. Used for writing initial values, so
|
31350 | * that static styling values do not pull in the style parser.
|
31351 | *
|
31352 | * @param renderer Renderer to use
|
31353 | * @param element The element which needs to be updated.
|
31354 | * @param newValue The new class list to write.
|
31355 | */
|
31356 | function writeDirectStyle(renderer, element, newValue) {
|
31357 | ngDevMode && assertString(newValue, '\'newValue\' should be a string');
|
31358 | if (isProceduralRenderer(renderer)) {
|
31359 | renderer.setAttribute(element, 'style', newValue);
|
31360 | }
|
31361 | else {
|
31362 | element.style.cssText = newValue;
|
31363 | }
|
31364 | ngDevMode && ngDevMode.rendererSetStyle++;
|
31365 | }
|
31366 | /**
|
31367 | * Write `className` to `RElement`.
|
31368 | *
|
31369 | * This function does direct write without any reconciliation. Used for writing initial values, so
|
31370 | * that static styling values do not pull in the style parser.
|
31371 | *
|
31372 | * @param renderer Renderer to use
|
31373 | * @param element The element which needs to be updated.
|
31374 | * @param newValue The new class list to write.
|
31375 | */
|
31376 | function writeDirectClass(renderer, element, newValue) {
|
31377 | ngDevMode && assertString(newValue, '\'newValue\' should be a string');
|
31378 | if (isProceduralRenderer(renderer)) {
|
31379 | if (newValue === '') {
|
31380 | // There are tests in `google3` which expect `element.getAttribute('class')` to be `null`.
|
31381 | renderer.removeAttribute(element, 'class');
|
31382 | }
|
31383 | else {
|
31384 | renderer.setAttribute(element, 'class', newValue);
|
31385 | }
|
31386 | }
|
31387 | else {
|
31388 | element.className = newValue;
|
31389 | }
|
31390 | ngDevMode && ngDevMode.rendererSetClassName++;
|
31391 | }
|
31392 |
|
31393 | /**
|
31394 | * @license
|
31395 | * Copyright Google LLC All Rights Reserved.
|
31396 | *
|
31397 | * Use of this source code is governed by an MIT-style license that can be
|
31398 | * found in the LICENSE file at https://angular.io/license
|
31399 | */
|
31400 | function isPositive(mode) {
|
31401 | return (mode & 1 /* NOT */) === 0;
|
31402 | }
|
31403 | function maybeWrapInNotSelector(isNegativeMode, chunk) {
|
31404 | return isNegativeMode ? ':not(' + chunk.trim() + ')' : chunk;
|
31405 | }
|
31406 | function stringifyCSSSelector(selector) {
|
31407 | let result = selector[0];
|
31408 | let i = 1;
|
31409 | let mode = 2 /* ATTRIBUTE */;
|
31410 | let currentChunk = '';
|
31411 | let isNegativeMode = false;
|
31412 | while (i < selector.length) {
|
31413 | let valueOrMarker = selector[i];
|
31414 | if (typeof valueOrMarker === 'string') {
|
31415 | if (mode & 2 /* ATTRIBUTE */) {
|
31416 | const attrValue = selector[++i];
|
31417 | currentChunk +=
|
31418 | '[' + valueOrMarker + (attrValue.length > 0 ? '="' + attrValue + '"' : '') + ']';
|
31419 | }
|
31420 | else if (mode & 8 /* CLASS */) {
|
31421 | currentChunk += '.' + valueOrMarker;
|
31422 | }
|
31423 | else if (mode & 4 /* ELEMENT */) {
|
31424 | currentChunk += ' ' + valueOrMarker;
|
31425 | }
|
31426 | }
|
31427 | else {
|
31428 | //
|
31429 | // Append current chunk to the final result in case we come across SelectorFlag, which
|
31430 | // indicates that the previous section of a selector is over. We need to accumulate content
|
31431 | // between flags to make sure we wrap the chunk later in :not() selector if needed, e.g.
|
31432 | // ```
|
31433 | // ['', Flags.CLASS, '.classA', Flags.CLASS | Flags.NOT, '.classB', '.classC']
|
31434 | // ```
|
31435 | // should be transformed to `.classA :not(.classB .classC)`.
|
31436 | //
|
31437 | // Note: for negative selector part, we accumulate content between flags until we find the
|
31438 | // next negative flag. This is needed to support a case where `:not()` rule contains more than
|
31439 | // one chunk, e.g. the following selector:
|
31440 | // ```
|
31441 | // ['', Flags.ELEMENT | Flags.NOT, 'p', Flags.CLASS, 'foo', Flags.CLASS | Flags.NOT, 'bar']
|
31442 | // ```
|
31443 | // should be stringified to `:not(p.foo) :not(.bar)`
|
31444 | //
|
31445 | if (currentChunk !== '' && !isPositive(valueOrMarker)) {
|
31446 | result += maybeWrapInNotSelector(isNegativeMode, currentChunk);
|
31447 | currentChunk = '';
|
31448 | }
|
31449 | mode = valueOrMarker;
|
31450 | // According to CssSelector spec, once we come across `SelectorFlags.NOT` flag, the negative
|
31451 | // mode is maintained for remaining chunks of a selector.
|
31452 | isNegativeMode = isNegativeMode || !isPositive(mode);
|
31453 | }
|
31454 | i++;
|
31455 | }
|
31456 | if (currentChunk !== '') {
|
31457 | result += maybeWrapInNotSelector(isNegativeMode, currentChunk);
|
31458 | }
|
31459 | return result;
|
31460 | }
|
31461 | /**
|
31462 | * Generates string representation of CSS selector in parsed form.
|
31463 | *
|
31464 | * ComponentDef and DirectiveDef are generated with the selector in parsed form to avoid doing
|
31465 | * additional parsing at runtime (for example, for directive matching). However in some cases (for
|
31466 | * example, while bootstrapping a component), a string version of the selector is required to query
|
31467 | * for the host element on the page. This function takes the parsed form of a selector and returns
|
31468 | * its string representation.
|
31469 | *
|
31470 | * @param selectorList selector in parsed form
|
31471 | * @returns string representation of a given selector
|
31472 | */
|
31473 | function stringifyCSSSelectorList(selectorList) {
|
31474 | return selectorList.map(stringifyCSSSelector).join(',');
|
31475 | }
|
31476 | /**
|
31477 | * Extracts attributes and classes information from a given CSS selector.
|
31478 | *
|
31479 | * This function is used while creating a component dynamically. In this case, the host element
|
31480 | * (that is created dynamically) should contain attributes and classes specified in component's CSS
|
31481 | * selector.
|
31482 | *
|
31483 | * @param selector CSS selector in parsed form (in a form of array)
|
31484 | * @returns object with `attrs` and `classes` fields that contain extracted information
|
31485 | */
|
31486 | function extractAttrsAndClassesFromSelector(selector) {
|
31487 | const attrs = [];
|
31488 | const classes = [];
|
31489 | let i = 1;
|
31490 | let mode = 2 /* ATTRIBUTE */;
|
31491 | while (i < selector.length) {
|
31492 | let valueOrMarker = selector[i];
|
31493 | if (typeof valueOrMarker === 'string') {
|
31494 | if (mode === 2 /* ATTRIBUTE */) {
|
31495 | if (valueOrMarker !== '') {
|
31496 | attrs.push(valueOrMarker, selector[++i]);
|
31497 | }
|
31498 | }
|
31499 | else if (mode === 8 /* CLASS */) {
|
31500 | classes.push(valueOrMarker);
|
31501 | }
|
31502 | }
|
31503 | else {
|
31504 | // According to CssSelector spec, once we come across `SelectorFlags.NOT` flag, the negative
|
31505 | // mode is maintained for remaining chunks of a selector. Since attributes and classes are
|
31506 | // extracted only for "positive" part of the selector, we can stop here.
|
31507 | if (!isPositive(mode))
|
31508 | break;
|
31509 | mode = valueOrMarker;
|
31510 | }
|
31511 | i++;
|
31512 | }
|
31513 | return { attrs, classes };
|
31514 | }
|
31515 |
|
31516 | /**
|
31517 | * @license
|
31518 | * Copyright Google LLC All Rights Reserved.
|
31519 | *
|
31520 | * Use of this source code is governed by an MIT-style license that can be
|
31521 | * found in the LICENSE file at https://angular.io/license
|
31522 | */
|
31523 | /** A special value which designates that a value has not changed. */
|
31524 | const NO_CHANGE = (typeof ngDevMode === 'undefined' || ngDevMode) ? { __brand__: 'NO_CHANGE' } : {};
|
31525 |
|
31526 | /**
|
31527 | * @license
|
31528 | * Copyright Google LLC All Rights Reserved.
|
31529 | *
|
31530 | * Use of this source code is governed by an MIT-style license that can be
|
31531 | * found in the LICENSE file at https://angular.io/license
|
31532 | */
|
31533 | function selectIndexInternal(tView, lView, index, checkNoChangesMode) {
|
31534 | ngDevMode && assertIndexInDeclRange(lView, index);
|
31535 | // Flush the initial hooks for elements in the view that have been added up to this point.
|
31536 | // PERF WARNING: do NOT extract this to a separate function without running benchmarks
|
31537 | if (!checkNoChangesMode) {
|
31538 | const hooksInitPhaseCompleted = (lView[FLAGS] & 3 /* InitPhaseStateMask */) === 3 /* InitPhaseCompleted */;
|
31539 | if (hooksInitPhaseCompleted) {
|
31540 | const preOrderCheckHooks = tView.preOrderCheckHooks;
|
31541 | if (preOrderCheckHooks !== null) {
|
31542 | executeCheckHooks(lView, preOrderCheckHooks, index);
|
31543 | }
|
31544 | }
|
31545 | else {
|
31546 | const preOrderHooks = tView.preOrderHooks;
|
31547 | if (preOrderHooks !== null) {
|
31548 | executeInitAndCheckHooks(lView, preOrderHooks, 0 /* OnInitHooksToBeRun */, index);
|
31549 | }
|
31550 | }
|
31551 | }
|
31552 | // We must set the selected index *after* running the hooks, because hooks may have side-effects
|
31553 | // that cause other template functions to run, thus updating the selected index, which is global
|
31554 | // state. If we run `setSelectedIndex` *before* we run the hooks, in some cases the selected index
|
31555 | // will be altered by the time we leave the `ɵɵadvance` instruction.
|
31556 | setSelectedIndex(index);
|
31557 | }
|
31558 |
|
31559 | /**
|
31560 | * @license
|
31561 | * Copyright Google LLC All Rights Reserved.
|
31562 | *
|
31563 | * Use of this source code is governed by an MIT-style license that can be
|
31564 | * found in the LICENSE file at https://angular.io/license
|
31565 | */
|
31566 | function getTStylingRangePrev(tStylingRange) {
|
31567 | ngDevMode && assertNumber(tStylingRange, 'expected number');
|
31568 | return (tStylingRange >> 17 /* PREV_SHIFT */) & 32767 /* UNSIGNED_MASK */;
|
31569 | }
|
31570 | function getTStylingRangePrevDuplicate(tStylingRange) {
|
31571 | ngDevMode && assertNumber(tStylingRange, 'expected number');
|
31572 | return (tStylingRange & 2 /* PREV_DUPLICATE */) ==
|
31573 | 2 /* PREV_DUPLICATE */;
|
31574 | }
|
31575 | function getTStylingRangeNext(tStylingRange) {
|
31576 | ngDevMode && assertNumber(tStylingRange, 'expected number');
|
31577 | return (tStylingRange & 131068 /* NEXT_MASK */) >> 2 /* NEXT_SHIFT */;
|
31578 | }
|
31579 | function getTStylingRangeNextDuplicate(tStylingRange) {
|
31580 | ngDevMode && assertNumber(tStylingRange, 'expected number');
|
31581 | return (tStylingRange & 1 /* NEXT_DUPLICATE */) ===
|
31582 | 1 /* NEXT_DUPLICATE */;
|
31583 | }
|
31584 |
|
31585 | /**
|
31586 | * @license
|
31587 | * Copyright Google LLC All Rights Reserved.
|
31588 | *
|
31589 | * Use of this source code is governed by an MIT-style license that can be
|
31590 | * found in the LICENSE file at https://angular.io/license
|
31591 | */
|
31592 | /**
|
31593 | * Patch a `debug` property on top of the existing object.
|
31594 | *
|
31595 | * NOTE: always call this method with `ngDevMode && attachDebugObject(...)`
|
31596 | *
|
31597 | * @param obj Object to patch
|
31598 | * @param debug Value to patch
|
31599 | */
|
31600 | function attachDebugObject(obj, debug) {
|
31601 | if (ngDevMode) {
|
31602 | Object.defineProperty(obj, 'debug', { value: debug, enumerable: false });
|
31603 | }
|
31604 | else {
|
31605 | throw new Error('This method should be guarded with `ngDevMode` so that it can be tree shaken in production!');
|
31606 | }
|
31607 | }
|
31608 |
|
31609 | /**
|
31610 | * @license
|
31611 | * Copyright Google LLC All Rights Reserved.
|
31612 | *
|
31613 | * Use of this source code is governed by an MIT-style license that can be
|
31614 | * found in the LICENSE file at https://angular.io/license
|
31615 | */
|
31616 | const NG_DEV_MODE = ((typeof ngDevMode === 'undefined' || !!ngDevMode) && initNgDevMode());
|
31617 | /*
|
31618 | * This file contains conditionally attached classes which provide human readable (debug) level
|
31619 | * information for `LView`, `LContainer` and other internal data structures. These data structures
|
31620 | * are stored internally as array which makes it very difficult during debugging to reason about the
|
31621 | * current state of the system.
|
31622 | *
|
31623 | * Patching the array with extra property does change the array's hidden class' but it does not
|
31624 | * change the cost of access, therefore this patching should not have significant if any impact in
|
31625 | * `ngDevMode` mode. (see: https://jsperf.com/array-vs-monkey-patch-array)
|
31626 | *
|
31627 | * So instead of seeing:
|
31628 | * ```
|
31629 | * Array(30) [Object, 659, null, …]
|
31630 | * ```
|
31631 | *
|
31632 | * You get to see:
|
31633 | * ```
|
31634 | * LViewDebug {
|
31635 | * views: [...],
|
31636 | * flags: {attached: true, ...}
|
31637 | * nodes: [
|
31638 | * {html: '<div id="123">', ..., nodes: [
|
31639 | * {html: '<span>', ..., nodes: null}
|
31640 | * ]}
|
31641 | * ]
|
31642 | * }
|
31643 | * ```
|
31644 | */
|
31645 | let LVIEW_COMPONENT_CACHE;
|
31646 | let LVIEW_EMBEDDED_CACHE;
|
31647 | let LVIEW_ROOT;
|
31648 | /**
|
31649 | * This function clones a blueprint and creates LView.
|
31650 | *
|
31651 | * Simple slice will keep the same type, and we need it to be LView
|
31652 | */
|
31653 | function cloneToLViewFromTViewBlueprint(tView) {
|
31654 | const debugTView = tView;
|
31655 | const lView = getLViewToClone(debugTView.type, tView.template && tView.template.name);
|
31656 | return lView.concat(tView.blueprint);
|
31657 | }
|
31658 | function getLViewToClone(type, name) {
|
31659 | switch (type) {
|
31660 | case 0 /* Root */:
|
31661 | if (LVIEW_ROOT === undefined)
|
31662 | LVIEW_ROOT = new (createNamedArrayType('LRootView'))();
|
31663 | return LVIEW_ROOT;
|
31664 | case 1 /* Component */:
|
31665 | if (LVIEW_COMPONENT_CACHE === undefined)
|
31666 | LVIEW_COMPONENT_CACHE = new Map();
|
31667 | let componentArray = LVIEW_COMPONENT_CACHE.get(name);
|
31668 | if (componentArray === undefined) {
|
31669 | componentArray = new (createNamedArrayType('LComponentView' + nameSuffix(name)))();
|
31670 | LVIEW_COMPONENT_CACHE.set(name, componentArray);
|
31671 | }
|
31672 | return componentArray;
|
31673 | case 2 /* Embedded */:
|
31674 | if (LVIEW_EMBEDDED_CACHE === undefined)
|
31675 | LVIEW_EMBEDDED_CACHE = new Map();
|
31676 | let embeddedArray = LVIEW_EMBEDDED_CACHE.get(name);
|
31677 | if (embeddedArray === undefined) {
|
31678 | embeddedArray = new (createNamedArrayType('LEmbeddedView' + nameSuffix(name)))();
|
31679 | LVIEW_EMBEDDED_CACHE.set(name, embeddedArray);
|
31680 | }
|
31681 | return embeddedArray;
|
31682 | }
|
31683 | }
|
31684 | function nameSuffix(text) {
|
31685 | if (text == null)
|
31686 | return '';
|
31687 | const index = text.lastIndexOf('_Template');
|
31688 | return '_' + (index === -1 ? text : text.substr(0, index));
|
31689 | }
|
31690 | /**
|
31691 | * This class is a debug version of Object literal so that we can have constructor name show up
|
31692 | * in
|
31693 | * debug tools in ngDevMode.
|
31694 | */
|
31695 | const TViewConstructor = class TView {
|
31696 | constructor(type, blueprint, template, queries, viewQuery, declTNode, data, bindingStartIndex, expandoStartIndex, hostBindingOpCodes, firstCreatePass, firstUpdatePass, staticViewQueries, staticContentQueries, preOrderHooks, preOrderCheckHooks, contentHooks, contentCheckHooks, viewHooks, viewCheckHooks, destroyHooks, cleanup, contentQueries, components, directiveRegistry, pipeRegistry, firstChild, schemas, consts, incompleteFirstPass, _decls, _vars) {
|
31697 | this.type = type;
|
31698 | this.blueprint = blueprint;
|
31699 | this.template = template;
|
31700 | this.queries = queries;
|
31701 | this.viewQuery = viewQuery;
|
31702 | this.declTNode = declTNode;
|
31703 | this.data = data;
|
31704 | this.bindingStartIndex = bindingStartIndex;
|
31705 | this.expandoStartIndex = expandoStartIndex;
|
31706 | this.hostBindingOpCodes = hostBindingOpCodes;
|
31707 | this.firstCreatePass = firstCreatePass;
|
31708 | this.firstUpdatePass = firstUpdatePass;
|
31709 | this.staticViewQueries = staticViewQueries;
|
31710 | this.staticContentQueries = staticContentQueries;
|
31711 | this.preOrderHooks = preOrderHooks;
|
31712 | this.preOrderCheckHooks = preOrderCheckHooks;
|
31713 | this.contentHooks = contentHooks;
|
31714 | this.contentCheckHooks = contentCheckHooks;
|
31715 | this.viewHooks = viewHooks;
|
31716 | this.viewCheckHooks = viewCheckHooks;
|
31717 | this.destroyHooks = destroyHooks;
|
31718 | this.cleanup = cleanup;
|
31719 | this.contentQueries = contentQueries;
|
31720 | this.components = components;
|
31721 | this.directiveRegistry = directiveRegistry;
|
31722 | this.pipeRegistry = pipeRegistry;
|
31723 | this.firstChild = firstChild;
|
31724 | this.schemas = schemas;
|
31725 | this.consts = consts;
|
31726 | this.incompleteFirstPass = incompleteFirstPass;
|
31727 | this._decls = _decls;
|
31728 | this._vars = _vars;
|
31729 | }
|
31730 | get template_() {
|
31731 | const buf = [];
|
31732 | processTNodeChildren(this.firstChild, buf);
|
31733 | return buf.join('');
|
31734 | }
|
31735 | get type_() {
|
31736 | return TViewTypeAsString[this.type] || `TViewType.?${this.type}?`;
|
31737 | }
|
31738 | };
|
31739 | class TNode {
|
31740 | constructor(tView_, //
|
31741 | type, //
|
31742 | index, //
|
31743 | insertBeforeIndex, //
|
31744 | injectorIndex, //
|
31745 | directiveStart, //
|
31746 | directiveEnd, //
|
31747 | directiveStylingLast, //
|
31748 | propertyBindings, //
|
31749 | flags, //
|
31750 | providerIndexes, //
|
31751 | value, //
|
31752 | attrs, //
|
31753 | mergedAttrs, //
|
31754 | localNames, //
|
31755 | initialInputs, //
|
31756 | inputs, //
|
31757 | outputs, //
|
31758 | tViews, //
|
31759 | next, //
|
31760 | projectionNext, //
|
31761 | child, //
|
31762 | parent, //
|
31763 | projection, //
|
31764 | styles, //
|
31765 | stylesWithoutHost, //
|
31766 | residualStyles, //
|
31767 | classes, //
|
31768 | classesWithoutHost, //
|
31769 | residualClasses, //
|
31770 | classBindings, //
|
31771 | styleBindings) {
|
31772 | this.tView_ = tView_;
|
31773 | this.type = type;
|
31774 | this.index = index;
|
31775 | this.insertBeforeIndex = insertBeforeIndex;
|
31776 | this.injectorIndex = injectorIndex;
|
31777 | this.directiveStart = directiveStart;
|
31778 | this.directiveEnd = directiveEnd;
|
31779 | this.directiveStylingLast = directiveStylingLast;
|
31780 | this.propertyBindings = propertyBindings;
|
31781 | this.flags = flags;
|
31782 | this.providerIndexes = providerIndexes;
|
31783 | this.value = value;
|
31784 | this.attrs = attrs;
|
31785 | this.mergedAttrs = mergedAttrs;
|
31786 | this.localNames = localNames;
|
31787 | this.initialInputs = initialInputs;
|
31788 | this.inputs = inputs;
|
31789 | this.outputs = outputs;
|
31790 | this.tViews = tViews;
|
31791 | this.next = next;
|
31792 | this.projectionNext = projectionNext;
|
31793 | this.child = child;
|
31794 | this.parent = parent;
|
31795 | this.projection = projection;
|
31796 | this.styles = styles;
|
31797 | this.stylesWithoutHost = stylesWithoutHost;
|
31798 | this.residualStyles = residualStyles;
|
31799 | this.classes = classes;
|
31800 | this.classesWithoutHost = classesWithoutHost;
|
31801 | this.residualClasses = residualClasses;
|
31802 | this.classBindings = classBindings;
|
31803 | this.styleBindings = styleBindings;
|
31804 | }
|
31805 | /**
|
31806 | * Return a human debug version of the set of `NodeInjector`s which will be consulted when
|
31807 | * resolving tokens from this `TNode`.
|
31808 | *
|
31809 | * When debugging applications, it is often difficult to determine which `NodeInjector`s will be
|
31810 | * consulted. This method shows a list of `DebugNode`s representing the `TNode`s which will be
|
31811 | * consulted in order when resolving a token starting at this `TNode`.
|
31812 | *
|
31813 | * The original data is stored in `LView` and `TView` with a lot of offset indexes, and so it is
|
31814 | * difficult to reason about.
|
31815 | *
|
31816 | * @param lView The `LView` instance for this `TNode`.
|
31817 | */
|
31818 | debugNodeInjectorPath(lView) {
|
31819 | const path = [];
|
31820 | let injectorIndex = getInjectorIndex(this, lView);
|
31821 | if (injectorIndex === -1) {
|
31822 | // Looks like the current `TNode` does not have `NodeInjector` associated with it => look for
|
31823 | // parent NodeInjector.
|
31824 | const parentLocation = getParentInjectorLocation(this, lView);
|
31825 | if (parentLocation !== NO_PARENT_INJECTOR) {
|
31826 | // We found a parent, so start searching from the parent location.
|
31827 | injectorIndex = getParentInjectorIndex(parentLocation);
|
31828 | lView = getParentInjectorView(parentLocation, lView);
|
31829 | }
|
31830 | }
|
31831 | while (injectorIndex !== -1) {
|
31832 | ngDevMode && assertNodeInjector(lView, injectorIndex);
|
31833 | const tNode = lView[TVIEW].data[injectorIndex + 8 /* TNODE */];
|
31834 | path.push(buildDebugNode(tNode, lView));
|
31835 | const parentLocation = lView[injectorIndex + 8 /* PARENT */];
|
31836 | if (parentLocation === NO_PARENT_INJECTOR) {
|
31837 | injectorIndex = -1;
|
31838 | }
|
31839 | else {
|
31840 | injectorIndex = getParentInjectorIndex(parentLocation);
|
31841 | lView = getParentInjectorView(parentLocation, lView);
|
31842 | }
|
31843 | }
|
31844 | return path;
|
31845 | }
|
31846 | get type_() {
|
31847 | return toTNodeTypeAsString(this.type) || `TNodeType.?${this.type}?`;
|
31848 | }
|
31849 | get flags_() {
|
31850 | const flags = [];
|
31851 | if (this.flags & 16 /* hasClassInput */)
|
31852 | flags.push('TNodeFlags.hasClassInput');
|
31853 | if (this.flags & 8 /* hasContentQuery */)
|
31854 | flags.push('TNodeFlags.hasContentQuery');
|
31855 | if (this.flags & 32 /* hasStyleInput */)
|
31856 | flags.push('TNodeFlags.hasStyleInput');
|
31857 | if (this.flags & 128 /* hasHostBindings */)
|
31858 | flags.push('TNodeFlags.hasHostBindings');
|
31859 | if (this.flags & 2 /* isComponentHost */)
|
31860 | flags.push('TNodeFlags.isComponentHost');
|
31861 | if (this.flags & 1 /* isDirectiveHost */)
|
31862 | flags.push('TNodeFlags.isDirectiveHost');
|
31863 | if (this.flags & 64 /* isDetached */)
|
31864 | flags.push('TNodeFlags.isDetached');
|
31865 | if (this.flags & 4 /* isProjected */)
|
31866 | flags.push('TNodeFlags.isProjected');
|
31867 | return flags.join('|');
|
31868 | }
|
31869 | get template_() {
|
31870 | if (this.type & 1 /* Text */)
|
31871 | return this.value;
|
31872 | const buf = [];
|
31873 | const tagName = typeof this.value === 'string' && this.value || this.type_;
|
31874 | buf.push('<', tagName);
|
31875 | if (this.flags) {
|
31876 | buf.push(' ', this.flags_);
|
31877 | }
|
31878 | if (this.attrs) {
|
31879 | for (let i = 0; i < this.attrs.length;) {
|
31880 | const attrName = this.attrs[i++];
|
31881 | if (typeof attrName == 'number') {
|
31882 | break;
|
31883 | }
|
31884 | const attrValue = this.attrs[i++];
|
31885 | buf.push(' ', attrName, '="', attrValue, '"');
|
31886 | }
|
31887 | }
|
31888 | buf.push('>');
|
31889 | processTNodeChildren(this.child, buf);
|
31890 | buf.push('</', tagName, '>');
|
31891 | return buf.join('');
|
31892 | }
|
31893 | get styleBindings_() {
|
31894 | return toDebugStyleBinding(this, false);
|
31895 | }
|
31896 | get classBindings_() {
|
31897 | return toDebugStyleBinding(this, true);
|
31898 | }
|
31899 | get providerIndexStart_() {
|
31900 | return this.providerIndexes & 1048575 /* ProvidersStartIndexMask */;
|
31901 | }
|
31902 | get providerIndexEnd_() {
|
31903 | return this.providerIndexStart_ +
|
31904 | (this.providerIndexes >>> 20 /* CptViewProvidersCountShift */);
|
31905 | }
|
31906 | }
|
31907 | const TNodeDebug = TNode;
|
31908 | function toDebugStyleBinding(tNode, isClassBased) {
|
31909 | const tData = tNode.tView_.data;
|
31910 | const bindings = [];
|
31911 | const range = isClassBased ? tNode.classBindings : tNode.styleBindings;
|
31912 | const prev = getTStylingRangePrev(range);
|
31913 | const next = getTStylingRangeNext(range);
|
31914 | let isTemplate = next !== 0;
|
31915 | let cursor = isTemplate ? next : prev;
|
31916 | while (cursor !== 0) {
|
31917 | const itemKey = tData[cursor];
|
31918 | const itemRange = tData[cursor + 1];
|
31919 | bindings.unshift({
|
31920 | key: itemKey,
|
31921 | index: cursor,
|
31922 | isTemplate: isTemplate,
|
31923 | prevDuplicate: getTStylingRangePrevDuplicate(itemRange),
|
31924 | nextDuplicate: getTStylingRangeNextDuplicate(itemRange),
|
31925 | nextIndex: getTStylingRangeNext(itemRange),
|
31926 | prevIndex: getTStylingRangePrev(itemRange),
|
31927 | });
|
31928 | if (cursor === prev)
|
31929 | isTemplate = false;
|
31930 | cursor = getTStylingRangePrev(itemRange);
|
31931 | }
|
31932 | bindings.push((isClassBased ? tNode.residualClasses : tNode.residualStyles) || null);
|
31933 | return bindings;
|
31934 | }
|
31935 | function processTNodeChildren(tNode, buf) {
|
31936 | while (tNode) {
|
31937 | buf.push(tNode.template_);
|
31938 | tNode = tNode.next;
|
31939 | }
|
31940 | }
|
31941 | const TViewData = NG_DEV_MODE && createNamedArrayType('TViewData') || null;
|
31942 | let TVIEWDATA_EMPTY; // can't initialize here or it will not be tree shaken, because
|
31943 | // `LView` constructor could have side-effects.
|
31944 | /**
|
31945 | * This function clones a blueprint and creates TData.
|
31946 | *
|
31947 | * Simple slice will keep the same type, and we need it to be TData
|
31948 | */
|
31949 | function cloneToTViewData(list) {
|
31950 | if (TVIEWDATA_EMPTY === undefined)
|
31951 | TVIEWDATA_EMPTY = new TViewData();
|
31952 | return TVIEWDATA_EMPTY.concat(list);
|
31953 | }
|
31954 | const LViewBlueprint = NG_DEV_MODE && createNamedArrayType('LViewBlueprint') || null;
|
31955 | const MatchesArray = NG_DEV_MODE && createNamedArrayType('MatchesArray') || null;
|
31956 | const TViewComponents = NG_DEV_MODE && createNamedArrayType('TViewComponents') || null;
|
31957 | const TNodeLocalNames = NG_DEV_MODE && createNamedArrayType('TNodeLocalNames') || null;
|
31958 | const TNodeInitialInputs = NG_DEV_MODE && createNamedArrayType('TNodeInitialInputs') || null;
|
31959 | const TNodeInitialData = NG_DEV_MODE && createNamedArrayType('TNodeInitialData') || null;
|
31960 | const LCleanup = NG_DEV_MODE && createNamedArrayType('LCleanup') || null;
|
31961 | const TCleanup = NG_DEV_MODE && createNamedArrayType('TCleanup') || null;
|
31962 | function attachLViewDebug(lView) {
|
31963 | attachDebugObject(lView, new LViewDebug(lView));
|
31964 | }
|
31965 | function toDebug(obj) {
|
31966 | if (obj) {
|
31967 | const debug = obj.debug;
|
31968 | assertDefined(debug, 'Object does not have a debug representation.');
|
31969 | return debug;
|
31970 | }
|
31971 | else {
|
31972 | return obj;
|
31973 | }
|
31974 | }
|
31975 | /**
|
31976 | * Use this method to unwrap a native element in `LView` and convert it into HTML for easier
|
31977 | * reading.
|
31978 | *
|
31979 | * @param value possibly wrapped native DOM node.
|
31980 | * @param includeChildren If `true` then the serialized HTML form will include child elements
|
31981 | * (same
|
31982 | * as `outerHTML`). If `false` then the serialized HTML form will only contain the element
|
31983 | * itself
|
31984 | * (will not serialize child elements).
|
31985 | */
|
31986 | function toHtml(value, includeChildren = false) {
|
31987 | const node = unwrapRNode(value);
|
31988 | if (node) {
|
31989 | switch (node.nodeType) {
|
31990 | case Node.TEXT_NODE:
|
31991 | return node.textContent;
|
31992 | case Node.COMMENT_NODE:
|
31993 | return `<!--${node.textContent}-->`;
|
31994 | case Node.ELEMENT_NODE:
|
31995 | const outerHTML = node.outerHTML;
|
31996 | if (includeChildren) {
|
31997 | return outerHTML;
|
31998 | }
|
31999 | else {
|
32000 | const innerHTML = '>' + node.innerHTML + '<';
|
32001 | return (outerHTML.split(innerHTML)[0]) + '>';
|
32002 | }
|
32003 | }
|
32004 | }
|
32005 | return null;
|
32006 | }
|
32007 | class LViewDebug {
|
32008 | constructor(_raw_lView) {
|
32009 | this._raw_lView = _raw_lView;
|
32010 | }
|
32011 | /**
|
32012 | * Flags associated with the `LView` unpacked into a more readable state.
|
32013 | */
|
32014 | get flags() {
|
32015 | const flags = this._raw_lView[FLAGS];
|
32016 | return {
|
32017 | __raw__flags__: flags,
|
32018 | initPhaseState: flags & 3 /* InitPhaseStateMask */,
|
32019 | creationMode: !!(flags & 4 /* CreationMode */),
|
32020 | firstViewPass: !!(flags & 8 /* FirstLViewPass */),
|
32021 | checkAlways: !!(flags & 16 /* CheckAlways */),
|
32022 | dirty: !!(flags & 64 /* Dirty */),
|
32023 | attached: !!(flags & 128 /* Attached */),
|
32024 | destroyed: !!(flags & 256 /* Destroyed */),
|
32025 | isRoot: !!(flags & 512 /* IsRoot */),
|
32026 | indexWithinInitPhase: flags >> 11 /* IndexWithinInitPhaseShift */,
|
32027 | };
|
32028 | }
|
32029 | get parent() {
|
32030 | return toDebug(this._raw_lView[PARENT]);
|
32031 | }
|
32032 | get hostHTML() {
|
32033 | return toHtml(this._raw_lView[HOST], true);
|
32034 | }
|
32035 | get html() {
|
32036 | return (this.nodes || []).map(mapToHTML).join('');
|
32037 | }
|
32038 | get context() {
|
32039 | return this._raw_lView[CONTEXT];
|
32040 | }
|
32041 | /**
|
32042 | * The tree of nodes associated with the current `LView`. The nodes have been normalized into
|
32043 | * a tree structure with relevant details pulled out for readability.
|
32044 | */
|
32045 | get nodes() {
|
32046 | const lView = this._raw_lView;
|
32047 | const tNode = lView[TVIEW].firstChild;
|
32048 | return toDebugNodes(tNode, lView);
|
32049 | }
|
32050 | get template() {
|
32051 | return this.tView.template_;
|
32052 | }
|
32053 | get tView() {
|
32054 | return this._raw_lView[TVIEW];
|
32055 | }
|
32056 | get cleanup() {
|
32057 | return this._raw_lView[CLEANUP];
|
32058 | }
|
32059 | get injector() {
|
32060 | return this._raw_lView[INJECTOR];
|
32061 | }
|
32062 | get rendererFactory() {
|
32063 | return this._raw_lView[RENDERER_FACTORY];
|
32064 | }
|
32065 | get renderer() {
|
32066 | return this._raw_lView[RENDERER];
|
32067 | }
|
32068 | get sanitizer() {
|
32069 | return this._raw_lView[SANITIZER];
|
32070 | }
|
32071 | get childHead() {
|
32072 | return toDebug(this._raw_lView[CHILD_HEAD]);
|
32073 | }
|
32074 | get next() {
|
32075 | return toDebug(this._raw_lView[NEXT]);
|
32076 | }
|
32077 | get childTail() {
|
32078 | return toDebug(this._raw_lView[CHILD_TAIL]);
|
32079 | }
|
32080 | get declarationView() {
|
32081 | return toDebug(this._raw_lView[DECLARATION_VIEW]);
|
32082 | }
|
32083 | get queries() {
|
32084 | return this._raw_lView[QUERIES];
|
32085 | }
|
32086 | get tHost() {
|
32087 | return this._raw_lView[T_HOST];
|
32088 | }
|
32089 | get decls() {
|
32090 | return toLViewRange(this.tView, this._raw_lView, HEADER_OFFSET, this.tView.bindingStartIndex);
|
32091 | }
|
32092 | get vars() {
|
32093 | return toLViewRange(this.tView, this._raw_lView, this.tView.bindingStartIndex, this.tView.expandoStartIndex);
|
32094 | }
|
32095 | get expando() {
|
32096 | return toLViewRange(this.tView, this._raw_lView, this.tView.expandoStartIndex, this._raw_lView.length);
|
32097 | }
|
32098 | /**
|
32099 | * Normalized view of child views (and containers) attached at this location.
|
32100 | */
|
32101 | get childViews() {
|
32102 | const childViews = [];
|
32103 | let child = this.childHead;
|
32104 | while (child) {
|
32105 | childViews.push(child);
|
32106 | child = child.next;
|
32107 | }
|
32108 | return childViews;
|
32109 | }
|
32110 | }
|
32111 | function mapToHTML(node) {
|
32112 | if (node.type === 'ElementContainer') {
|
32113 | return (node.children || []).map(mapToHTML).join('');
|
32114 | }
|
32115 | else if (node.type === 'IcuContainer') {
|
32116 | throw new Error('Not implemented');
|
32117 | }
|
32118 | else {
|
32119 | return toHtml(node.native, true) || '';
|
32120 | }
|
32121 | }
|
32122 | function toLViewRange(tView, lView, start, end) {
|
32123 | let content = [];
|
32124 | for (let index = start; index < end; index++) {
|
32125 | content.push({ index: index, t: tView.data[index], l: lView[index] });
|
32126 | }
|
32127 | return { start: start, end: end, length: end - start, content: content };
|
32128 | }
|
32129 | /**
|
32130 | * Turns a flat list of nodes into a tree by walking the associated `TNode` tree.
|
32131 | *
|
32132 | * @param tNode
|
32133 | * @param lView
|
32134 | */
|
32135 | function toDebugNodes(tNode, lView) {
|
32136 | if (tNode) {
|
32137 | const debugNodes = [];
|
32138 | let tNodeCursor = tNode;
|
32139 | while (tNodeCursor) {
|
32140 | debugNodes.push(buildDebugNode(tNodeCursor, lView));
|
32141 | tNodeCursor = tNodeCursor.next;
|
32142 | }
|
32143 | return debugNodes;
|
32144 | }
|
32145 | else {
|
32146 | return [];
|
32147 | }
|
32148 | }
|
32149 | function buildDebugNode(tNode, lView) {
|
32150 | const rawValue = lView[tNode.index];
|
32151 | const native = unwrapRNode(rawValue);
|
32152 | const factories = [];
|
32153 | const instances = [];
|
32154 | const tView = lView[TVIEW];
|
32155 | for (let i = tNode.directiveStart; i < tNode.directiveEnd; i++) {
|
32156 | const def = tView.data[i];
|
32157 | factories.push(def.type);
|
32158 | instances.push(lView[i]);
|
32159 | }
|
32160 | return {
|
32161 | html: toHtml(native),
|
32162 | type: toTNodeTypeAsString(tNode.type),
|
32163 | tNode,
|
32164 | native: native,
|
32165 | children: toDebugNodes(tNode.child, lView),
|
32166 | factories,
|
32167 | instances,
|
32168 | injector: buildNodeInjectorDebug(tNode, tView, lView),
|
32169 | get injectorResolutionPath() {
|
32170 | return tNode.debugNodeInjectorPath(lView);
|
32171 | },
|
32172 | };
|
32173 | }
|
32174 | function buildNodeInjectorDebug(tNode, tView, lView) {
|
32175 | const viewProviders = [];
|
32176 | for (let i = tNode.providerIndexStart_; i < tNode.providerIndexEnd_; i++) {
|
32177 | viewProviders.push(tView.data[i]);
|
32178 | }
|
32179 | const providers = [];
|
32180 | for (let i = tNode.providerIndexEnd_; i < tNode.directiveEnd; i++) {
|
32181 | providers.push(tView.data[i]);
|
32182 | }
|
32183 | const nodeInjectorDebug = {
|
32184 | bloom: toBloom(lView, tNode.injectorIndex),
|
32185 | cumulativeBloom: toBloom(tView.data, tNode.injectorIndex),
|
32186 | providers,
|
32187 | viewProviders,
|
32188 | parentInjectorIndex: lView[tNode.providerIndexStart_ - 1],
|
32189 | };
|
32190 | return nodeInjectorDebug;
|
32191 | }
|
32192 | /**
|
32193 | * Convert a number at `idx` location in `array` into binary representation.
|
32194 | *
|
32195 | * @param array
|
32196 | * @param idx
|
32197 | */
|
32198 | function binary(array, idx) {
|
32199 | const value = array[idx];
|
32200 | // If not a number we print 8 `?` to retain alignment but let user know that it was called on
|
32201 | // wrong type.
|
32202 | if (typeof value !== 'number')
|
32203 | return '????????';
|
32204 | // We prefix 0s so that we have constant length number
|
32205 | const text = '00000000' + value.toString(2);
|
32206 | return text.substring(text.length - 8);
|
32207 | }
|
32208 | /**
|
32209 | * Convert a bloom filter at location `idx` in `array` into binary representation.
|
32210 | *
|
32211 | * @param array
|
32212 | * @param idx
|
32213 | */
|
32214 | function toBloom(array, idx) {
|
32215 | if (idx < 0) {
|
32216 | return 'NO_NODE_INJECTOR';
|
32217 | }
|
32218 | return `${binary(array, idx + 7)}_${binary(array, idx + 6)}_${binary(array, idx + 5)}_${binary(array, idx + 4)}_${binary(array, idx + 3)}_${binary(array, idx + 2)}_${binary(array, idx + 1)}_${binary(array, idx + 0)}`;
|
32219 | }
|
32220 |
|
32221 | const ɵ0$5 = () => Promise.resolve(null);
|
32222 | /**
|
32223 | * A permanent marker promise which signifies that the current CD tree is
|
32224 | * clean.
|
32225 | */
|
32226 | const _CLEAN_PROMISE = (ɵ0$5)();
|
32227 | /**
|
32228 | * Invoke `HostBindingsFunction`s for view.
|
32229 | *
|
32230 | * This methods executes `TView.hostBindingOpCodes`. It is used to execute the
|
32231 | * `HostBindingsFunction`s associated with the current `LView`.
|
32232 | *
|
32233 | * @param tView Current `TView`.
|
32234 | * @param lView Current `LView`.
|
32235 | */
|
32236 | function processHostBindingOpCodes(tView, lView) {
|
32237 | const hostBindingOpCodes = tView.hostBindingOpCodes;
|
32238 | if (hostBindingOpCodes === null)
|
32239 | return;
|
32240 | try {
|
32241 | for (let i = 0; i < hostBindingOpCodes.length; i++) {
|
32242 | const opCode = hostBindingOpCodes[i];
|
32243 | if (opCode < 0) {
|
32244 | // Negative numbers are element indexes.
|
32245 | setSelectedIndex(~opCode);
|
32246 | }
|
32247 | else {
|
32248 | // Positive numbers are NumberTuple which store bindingRootIndex and directiveIndex.
|
32249 | const directiveIdx = opCode;
|
32250 | const bindingRootIndx = hostBindingOpCodes[++i];
|
32251 | const hostBindingFn = hostBindingOpCodes[++i];
|
32252 | setBindingRootForHostBindings(bindingRootIndx, directiveIdx);
|
32253 | const context = lView[directiveIdx];
|
32254 | hostBindingFn(2 /* Update */, context);
|
32255 | }
|
32256 | }
|
32257 | }
|
32258 | finally {
|
32259 | setSelectedIndex(-1);
|
32260 | }
|
32261 | }
|
32262 | /** Refreshes all content queries declared by directives in a given view */
|
32263 | function refreshContentQueries(tView, lView) {
|
32264 | const contentQueries = tView.contentQueries;
|
32265 | if (contentQueries !== null) {
|
32266 | for (let i = 0; i < contentQueries.length; i += 2) {
|
32267 | const queryStartIdx = contentQueries[i];
|
32268 | const directiveDefIdx = contentQueries[i + 1];
|
32269 | if (directiveDefIdx !== -1) {
|
32270 | const directiveDef = tView.data[directiveDefIdx];
|
32271 | ngDevMode && assertDefined(directiveDef, 'DirectiveDef not found.');
|
32272 | ngDevMode &&
|
32273 | assertDefined(directiveDef.contentQueries, 'contentQueries function should be defined');
|
32274 | setCurrentQueryIndex(queryStartIdx);
|
32275 | directiveDef.contentQueries(2 /* Update */, lView[directiveDefIdx], directiveDefIdx);
|
32276 | }
|
32277 | }
|
32278 | }
|
32279 | }
|
32280 | /** Refreshes child components in the current view (update mode). */
|
32281 | function refreshChildComponents(hostLView, components) {
|
32282 | for (let i = 0; i < components.length; i++) {
|
32283 | refreshComponent(hostLView, components[i]);
|
32284 | }
|
32285 | }
|
32286 | /** Renders child components in the current view (creation mode). */
|
32287 | function renderChildComponents(hostLView, components) {
|
32288 | for (let i = 0; i < components.length; i++) {
|
32289 | renderComponent(hostLView, components[i]);
|
32290 | }
|
32291 | }
|
32292 | function createLView(parentLView, tView, context, flags, host, tHostNode, rendererFactory, renderer, sanitizer, injector) {
|
32293 | const lView = ngDevMode ? cloneToLViewFromTViewBlueprint(tView) : tView.blueprint.slice();
|
32294 | lView[HOST] = host;
|
32295 | lView[FLAGS] = flags | 4 /* CreationMode */ | 128 /* Attached */ | 8 /* FirstLViewPass */;
|
32296 | resetPreOrderHookFlags(lView);
|
32297 | ngDevMode && tView.declTNode && parentLView && assertTNodeForLView(tView.declTNode, parentLView);
|
32298 | lView[PARENT] = lView[DECLARATION_VIEW] = parentLView;
|
32299 | lView[CONTEXT] = context;
|
32300 | lView[RENDERER_FACTORY] = (rendererFactory || parentLView && parentLView[RENDERER_FACTORY]);
|
32301 | ngDevMode && assertDefined(lView[RENDERER_FACTORY], 'RendererFactory is required');
|
32302 | lView[RENDERER] = (renderer || parentLView && parentLView[RENDERER]);
|
32303 | ngDevMode && assertDefined(lView[RENDERER], 'Renderer is required');
|
32304 | lView[SANITIZER] = sanitizer || parentLView && parentLView[SANITIZER] || null;
|
32305 | lView[INJECTOR] = injector || parentLView && parentLView[INJECTOR] || null;
|
32306 | lView[T_HOST] = tHostNode;
|
32307 | ngDevMode &&
|
32308 | assertEqual(tView.type == 2 /* Embedded */ ? parentLView !== null : true, true, 'Embedded views must have parentLView');
|
32309 | lView[DECLARATION_COMPONENT_VIEW] =
|
32310 | tView.type == 2 /* Embedded */ ? parentLView[DECLARATION_COMPONENT_VIEW] : lView;
|
32311 | ngDevMode && attachLViewDebug(lView);
|
32312 | return lView;
|
32313 | }
|
32314 | function getOrCreateTNode(tView, index, type, name, attrs) {
|
32315 | ngDevMode && index !== 0 && // 0 are bogus nodes and they are OK. See `createContainerRef` in
|
32316 | // `view_engine_compatibility` for additional context.
|
32317 | assertGreaterThanOrEqual(index, HEADER_OFFSET, 'TNodes can\'t be in the LView header.');
|
32318 | // Keep this function short, so that the VM will inline it.
|
32319 | ngDevMode && assertPureTNodeType(type);
|
32320 | let tNode = tView.data[index];
|
32321 | if (tNode === null) {
|
32322 | tNode = createTNodeAtIndex(tView, index, type, name, attrs);
|
32323 | if (isInI18nBlock()) {
|
32324 | // If we are in i18n block then all elements should be pre declared through `Placeholder`
|
32325 | // See `TNodeType.Placeholder` and `LFrame.inI18n` for more context.
|
32326 | // If the `TNode` was not pre-declared than it means it was not mentioned which means it was
|
32327 | // removed, so we mark it as detached.
|
32328 | tNode.flags |= 64 /* isDetached */;
|
32329 | }
|
32330 | }
|
32331 | else if (tNode.type & 64 /* Placeholder */) {
|
32332 | tNode.type = type;
|
32333 | tNode.value = name;
|
32334 | tNode.attrs = attrs;
|
32335 | const parent = getCurrentParentTNode();
|
32336 | tNode.injectorIndex = parent === null ? -1 : parent.injectorIndex;
|
32337 | ngDevMode && assertTNodeForTView(tNode, tView);
|
32338 | ngDevMode && assertEqual(index, tNode.index, 'Expecting same index');
|
32339 | }
|
32340 | setCurrentTNode(tNode, true);
|
32341 | return tNode;
|
32342 | }
|
32343 | function createTNodeAtIndex(tView, index, type, name, attrs) {
|
32344 | const currentTNode = getCurrentTNodePlaceholderOk();
|
32345 | const isParent = isCurrentTNodeParent();
|
32346 | const parent = isParent ? currentTNode : currentTNode && currentTNode.parent;
|
32347 | // Parents cannot cross component boundaries because components will be used in multiple places.
|
32348 | const tNode = tView.data[index] =
|
32349 | createTNode(tView, parent, type, index, name, attrs);
|
32350 | // Assign a pointer to the first child node of a given view. The first node is not always the one
|
32351 | // at index 0, in case of i18n, index 0 can be the instruction `i18nStart` and the first node has
|
32352 | // the index 1 or more, so we can't just check node index.
|
32353 | if (tView.firstChild === null) {
|
32354 | tView.firstChild = tNode;
|
32355 | }
|
32356 | if (currentTNode !== null) {
|
32357 | if (isParent) {
|
32358 | // FIXME(misko): This logic looks unnecessarily complicated. Could we simplify?
|
32359 | if (currentTNode.child == null && tNode.parent !== null) {
|
32360 | // We are in the same view, which means we are adding content node to the parent view.
|
32361 | currentTNode.child = tNode;
|
32362 | }
|
32363 | }
|
32364 | else {
|
32365 | if (currentTNode.next === null) {
|
32366 | // In the case of i18n the `currentTNode` may already be linked, in which case we don't want
|
32367 | // to break the links which i18n created.
|
32368 | currentTNode.next = tNode;
|
32369 | }
|
32370 | }
|
32371 | }
|
32372 | return tNode;
|
32373 | }
|
32374 | /**
|
32375 | * When elements are created dynamically after a view blueprint is created (e.g. through
|
32376 | * i18nApply()), we need to adjust the blueprint for future
|
32377 | * template passes.
|
32378 | *
|
32379 | * @param tView `TView` associated with `LView`
|
32380 | * @param lView The `LView` containing the blueprint to adjust
|
32381 | * @param numSlotsToAlloc The number of slots to alloc in the LView, should be >0
|
32382 | * @param initialValue Initial value to store in blueprint
|
32383 | */
|
32384 | function allocExpando(tView, lView, numSlotsToAlloc, initialValue) {
|
32385 | if (numSlotsToAlloc === 0)
|
32386 | return -1;
|
32387 | if (ngDevMode) {
|
32388 | assertFirstCreatePass(tView);
|
32389 | assertSame(tView, lView[TVIEW], '`LView` must be associated with `TView`!');
|
32390 | assertEqual(tView.data.length, lView.length, 'Expecting LView to be same size as TView');
|
32391 | assertEqual(tView.data.length, tView.blueprint.length, 'Expecting Blueprint to be same size as TView');
|
32392 | assertFirstUpdatePass(tView);
|
32393 | }
|
32394 | const allocIdx = lView.length;
|
32395 | for (let i = 0; i < numSlotsToAlloc; i++) {
|
32396 | lView.push(initialValue);
|
32397 | tView.blueprint.push(initialValue);
|
32398 | tView.data.push(null);
|
32399 | }
|
32400 | return allocIdx;
|
32401 | }
|
32402 | //////////////////////////
|
32403 | //// Render
|
32404 | //////////////////////////
|
32405 | /**
|
32406 | * Processes a view in the creation mode. This includes a number of steps in a specific order:
|
32407 | * - creating view query functions (if any);
|
32408 | * - executing a template function in the creation mode;
|
32409 | * - updating static queries (if any);
|
32410 | * - creating child components defined in a given view.
|
32411 | */
|
32412 | function renderView(tView, lView, context) {
|
32413 | ngDevMode && assertEqual(isCreationMode(lView), true, 'Should be run in creation mode');
|
32414 | enterView(lView);
|
32415 | try {
|
32416 | const viewQuery = tView.viewQuery;
|
32417 | if (viewQuery !== null) {
|
32418 | executeViewQueryFn(1 /* Create */, viewQuery, context);
|
32419 | }
|
32420 | // Execute a template associated with this view, if it exists. A template function might not be
|
32421 | // defined for the root component views.
|
32422 | const templateFn = tView.template;
|
32423 | if (templateFn !== null) {
|
32424 | executeTemplate(tView, lView, templateFn, 1 /* Create */, context);
|
32425 | }
|
32426 | // This needs to be set before children are processed to support recursive components.
|
32427 | // This must be set to false immediately after the first creation run because in an
|
32428 | // ngFor loop, all the views will be created together before update mode runs and turns
|
32429 | // off firstCreatePass. If we don't set it here, instances will perform directive
|
32430 | // matching, etc again and again.
|
32431 | if (tView.firstCreatePass) {
|
32432 | tView.firstCreatePass = false;
|
32433 | }
|
32434 | // We resolve content queries specifically marked as `static` in creation mode. Dynamic
|
32435 | // content queries are resolved during change detection (i.e. update mode), after embedded
|
32436 | // views are refreshed (see block above).
|
32437 | if (tView.staticContentQueries) {
|
32438 | refreshContentQueries(tView, lView);
|
32439 | }
|
32440 | // We must materialize query results before child components are processed
|
32441 | // in case a child component has projected a container. The LContainer needs
|
32442 | // to exist so the embedded views are properly attached by the container.
|
32443 | if (tView.staticViewQueries) {
|
32444 | executeViewQueryFn(2 /* Update */, tView.viewQuery, context);
|
32445 | }
|
32446 | // Render child component views.
|
32447 | const components = tView.components;
|
32448 | if (components !== null) {
|
32449 | renderChildComponents(lView, components);
|
32450 | }
|
32451 | }
|
32452 | catch (error) {
|
32453 | // If we didn't manage to get past the first template pass due to
|
32454 | // an error, mark the view as corrupted so we can try to recover.
|
32455 | if (tView.firstCreatePass) {
|
32456 | tView.incompleteFirstPass = true;
|
32457 | }
|
32458 | throw error;
|
32459 | }
|
32460 | finally {
|
32461 | lView[FLAGS] &= ~4 /* CreationMode */;
|
32462 | leaveView();
|
32463 | }
|
32464 | }
|
32465 | /**
|
32466 | * Processes a view in update mode. This includes a number of steps in a specific order:
|
32467 | * - executing a template function in update mode;
|
32468 | * - executing hooks;
|
32469 | * - refreshing queries;
|
32470 | * - setting host bindings;
|
32471 | * - refreshing child (embedded and component) views.
|
32472 | */
|
32473 | function refreshView(tView, lView, templateFn, context) {
|
32474 | ngDevMode && assertEqual(isCreationMode(lView), false, 'Should be run in update mode');
|
32475 | const flags = lView[FLAGS];
|
32476 | if ((flags & 256 /* Destroyed */) === 256 /* Destroyed */)
|
32477 | return;
|
32478 | enterView(lView);
|
32479 | // Check no changes mode is a dev only mode used to verify that bindings have not changed
|
32480 | // since they were assigned. We do not want to execute lifecycle hooks in that mode.
|
32481 | const isInCheckNoChangesPass = isInCheckNoChangesMode();
|
32482 | try {
|
32483 | resetPreOrderHookFlags(lView);
|
32484 | setBindingIndex(tView.bindingStartIndex);
|
32485 | if (templateFn !== null) {
|
32486 | executeTemplate(tView, lView, templateFn, 2 /* Update */, context);
|
32487 | }
|
32488 | const hooksInitPhaseCompleted = (flags & 3 /* InitPhaseStateMask */) === 3 /* InitPhaseCompleted */;
|
32489 | // execute pre-order hooks (OnInit, OnChanges, DoCheck)
|
32490 | // PERF WARNING: do NOT extract this to a separate function without running benchmarks
|
32491 | if (!isInCheckNoChangesPass) {
|
32492 | if (hooksInitPhaseCompleted) {
|
32493 | const preOrderCheckHooks = tView.preOrderCheckHooks;
|
32494 | if (preOrderCheckHooks !== null) {
|
32495 | executeCheckHooks(lView, preOrderCheckHooks, null);
|
32496 | }
|
32497 | }
|
32498 | else {
|
32499 | const preOrderHooks = tView.preOrderHooks;
|
32500 | if (preOrderHooks !== null) {
|
32501 | executeInitAndCheckHooks(lView, preOrderHooks, 0 /* OnInitHooksToBeRun */, null);
|
32502 | }
|
32503 | incrementInitPhaseFlags(lView, 0 /* OnInitHooksToBeRun */);
|
32504 | }
|
32505 | }
|
32506 | // First mark transplanted views that are declared in this lView as needing a refresh at their
|
32507 | // insertion points. This is needed to avoid the situation where the template is defined in this
|
32508 | // `LView` but its declaration appears after the insertion component.
|
32509 | markTransplantedViewsForRefresh(lView);
|
32510 | refreshEmbeddedViews(lView);
|
32511 | // Content query results must be refreshed before content hooks are called.
|
32512 | if (tView.contentQueries !== null) {
|
32513 | refreshContentQueries(tView, lView);
|
32514 | }
|
32515 | // execute content hooks (AfterContentInit, AfterContentChecked)
|
32516 | // PERF WARNING: do NOT extract this to a separate function without running benchmarks
|
32517 | if (!isInCheckNoChangesPass) {
|
32518 | if (hooksInitPhaseCompleted) {
|
32519 | const contentCheckHooks = tView.contentCheckHooks;
|
32520 | if (contentCheckHooks !== null) {
|
32521 | executeCheckHooks(lView, contentCheckHooks);
|
32522 | }
|
32523 | }
|
32524 | else {
|
32525 | const contentHooks = tView.contentHooks;
|
32526 | if (contentHooks !== null) {
|
32527 | executeInitAndCheckHooks(lView, contentHooks, 1 /* AfterContentInitHooksToBeRun */);
|
32528 | }
|
32529 | incrementInitPhaseFlags(lView, 1 /* AfterContentInitHooksToBeRun */);
|
32530 | }
|
32531 | }
|
32532 | processHostBindingOpCodes(tView, lView);
|
32533 | // Refresh child component views.
|
32534 | const components = tView.components;
|
32535 | if (components !== null) {
|
32536 | refreshChildComponents(lView, components);
|
32537 | }
|
32538 | // View queries must execute after refreshing child components because a template in this view
|
32539 | // could be inserted in a child component. If the view query executes before child component
|
32540 | // refresh, the template might not yet be inserted.
|
32541 | const viewQuery = tView.viewQuery;
|
32542 | if (viewQuery !== null) {
|
32543 | executeViewQueryFn(2 /* Update */, viewQuery, context);
|
32544 | }
|
32545 | // execute view hooks (AfterViewInit, AfterViewChecked)
|
32546 | // PERF WARNING: do NOT extract this to a separate function without running benchmarks
|
32547 | if (!isInCheckNoChangesPass) {
|
32548 | if (hooksInitPhaseCompleted) {
|
32549 | const viewCheckHooks = tView.viewCheckHooks;
|
32550 | if (viewCheckHooks !== null) {
|
32551 | executeCheckHooks(lView, viewCheckHooks);
|
32552 | }
|
32553 | }
|
32554 | else {
|
32555 | const viewHooks = tView.viewHooks;
|
32556 | if (viewHooks !== null) {
|
32557 | executeInitAndCheckHooks(lView, viewHooks, 2 /* AfterViewInitHooksToBeRun */);
|
32558 | }
|
32559 | incrementInitPhaseFlags(lView, 2 /* AfterViewInitHooksToBeRun */);
|
32560 | }
|
32561 | }
|
32562 | if (tView.firstUpdatePass === true) {
|
32563 | // We need to make sure that we only flip the flag on successful `refreshView` only
|
32564 | // Don't do this in `finally` block.
|
32565 | // If we did this in `finally` block then an exception could block the execution of styling
|
32566 | // instructions which in turn would be unable to insert themselves into the styling linked
|
32567 | // list. The result of this would be that if the exception would not be throw on subsequent CD
|
32568 | // the styling would be unable to process it data and reflect to the DOM.
|
32569 | tView.firstUpdatePass = false;
|
32570 | }
|
32571 | // Do not reset the dirty state when running in check no changes mode. We don't want components
|
32572 | // to behave differently depending on whether check no changes is enabled or not. For example:
|
32573 | // Marking an OnPush component as dirty from within the `ngAfterViewInit` hook in order to
|
32574 | // refresh a `NgClass` binding should work. If we would reset the dirty state in the check
|
32575 | // no changes cycle, the component would be not be dirty for the next update pass. This would
|
32576 | // be different in production mode where the component dirty state is not reset.
|
32577 | if (!isInCheckNoChangesPass) {
|
32578 | lView[FLAGS] &= ~(64 /* Dirty */ | 8 /* FirstLViewPass */);
|
32579 | }
|
32580 | if (lView[FLAGS] & 1024 /* RefreshTransplantedView */) {
|
32581 | lView[FLAGS] &= ~1024 /* RefreshTransplantedView */;
|
32582 | updateTransplantedViewCount(lView[PARENT], -1);
|
32583 | }
|
32584 | }
|
32585 | finally {
|
32586 | leaveView();
|
32587 | }
|
32588 | }
|
32589 | function renderComponentOrTemplate(tView, lView, templateFn, context) {
|
32590 | const rendererFactory = lView[RENDERER_FACTORY];
|
32591 | const normalExecutionPath = !isInCheckNoChangesMode();
|
32592 | const creationModeIsActive = isCreationMode(lView);
|
32593 | try {
|
32594 | if (normalExecutionPath && !creationModeIsActive && rendererFactory.begin) {
|
32595 | rendererFactory.begin();
|
32596 | }
|
32597 | if (creationModeIsActive) {
|
32598 | renderView(tView, lView, context);
|
32599 | }
|
32600 | refreshView(tView, lView, templateFn, context);
|
32601 | }
|
32602 | finally {
|
32603 | if (normalExecutionPath && !creationModeIsActive && rendererFactory.end) {
|
32604 | rendererFactory.end();
|
32605 | }
|
32606 | }
|
32607 | }
|
32608 | function executeTemplate(tView, lView, templateFn, rf, context) {
|
32609 | const prevSelectedIndex = getSelectedIndex();
|
32610 | try {
|
32611 | setSelectedIndex(-1);
|
32612 | if (rf & 2 /* Update */ && lView.length > HEADER_OFFSET) {
|
32613 | // When we're updating, inherently select 0 so we don't
|
32614 | // have to generate that instruction for most update blocks.
|
32615 | selectIndexInternal(tView, lView, HEADER_OFFSET, isInCheckNoChangesMode());
|
32616 | }
|
32617 | templateFn(rf, context);
|
32618 | }
|
32619 | finally {
|
32620 | setSelectedIndex(prevSelectedIndex);
|
32621 | }
|
32622 | }
|
32623 | /**
|
32624 | * Gets TView from a template function or creates a new TView
|
32625 | * if it doesn't already exist.
|
32626 | *
|
32627 | * @param def ComponentDef
|
32628 | * @returns TView
|
32629 | */
|
32630 | function getOrCreateTComponentView(def) {
|
32631 | const tView = def.tView;
|
32632 | // Create a TView if there isn't one, or recreate it if the first create pass didn't
|
32633 | // complete successfully since we can't know for sure whether it's in a usable shape.
|
32634 | if (tView === null || tView.incompleteFirstPass) {
|
32635 | // Declaration node here is null since this function is called when we dynamically create a
|
32636 | // component and hence there is no declaration.
|
32637 | const declTNode = null;
|
32638 | return def.tView = createTView(1 /* Component */, declTNode, def.template, def.decls, def.vars, def.directiveDefs, def.pipeDefs, def.viewQuery, def.schemas, def.consts);
|
32639 | }
|
32640 | return tView;
|
32641 | }
|
32642 | /**
|
32643 | * Creates a TView instance
|
32644 | *
|
32645 | * @param type Type of `TView`.
|
32646 | * @param declTNode Declaration location of this `TView`.
|
32647 | * @param templateFn Template function
|
32648 | * @param decls The number of nodes, local refs, and pipes in this template
|
32649 | * @param directives Registry of directives for this view
|
32650 | * @param pipes Registry of pipes for this view
|
32651 | * @param viewQuery View queries for this view
|
32652 | * @param schemas Schemas for this view
|
32653 | * @param consts Constants for this view
|
32654 | */
|
32655 | function createTView(type, declTNode, templateFn, decls, vars, directives, pipes, viewQuery, schemas, constsOrFactory) {
|
32656 | ngDevMode && ngDevMode.tView++;
|
32657 | const bindingStartIndex = HEADER_OFFSET + decls;
|
32658 | // This length does not yet contain host bindings from child directives because at this point,
|
32659 | // we don't know which directives are active on this template. As soon as a directive is matched
|
32660 | // that has a host binding, we will update the blueprint with that def's hostVars count.
|
32661 | const initialViewLength = bindingStartIndex + vars;
|
32662 | const blueprint = createViewBlueprint(bindingStartIndex, initialViewLength);
|
32663 | const consts = typeof constsOrFactory === 'function' ? constsOrFactory() : constsOrFactory;
|
32664 | const tView = blueprint[TVIEW] = ngDevMode ?
|
32665 | new TViewConstructor(type, // type: TViewType,
|
32666 | blueprint, // blueprint: LView,
|
32667 | templateFn, // template: ComponentTemplate<{}>|null,
|
32668 | null, // queries: TQueries|null
|
32669 | viewQuery, // viewQuery: ViewQueriesFunction<{}>|null,
|
32670 | declTNode, // declTNode: TNode|null,
|
32671 | cloneToTViewData(blueprint).fill(null, bindingStartIndex), // data: TData,
|
32672 | bindingStartIndex, // bindingStartIndex: number,
|
32673 | initialViewLength, // expandoStartIndex: number,
|
32674 | null, // hostBindingOpCodes: HostBindingOpCodes,
|
32675 | true, // firstCreatePass: boolean,
|
32676 | true, // firstUpdatePass: boolean,
|
32677 | false, // staticViewQueries: boolean,
|
32678 | false, // staticContentQueries: boolean,
|
32679 | null, // preOrderHooks: HookData|null,
|
32680 | null, // preOrderCheckHooks: HookData|null,
|
32681 | null, // contentHooks: HookData|null,
|
32682 | null, // contentCheckHooks: HookData|null,
|
32683 | null, // viewHooks: HookData|null,
|
32684 | null, // viewCheckHooks: HookData|null,
|
32685 | null, // destroyHooks: DestroyHookData|null,
|
32686 | null, // cleanup: any[]|null,
|
32687 | null, // contentQueries: number[]|null,
|
32688 | null, // components: number[]|null,
|
32689 | typeof directives === 'function' ? //
|
32690 | directives() : //
|
32691 | directives, // directiveRegistry: DirectiveDefList|null,
|
32692 | typeof pipes === 'function' ? pipes() : pipes, // pipeRegistry: PipeDefList|null,
|
32693 | null, // firstChild: TNode|null,
|
32694 | schemas, // schemas: SchemaMetadata[]|null,
|
32695 | consts, // consts: TConstants|null
|
32696 | false, // incompleteFirstPass: boolean
|
32697 | decls, // ngDevMode only: decls
|
32698 | vars) :
|
32699 | {
|
32700 | type: type,
|
32701 | blueprint: blueprint,
|
32702 | template: templateFn,
|
32703 | queries: null,
|
32704 | viewQuery: viewQuery,
|
32705 | declTNode: declTNode,
|
32706 | data: blueprint.slice().fill(null, bindingStartIndex),
|
32707 | bindingStartIndex: bindingStartIndex,
|
32708 | expandoStartIndex: initialViewLength,
|
32709 | hostBindingOpCodes: null,
|
32710 | firstCreatePass: true,
|
32711 | firstUpdatePass: true,
|
32712 | staticViewQueries: false,
|
32713 | staticContentQueries: false,
|
32714 | preOrderHooks: null,
|
32715 | preOrderCheckHooks: null,
|
32716 | contentHooks: null,
|
32717 | contentCheckHooks: null,
|
32718 | viewHooks: null,
|
32719 | viewCheckHooks: null,
|
32720 | destroyHooks: null,
|
32721 | cleanup: null,
|
32722 | contentQueries: null,
|
32723 | components: null,
|
32724 | directiveRegistry: typeof directives === 'function' ? directives() : directives,
|
32725 | pipeRegistry: typeof pipes === 'function' ? pipes() : pipes,
|
32726 | firstChild: null,
|
32727 | schemas: schemas,
|
32728 | consts: consts,
|
32729 | incompleteFirstPass: false
|
32730 | };
|
32731 | if (ngDevMode) {
|
32732 | // For performance reasons it is important that the tView retains the same shape during runtime.
|
32733 | // (To make sure that all of the code is monomorphic.) For this reason we seal the object to
|
32734 | // prevent class transitions.
|
32735 | Object.seal(tView);
|
32736 | }
|
32737 | return tView;
|
32738 | }
|
32739 | function createViewBlueprint(bindingStartIndex, initialViewLength) {
|
32740 | const blueprint = ngDevMode ? new LViewBlueprint() : [];
|
32741 | for (let i = 0; i < initialViewLength; i++) {
|
32742 | blueprint.push(i < bindingStartIndex ? null : NO_CHANGE);
|
32743 | }
|
32744 | return blueprint;
|
32745 | }
|
32746 | function createError(text, token) {
|
32747 | return new Error(`Renderer: ${text} [${stringifyForError(token)}]`);
|
32748 | }
|
32749 | function assertHostNodeExists(rElement, elementOrSelector) {
|
32750 | if (!rElement) {
|
32751 | if (typeof elementOrSelector === 'string') {
|
32752 | throw createError('Host node with selector not found:', elementOrSelector);
|
32753 | }
|
32754 | else {
|
32755 | throw createError('Host node is required:', elementOrSelector);
|
32756 | }
|
32757 | }
|
32758 | }
|
32759 | /**
|
32760 | * Locates the host native element, used for bootstrapping existing nodes into rendering pipeline.
|
32761 | *
|
32762 | * @param rendererFactory Factory function to create renderer instance.
|
32763 | * @param elementOrSelector Render element or CSS selector to locate the element.
|
32764 | * @param encapsulation View Encapsulation defined for component that requests host element.
|
32765 | */
|
32766 | function locateHostElement(renderer, elementOrSelector, encapsulation) {
|
32767 | if (isProceduralRenderer(renderer)) {
|
32768 | // When using native Shadow DOM, do not clear host element to allow native slot projection
|
32769 | const preserveContent = encapsulation === ViewEncapsulation$1.ShadowDom;
|
32770 | return renderer.selectRootElement(elementOrSelector, preserveContent);
|
32771 | }
|
32772 | let rElement = typeof elementOrSelector === 'string' ?
|
32773 | renderer.querySelector(elementOrSelector) :
|
32774 | elementOrSelector;
|
32775 | ngDevMode && assertHostNodeExists(rElement, elementOrSelector);
|
32776 | // Always clear host element's content when Renderer3 is in use. For procedural renderer case we
|
32777 | // make it depend on whether ShadowDom encapsulation is used (in which case the content should be
|
32778 | // preserved to allow native slot projection). ShadowDom encapsulation requires procedural
|
32779 | // renderer, and procedural renderer case is handled above.
|
32780 | rElement.textContent = '';
|
32781 | return rElement;
|
32782 | }
|
32783 | /**
|
32784 | * Saves context for this cleanup function in LView.cleanupInstances.
|
32785 | *
|
32786 | * On the first template pass, saves in TView:
|
32787 | * - Cleanup function
|
32788 | * - Index of context we just saved in LView.cleanupInstances
|
32789 | *
|
32790 | * This function can also be used to store instance specific cleanup fns. In that case the `context`
|
32791 | * is `null` and the function is store in `LView` (rather than it `TView`).
|
32792 | */
|
32793 | function storeCleanupWithContext(tView, lView, context, cleanupFn) {
|
32794 | const lCleanup = getOrCreateLViewCleanup(lView);
|
32795 | if (context === null) {
|
32796 | // If context is null that this is instance specific callback. These callbacks can only be
|
32797 | // inserted after template shared instances. For this reason in ngDevMode we freeze the TView.
|
32798 | if (ngDevMode) {
|
32799 | Object.freeze(getOrCreateTViewCleanup(tView));
|
32800 | }
|
32801 | lCleanup.push(cleanupFn);
|
32802 | }
|
32803 | else {
|
32804 | lCleanup.push(context);
|
32805 | if (tView.firstCreatePass) {
|
32806 | getOrCreateTViewCleanup(tView).push(cleanupFn, lCleanup.length - 1);
|
32807 | }
|
32808 | }
|
32809 | }
|
32810 | function createTNode(tView, tParent, type, index, value, attrs) {
|
32811 | ngDevMode && index !== 0 && // 0 are bogus nodes and they are OK. See `createContainerRef` in
|
32812 | // `view_engine_compatibility` for additional context.
|
32813 | assertGreaterThanOrEqual(index, HEADER_OFFSET, 'TNodes can\'t be in the LView header.');
|
32814 | ngDevMode && assertNotSame(attrs, undefined, '\'undefined\' is not valid value for \'attrs\'');
|
32815 | ngDevMode && ngDevMode.tNode++;
|
32816 | ngDevMode && tParent && assertTNodeForTView(tParent, tView);
|
32817 | let injectorIndex = tParent ? tParent.injectorIndex : -1;
|
32818 | const tNode = ngDevMode ?
|
32819 | new TNodeDebug(tView, // tView_: TView
|
32820 | type, // type: TNodeType
|
32821 | index, // index: number
|
32822 | null, // insertBeforeIndex: null|-1|number|number[]
|
32823 | injectorIndex, // injectorIndex: number
|
32824 | -1, // directiveStart: number
|
32825 | -1, // directiveEnd: number
|
32826 | -1, // directiveStylingLast: number
|
32827 | null, // propertyBindings: number[]|null
|
32828 | 0, // flags: TNodeFlags
|
32829 | 0, // providerIndexes: TNodeProviderIndexes
|
32830 | value, // value: string|null
|
32831 | attrs, // attrs: (string|AttributeMarker|(string|SelectorFlags)[])[]|null
|
32832 | null, // mergedAttrs
|
32833 | null, // localNames: (string|number)[]|null
|
32834 | undefined, // initialInputs: (string[]|null)[]|null|undefined
|
32835 | null, // inputs: PropertyAliases|null
|
32836 | null, // outputs: PropertyAliases|null
|
32837 | null, // tViews: ITView|ITView[]|null
|
32838 | null, // next: ITNode|null
|
32839 | null, // projectionNext: ITNode|null
|
32840 | null, // child: ITNode|null
|
32841 | tParent, // parent: TElementNode|TContainerNode|null
|
32842 | null, // projection: number|(ITNode|RNode[])[]|null
|
32843 | null, // styles: string|null
|
32844 | null, // stylesWithoutHost: string|null
|
32845 | undefined, // residualStyles: string|null
|
32846 | null, // classes: string|null
|
32847 | null, // classesWithoutHost: string|null
|
32848 | undefined, // residualClasses: string|null
|
32849 | 0, // classBindings: TStylingRange;
|
32850 | 0) :
|
32851 | {
|
32852 | type,
|
32853 | index,
|
32854 | insertBeforeIndex: null,
|
32855 | injectorIndex,
|
32856 | directiveStart: -1,
|
32857 | directiveEnd: -1,
|
32858 | directiveStylingLast: -1,
|
32859 | propertyBindings: null,
|
32860 | flags: 0,
|
32861 | providerIndexes: 0,
|
32862 | value: value,
|
32863 | attrs: attrs,
|
32864 | mergedAttrs: null,
|
32865 | localNames: null,
|
32866 | initialInputs: undefined,
|
32867 | inputs: null,
|
32868 | outputs: null,
|
32869 | tViews: null,
|
32870 | next: null,
|
32871 | projectionNext: null,
|
32872 | child: null,
|
32873 | parent: tParent,
|
32874 | projection: null,
|
32875 | styles: null,
|
32876 | stylesWithoutHost: null,
|
32877 | residualStyles: undefined,
|
32878 | classes: null,
|
32879 | classesWithoutHost: null,
|
32880 | residualClasses: undefined,
|
32881 | classBindings: 0,
|
32882 | styleBindings: 0,
|
32883 | };
|
32884 | if (ngDevMode) {
|
32885 | // For performance reasons it is important that the tNode retains the same shape during runtime.
|
32886 | // (To make sure that all of the code is monomorphic.) For this reason we seal the object to
|
32887 | // prevent class transitions.
|
32888 | Object.seal(tNode);
|
32889 | }
|
32890 | return tNode;
|
32891 | }
|
32892 | /**
|
32893 | * Instantiate a root component.
|
32894 | */
|
32895 | function instantiateRootComponent(tView, lView, def) {
|
32896 | const rootTNode = getCurrentTNode();
|
32897 | if (tView.firstCreatePass) {
|
32898 | if (def.providersResolver)
|
32899 | def.providersResolver(def);
|
32900 | const directiveIndex = allocExpando(tView, lView, 1, null);
|
32901 | ngDevMode &&
|
32902 | assertEqual(directiveIndex, rootTNode.directiveStart, 'Because this is a root component the allocated expando should match the TNode component.');
|
32903 | configureViewWithDirective(tView, rootTNode, lView, directiveIndex, def);
|
32904 | }
|
32905 | const directive = getNodeInjectable(lView, tView, rootTNode.directiveStart, rootTNode);
|
32906 | attachPatchData(directive, lView);
|
32907 | const native = getNativeByTNode(rootTNode, lView);
|
32908 | if (native) {
|
32909 | attachPatchData(native, lView);
|
32910 | }
|
32911 | return directive;
|
32912 | }
|
32913 | /**
|
32914 | * Add `hostBindings` to the `TView.hostBindingOpCodes`.
|
32915 | *
|
32916 | * @param tView `TView` to which the `hostBindings` should be added.
|
32917 | * @param tNode `TNode` the element which contains the directive
|
32918 | * @param lView `LView` current `LView`
|
32919 | * @param directiveIdx Directive index in view.
|
32920 | * @param directiveVarsIdx Where will the directive's vars be stored
|
32921 | * @param def `ComponentDef`/`DirectiveDef`, which contains the `hostVars`/`hostBindings` to add.
|
32922 | */
|
32923 | function registerHostBindingOpCodes(tView, tNode, lView, directiveIdx, directiveVarsIdx, def) {
|
32924 | ngDevMode && assertFirstCreatePass(tView);
|
32925 | const hostBindings = def.hostBindings;
|
32926 | if (hostBindings) {
|
32927 | let hostBindingOpCodes = tView.hostBindingOpCodes;
|
32928 | if (hostBindingOpCodes === null) {
|
32929 | hostBindingOpCodes = tView.hostBindingOpCodes = [];
|
32930 | }
|
32931 | const elementIndx = ~tNode.index;
|
32932 | if (lastSelectedElementIdx(hostBindingOpCodes) != elementIndx) {
|
32933 | // Conditionally add select element so that we are more efficient in execution.
|
32934 | // NOTE: this is strictly not necessary and it trades code size for runtime perf.
|
32935 | // (We could just always add it.)
|
32936 | hostBindingOpCodes.push(elementIndx);
|
32937 | }
|
32938 | hostBindingOpCodes.push(directiveIdx, directiveVarsIdx, hostBindings);
|
32939 | }
|
32940 | }
|
32941 | /**
|
32942 | * Returns the last selected element index in the `HostBindingOpCodes`
|
32943 | *
|
32944 | * For perf reasons we don't need to update the selected element index in `HostBindingOpCodes` only
|
32945 | * if it changes. This method returns the last index (or '0' if not found.)
|
32946 | *
|
32947 | * Selected element index are only the ones which are negative.
|
32948 | */
|
32949 | function lastSelectedElementIdx(hostBindingOpCodes) {
|
32950 | let i = hostBindingOpCodes.length;
|
32951 | while (i > 0) {
|
32952 | const value = hostBindingOpCodes[--i];
|
32953 | if (typeof value === 'number' && value < 0) {
|
32954 | return value;
|
32955 | }
|
32956 | }
|
32957 | return 0;
|
32958 | }
|
32959 | /**
|
32960 | * Invoke the host bindings in creation mode.
|
32961 | *
|
32962 | * @param def `DirectiveDef` which may contain the `hostBindings` function.
|
32963 | * @param directive Instance of directive.
|
32964 | */
|
32965 | function invokeHostBindingsInCreationMode(def, directive) {
|
32966 | if (def.hostBindings !== null) {
|
32967 | def.hostBindings(1 /* Create */, directive);
|
32968 | }
|
32969 | }
|
32970 | /**
|
32971 | * Marks a given TNode as a component's host. This consists of:
|
32972 | * - setting appropriate TNode flags;
|
32973 | * - storing index of component's host element so it will be queued for view refresh during CD.
|
32974 | */
|
32975 | function markAsComponentHost(tView, hostTNode) {
|
32976 | ngDevMode && assertFirstCreatePass(tView);
|
32977 | hostTNode.flags |= 2 /* isComponentHost */;
|
32978 | (tView.components || (tView.components = ngDevMode ? new TViewComponents() : []))
|
32979 | .push(hostTNode.index);
|
32980 | }
|
32981 | /**
|
32982 | * Initializes the flags on the current node, setting all indices to the initial index,
|
32983 | * the directive count to 0, and adding the isComponent flag.
|
32984 | * @param index the initial index
|
32985 | */
|
32986 | function initTNodeFlags(tNode, index, numberOfDirectives) {
|
32987 | ngDevMode &&
|
32988 | assertNotEqual(numberOfDirectives, tNode.directiveEnd - tNode.directiveStart, 'Reached the max number of directives');
|
32989 | tNode.flags |= 1 /* isDirectiveHost */;
|
32990 | // When the first directive is created on a node, save the index
|
32991 | tNode.directiveStart = index;
|
32992 | tNode.directiveEnd = index + numberOfDirectives;
|
32993 | tNode.providerIndexes = index;
|
32994 | }
|
32995 | /**
|
32996 | * Setup directive for instantiation.
|
32997 | *
|
32998 | * We need to create a `NodeInjectorFactory` which is then inserted in both the `Blueprint` as well
|
32999 | * as `LView`. `TView` gets the `DirectiveDef`.
|
33000 | *
|
33001 | * @param tView `TView`
|
33002 | * @param tNode `TNode`
|
33003 | * @param lView `LView`
|
33004 | * @param directiveIndex Index where the directive will be stored in the Expando.
|
33005 | * @param def `DirectiveDef`
|
33006 | */
|
33007 | function configureViewWithDirective(tView, tNode, lView, directiveIndex, def) {
|
33008 | ngDevMode &&
|
33009 | assertGreaterThanOrEqual(directiveIndex, HEADER_OFFSET, 'Must be in Expando section');
|
33010 | tView.data[directiveIndex] = def;
|
33011 | const directiveFactory = def.factory || (def.factory = getFactoryDef(def.type, true));
|
33012 | const nodeInjectorFactory = new NodeInjectorFactory(directiveFactory, isComponentDef(def), null);
|
33013 | tView.blueprint[directiveIndex] = nodeInjectorFactory;
|
33014 | lView[directiveIndex] = nodeInjectorFactory;
|
33015 | registerHostBindingOpCodes(tView, tNode, lView, directiveIndex, allocExpando(tView, lView, def.hostVars, NO_CHANGE), def);
|
33016 | }
|
33017 | //////////////////////////
|
33018 | //// ViewContainer & View
|
33019 | //////////////////////////
|
33020 | // Not sure why I need to do `any` here but TS complains later.
|
33021 | const LContainerArray = ((typeof ngDevMode === 'undefined' || ngDevMode) && initNgDevMode()) &&
|
33022 | createNamedArrayType('LContainer');
|
33023 | /**
|
33024 | * Goes over embedded views (ones created through ViewContainerRef APIs) and refreshes
|
33025 | * them by executing an associated template function.
|
33026 | */
|
33027 | function refreshEmbeddedViews(lView) {
|
33028 | for (let lContainer = getFirstLContainer(lView); lContainer !== null; lContainer = getNextLContainer(lContainer)) {
|
33029 | for (let i = CONTAINER_HEADER_OFFSET; i < lContainer.length; i++) {
|
33030 | const embeddedLView = lContainer[i];
|
33031 | const embeddedTView = embeddedLView[TVIEW];
|
33032 | ngDevMode && assertDefined(embeddedTView, 'TView must be allocated');
|
33033 | if (viewAttachedToChangeDetector(embeddedLView)) {
|
33034 | refreshView(embeddedTView, embeddedLView, embeddedTView.template, embeddedLView[CONTEXT]);
|
33035 | }
|
33036 | }
|
33037 | }
|
33038 | }
|
33039 | /**
|
33040 | * Mark transplanted views as needing to be refreshed at their insertion points.
|
33041 | *
|
33042 | * @param lView The `LView` that may have transplanted views.
|
33043 | */
|
33044 | function markTransplantedViewsForRefresh(lView) {
|
33045 | for (let lContainer = getFirstLContainer(lView); lContainer !== null; lContainer = getNextLContainer(lContainer)) {
|
33046 | if (!lContainer[HAS_TRANSPLANTED_VIEWS])
|
33047 | continue;
|
33048 | const movedViews = lContainer[MOVED_VIEWS];
|
33049 | ngDevMode && assertDefined(movedViews, 'Transplanted View flags set but missing MOVED_VIEWS');
|
33050 | for (let i = 0; i < movedViews.length; i++) {
|
33051 | const movedLView = movedViews[i];
|
33052 | const insertionLContainer = movedLView[PARENT];
|
33053 | ngDevMode && assertLContainer(insertionLContainer);
|
33054 | // We don't want to increment the counter if the moved LView was already marked for
|
33055 | // refresh.
|
33056 | if ((movedLView[FLAGS] & 1024 /* RefreshTransplantedView */) === 0) {
|
33057 | updateTransplantedViewCount(insertionLContainer, 1);
|
33058 | }
|
33059 | // Note, it is possible that the `movedViews` is tracking views that are transplanted *and*
|
33060 | // those that aren't (declaration component === insertion component). In the latter case,
|
33061 | // it's fine to add the flag, as we will clear it immediately in
|
33062 | // `refreshEmbeddedViews` for the view currently being refreshed.
|
33063 | movedLView[FLAGS] |= 1024 /* RefreshTransplantedView */;
|
33064 | }
|
33065 | }
|
33066 | }
|
33067 | /////////////
|
33068 | /**
|
33069 | * Refreshes components by entering the component view and processing its bindings, queries, etc.
|
33070 | *
|
33071 | * @param componentHostIdx Element index in LView[] (adjusted for HEADER_OFFSET)
|
33072 | */
|
33073 | function refreshComponent(hostLView, componentHostIdx) {
|
33074 | ngDevMode && assertEqual(isCreationMode(hostLView), false, 'Should be run in update mode');
|
33075 | const componentView = getComponentLViewByIndex(componentHostIdx, hostLView);
|
33076 | // Only attached components that are CheckAlways or OnPush and dirty should be refreshed
|
33077 | if (viewAttachedToChangeDetector(componentView)) {
|
33078 | const tView = componentView[TVIEW];
|
33079 | if (componentView[FLAGS] & (16 /* CheckAlways */ | 64 /* Dirty */)) {
|
33080 | refreshView(tView, componentView, tView.template, componentView[CONTEXT]);
|
33081 | }
|
33082 | else if (componentView[TRANSPLANTED_VIEWS_TO_REFRESH] > 0) {
|
33083 | // Only attached components that are CheckAlways or OnPush and dirty should be refreshed
|
33084 | refreshContainsDirtyView(componentView);
|
33085 | }
|
33086 | }
|
33087 | }
|
33088 | /**
|
33089 | * Refreshes all transplanted views marked with `LViewFlags.RefreshTransplantedView` that are
|
33090 | * children or descendants of the given lView.
|
33091 | *
|
33092 | * @param lView The lView which contains descendant transplanted views that need to be refreshed.
|
33093 | */
|
33094 | function refreshContainsDirtyView(lView) {
|
33095 | for (let lContainer = getFirstLContainer(lView); lContainer !== null; lContainer = getNextLContainer(lContainer)) {
|
33096 | for (let i = CONTAINER_HEADER_OFFSET; i < lContainer.length; i++) {
|
33097 | const embeddedLView = lContainer[i];
|
33098 | if (embeddedLView[FLAGS] & 1024 /* RefreshTransplantedView */) {
|
33099 | const embeddedTView = embeddedLView[TVIEW];
|
33100 | ngDevMode && assertDefined(embeddedTView, 'TView must be allocated');
|
33101 | refreshView(embeddedTView, embeddedLView, embeddedTView.template, embeddedLView[CONTEXT]);
|
33102 | }
|
33103 | else if (embeddedLView[TRANSPLANTED_VIEWS_TO_REFRESH] > 0) {
|
33104 | refreshContainsDirtyView(embeddedLView);
|
33105 | }
|
33106 | }
|
33107 | }
|
33108 | const tView = lView[TVIEW];
|
33109 | // Refresh child component views.
|
33110 | const components = tView.components;
|
33111 | if (components !== null) {
|
33112 | for (let i = 0; i < components.length; i++) {
|
33113 | const componentView = getComponentLViewByIndex(components[i], lView);
|
33114 | // Only attached components that are CheckAlways or OnPush and dirty should be refreshed
|
33115 | if (viewAttachedToChangeDetector(componentView) &&
|
33116 | componentView[TRANSPLANTED_VIEWS_TO_REFRESH] > 0) {
|
33117 | refreshContainsDirtyView(componentView);
|
33118 | }
|
33119 | }
|
33120 | }
|
33121 | }
|
33122 | function renderComponent(hostLView, componentHostIdx) {
|
33123 | ngDevMode && assertEqual(isCreationMode(hostLView), true, 'Should be run in creation mode');
|
33124 | const componentView = getComponentLViewByIndex(componentHostIdx, hostLView);
|
33125 | const componentTView = componentView[TVIEW];
|
33126 | syncViewWithBlueprint(componentTView, componentView);
|
33127 | renderView(componentTView, componentView, componentView[CONTEXT]);
|
33128 | }
|
33129 | /**
|
33130 | * Syncs an LView instance with its blueprint if they have gotten out of sync.
|
33131 | *
|
33132 | * Typically, blueprints and their view instances should always be in sync, so the loop here
|
33133 | * will be skipped. However, consider this case of two components side-by-side:
|
33134 | *
|
33135 | * App template:
|
33136 | * ```
|
33137 | * <comp></comp>
|
33138 | * <comp></comp>
|
33139 | * ```
|
33140 | *
|
33141 | * The following will happen:
|
33142 | * 1. App template begins processing.
|
33143 | * 2. First <comp> is matched as a component and its LView is created.
|
33144 | * 3. Second <comp> is matched as a component and its LView is created.
|
33145 | * 4. App template completes processing, so it's time to check child templates.
|
33146 | * 5. First <comp> template is checked. It has a directive, so its def is pushed to blueprint.
|
33147 | * 6. Second <comp> template is checked. Its blueprint has been updated by the first
|
33148 | * <comp> template, but its LView was created before this update, so it is out of sync.
|
33149 | *
|
33150 | * Note that embedded views inside ngFor loops will never be out of sync because these views
|
33151 | * are processed as soon as they are created.
|
33152 | *
|
33153 | * @param tView The `TView` that contains the blueprint for syncing
|
33154 | * @param lView The view to sync
|
33155 | */
|
33156 | function syncViewWithBlueprint(tView, lView) {
|
33157 | for (let i = lView.length; i < tView.blueprint.length; i++) {
|
33158 | lView.push(tView.blueprint[i]);
|
33159 | }
|
33160 | }
|
33161 | /**
|
33162 | * Adds LView or LContainer to the end of the current view tree.
|
33163 | *
|
33164 | * This structure will be used to traverse through nested views to remove listeners
|
33165 | * and call onDestroy callbacks.
|
33166 | *
|
33167 | * @param lView The view where LView or LContainer should be added
|
33168 | * @param adjustedHostIndex Index of the view's host node in LView[], adjusted for header
|
33169 | * @param lViewOrLContainer The LView or LContainer to add to the view tree
|
33170 | * @returns The state passed in
|
33171 | */
|
33172 | function addToViewTree(lView, lViewOrLContainer) {
|
33173 | // TODO(benlesh/misko): This implementation is incorrect, because it always adds the LContainer
|
33174 | // to the end of the queue, which means if the developer retrieves the LContainers from RNodes out
|
33175 | // of order, the change detection will run out of order, as the act of retrieving the the
|
33176 | // LContainer from the RNode is what adds it to the queue.
|
33177 | if (lView[CHILD_HEAD]) {
|
33178 | lView[CHILD_TAIL][NEXT] = lViewOrLContainer;
|
33179 | }
|
33180 | else {
|
33181 | lView[CHILD_HEAD] = lViewOrLContainer;
|
33182 | }
|
33183 | lView[CHILD_TAIL] = lViewOrLContainer;
|
33184 | return lViewOrLContainer;
|
33185 | }
|
33186 | ///////////////////////////////
|
33187 | //// Change detection
|
33188 | ///////////////////////////////
|
33189 | /**
|
33190 | * Marks current view and all ancestors dirty.
|
33191 | *
|
33192 | * Returns the root view because it is found as a byproduct of marking the view tree
|
33193 | * dirty, and can be used by methods that consume markViewDirty() to easily schedule
|
33194 | * change detection. Otherwise, such methods would need to traverse up the view tree
|
33195 | * an additional time to get the root view and schedule a tick on it.
|
33196 | *
|
33197 | * @param lView The starting LView to mark dirty
|
33198 | * @returns the root LView
|
33199 | */
|
33200 | function markViewDirty(lView) {
|
33201 | while (lView) {
|
33202 | lView[FLAGS] |= 64 /* Dirty */;
|
33203 | const parent = getLViewParent(lView);
|
33204 | // Stop traversing up as soon as you find a root view that wasn't attached to any container
|
33205 | if (isRootView(lView) && !parent) {
|
33206 | return lView;
|
33207 | }
|
33208 | // continue otherwise
|
33209 | lView = parent;
|
33210 | }
|
33211 | return null;
|
33212 | }
|
33213 | function tickRootContext(rootContext) {
|
33214 | for (let i = 0; i < rootContext.components.length; i++) {
|
33215 | const rootComponent = rootContext.components[i];
|
33216 | const lView = readPatchedLView(rootComponent);
|
33217 | const tView = lView[TVIEW];
|
33218 | renderComponentOrTemplate(tView, lView, tView.template, rootComponent);
|
33219 | }
|
33220 | }
|
33221 | function detectChangesInternal(tView, lView, context) {
|
33222 | const rendererFactory = lView[RENDERER_FACTORY];
|
33223 | if (rendererFactory.begin)
|
33224 | rendererFactory.begin();
|
33225 | try {
|
33226 | refreshView(tView, lView, tView.template, context);
|
33227 | }
|
33228 | catch (error) {
|
33229 | handleError(lView, error);
|
33230 | throw error;
|
33231 | }
|
33232 | finally {
|
33233 | if (rendererFactory.end)
|
33234 | rendererFactory.end();
|
33235 | }
|
33236 | }
|
33237 | /**
|
33238 | * Synchronously perform change detection on a root view and its components.
|
33239 | *
|
33240 | * @param lView The view which the change detection should be performed on.
|
33241 | */
|
33242 | function detectChangesInRootView(lView) {
|
33243 | tickRootContext(lView[CONTEXT]);
|
33244 | }
|
33245 | function checkNoChangesInternal(tView, view, context) {
|
33246 | setIsInCheckNoChangesMode(true);
|
33247 | try {
|
33248 | detectChangesInternal(tView, view, context);
|
33249 | }
|
33250 | finally {
|
33251 | setIsInCheckNoChangesMode(false);
|
33252 | }
|
33253 | }
|
33254 | /**
|
33255 | * Checks the change detector on a root view and its components, and throws if any changes are
|
33256 | * detected.
|
33257 | *
|
33258 | * This is used in development mode to verify that running change detection doesn't
|
33259 | * introduce other changes.
|
33260 | *
|
33261 | * @param lView The view which the change detection should be checked on.
|
33262 | */
|
33263 | function checkNoChangesInRootView(lView) {
|
33264 | setIsInCheckNoChangesMode(true);
|
33265 | try {
|
33266 | detectChangesInRootView(lView);
|
33267 | }
|
33268 | finally {
|
33269 | setIsInCheckNoChangesMode(false);
|
33270 | }
|
33271 | }
|
33272 | function executeViewQueryFn(flags, viewQueryFn, component) {
|
33273 | ngDevMode && assertDefined(viewQueryFn, 'View queries function to execute must be defined.');
|
33274 | setCurrentQueryIndex(0);
|
33275 | viewQueryFn(flags, component);
|
33276 | }
|
33277 | const CLEAN_PROMISE = _CLEAN_PROMISE;
|
33278 | function getOrCreateLViewCleanup(view) {
|
33279 | // top level variables should not be exported for performance reasons (PERF_NOTES.md)
|
33280 | return view[CLEANUP] || (view[CLEANUP] = ngDevMode ? new LCleanup() : []);
|
33281 | }
|
33282 | function getOrCreateTViewCleanup(tView) {
|
33283 | return tView.cleanup || (tView.cleanup = ngDevMode ? new TCleanup() : []);
|
33284 | }
|
33285 | /** Handles an error thrown in an LView. */
|
33286 | function handleError(lView, error) {
|
33287 | const injector = lView[INJECTOR];
|
33288 | const errorHandler = injector ? injector.get(ErrorHandler, null) : null;
|
33289 | errorHandler && errorHandler.handleError(error);
|
33290 | }
|
33291 |
|
33292 | /**
|
33293 | * @license
|
33294 | * Copyright Google LLC All Rights Reserved.
|
33295 | *
|
33296 | * Use of this source code is governed by an MIT-style license that can be
|
33297 | * found in the LICENSE file at https://angular.io/license
|
33298 | */
|
33299 | /**
|
33300 | * Compute the static styling (class/style) from `TAttributes`.
|
33301 | *
|
33302 | * This function should be called during `firstCreatePass` only.
|
33303 | *
|
33304 | * @param tNode The `TNode` into which the styling information should be loaded.
|
33305 | * @param attrs `TAttributes` containing the styling information.
|
33306 | * @param writeToHost Where should the resulting static styles be written?
|
33307 | * - `false` Write to `TNode.stylesWithoutHost` / `TNode.classesWithoutHost`
|
33308 | * - `true` Write to `TNode.styles` / `TNode.classes`
|
33309 | */
|
33310 | function computeStaticStyling(tNode, attrs, writeToHost) {
|
33311 | ngDevMode &&
|
33312 | assertFirstCreatePass(getTView(), 'Expecting to be called in first template pass only');
|
33313 | let styles = writeToHost ? tNode.styles : null;
|
33314 | let classes = writeToHost ? tNode.classes : null;
|
33315 | let mode = 0;
|
33316 | if (attrs !== null) {
|
33317 | for (let i = 0; i < attrs.length; i++) {
|
33318 | const value = attrs[i];
|
33319 | if (typeof value === 'number') {
|
33320 | mode = value;
|
33321 | }
|
33322 | else if (mode == 1 /* Classes */) {
|
33323 | classes = concatStringsWithSpace(classes, value);
|
33324 | }
|
33325 | else if (mode == 2 /* Styles */) {
|
33326 | const style = value;
|
33327 | const styleValue = attrs[++i];
|
33328 | styles = concatStringsWithSpace(styles, style + ': ' + styleValue + ';');
|
33329 | }
|
33330 | }
|
33331 | }
|
33332 | writeToHost ? tNode.styles = styles : tNode.stylesWithoutHost = styles;
|
33333 | writeToHost ? tNode.classes = classes : tNode.classesWithoutHost = classes;
|
33334 | }
|
33335 |
|
33336 | /**
|
33337 | * @license
|
33338 | * Copyright Google LLC All Rights Reserved.
|
33339 | *
|
33340 | * Use of this source code is governed by an MIT-style license that can be
|
33341 | * found in the LICENSE file at https://angular.io/license
|
33342 | */
|
33343 | /**
|
33344 | * An InjectionToken that gets the current `Injector` for `createInjector()`-style injectors.
|
33345 | *
|
33346 | * Requesting this token instead of `Injector` allows `StaticInjector` to be tree-shaken from a
|
33347 | * project.
|
33348 | *
|
33349 | * @publicApi
|
33350 | */
|
33351 | const INJECTOR$1 = new InjectionToken('INJECTOR',
|
33352 | // Dissable tslint because this is const enum which gets inlined not top level prop access.
|
33353 | // tslint:disable-next-line: no-toplevel-property-access
|
33354 | -1 /* Injector */);
|
33355 |
|
33356 | /**
|
33357 | * @license
|
33358 | * Copyright Google LLC All Rights Reserved.
|
33359 | *
|
33360 | * Use of this source code is governed by an MIT-style license that can be
|
33361 | * found in the LICENSE file at https://angular.io/license
|
33362 | */
|
33363 | class NullInjector {
|
33364 | get(token, notFoundValue = THROW_IF_NOT_FOUND) {
|
33365 | if (notFoundValue === THROW_IF_NOT_FOUND) {
|
33366 | const error = new Error(`NullInjectorError: No provider for ${stringify$1(token)}!`);
|
33367 | error.name = 'NullInjectorError';
|
33368 | throw error;
|
33369 | }
|
33370 | return notFoundValue;
|
33371 | }
|
33372 | }
|
33373 |
|
33374 | /**
|
33375 | * @license
|
33376 | * Copyright Google LLC All Rights Reserved.
|
33377 | *
|
33378 | * Use of this source code is governed by an MIT-style license that can be
|
33379 | * found in the LICENSE file at https://angular.io/license
|
33380 | */
|
33381 | /**
|
33382 | * An internal token whose presence in an injector indicates that the injector should treat itself
|
33383 | * as a root scoped injector when processing requests for unknown tokens which may indicate
|
33384 | * they are provided in the root scope.
|
33385 | */
|
33386 | const INJECTOR_SCOPE = new InjectionToken('Set Injector scope.');
|
33387 |
|
33388 | /**
|
33389 | * @license
|
33390 | * Copyright Google LLC All Rights Reserved.
|
33391 | *
|
33392 | * Use of this source code is governed by an MIT-style license that can be
|
33393 | * found in the LICENSE file at https://angular.io/license
|
33394 | */
|
33395 | function INJECTOR_IMPL__PRE_R3__(providers, parent, name) {
|
33396 | return new StaticInjector(providers, parent, name);
|
33397 | }
|
33398 | const INJECTOR_IMPL = INJECTOR_IMPL__PRE_R3__;
|
33399 | /**
|
33400 | * Concrete injectors implement this interface. Injectors are configured
|
33401 | * with [providers](guide/glossary#provider) that associate
|
33402 | * dependencies of various types with [injection tokens](guide/glossary#di-token).
|
33403 | *
|
33404 | * @see ["DI Providers"](guide/dependency-injection-providers).
|
33405 | * @see `StaticProvider`
|
33406 | *
|
33407 | * @usageNotes
|
33408 | *
|
33409 | * The following example creates a service injector instance.
|
33410 | *
|
33411 | * {@example core/di/ts/provider_spec.ts region='ConstructorProvider'}
|
33412 | *
|
33413 | * ### Usage example
|
33414 | *
|
33415 | * {@example core/di/ts/injector_spec.ts region='Injector'}
|
33416 | *
|
33417 | * `Injector` returns itself when given `Injector` as a token:
|
33418 | *
|
33419 | * {@example core/di/ts/injector_spec.ts region='injectInjector'}
|
33420 | *
|
33421 | * @publicApi
|
33422 | */
|
33423 | class Injector {
|
33424 | static create(options, parent) {
|
33425 | if (Array.isArray(options)) {
|
33426 | return INJECTOR_IMPL(options, parent, '');
|
33427 | }
|
33428 | else {
|
33429 | return INJECTOR_IMPL(options.providers, options.parent, options.name || '');
|
33430 | }
|
33431 | }
|
33432 | }
|
33433 | Injector.THROW_IF_NOT_FOUND = THROW_IF_NOT_FOUND;
|
33434 | Injector.NULL = new NullInjector();
|
33435 | /** @nocollapse */
|
33436 | Injector.ɵprov = ɵɵdefineInjectable({
|
33437 | token: Injector,
|
33438 | providedIn: 'any',
|
33439 | factory: () => ɵɵinject(INJECTOR$1),
|
33440 | });
|
33441 | /**
|
33442 | * @internal
|
33443 | * @nocollapse
|
33444 | */
|
33445 | Injector.__NG_ELEMENT_ID__ = -1 /* Injector */;
|
33446 | const IDENT = function (value) {
|
33447 | return value;
|
33448 | };
|
33449 | const EMPTY = [];
|
33450 | const CIRCULAR = IDENT;
|
33451 | const MULTI_PROVIDER_FN = function () {
|
33452 | return Array.prototype.slice.call(arguments);
|
33453 | };
|
33454 | const NO_NEW_LINE$1 = 'ɵ';
|
33455 | class StaticInjector {
|
33456 | constructor(providers, parent = Injector.NULL, source = null) {
|
33457 | this.parent = parent;
|
33458 | this.source = source;
|
33459 | const records = this._records = new Map();
|
33460 | records.set(Injector, { token: Injector, fn: IDENT, deps: EMPTY, value: this, useNew: false });
|
33461 | records.set(INJECTOR$1, { token: INJECTOR$1, fn: IDENT, deps: EMPTY, value: this, useNew: false });
|
33462 | this.scope = recursivelyProcessProviders(records, providers);
|
33463 | }
|
33464 | get(token, notFoundValue, flags = InjectFlags.Default) {
|
33465 | const records = this._records;
|
33466 | let record = records.get(token);
|
33467 | if (record === undefined) {
|
33468 | // This means we have never seen this record, see if it is tree shakable provider.
|
33469 | const injectableDef = getInjectableDef(token);
|
33470 | if (injectableDef) {
|
33471 | const providedIn = injectableDef && injectableDef.providedIn;
|
33472 | if (providedIn === 'any' || providedIn != null && providedIn === this.scope) {
|
33473 | records.set(token, record = resolveProvider({ provide: token, useFactory: injectableDef.factory, deps: EMPTY }));
|
33474 | }
|
33475 | }
|
33476 | if (record === undefined) {
|
33477 | // Set record to null to make sure that we don't go through expensive lookup above again.
|
33478 | records.set(token, null);
|
33479 | }
|
33480 | }
|
33481 | let lastInjector = setCurrentInjector(this);
|
33482 | try {
|
33483 | return tryResolveToken(token, record, records, this.parent, notFoundValue, flags);
|
33484 | }
|
33485 | catch (e) {
|
33486 | return catchInjectorError(e, token, 'StaticInjectorError', this.source);
|
33487 | }
|
33488 | finally {
|
33489 | setCurrentInjector(lastInjector);
|
33490 | }
|
33491 | }
|
33492 | toString() {
|
33493 | const tokens = [], records = this._records;
|
33494 | records.forEach((v, token) => tokens.push(stringify$1(token)));
|
33495 | return `StaticInjector[${tokens.join(', ')}]`;
|
33496 | }
|
33497 | }
|
33498 | function resolveProvider(provider) {
|
33499 | const deps = computeDeps(provider);
|
33500 | let fn = IDENT;
|
33501 | let value = EMPTY;
|
33502 | let useNew = false;
|
33503 | let provide = resolveForwardRef$1(provider.provide);
|
33504 | if (USE_VALUE$2 in provider) {
|
33505 | // We need to use USE_VALUE in provider since provider.useValue could be defined as undefined.
|
33506 | value = provider.useValue;
|
33507 | }
|
33508 | else if (provider.useFactory) {
|
33509 | fn = provider.useFactory;
|
33510 | }
|
33511 | else if (provider.useExisting) ;
|
33512 | else if (provider.useClass) {
|
33513 | useNew = true;
|
33514 | fn = resolveForwardRef$1(provider.useClass);
|
33515 | }
|
33516 | else if (typeof provide == 'function') {
|
33517 | useNew = true;
|
33518 | fn = provide;
|
33519 | }
|
33520 | else {
|
33521 | throw staticError('StaticProvider does not have [useValue|useFactory|useExisting|useClass] or [provide] is not newable', provider);
|
33522 | }
|
33523 | return { deps, fn, useNew, value };
|
33524 | }
|
33525 | function multiProviderMixError(token) {
|
33526 | return staticError('Cannot mix multi providers and regular providers', token);
|
33527 | }
|
33528 | function recursivelyProcessProviders(records, provider) {
|
33529 | let scope = null;
|
33530 | if (provider) {
|
33531 | provider = resolveForwardRef$1(provider);
|
33532 | if (Array.isArray(provider)) {
|
33533 | // if we have an array recurse into the array
|
33534 | for (let i = 0; i < provider.length; i++) {
|
33535 | scope = recursivelyProcessProviders(records, provider[i]) || scope;
|
33536 | }
|
33537 | }
|
33538 | else if (typeof provider === 'function') {
|
33539 | // Functions were supported in ReflectiveInjector, but are not here. For safety give useful
|
33540 | // error messages
|
33541 | throw staticError('Function/Class not supported', provider);
|
33542 | }
|
33543 | else if (provider && typeof provider === 'object' && provider.provide) {
|
33544 | // At this point we have what looks like a provider: {provide: ?, ....}
|
33545 | let token = resolveForwardRef$1(provider.provide);
|
33546 | const resolvedProvider = resolveProvider(provider);
|
33547 | if (provider.multi === true) {
|
33548 | // This is a multi provider.
|
33549 | let multiProvider = records.get(token);
|
33550 | if (multiProvider) {
|
33551 | if (multiProvider.fn !== MULTI_PROVIDER_FN) {
|
33552 | throw multiProviderMixError(token);
|
33553 | }
|
33554 | }
|
33555 | else {
|
33556 | // Create a placeholder factory which will look up the constituents of the multi provider.
|
33557 | records.set(token, multiProvider = {
|
33558 | token: provider.provide,
|
33559 | deps: [],
|
33560 | useNew: false,
|
33561 | fn: MULTI_PROVIDER_FN,
|
33562 | value: EMPTY
|
33563 | });
|
33564 | }
|
33565 | // Treat the provider as the token.
|
33566 | token = provider;
|
33567 | multiProvider.deps.push({ token, options: 6 /* Default */ });
|
33568 | }
|
33569 | const record = records.get(token);
|
33570 | if (record && record.fn == MULTI_PROVIDER_FN) {
|
33571 | throw multiProviderMixError(token);
|
33572 | }
|
33573 | if (token === INJECTOR_SCOPE) {
|
33574 | scope = resolvedProvider.value;
|
33575 | }
|
33576 | records.set(token, resolvedProvider);
|
33577 | }
|
33578 | else {
|
33579 | throw staticError('Unexpected provider', provider);
|
33580 | }
|
33581 | }
|
33582 | return scope;
|
33583 | }
|
33584 | function tryResolveToken(token, record, records, parent, notFoundValue, flags) {
|
33585 | try {
|
33586 | return resolveToken(token, record, records, parent, notFoundValue, flags);
|
33587 | }
|
33588 | catch (e) {
|
33589 | // ensure that 'e' is of type Error.
|
33590 | if (!(e instanceof Error)) {
|
33591 | e = new Error(e);
|
33592 | }
|
33593 | const path = e[NG_TEMP_TOKEN_PATH] = e[NG_TEMP_TOKEN_PATH] || [];
|
33594 | path.unshift(token);
|
33595 | if (record && record.value == CIRCULAR) {
|
33596 | // Reset the Circular flag.
|
33597 | record.value = EMPTY;
|
33598 | }
|
33599 | throw e;
|
33600 | }
|
33601 | }
|
33602 | function resolveToken(token, record, records, parent, notFoundValue, flags) {
|
33603 | let value;
|
33604 | if (record && !(flags & InjectFlags.SkipSelf)) {
|
33605 | // If we don't have a record, this implies that we don't own the provider hence don't know how
|
33606 | // to resolve it.
|
33607 | value = record.value;
|
33608 | if (value == CIRCULAR) {
|
33609 | throw Error(NO_NEW_LINE$1 + 'Circular dependency');
|
33610 | }
|
33611 | else if (value === EMPTY) {
|
33612 | record.value = CIRCULAR;
|
33613 | let obj = undefined;
|
33614 | let useNew = record.useNew;
|
33615 | let fn = record.fn;
|
33616 | let depRecords = record.deps;
|
33617 | let deps = EMPTY;
|
33618 | if (depRecords.length) {
|
33619 | deps = [];
|
33620 | for (let i = 0; i < depRecords.length; i++) {
|
33621 | const depRecord = depRecords[i];
|
33622 | const options = depRecord.options;
|
33623 | const childRecord = options & 2 /* CheckSelf */ ? records.get(depRecord.token) : undefined;
|
33624 | deps.push(tryResolveToken(
|
33625 | // Current Token to resolve
|
33626 | depRecord.token,
|
33627 | // A record which describes how to resolve the token.
|
33628 | // If undefined, this means we don't have such a record
|
33629 | childRecord,
|
33630 | // Other records we know about.
|
33631 | records,
|
33632 | // If we don't know how to resolve dependency and we should not check parent for it,
|
33633 | // than pass in Null injector.
|
33634 | !childRecord && !(options & 4 /* CheckParent */) ? Injector.NULL : parent, options & 1 /* Optional */ ? null : Injector.THROW_IF_NOT_FOUND, InjectFlags.Default));
|
33635 | }
|
33636 | }
|
33637 | record.value = value = useNew ? new fn(...deps) : fn.apply(obj, deps);
|
33638 | }
|
33639 | }
|
33640 | else if (!(flags & InjectFlags.Self)) {
|
33641 | value = parent.get(token, notFoundValue, InjectFlags.Default);
|
33642 | }
|
33643 | else if (!(flags & InjectFlags.Optional)) {
|
33644 | value = Injector.NULL.get(token, notFoundValue);
|
33645 | }
|
33646 | else {
|
33647 | value = Injector.NULL.get(token, typeof notFoundValue !== 'undefined' ? notFoundValue : null);
|
33648 | }
|
33649 | return value;
|
33650 | }
|
33651 | function computeDeps(provider) {
|
33652 | let deps = EMPTY;
|
33653 | const providerDeps = provider.deps;
|
33654 | if (providerDeps && providerDeps.length) {
|
33655 | deps = [];
|
33656 | for (let i = 0; i < providerDeps.length; i++) {
|
33657 | let options = 6 /* Default */;
|
33658 | let token = resolveForwardRef$1(providerDeps[i]);
|
33659 | if (Array.isArray(token)) {
|
33660 | for (let j = 0, annotations = token; j < annotations.length; j++) {
|
33661 | const annotation = annotations[j];
|
33662 | if (annotation instanceof Optional || annotation == Optional) {
|
33663 | options = options | 1 /* Optional */;
|
33664 | }
|
33665 | else if (annotation instanceof SkipSelf || annotation == SkipSelf) {
|
33666 | options = options & ~2 /* CheckSelf */;
|
33667 | }
|
33668 | else if (annotation instanceof Self || annotation == Self) {
|
33669 | options = options & ~4 /* CheckParent */;
|
33670 | }
|
33671 | else if (annotation instanceof Inject) {
|
33672 | token = annotation.token;
|
33673 | }
|
33674 | else {
|
33675 | token = resolveForwardRef$1(annotation);
|
33676 | }
|
33677 | }
|
33678 | }
|
33679 | deps.push({ token, options });
|
33680 | }
|
33681 | }
|
33682 | else if (provider.useExisting) {
|
33683 | const token = resolveForwardRef$1(provider.useExisting);
|
33684 | deps = [{ token, options: 6 /* Default */ }];
|
33685 | }
|
33686 | else if (!providerDeps && !(USE_VALUE$2 in provider)) {
|
33687 | // useValue & useExisting are the only ones which are exempt from deps all others need it.
|
33688 | throw staticError('\'deps\' required', provider);
|
33689 | }
|
33690 | return deps;
|
33691 | }
|
33692 | function staticError(text, obj) {
|
33693 | return new Error(formatError(text, obj, 'StaticInjectorError'));
|
33694 | }
|
33695 |
|
33696 | /**
|
33697 | * @license
|
33698 | * Copyright Google LLC All Rights Reserved.
|
33699 | *
|
33700 | * Use of this source code is governed by an MIT-style license that can be
|
33701 | * found in the LICENSE file at https://angular.io/license
|
33702 | */
|
33703 | /**
|
33704 | * Creates the root component view and the root component node.
|
33705 | *
|
33706 | * @param rNode Render host element.
|
33707 | * @param def ComponentDef
|
33708 | * @param rootView The parent view where the host node is stored
|
33709 | * @param rendererFactory Factory to be used for creating child renderers.
|
33710 | * @param hostRenderer The current renderer
|
33711 | * @param sanitizer The sanitizer, if provided
|
33712 | *
|
33713 | * @returns Component view created
|
33714 | */
|
33715 | function createRootComponentView(rNode, def, rootView, rendererFactory, hostRenderer, sanitizer) {
|
33716 | const tView = rootView[TVIEW];
|
33717 | const index = HEADER_OFFSET;
|
33718 | ngDevMode && assertIndexInRange(rootView, index);
|
33719 | rootView[index] = rNode;
|
33720 | // '#host' is added here as we don't know the real host DOM name (we don't want to read it) and at
|
33721 | // the same time we want to communicate the debug `TNode` that this is a special `TNode`
|
33722 | // representing a host element.
|
33723 | const tNode = getOrCreateTNode(tView, index, 2 /* Element */, '#host', null);
|
33724 | const mergedAttrs = tNode.mergedAttrs = def.hostAttrs;
|
33725 | if (mergedAttrs !== null) {
|
33726 | computeStaticStyling(tNode, mergedAttrs, true);
|
33727 | if (rNode !== null) {
|
33728 | setUpAttributes(hostRenderer, rNode, mergedAttrs);
|
33729 | if (tNode.classes !== null) {
|
33730 | writeDirectClass(hostRenderer, rNode, tNode.classes);
|
33731 | }
|
33732 | if (tNode.styles !== null) {
|
33733 | writeDirectStyle(hostRenderer, rNode, tNode.styles);
|
33734 | }
|
33735 | }
|
33736 | }
|
33737 | const viewRenderer = rendererFactory.createRenderer(rNode, def);
|
33738 | const componentView = createLView(rootView, getOrCreateTComponentView(def), null, def.onPush ? 64 /* Dirty */ : 16 /* CheckAlways */, rootView[index], tNode, rendererFactory, viewRenderer, sanitizer || null, null);
|
33739 | if (tView.firstCreatePass) {
|
33740 | diPublicInInjector(getOrCreateNodeInjectorForNode(tNode, rootView), tView, def.type);
|
33741 | markAsComponentHost(tView, tNode);
|
33742 | initTNodeFlags(tNode, rootView.length, 1);
|
33743 | }
|
33744 | addToViewTree(rootView, componentView);
|
33745 | // Store component view at node index, with node as the HOST
|
33746 | return rootView[index] = componentView;
|
33747 | }
|
33748 | /**
|
33749 | * Creates a root component and sets it up with features and host bindings. Shared by
|
33750 | * renderComponent() and ViewContainerRef.createComponent().
|
33751 | */
|
33752 | function createRootComponent(componentView, componentDef, rootLView, rootContext, hostFeatures) {
|
33753 | const tView = rootLView[TVIEW];
|
33754 | // Create directive instance with factory() and store at next index in viewData
|
33755 | const component = instantiateRootComponent(tView, rootLView, componentDef);
|
33756 | rootContext.components.push(component);
|
33757 | componentView[CONTEXT] = component;
|
33758 | hostFeatures && hostFeatures.forEach((feature) => feature(component, componentDef));
|
33759 | // We want to generate an empty QueryList for root content queries for backwards
|
33760 | // compatibility with ViewEngine.
|
33761 | if (componentDef.contentQueries) {
|
33762 | const tNode = getCurrentTNode();
|
33763 | ngDevMode && assertDefined(tNode, 'TNode expected');
|
33764 | componentDef.contentQueries(1 /* Create */, component, tNode.directiveStart);
|
33765 | }
|
33766 | const rootTNode = getCurrentTNode();
|
33767 | ngDevMode && assertDefined(rootTNode, 'tNode should have been already created');
|
33768 | if (tView.firstCreatePass &&
|
33769 | (componentDef.hostBindings !== null || componentDef.hostAttrs !== null)) {
|
33770 | setSelectedIndex(rootTNode.index);
|
33771 | const rootTView = rootLView[TVIEW];
|
33772 | registerHostBindingOpCodes(rootTView, rootTNode, rootLView, rootTNode.directiveStart, rootTNode.directiveEnd, componentDef);
|
33773 | invokeHostBindingsInCreationMode(componentDef, component);
|
33774 | }
|
33775 | return component;
|
33776 | }
|
33777 | function createRootContext(scheduler, playerHandler) {
|
33778 | return {
|
33779 | components: [],
|
33780 | scheduler: scheduler || defaultScheduler,
|
33781 | clean: CLEAN_PROMISE,
|
33782 | playerHandler: playerHandler || null,
|
33783 | flags: 0 /* Empty */
|
33784 | };
|
33785 | }
|
33786 | /**
|
33787 | * Used to enable lifecycle hooks on the root component.
|
33788 | *
|
33789 | * Include this feature when calling `renderComponent` if the root component
|
33790 | * you are rendering has lifecycle hooks defined. Otherwise, the hooks won't
|
33791 | * be called properly.
|
33792 | *
|
33793 | * Example:
|
33794 | *
|
33795 | * ```
|
33796 | * renderComponent(AppComponent, {hostFeatures: [LifecycleHooksFeature]});
|
33797 | * ```
|
33798 | */
|
33799 | function LifecycleHooksFeature(component, def) {
|
33800 | const lView = readPatchedLView(component);
|
33801 | ngDevMode && assertDefined(lView, 'LView is required');
|
33802 | const tView = lView[TVIEW];
|
33803 | const tNode = getCurrentTNode();
|
33804 | ngDevMode && assertDefined(tNode, 'TNode is required');
|
33805 | registerPostOrderHooks(tView, tNode);
|
33806 | }
|
33807 |
|
33808 | /**
|
33809 | * @license
|
33810 | * Copyright Google LLC All Rights Reserved.
|
33811 | *
|
33812 | * Use of this source code is governed by an MIT-style license that can be
|
33813 | * found in the LICENSE file at https://angular.io/license
|
33814 | */
|
33815 | let _symbolIterator = null;
|
33816 | function getSymbolIterator() {
|
33817 | if (!_symbolIterator) {
|
33818 | const Symbol = _global$1['Symbol'];
|
33819 | if (Symbol && Symbol.iterator) {
|
33820 | _symbolIterator = Symbol.iterator;
|
33821 | }
|
33822 | else {
|
33823 | // es6-shim specific logic
|
33824 | const keys = Object.getOwnPropertyNames(Map.prototype);
|
33825 | for (let i = 0; i < keys.length; ++i) {
|
33826 | const key = keys[i];
|
33827 | if (key !== 'entries' && key !== 'size' &&
|
33828 | Map.prototype[key] === Map.prototype['entries']) {
|
33829 | _symbolIterator = key;
|
33830 | }
|
33831 | }
|
33832 | }
|
33833 | }
|
33834 | return _symbolIterator;
|
33835 | }
|
33836 |
|
33837 | /**
|
33838 | * @license
|
33839 | * Copyright Google LLC All Rights Reserved.
|
33840 | *
|
33841 | * Use of this source code is governed by an MIT-style license that can be
|
33842 | * found in the LICENSE file at https://angular.io/license
|
33843 | */
|
33844 | function isListLikeIterable(obj) {
|
33845 | if (!isJsObject(obj))
|
33846 | return false;
|
33847 | return Array.isArray(obj) ||
|
33848 | (!(obj instanceof Map) && // JS Map are iterables but return entries as [k, v]
|
33849 | getSymbolIterator() in obj); // JS Iterable have a Symbol.iterator prop
|
33850 | }
|
33851 | function iterateListLike(obj, fn) {
|
33852 | if (Array.isArray(obj)) {
|
33853 | for (let i = 0; i < obj.length; i++) {
|
33854 | fn(obj[i]);
|
33855 | }
|
33856 | }
|
33857 | else {
|
33858 | const iterator = obj[getSymbolIterator()]();
|
33859 | let item;
|
33860 | while (!((item = iterator.next()).done)) {
|
33861 | fn(item.value);
|
33862 | }
|
33863 | }
|
33864 | }
|
33865 | function isJsObject(o) {
|
33866 | return o !== null && (typeof o === 'function' || typeof o === 'object');
|
33867 | }
|
33868 |
|
33869 | /**
|
33870 | * @license
|
33871 | * Copyright Google LLC All Rights Reserved.
|
33872 | *
|
33873 | * Use of this source code is governed by an MIT-style license that can be
|
33874 | * found in the LICENSE file at https://angular.io/license
|
33875 | */
|
33876 | const ɵ0$6 = getClosureSafeProperty;
|
33877 | const USE_VALUE$3 = getClosureSafeProperty({ provide: String, useValue: ɵ0$6 });
|
33878 |
|
33879 | /**
|
33880 | * @license
|
33881 | * Copyright Google LLC All Rights Reserved.
|
33882 | *
|
33883 | * Use of this source code is governed by an MIT-style license that can be
|
33884 | * found in the LICENSE file at https://angular.io/license
|
33885 | */
|
33886 | const ɵ0$7 = getClosureSafeProperty;
|
33887 | const USE_VALUE$4 = getClosureSafeProperty({ provide: String, useValue: ɵ0$7 });
|
33888 | const EMPTY_ARRAY$1 = [];
|
33889 | function convertInjectableProviderToFactory(type, provider) {
|
33890 | if (!provider) {
|
33891 | const reflectionCapabilities = new ReflectionCapabilities();
|
33892 | const deps = reflectionCapabilities.parameters(type);
|
33893 | // TODO - convert to flags.
|
33894 | return () => new type(...injectArgs(deps));
|
33895 | }
|
33896 | if (USE_VALUE$4 in provider) {
|
33897 | const valueProvider = provider;
|
33898 | return () => valueProvider.useValue;
|
33899 | }
|
33900 | else if (provider.useExisting) {
|
33901 | const existingProvider = provider;
|
33902 | return () => ɵɵinject(resolveForwardRef$1(existingProvider.useExisting));
|
33903 | }
|
33904 | else if (provider.useFactory) {
|
33905 | const factoryProvider = provider;
|
33906 | return () => factoryProvider.useFactory(...injectArgs(factoryProvider.deps || EMPTY_ARRAY$1));
|
33907 | }
|
33908 | else if (provider.useClass) {
|
33909 | const classProvider = provider;
|
33910 | let deps = provider.deps;
|
33911 | if (!deps) {
|
33912 | const reflectionCapabilities = new ReflectionCapabilities();
|
33913 | deps = reflectionCapabilities.parameters(type);
|
33914 | }
|
33915 | return () => new (resolveForwardRef$1(classProvider.useClass))(...injectArgs(deps));
|
33916 | }
|
33917 | else {
|
33918 | let deps = provider.deps;
|
33919 | if (!deps) {
|
33920 | const reflectionCapabilities = new ReflectionCapabilities();
|
33921 | deps = reflectionCapabilities.parameters(type);
|
33922 | }
|
33923 | return () => new type(...injectArgs(deps));
|
33924 | }
|
33925 | }
|
33926 |
|
33927 | /**
|
33928 | * @license
|
33929 | * Copyright Google LLC All Rights Reserved.
|
33930 | *
|
33931 | * Use of this source code is governed by an MIT-style license that can be
|
33932 | * found in the LICENSE file at https://angular.io/license
|
33933 | */
|
33934 | const ɵ0$8 = (type, meta) => SWITCH_COMPILE_INJECTABLE(type, meta);
|
33935 | /**
|
33936 | * Injectable decorator and metadata.
|
33937 | *
|
33938 | * @Annotation
|
33939 | * @publicApi
|
33940 | */
|
33941 | const Injectable = makeDecorator('Injectable', undefined, undefined, undefined, ɵ0$8);
|
33942 | /**
|
33943 | * Supports @Injectable() in JIT mode for Render2.
|
33944 | */
|
33945 | function render2CompileInjectable(injectableType, options) {
|
33946 | if (options && options.providedIn !== undefined && !getInjectableDef(injectableType)) {
|
33947 | injectableType.ɵprov = ɵɵdefineInjectable({
|
33948 | token: injectableType,
|
33949 | providedIn: options.providedIn,
|
33950 | factory: convertInjectableProviderToFactory(injectableType, options),
|
33951 | });
|
33952 | }
|
33953 | }
|
33954 | const SWITCH_COMPILE_INJECTABLE__PRE_R3__ = render2CompileInjectable;
|
33955 | const SWITCH_COMPILE_INJECTABLE = SWITCH_COMPILE_INJECTABLE__PRE_R3__;
|
33956 |
|
33957 | /**
|
33958 | * @license
|
33959 | * Copyright Google LLC All Rights Reserved.
|
33960 | *
|
33961 | * Use of this source code is governed by an MIT-style license that can be
|
33962 | * found in the LICENSE file at https://angular.io/license
|
33963 | */
|
33964 | function findFirstClosedCycle(keys) {
|
33965 | const res = [];
|
33966 | for (let i = 0; i < keys.length; ++i) {
|
33967 | if (res.indexOf(keys[i]) > -1) {
|
33968 | res.push(keys[i]);
|
33969 | return res;
|
33970 | }
|
33971 | res.push(keys[i]);
|
33972 | }
|
33973 | return res;
|
33974 | }
|
33975 | function constructResolvingPath(keys) {
|
33976 | if (keys.length > 1) {
|
33977 | const reversed = findFirstClosedCycle(keys.slice().reverse());
|
33978 | const tokenStrs = reversed.map(k => stringify$1(k.token));
|
33979 | return ' (' + tokenStrs.join(' -> ') + ')';
|
33980 | }
|
33981 | return '';
|
33982 | }
|
33983 | function injectionError(injector, key, constructResolvingMessage, originalError) {
|
33984 | const keys = [key];
|
33985 | const errMsg = constructResolvingMessage(keys);
|
33986 | const error = (originalError ? wrappedError(errMsg, originalError) : Error(errMsg));
|
33987 | error.addKey = addKey;
|
33988 | error.keys = keys;
|
33989 | error.injectors = [injector];
|
33990 | error.constructResolvingMessage = constructResolvingMessage;
|
33991 | error[ERROR_ORIGINAL_ERROR] = originalError;
|
33992 | return error;
|
33993 | }
|
33994 | function addKey(injector, key) {
|
33995 | this.injectors.push(injector);
|
33996 | this.keys.push(key);
|
33997 | // Note: This updated message won't be reflected in the `.stack` property
|
33998 | this.message = this.constructResolvingMessage(this.keys);
|
33999 | }
|
34000 | /**
|
34001 | * Thrown when trying to retrieve a dependency by key from {@link Injector}, but the
|
34002 | * {@link Injector} does not have a {@link Provider} for the given key.
|
34003 | *
|
34004 | * @usageNotes
|
34005 | * ### Example
|
34006 | *
|
34007 | * ```typescript
|
34008 | * class A {
|
34009 | * constructor(b:B) {}
|
34010 | * }
|
34011 | *
|
34012 | * expect(() => Injector.resolveAndCreate([A])).toThrowError();
|
34013 | * ```
|
34014 | */
|
34015 | function noProviderError(injector, key) {
|
34016 | return injectionError(injector, key, function (keys) {
|
34017 | const first = stringify$1(keys[0].token);
|
34018 | return `No provider for ${first}!${constructResolvingPath(keys)}`;
|
34019 | });
|
34020 | }
|
34021 | /**
|
34022 | * Thrown when dependencies form a cycle.
|
34023 | *
|
34024 | * @usageNotes
|
34025 | * ### Example
|
34026 | *
|
34027 | * ```typescript
|
34028 | * var injector = Injector.resolveAndCreate([
|
34029 | * {provide: "one", useFactory: (two) => "two", deps: [[new Inject("two")]]},
|
34030 | * {provide: "two", useFactory: (one) => "one", deps: [[new Inject("one")]]}
|
34031 | * ]);
|
34032 | *
|
34033 | * expect(() => injector.get("one")).toThrowError();
|
34034 | * ```
|
34035 | *
|
34036 | * Retrieving `A` or `B` throws a `CyclicDependencyError` as the graph above cannot be constructed.
|
34037 | */
|
34038 | function cyclicDependencyError(injector, key) {
|
34039 | return injectionError(injector, key, function (keys) {
|
34040 | return `Cannot instantiate cyclic dependency!${constructResolvingPath(keys)}`;
|
34041 | });
|
34042 | }
|
34043 | /**
|
34044 | * Thrown when a constructing type returns with an Error.
|
34045 | *
|
34046 | * The `InstantiationError` class contains the original error plus the dependency graph which caused
|
34047 | * this object to be instantiated.
|
34048 | *
|
34049 | * @usageNotes
|
34050 | * ### Example
|
34051 | *
|
34052 | * ```typescript
|
34053 | * class A {
|
34054 | * constructor() {
|
34055 | * throw new Error('message');
|
34056 | * }
|
34057 | * }
|
34058 | *
|
34059 | * var injector = Injector.resolveAndCreate([A]);
|
34060 |
|
34061 | * try {
|
34062 | * injector.get(A);
|
34063 | * } catch (e) {
|
34064 | * expect(e instanceof InstantiationError).toBe(true);
|
34065 | * expect(e.originalException.message).toEqual("message");
|
34066 | * expect(e.originalStack).toBeDefined();
|
34067 | * }
|
34068 | * ```
|
34069 | */
|
34070 | function instantiationError(injector, originalException, originalStack, key) {
|
34071 | return injectionError(injector, key, function (keys) {
|
34072 | const first = stringify$1(keys[0].token);
|
34073 | return `${originalException.message}: Error during instantiation of ${first}!${constructResolvingPath(keys)}.`;
|
34074 | }, originalException);
|
34075 | }
|
34076 | /**
|
34077 | * Thrown when an object other then {@link Provider} (or `Type`) is passed to {@link Injector}
|
34078 | * creation.
|
34079 | *
|
34080 | * @usageNotes
|
34081 | * ### Example
|
34082 | *
|
34083 | * ```typescript
|
34084 | * expect(() => Injector.resolveAndCreate(["not a type"])).toThrowError();
|
34085 | * ```
|
34086 | */
|
34087 | function invalidProviderError(provider) {
|
34088 | return Error(`Invalid provider - only instances of Provider and Type are allowed, got: ${provider}`);
|
34089 | }
|
34090 | /**
|
34091 | * Thrown when the class has no annotation information.
|
34092 | *
|
34093 | * Lack of annotation information prevents the {@link Injector} from determining which dependencies
|
34094 | * need to be injected into the constructor.
|
34095 | *
|
34096 | * @usageNotes
|
34097 | * ### Example
|
34098 | *
|
34099 | * ```typescript
|
34100 | * class A {
|
34101 | * constructor(b) {}
|
34102 | * }
|
34103 | *
|
34104 | * expect(() => Injector.resolveAndCreate([A])).toThrowError();
|
34105 | * ```
|
34106 | *
|
34107 | * This error is also thrown when the class not marked with {@link Injectable} has parameter types.
|
34108 | *
|
34109 | * ```typescript
|
34110 | * class B {}
|
34111 | *
|
34112 | * class A {
|
34113 | * constructor(b:B) {} // no information about the parameter types of A is available at runtime.
|
34114 | * }
|
34115 | *
|
34116 | * expect(() => Injector.resolveAndCreate([A,B])).toThrowError();
|
34117 | * ```
|
34118 | *
|
34119 | */
|
34120 | function noAnnotationError(typeOrFunc, params) {
|
34121 | const signature = [];
|
34122 | for (let i = 0, ii = params.length; i < ii; i++) {
|
34123 | const parameter = params[i];
|
34124 | if (!parameter || parameter.length == 0) {
|
34125 | signature.push('?');
|
34126 | }
|
34127 | else {
|
34128 | signature.push(parameter.map(stringify$1).join(' '));
|
34129 | }
|
34130 | }
|
34131 | return Error('Cannot resolve all parameters for \'' + stringify$1(typeOrFunc) + '\'(' +
|
34132 | signature.join(', ') + '). ' +
|
34133 | 'Make sure that all the parameters are decorated with Inject or have valid type annotations and that \'' +
|
34134 | stringify$1(typeOrFunc) + '\' is decorated with Injectable.');
|
34135 | }
|
34136 | /**
|
34137 | * Thrown when getting an object by index.
|
34138 | *
|
34139 | * @usageNotes
|
34140 | * ### Example
|
34141 | *
|
34142 | * ```typescript
|
34143 | * class A {}
|
34144 | *
|
34145 | * var injector = Injector.resolveAndCreate([A]);
|
34146 | *
|
34147 | * expect(() => injector.getAt(100)).toThrowError();
|
34148 | * ```
|
34149 | *
|
34150 | */
|
34151 | function outOfBoundsError(index) {
|
34152 | return Error(`Index ${index} is out-of-bounds.`);
|
34153 | }
|
34154 | // TODO: add a working example after alpha38 is released
|
34155 | /**
|
34156 | * Thrown when a multi provider and a regular provider are bound to the same token.
|
34157 | *
|
34158 | * @usageNotes
|
34159 | * ### Example
|
34160 | *
|
34161 | * ```typescript
|
34162 | * expect(() => Injector.resolveAndCreate([
|
34163 | * { provide: "Strings", useValue: "string1", multi: true},
|
34164 | * { provide: "Strings", useValue: "string2", multi: false}
|
34165 | * ])).toThrowError();
|
34166 | * ```
|
34167 | */
|
34168 | function mixingMultiProvidersWithRegularProvidersError(provider1, provider2) {
|
34169 | return Error(`Cannot mix multi providers and regular providers, got: ${provider1} ${provider2}`);
|
34170 | }
|
34171 |
|
34172 | /**
|
34173 | * @license
|
34174 | * Copyright Google LLC All Rights Reserved.
|
34175 | *
|
34176 | * Use of this source code is governed by an MIT-style license that can be
|
34177 | * found in the LICENSE file at https://angular.io/license
|
34178 | */
|
34179 | /**
|
34180 | * A unique object used for retrieving items from the {@link ReflectiveInjector}.
|
34181 | *
|
34182 | * Keys have:
|
34183 | * - a system-wide unique `id`.
|
34184 | * - a `token`.
|
34185 | *
|
34186 | * `Key` is used internally by {@link ReflectiveInjector} because its system-wide unique `id` allows
|
34187 | * the
|
34188 | * injector to store created objects in a more efficient way.
|
34189 | *
|
34190 | * `Key` should not be created directly. {@link ReflectiveInjector} creates keys automatically when
|
34191 | * resolving
|
34192 | * providers.
|
34193 | *
|
34194 | * @deprecated No replacement
|
34195 | * @publicApi
|
34196 | */
|
34197 | class ReflectiveKey {
|
34198 | /**
|
34199 | * Private
|
34200 | */
|
34201 | constructor(token, id) {
|
34202 | this.token = token;
|
34203 | this.id = id;
|
34204 | if (!token) {
|
34205 | throw new Error('Token must be defined!');
|
34206 | }
|
34207 | this.displayName = stringify$1(this.token);
|
34208 | }
|
34209 | /**
|
34210 | * Retrieves a `Key` for a token.
|
34211 | */
|
34212 | static get(token) {
|
34213 | return _globalKeyRegistry.get(resolveForwardRef$1(token));
|
34214 | }
|
34215 | /**
|
34216 | * @returns the number of keys registered in the system.
|
34217 | */
|
34218 | static get numberOfKeys() {
|
34219 | return _globalKeyRegistry.numberOfKeys;
|
34220 | }
|
34221 | }
|
34222 | class KeyRegistry {
|
34223 | constructor() {
|
34224 | this._allKeys = new Map();
|
34225 | }
|
34226 | get(token) {
|
34227 | if (token instanceof ReflectiveKey)
|
34228 | return token;
|
34229 | if (this._allKeys.has(token)) {
|
34230 | return this._allKeys.get(token);
|
34231 | }
|
34232 | const newKey = new ReflectiveKey(token, ReflectiveKey.numberOfKeys);
|
34233 | this._allKeys.set(token, newKey);
|
34234 | return newKey;
|
34235 | }
|
34236 | get numberOfKeys() {
|
34237 | return this._allKeys.size;
|
34238 | }
|
34239 | }
|
34240 | const _globalKeyRegistry = new KeyRegistry();
|
34241 |
|
34242 | /**
|
34243 | * @license
|
34244 | * Copyright Google LLC All Rights Reserved.
|
34245 | *
|
34246 | * Use of this source code is governed by an MIT-style license that can be
|
34247 | * found in the LICENSE file at https://angular.io/license
|
34248 | */
|
34249 | /**
|
34250 | * Provides access to reflection data about symbols. Used internally by Angular
|
34251 | * to power dependency injection and compilation.
|
34252 | */
|
34253 | class Reflector {
|
34254 | constructor(reflectionCapabilities) {
|
34255 | this.reflectionCapabilities = reflectionCapabilities;
|
34256 | }
|
34257 | updateCapabilities(caps) {
|
34258 | this.reflectionCapabilities = caps;
|
34259 | }
|
34260 | factory(type) {
|
34261 | return this.reflectionCapabilities.factory(type);
|
34262 | }
|
34263 | parameters(typeOrFunc) {
|
34264 | return this.reflectionCapabilities.parameters(typeOrFunc);
|
34265 | }
|
34266 | annotations(typeOrFunc) {
|
34267 | return this.reflectionCapabilities.annotations(typeOrFunc);
|
34268 | }
|
34269 | propMetadata(typeOrFunc) {
|
34270 | return this.reflectionCapabilities.propMetadata(typeOrFunc);
|
34271 | }
|
34272 | hasLifecycleHook(type, lcProperty) {
|
34273 | return this.reflectionCapabilities.hasLifecycleHook(type, lcProperty);
|
34274 | }
|
34275 | getter(name) {
|
34276 | return this.reflectionCapabilities.getter(name);
|
34277 | }
|
34278 | setter(name) {
|
34279 | return this.reflectionCapabilities.setter(name);
|
34280 | }
|
34281 | method(name) {
|
34282 | return this.reflectionCapabilities.method(name);
|
34283 | }
|
34284 | importUri(type) {
|
34285 | return this.reflectionCapabilities.importUri(type);
|
34286 | }
|
34287 | resourceUri(type) {
|
34288 | return this.reflectionCapabilities.resourceUri(type);
|
34289 | }
|
34290 | resolveIdentifier(name, moduleUrl, members, runtime) {
|
34291 | return this.reflectionCapabilities.resolveIdentifier(name, moduleUrl, members, runtime);
|
34292 | }
|
34293 | resolveEnum(identifier, name) {
|
34294 | return this.reflectionCapabilities.resolveEnum(identifier, name);
|
34295 | }
|
34296 | }
|
34297 |
|
34298 | /**
|
34299 | * @license
|
34300 | * Copyright Google LLC All Rights Reserved.
|
34301 | *
|
34302 | * Use of this source code is governed by an MIT-style license that can be
|
34303 | * found in the LICENSE file at https://angular.io/license
|
34304 | */
|
34305 | /**
|
34306 | * The {@link Reflector} used internally in Angular to access metadata
|
34307 | * about symbols.
|
34308 | */
|
34309 | const reflector = new Reflector(new ReflectionCapabilities());
|
34310 |
|
34311 | /**
|
34312 | * @license
|
34313 | * Copyright Google LLC All Rights Reserved.
|
34314 | *
|
34315 | * Use of this source code is governed by an MIT-style license that can be
|
34316 | * found in the LICENSE file at https://angular.io/license
|
34317 | */
|
34318 | /**
|
34319 | * `Dependency` is used by the framework to extend DI.
|
34320 | * This is internal to Angular and should not be used directly.
|
34321 | */
|
34322 | class ReflectiveDependency {
|
34323 | constructor(key, optional, visibility) {
|
34324 | this.key = key;
|
34325 | this.optional = optional;
|
34326 | this.visibility = visibility;
|
34327 | }
|
34328 | static fromKey(key) {
|
34329 | return new ReflectiveDependency(key, false, null);
|
34330 | }
|
34331 | }
|
34332 | const _EMPTY_LIST = [];
|
34333 | class ResolvedReflectiveProvider_ {
|
34334 | constructor(key, resolvedFactories, multiProvider) {
|
34335 | this.key = key;
|
34336 | this.resolvedFactories = resolvedFactories;
|
34337 | this.multiProvider = multiProvider;
|
34338 | this.resolvedFactory = this.resolvedFactories[0];
|
34339 | }
|
34340 | }
|
34341 | /**
|
34342 | * An internal resolved representation of a factory function created by resolving `Provider`.
|
34343 | * @publicApi
|
34344 | */
|
34345 | class ResolvedReflectiveFactory {
|
34346 | constructor(
|
34347 | /**
|
34348 | * Factory function which can return an instance of an object represented by a key.
|
34349 | */
|
34350 | factory,
|
34351 | /**
|
34352 | * Arguments (dependencies) to the `factory` function.
|
34353 | */
|
34354 | dependencies) {
|
34355 | this.factory = factory;
|
34356 | this.dependencies = dependencies;
|
34357 | }
|
34358 | }
|
34359 | /**
|
34360 | * Resolve a single provider.
|
34361 | */
|
34362 | function resolveReflectiveFactory(provider) {
|
34363 | let factoryFn;
|
34364 | let resolvedDeps;
|
34365 | if (provider.useClass) {
|
34366 | const useClass = resolveForwardRef$1(provider.useClass);
|
34367 | factoryFn = reflector.factory(useClass);
|
34368 | resolvedDeps = _dependenciesFor(useClass);
|
34369 | }
|
34370 | else if (provider.useExisting) {
|
34371 | factoryFn = (aliasInstance) => aliasInstance;
|
34372 | resolvedDeps = [ReflectiveDependency.fromKey(ReflectiveKey.get(provider.useExisting))];
|
34373 | }
|
34374 | else if (provider.useFactory) {
|
34375 | factoryFn = provider.useFactory;
|
34376 | resolvedDeps = constructDependencies(provider.useFactory, provider.deps);
|
34377 | }
|
34378 | else {
|
34379 | factoryFn = () => provider.useValue;
|
34380 | resolvedDeps = _EMPTY_LIST;
|
34381 | }
|
34382 | return new ResolvedReflectiveFactory(factoryFn, resolvedDeps);
|
34383 | }
|
34384 | /**
|
34385 | * Converts the `Provider` into `ResolvedProvider`.
|
34386 | *
|
34387 | * `Injector` internally only uses `ResolvedProvider`, `Provider` contains convenience provider
|
34388 | * syntax.
|
34389 | */
|
34390 | function resolveReflectiveProvider(provider) {
|
34391 | return new ResolvedReflectiveProvider_(ReflectiveKey.get(provider.provide), [resolveReflectiveFactory(provider)], provider.multi || false);
|
34392 | }
|
34393 | /**
|
34394 | * Resolve a list of Providers.
|
34395 | */
|
34396 | function resolveReflectiveProviders(providers) {
|
34397 | const normalized = _normalizeProviders(providers, []);
|
34398 | const resolved = normalized.map(resolveReflectiveProvider);
|
34399 | const resolvedProviderMap = mergeResolvedReflectiveProviders(resolved, new Map());
|
34400 | return Array.from(resolvedProviderMap.values());
|
34401 | }
|
34402 | /**
|
34403 | * Merges a list of ResolvedProviders into a list where each key is contained exactly once and
|
34404 | * multi providers have been merged.
|
34405 | */
|
34406 | function mergeResolvedReflectiveProviders(providers, normalizedProvidersMap) {
|
34407 | for (let i = 0; i < providers.length; i++) {
|
34408 | const provider = providers[i];
|
34409 | const existing = normalizedProvidersMap.get(provider.key.id);
|
34410 | if (existing) {
|
34411 | if (provider.multiProvider !== existing.multiProvider) {
|
34412 | throw mixingMultiProvidersWithRegularProvidersError(existing, provider);
|
34413 | }
|
34414 | if (provider.multiProvider) {
|
34415 | for (let j = 0; j < provider.resolvedFactories.length; j++) {
|
34416 | existing.resolvedFactories.push(provider.resolvedFactories[j]);
|
34417 | }
|
34418 | }
|
34419 | else {
|
34420 | normalizedProvidersMap.set(provider.key.id, provider);
|
34421 | }
|
34422 | }
|
34423 | else {
|
34424 | let resolvedProvider;
|
34425 | if (provider.multiProvider) {
|
34426 | resolvedProvider = new ResolvedReflectiveProvider_(provider.key, provider.resolvedFactories.slice(), provider.multiProvider);
|
34427 | }
|
34428 | else {
|
34429 | resolvedProvider = provider;
|
34430 | }
|
34431 | normalizedProvidersMap.set(provider.key.id, resolvedProvider);
|
34432 | }
|
34433 | }
|
34434 | return normalizedProvidersMap;
|
34435 | }
|
34436 | function _normalizeProviders(providers, res) {
|
34437 | providers.forEach(b => {
|
34438 | if (b instanceof Type$2) {
|
34439 | res.push({ provide: b, useClass: b });
|
34440 | }
|
34441 | else if (b && typeof b == 'object' && b.provide !== undefined) {
|
34442 | res.push(b);
|
34443 | }
|
34444 | else if (Array.isArray(b)) {
|
34445 | _normalizeProviders(b, res);
|
34446 | }
|
34447 | else {
|
34448 | throw invalidProviderError(b);
|
34449 | }
|
34450 | });
|
34451 | return res;
|
34452 | }
|
34453 | function constructDependencies(typeOrFunc, dependencies) {
|
34454 | if (!dependencies) {
|
34455 | return _dependenciesFor(typeOrFunc);
|
34456 | }
|
34457 | else {
|
34458 | const params = dependencies.map(t => [t]);
|
34459 | return dependencies.map(t => _extractToken(typeOrFunc, t, params));
|
34460 | }
|
34461 | }
|
34462 | function _dependenciesFor(typeOrFunc) {
|
34463 | const params = reflector.parameters(typeOrFunc);
|
34464 | if (!params)
|
34465 | return [];
|
34466 | if (params.some(p => p == null)) {
|
34467 | throw noAnnotationError(typeOrFunc, params);
|
34468 | }
|
34469 | return params.map(p => _extractToken(typeOrFunc, p, params));
|
34470 | }
|
34471 | function _extractToken(typeOrFunc, metadata, params) {
|
34472 | let token = null;
|
34473 | let optional = false;
|
34474 | if (!Array.isArray(metadata)) {
|
34475 | if (metadata instanceof Inject) {
|
34476 | return _createDependency(metadata.token, optional, null);
|
34477 | }
|
34478 | else {
|
34479 | return _createDependency(metadata, optional, null);
|
34480 | }
|
34481 | }
|
34482 | let visibility = null;
|
34483 | for (let i = 0; i < metadata.length; ++i) {
|
34484 | const paramMetadata = metadata[i];
|
34485 | if (paramMetadata instanceof Type$2) {
|
34486 | token = paramMetadata;
|
34487 | }
|
34488 | else if (paramMetadata instanceof Inject) {
|
34489 | token = paramMetadata.token;
|
34490 | }
|
34491 | else if (paramMetadata instanceof Optional) {
|
34492 | optional = true;
|
34493 | }
|
34494 | else if (paramMetadata instanceof Self || paramMetadata instanceof SkipSelf) {
|
34495 | visibility = paramMetadata;
|
34496 | }
|
34497 | else if (paramMetadata instanceof InjectionToken) {
|
34498 | token = paramMetadata;
|
34499 | }
|
34500 | }
|
34501 | token = resolveForwardRef$1(token);
|
34502 | if (token != null) {
|
34503 | return _createDependency(token, optional, visibility);
|
34504 | }
|
34505 | else {
|
34506 | throw noAnnotationError(typeOrFunc, params);
|
34507 | }
|
34508 | }
|
34509 | function _createDependency(token, optional, visibility) {
|
34510 | return new ReflectiveDependency(ReflectiveKey.get(token), optional, visibility);
|
34511 | }
|
34512 |
|
34513 | /**
|
34514 | * @license
|
34515 | * Copyright Google LLC All Rights Reserved.
|
34516 | *
|
34517 | * Use of this source code is governed by an MIT-style license that can be
|
34518 | * found in the LICENSE file at https://angular.io/license
|
34519 | */
|
34520 | // Threshold for the dynamic version
|
34521 | const UNDEFINED = {};
|
34522 | /**
|
34523 | * A ReflectiveDependency injection container used for instantiating objects and resolving
|
34524 | * dependencies.
|
34525 | *
|
34526 | * An `Injector` is a replacement for a `new` operator, which can automatically resolve the
|
34527 | * constructor dependencies.
|
34528 | *
|
34529 | * In typical use, application code asks for the dependencies in the constructor and they are
|
34530 | * resolved by the `Injector`.
|
34531 | *
|
34532 | * @usageNotes
|
34533 | * ### Example
|
34534 | *
|
34535 | * The following example creates an `Injector` configured to create `Engine` and `Car`.
|
34536 | *
|
34537 | * ```typescript
|
34538 | * @Injectable()
|
34539 | * class Engine {
|
34540 | * }
|
34541 | *
|
34542 | * @Injectable()
|
34543 | * class Car {
|
34544 | * constructor(public engine:Engine) {}
|
34545 | * }
|
34546 | *
|
34547 | * var injector = ReflectiveInjector.resolveAndCreate([Car, Engine]);
|
34548 | * var car = injector.get(Car);
|
34549 | * expect(car instanceof Car).toBe(true);
|
34550 | * expect(car.engine instanceof Engine).toBe(true);
|
34551 | * ```
|
34552 | *
|
34553 | * Notice, we don't use the `new` operator because we explicitly want to have the `Injector`
|
34554 | * resolve all of the object's dependencies automatically.
|
34555 | *
|
34556 | * @deprecated from v5 - slow and brings in a lot of code, Use `Injector.create` instead.
|
34557 | * @publicApi
|
34558 | */
|
34559 | class ReflectiveInjector {
|
34560 | /**
|
34561 | * Turns an array of provider definitions into an array of resolved providers.
|
34562 | *
|
34563 | * A resolution is a process of flattening multiple nested arrays and converting individual
|
34564 | * providers into an array of `ResolvedReflectiveProvider`s.
|
34565 | *
|
34566 | * @usageNotes
|
34567 | * ### Example
|
34568 | *
|
34569 | * ```typescript
|
34570 | * @Injectable()
|
34571 | * class Engine {
|
34572 | * }
|
34573 | *
|
34574 | * @Injectable()
|
34575 | * class Car {
|
34576 | * constructor(public engine:Engine) {}
|
34577 | * }
|
34578 | *
|
34579 | * var providers = ReflectiveInjector.resolve([Car, [[Engine]]]);
|
34580 | *
|
34581 | * expect(providers.length).toEqual(2);
|
34582 | *
|
34583 | * expect(providers[0] instanceof ResolvedReflectiveProvider).toBe(true);
|
34584 | * expect(providers[0].key.displayName).toBe("Car");
|
34585 | * expect(providers[0].dependencies.length).toEqual(1);
|
34586 | * expect(providers[0].factory).toBeDefined();
|
34587 | *
|
34588 | * expect(providers[1].key.displayName).toBe("Engine");
|
34589 | * });
|
34590 | * ```
|
34591 | *
|
34592 | */
|
34593 | static resolve(providers) {
|
34594 | return resolveReflectiveProviders(providers);
|
34595 | }
|
34596 | /**
|
34597 | * Resolves an array of providers and creates an injector from those providers.
|
34598 | *
|
34599 | * The passed-in providers can be an array of `Type`, `Provider`,
|
34600 | * or a recursive array of more providers.
|
34601 | *
|
34602 | * @usageNotes
|
34603 | * ### Example
|
34604 | *
|
34605 | * ```typescript
|
34606 | * @Injectable()
|
34607 | * class Engine {
|
34608 | * }
|
34609 | *
|
34610 | * @Injectable()
|
34611 | * class Car {
|
34612 | * constructor(public engine:Engine) {}
|
34613 | * }
|
34614 | *
|
34615 | * var injector = ReflectiveInjector.resolveAndCreate([Car, Engine]);
|
34616 | * expect(injector.get(Car) instanceof Car).toBe(true);
|
34617 | * ```
|
34618 | */
|
34619 | static resolveAndCreate(providers, parent) {
|
34620 | const ResolvedReflectiveProviders = ReflectiveInjector.resolve(providers);
|
34621 | return ReflectiveInjector.fromResolvedProviders(ResolvedReflectiveProviders, parent);
|
34622 | }
|
34623 | /**
|
34624 | * Creates an injector from previously resolved providers.
|
34625 | *
|
34626 | * This API is the recommended way to construct injectors in performance-sensitive parts.
|
34627 | *
|
34628 | * @usageNotes
|
34629 | * ### Example
|
34630 | *
|
34631 | * ```typescript
|
34632 | * @Injectable()
|
34633 | * class Engine {
|
34634 | * }
|
34635 | *
|
34636 | * @Injectable()
|
34637 | * class Car {
|
34638 | * constructor(public engine:Engine) {}
|
34639 | * }
|
34640 | *
|
34641 | * var providers = ReflectiveInjector.resolve([Car, Engine]);
|
34642 | * var injector = ReflectiveInjector.fromResolvedProviders(providers);
|
34643 | * expect(injector.get(Car) instanceof Car).toBe(true);
|
34644 | * ```
|
34645 | */
|
34646 | static fromResolvedProviders(providers, parent) {
|
34647 | return new ReflectiveInjector_(providers, parent);
|
34648 | }
|
34649 | }
|
34650 | class ReflectiveInjector_ {
|
34651 | /**
|
34652 | * Private
|
34653 | */
|
34654 | constructor(_providers, _parent) {
|
34655 | /** @internal */
|
34656 | this._constructionCounter = 0;
|
34657 | this._providers = _providers;
|
34658 | this.parent = _parent || null;
|
34659 | const len = _providers.length;
|
34660 | this.keyIds = [];
|
34661 | this.objs = [];
|
34662 | for (let i = 0; i < len; i++) {
|
34663 | this.keyIds[i] = _providers[i].key.id;
|
34664 | this.objs[i] = UNDEFINED;
|
34665 | }
|
34666 | }
|
34667 | get(token, notFoundValue = THROW_IF_NOT_FOUND) {
|
34668 | return this._getByKey(ReflectiveKey.get(token), null, notFoundValue);
|
34669 | }
|
34670 | resolveAndCreateChild(providers) {
|
34671 | const ResolvedReflectiveProviders = ReflectiveInjector.resolve(providers);
|
34672 | return this.createChildFromResolved(ResolvedReflectiveProviders);
|
34673 | }
|
34674 | createChildFromResolved(providers) {
|
34675 | const inj = new ReflectiveInjector_(providers);
|
34676 | inj.parent = this;
|
34677 | return inj;
|
34678 | }
|
34679 | resolveAndInstantiate(provider) {
|
34680 | return this.instantiateResolved(ReflectiveInjector.resolve([provider])[0]);
|
34681 | }
|
34682 | instantiateResolved(provider) {
|
34683 | return this._instantiateProvider(provider);
|
34684 | }
|
34685 | getProviderAtIndex(index) {
|
34686 | if (index < 0 || index >= this._providers.length) {
|
34687 | throw outOfBoundsError(index);
|
34688 | }
|
34689 | return this._providers[index];
|
34690 | }
|
34691 | /** @internal */
|
34692 | _new(provider) {
|
34693 | if (this._constructionCounter++ > this._getMaxNumberOfObjects()) {
|
34694 | throw cyclicDependencyError(this, provider.key);
|
34695 | }
|
34696 | return this._instantiateProvider(provider);
|
34697 | }
|
34698 | _getMaxNumberOfObjects() {
|
34699 | return this.objs.length;
|
34700 | }
|
34701 | _instantiateProvider(provider) {
|
34702 | if (provider.multiProvider) {
|
34703 | const res = [];
|
34704 | for (let i = 0; i < provider.resolvedFactories.length; ++i) {
|
34705 | res[i] = this._instantiate(provider, provider.resolvedFactories[i]);
|
34706 | }
|
34707 | return res;
|
34708 | }
|
34709 | else {
|
34710 | return this._instantiate(provider, provider.resolvedFactories[0]);
|
34711 | }
|
34712 | }
|
34713 | _instantiate(provider, ResolvedReflectiveFactory) {
|
34714 | const factory = ResolvedReflectiveFactory.factory;
|
34715 | let deps;
|
34716 | try {
|
34717 | deps =
|
34718 | ResolvedReflectiveFactory.dependencies.map(dep => this._getByReflectiveDependency(dep));
|
34719 | }
|
34720 | catch (e) {
|
34721 | if (e.addKey) {
|
34722 | e.addKey(this, provider.key);
|
34723 | }
|
34724 | throw e;
|
34725 | }
|
34726 | let obj;
|
34727 | try {
|
34728 | obj = factory(...deps);
|
34729 | }
|
34730 | catch (e) {
|
34731 | throw instantiationError(this, e, e.stack, provider.key);
|
34732 | }
|
34733 | return obj;
|
34734 | }
|
34735 | _getByReflectiveDependency(dep) {
|
34736 | return this._getByKey(dep.key, dep.visibility, dep.optional ? null : THROW_IF_NOT_FOUND);
|
34737 | }
|
34738 | _getByKey(key, visibility, notFoundValue) {
|
34739 | if (key === ReflectiveInjector_.INJECTOR_KEY) {
|
34740 | return this;
|
34741 | }
|
34742 | if (visibility instanceof Self) {
|
34743 | return this._getByKeySelf(key, notFoundValue);
|
34744 | }
|
34745 | else {
|
34746 | return this._getByKeyDefault(key, notFoundValue, visibility);
|
34747 | }
|
34748 | }
|
34749 | _getObjByKeyId(keyId) {
|
34750 | for (let i = 0; i < this.keyIds.length; i++) {
|
34751 | if (this.keyIds[i] === keyId) {
|
34752 | if (this.objs[i] === UNDEFINED) {
|
34753 | this.objs[i] = this._new(this._providers[i]);
|
34754 | }
|
34755 | return this.objs[i];
|
34756 | }
|
34757 | }
|
34758 | return UNDEFINED;
|
34759 | }
|
34760 | /** @internal */
|
34761 | _throwOrNull(key, notFoundValue) {
|
34762 | if (notFoundValue !== THROW_IF_NOT_FOUND) {
|
34763 | return notFoundValue;
|
34764 | }
|
34765 | else {
|
34766 | throw noProviderError(this, key);
|
34767 | }
|
34768 | }
|
34769 | /** @internal */
|
34770 | _getByKeySelf(key, notFoundValue) {
|
34771 | const obj = this._getObjByKeyId(key.id);
|
34772 | return (obj !== UNDEFINED) ? obj : this._throwOrNull(key, notFoundValue);
|
34773 | }
|
34774 | /** @internal */
|
34775 | _getByKeyDefault(key, notFoundValue, visibility) {
|
34776 | let inj;
|
34777 | if (visibility instanceof SkipSelf) {
|
34778 | inj = this.parent;
|
34779 | }
|
34780 | else {
|
34781 | inj = this;
|
34782 | }
|
34783 | while (inj instanceof ReflectiveInjector_) {
|
34784 | const inj_ = inj;
|
34785 | const obj = inj_._getObjByKeyId(key.id);
|
34786 | if (obj !== UNDEFINED)
|
34787 | return obj;
|
34788 | inj = inj_.parent;
|
34789 | }
|
34790 | if (inj !== null) {
|
34791 | return inj.get(key.token, notFoundValue);
|
34792 | }
|
34793 | else {
|
34794 | return this._throwOrNull(key, notFoundValue);
|
34795 | }
|
34796 | }
|
34797 | get displayName() {
|
34798 | const providers = _mapProviders(this, (b) => ' "' + b.key.displayName + '" ')
|
34799 | .join(', ');
|
34800 | return `ReflectiveInjector(providers: [${providers}])`;
|
34801 | }
|
34802 | toString() {
|
34803 | return this.displayName;
|
34804 | }
|
34805 | }
|
34806 | ReflectiveInjector_.INJECTOR_KEY = ReflectiveKey.get(Injector);
|
34807 | function _mapProviders(injector, fn) {
|
34808 | const res = [];
|
34809 | for (let i = 0; i < injector._providers.length; ++i) {
|
34810 | res[i] = fn(injector.getProviderAtIndex(i));
|
34811 | }
|
34812 | return res;
|
34813 | }
|
34814 |
|
34815 | /**
|
34816 | * @license
|
34817 | * Copyright Google LLC All Rights Reserved.
|
34818 | *
|
34819 | * Use of this source code is governed by an MIT-style license that can be
|
34820 | * found in the LICENSE file at https://angular.io/license
|
34821 | */
|
34822 | /**
|
34823 | * Determine if the argument is shaped like a Promise
|
34824 | */
|
34825 | function isPromise$1(obj) {
|
34826 | // allow any Promise/A+ compliant thenable.
|
34827 | // It's up to the caller to ensure that obj.then conforms to the spec
|
34828 | return !!obj && typeof obj.then === 'function';
|
34829 | }
|
34830 |
|
34831 | /**
|
34832 | * @license
|
34833 | * Copyright Google LLC All Rights Reserved.
|
34834 | *
|
34835 | * Use of this source code is governed by an MIT-style license that can be
|
34836 | * found in the LICENSE file at https://angular.io/license
|
34837 | */
|
34838 | /**
|
34839 | * This file contains reuseable "empty" symbols that can be used as default return values
|
34840 | * in different parts of the rendering code. Because the same symbols are returned, this
|
34841 | * allows for identity checks against these values to be consistently used by the framework
|
34842 | * code.
|
34843 | */
|
34844 | const EMPTY_OBJ$1 = {};
|
34845 | const EMPTY_ARRAY$2 = [];
|
34846 | // freezing the values prevents any code from accidentally inserting new values in
|
34847 | if ((typeof ngDevMode === 'undefined' || ngDevMode) && initNgDevMode()) {
|
34848 | // These property accesses can be ignored because ngDevMode will be set to false
|
34849 | // when optimizing code and the whole if statement will be dropped.
|
34850 | // tslint:disable-next-line:no-toplevel-property-access
|
34851 | Object.freeze(EMPTY_OBJ$1);
|
34852 | // tslint:disable-next-line:no-toplevel-property-access
|
34853 | Object.freeze(EMPTY_ARRAY$2);
|
34854 | }
|
34855 |
|
34856 | /**
|
34857 | * @license
|
34858 | * Copyright Google LLC All Rights Reserved.
|
34859 | *
|
34860 | * Use of this source code is governed by an MIT-style license that can be
|
34861 | * found in the LICENSE file at https://angular.io/license
|
34862 | */
|
34863 | /**
|
34864 | * NOTE: changes to the `ngI18nClosureMode` name must be synced with `compiler-cli/src/tooling.ts`.
|
34865 | */
|
34866 | if (typeof ngI18nClosureMode === 'undefined') {
|
34867 | // These property accesses can be ignored because ngI18nClosureMode will be set to false
|
34868 | // when optimizing code and the whole if statement will be dropped.
|
34869 | // Make sure to refer to ngI18nClosureMode as ['ngI18nClosureMode'] for closure.
|
34870 | // NOTE: we need to have it in IIFE so that the tree-shaker is happy.
|
34871 | (function () {
|
34872 | // tslint:disable-next-line:no-toplevel-property-access
|
34873 | _global$1['ngI18nClosureMode'] =
|
34874 | // TODO(FW-1250): validate that this actually, you know, works.
|
34875 | // tslint:disable-next-line:no-toplevel-property-access
|
34876 | typeof goog !== 'undefined' && typeof goog.getMsg === 'function';
|
34877 | })();
|
34878 | }
|
34879 |
|
34880 | /**
|
34881 | * @license
|
34882 | * Copyright Google LLC All Rights Reserved.
|
34883 | *
|
34884 | * Use of this source code is governed by an MIT-style license that can be
|
34885 | * found in the LICENSE file at https://angular.io/license
|
34886 | */
|
34887 | /**
|
34888 | * Index of each type of locale data from the locale data array
|
34889 | */
|
34890 | var LocaleDataIndex;
|
34891 | (function (LocaleDataIndex) {
|
34892 | LocaleDataIndex[LocaleDataIndex["LocaleId"] = 0] = "LocaleId";
|
34893 | LocaleDataIndex[LocaleDataIndex["DayPeriodsFormat"] = 1] = "DayPeriodsFormat";
|
34894 | LocaleDataIndex[LocaleDataIndex["DayPeriodsStandalone"] = 2] = "DayPeriodsStandalone";
|
34895 | LocaleDataIndex[LocaleDataIndex["DaysFormat"] = 3] = "DaysFormat";
|
34896 | LocaleDataIndex[LocaleDataIndex["DaysStandalone"] = 4] = "DaysStandalone";
|
34897 | LocaleDataIndex[LocaleDataIndex["MonthsFormat"] = 5] = "MonthsFormat";
|
34898 | LocaleDataIndex[LocaleDataIndex["MonthsStandalone"] = 6] = "MonthsStandalone";
|
34899 | LocaleDataIndex[LocaleDataIndex["Eras"] = 7] = "Eras";
|
34900 | LocaleDataIndex[LocaleDataIndex["FirstDayOfWeek"] = 8] = "FirstDayOfWeek";
|
34901 | LocaleDataIndex[LocaleDataIndex["WeekendRange"] = 9] = "WeekendRange";
|
34902 | LocaleDataIndex[LocaleDataIndex["DateFormat"] = 10] = "DateFormat";
|
34903 | LocaleDataIndex[LocaleDataIndex["TimeFormat"] = 11] = "TimeFormat";
|
34904 | LocaleDataIndex[LocaleDataIndex["DateTimeFormat"] = 12] = "DateTimeFormat";
|
34905 | LocaleDataIndex[LocaleDataIndex["NumberSymbols"] = 13] = "NumberSymbols";
|
34906 | LocaleDataIndex[LocaleDataIndex["NumberFormats"] = 14] = "NumberFormats";
|
34907 | LocaleDataIndex[LocaleDataIndex["CurrencyCode"] = 15] = "CurrencyCode";
|
34908 | LocaleDataIndex[LocaleDataIndex["CurrencySymbol"] = 16] = "CurrencySymbol";
|
34909 | LocaleDataIndex[LocaleDataIndex["CurrencyName"] = 17] = "CurrencyName";
|
34910 | LocaleDataIndex[LocaleDataIndex["Currencies"] = 18] = "Currencies";
|
34911 | LocaleDataIndex[LocaleDataIndex["Directionality"] = 19] = "Directionality";
|
34912 | LocaleDataIndex[LocaleDataIndex["PluralCase"] = 20] = "PluralCase";
|
34913 | LocaleDataIndex[LocaleDataIndex["ExtraData"] = 21] = "ExtraData";
|
34914 | })(LocaleDataIndex || (LocaleDataIndex = {}));
|
34915 |
|
34916 | /**
|
34917 | * @license
|
34918 | * Copyright Google LLC All Rights Reserved.
|
34919 | *
|
34920 | * Use of this source code is governed by an MIT-style license that can be
|
34921 | * found in the LICENSE file at https://angular.io/license
|
34922 | */
|
34923 | /**
|
34924 | * The locale id that the application is using by default (for translations and ICU expressions).
|
34925 | */
|
34926 | const DEFAULT_LOCALE_ID = 'en-US';
|
34927 | /**
|
34928 | * USD currency code that the application uses by default for CurrencyPipe when no
|
34929 | * DEFAULT_CURRENCY_CODE is provided.
|
34930 | */
|
34931 | const USD_CURRENCY_CODE = 'USD';
|
34932 |
|
34933 | /**
|
34934 | * @license
|
34935 | * Copyright Google LLC All Rights Reserved.
|
34936 | *
|
34937 | * Use of this source code is governed by an MIT-style license that can be
|
34938 | * found in the LICENSE file at https://angular.io/license
|
34939 | */
|
34940 | /**
|
34941 | * See `I18nCreateOpCodes`
|
34942 | */
|
34943 | var I18nCreateOpCode;
|
34944 | (function (I18nCreateOpCode) {
|
34945 | /**
|
34946 | * Number of bits to shift index so that it can be combined with the `APPEND_EAGERLY` and
|
34947 | * `COMMENT`.
|
34948 | */
|
34949 | I18nCreateOpCode[I18nCreateOpCode["SHIFT"] = 2] = "SHIFT";
|
34950 | /**
|
34951 | * Should the node be appended to parent imedditatly after creation.
|
34952 | */
|
34953 | I18nCreateOpCode[I18nCreateOpCode["APPEND_EAGERLY"] = 1] = "APPEND_EAGERLY";
|
34954 | /**
|
34955 | * If set the node should be comment (rather than a text) node.
|
34956 | */
|
34957 | I18nCreateOpCode[I18nCreateOpCode["COMMENT"] = 2] = "COMMENT";
|
34958 | })(I18nCreateOpCode || (I18nCreateOpCode = {}));
|
34959 |
|
34960 | /**
|
34961 | * @license
|
34962 | * Copyright Google LLC All Rights Reserved.
|
34963 | *
|
34964 | * Use of this source code is governed by an MIT-style license that can be
|
34965 | * found in the LICENSE file at https://angular.io/license
|
34966 | */
|
34967 | /**
|
34968 | * The locale id that the application is currently using (for translations and ICU expressions).
|
34969 | * This is the ivy version of `LOCALE_ID` that was defined as an injection token for the view engine
|
34970 | * but is now defined as a global value.
|
34971 | */
|
34972 | let LOCALE_ID = DEFAULT_LOCALE_ID;
|
34973 | /**
|
34974 | * Sets the locale id that will be used for translations and ICU expressions.
|
34975 | * This is the ivy version of `LOCALE_ID` that was defined as an injection token for the view engine
|
34976 | * but is now defined as a global value.
|
34977 | *
|
34978 | * @param localeId
|
34979 | */
|
34980 | function setLocaleId(localeId) {
|
34981 | assertDefined(localeId, `Expected localeId to be defined`);
|
34982 | if (typeof localeId === 'string') {
|
34983 | LOCALE_ID = localeId.toLowerCase().replace(/_/g, '-');
|
34984 | }
|
34985 | }
|
34986 |
|
34987 | /**
|
34988 | * @license
|
34989 | * Copyright Google LLC All Rights Reserved.
|
34990 | *
|
34991 | * Use of this source code is governed by an MIT-style license that can be
|
34992 | * found in the LICENSE file at https://angular.io/license
|
34993 | */
|
34994 | /**
|
34995 | * Represents a component created by a `ComponentFactory`.
|
34996 | * Provides access to the component instance and related objects,
|
34997 | * and provides the means of destroying the instance.
|
34998 | *
|
34999 | * @publicApi
|
35000 | */
|
35001 | class ComponentRef {
|
35002 | }
|
35003 | /**
|
35004 | * Base class for a factory that can create a component dynamically.
|
35005 | * Instantiate a factory for a given type of component with `resolveComponentFactory()`.
|
35006 | * Use the resulting `ComponentFactory.create()` method to create a component of that type.
|
35007 | *
|
35008 | * @see [Dynamic Components](guide/dynamic-component-loader)
|
35009 | *
|
35010 | * @publicApi
|
35011 | */
|
35012 | class ComponentFactory {
|
35013 | }
|
35014 |
|
35015 | /**
|
35016 | * @license
|
35017 | * Copyright Google LLC All Rights Reserved.
|
35018 | *
|
35019 | * Use of this source code is governed by an MIT-style license that can be
|
35020 | * found in the LICENSE file at https://angular.io/license
|
35021 | */
|
35022 | function noComponentFactoryError(component) {
|
35023 | const error = Error(`No component factory found for ${stringify$1(component)}. Did you add it to @NgModule.entryComponents?`);
|
35024 | error[ERROR_COMPONENT] = component;
|
35025 | return error;
|
35026 | }
|
35027 | const ERROR_COMPONENT = 'ngComponent';
|
35028 | class _NullComponentFactoryResolver {
|
35029 | resolveComponentFactory(component) {
|
35030 | throw noComponentFactoryError(component);
|
35031 | }
|
35032 | }
|
35033 | /**
|
35034 | * A simple registry that maps `Components` to generated `ComponentFactory` classes
|
35035 | * that can be used to create instances of components.
|
35036 | * Use to obtain the factory for a given component type,
|
35037 | * then use the factory's `create()` method to create a component of that type.
|
35038 | *
|
35039 | * @see [Dynamic Components](guide/dynamic-component-loader)
|
35040 | * @publicApi
|
35041 | */
|
35042 | class ComponentFactoryResolver {
|
35043 | }
|
35044 | ComponentFactoryResolver.NULL = new _NullComponentFactoryResolver();
|
35045 | class ComponentFactoryBoundToModule extends ComponentFactory {
|
35046 | constructor(factory, ngModule) {
|
35047 | super();
|
35048 | this.factory = factory;
|
35049 | this.ngModule = ngModule;
|
35050 | this.selector = factory.selector;
|
35051 | this.componentType = factory.componentType;
|
35052 | this.ngContentSelectors = factory.ngContentSelectors;
|
35053 | this.inputs = factory.inputs;
|
35054 | this.outputs = factory.outputs;
|
35055 | }
|
35056 | create(injector, projectableNodes, rootSelectorOrNode, ngModule) {
|
35057 | return this.factory.create(injector, projectableNodes, rootSelectorOrNode, ngModule || this.ngModule);
|
35058 | }
|
35059 | }
|
35060 |
|
35061 | /**
|
35062 | * @license
|
35063 | * Copyright Google LLC All Rights Reserved.
|
35064 | *
|
35065 | * Use of this source code is governed by an MIT-style license that can be
|
35066 | * found in the LICENSE file at https://angular.io/license
|
35067 | */
|
35068 | function noop(...args) {
|
35069 | // Do nothing.
|
35070 | }
|
35071 |
|
35072 | /**
|
35073 | * @license
|
35074 | * Copyright Google LLC All Rights Reserved.
|
35075 | *
|
35076 | * Use of this source code is governed by an MIT-style license that can be
|
35077 | * found in the LICENSE file at https://angular.io/license
|
35078 | */
|
35079 | /**
|
35080 | * Creates an ElementRef given a node.
|
35081 | *
|
35082 | * @param tNode The node for which you'd like an ElementRef
|
35083 | * @param lView The view to which the node belongs
|
35084 | * @returns The ElementRef instance to use
|
35085 | */
|
35086 | function createElementRef(tNode, lView) {
|
35087 | return new ElementRef(getNativeByTNode(tNode, lView));
|
35088 | }
|
35089 | const SWITCH_ELEMENT_REF_FACTORY__PRE_R3__ = noop;
|
35090 | const SWITCH_ELEMENT_REF_FACTORY = SWITCH_ELEMENT_REF_FACTORY__PRE_R3__;
|
35091 | /**
|
35092 | * A wrapper around a native element inside of a View.
|
35093 | *
|
35094 | * An `ElementRef` is backed by a render-specific element. In the browser, this is usually a DOM
|
35095 | * element.
|
35096 | *
|
35097 | * @security Permitting direct access to the DOM can make your application more vulnerable to
|
35098 | * XSS attacks. Carefully review any use of `ElementRef` in your code. For more detail, see the
|
35099 | * [Security Guide](https://g.co/ng/security).
|
35100 | *
|
35101 | * @publicApi
|
35102 | */
|
35103 | // Note: We don't expose things like `Injector`, `ViewContainer`, ... here,
|
35104 | // i.e. users have to ask for what they need. With that, we can build better analysis tools
|
35105 | // and could do better codegen in the future.
|
35106 | class ElementRef {
|
35107 | constructor(nativeElement) {
|
35108 | this.nativeElement = nativeElement;
|
35109 | }
|
35110 | }
|
35111 | /**
|
35112 | * @internal
|
35113 | * @nocollapse
|
35114 | */
|
35115 | ElementRef.__NG_ELEMENT_ID__ = SWITCH_ELEMENT_REF_FACTORY;
|
35116 |
|
35117 | /**
|
35118 | * @license
|
35119 | * Copyright Google LLC All Rights Reserved.
|
35120 | *
|
35121 | * Use of this source code is governed by an MIT-style license that can be
|
35122 | * found in the LICENSE file at https://angular.io/license
|
35123 | */
|
35124 | const Renderer2Interceptor = new InjectionToken('Renderer2Interceptor');
|
35125 | /**
|
35126 | * Creates and initializes a custom renderer that implements the `Renderer2` base class.
|
35127 | *
|
35128 | * @publicApi
|
35129 | */
|
35130 | class RendererFactory2 {
|
35131 | }
|
35132 | /**
|
35133 | * Extend this base class to implement custom rendering. By default, Angular
|
35134 | * renders a template into DOM. You can use custom rendering to intercept
|
35135 | * rendering calls, or to render to something other than DOM.
|
35136 | *
|
35137 | * Create your custom renderer using `RendererFactory2`.
|
35138 | *
|
35139 | * Use a custom renderer to bypass Angular's templating and
|
35140 | * make custom UI changes that can't be expressed declaratively.
|
35141 | * For example if you need to set a property or an attribute whose name is
|
35142 | * not statically known, use the `setProperty()` or
|
35143 | * `setAttribute()` method.
|
35144 | *
|
35145 | * @publicApi
|
35146 | */
|
35147 | class Renderer2 {
|
35148 | }
|
35149 | /**
|
35150 | * @internal
|
35151 | * @nocollapse
|
35152 | */
|
35153 | Renderer2.__NG_ELEMENT_ID__ = () => SWITCH_RENDERER2_FACTORY();
|
35154 | const SWITCH_RENDERER2_FACTORY__PRE_R3__ = noop;
|
35155 | const SWITCH_RENDERER2_FACTORY = SWITCH_RENDERER2_FACTORY__PRE_R3__;
|
35156 |
|
35157 | /**
|
35158 | * @license
|
35159 | * Copyright Google LLC All Rights Reserved.
|
35160 | *
|
35161 | * Use of this source code is governed by an MIT-style license that can be
|
35162 | * found in the LICENSE file at https://angular.io/license
|
35163 | */
|
35164 | /**
|
35165 | * Sanitizer is used by the views to sanitize potentially dangerous values.
|
35166 | *
|
35167 | * @publicApi
|
35168 | */
|
35169 | class Sanitizer {
|
35170 | }
|
35171 | /** @nocollapse */
|
35172 | Sanitizer.ɵprov = ɵɵdefineInjectable({
|
35173 | token: Sanitizer,
|
35174 | providedIn: 'root',
|
35175 | factory: () => null,
|
35176 | });
|
35177 |
|
35178 | /**
|
35179 | * @license
|
35180 | * Copyright Google LLC All Rights Reserved.
|
35181 | *
|
35182 | * Use of this source code is governed by an MIT-style license that can be
|
35183 | * found in the LICENSE file at https://angular.io/license
|
35184 | */
|
35185 | /**
|
35186 | * @description Represents the version of Angular
|
35187 | *
|
35188 | * @publicApi
|
35189 | */
|
35190 | class Version$1 {
|
35191 | constructor(full) {
|
35192 | this.full = full;
|
35193 | this.major = full.split('.')[0];
|
35194 | this.minor = full.split('.')[1];
|
35195 | this.patch = full.split('.').slice(2).join('.');
|
35196 | }
|
35197 | }
|
35198 | /**
|
35199 | * @publicApi
|
35200 | */
|
35201 | const VERSION$2 = new Version$1('11.2.4');
|
35202 |
|
35203 | /**
|
35204 | * @license
|
35205 | * Copyright Google LLC All Rights Reserved.
|
35206 | *
|
35207 | * Use of this source code is governed by an MIT-style license that can be
|
35208 | * found in the LICENSE file at https://angular.io/license
|
35209 | */
|
35210 | class DefaultIterableDifferFactory {
|
35211 | constructor() { }
|
35212 | supports(obj) {
|
35213 | return isListLikeIterable(obj);
|
35214 | }
|
35215 | create(trackByFn) {
|
35216 | return new DefaultIterableDiffer(trackByFn);
|
35217 | }
|
35218 | }
|
35219 | const trackByIdentity = (index, item) => item;
|
35220 | /**
|
35221 | * @deprecated v4.0.0 - Should not be part of public API.
|
35222 | * @publicApi
|
35223 | */
|
35224 | class DefaultIterableDiffer {
|
35225 | constructor(trackByFn) {
|
35226 | this.length = 0;
|
35227 | // Keeps track of the used records at any point in time (during & across `_check()` calls)
|
35228 | this._linkedRecords = null;
|
35229 | // Keeps track of the removed records at any point in time during `_check()` calls.
|
35230 | this._unlinkedRecords = null;
|
35231 | this._previousItHead = null;
|
35232 | this._itHead = null;
|
35233 | this._itTail = null;
|
35234 | this._additionsHead = null;
|
35235 | this._additionsTail = null;
|
35236 | this._movesHead = null;
|
35237 | this._movesTail = null;
|
35238 | this._removalsHead = null;
|
35239 | this._removalsTail = null;
|
35240 | // Keeps track of records where custom track by is the same, but item identity has changed
|
35241 | this._identityChangesHead = null;
|
35242 | this._identityChangesTail = null;
|
35243 | this._trackByFn = trackByFn || trackByIdentity;
|
35244 | }
|
35245 | forEachItem(fn) {
|
35246 | let record;
|
35247 | for (record = this._itHead; record !== null; record = record._next) {
|
35248 | fn(record);
|
35249 | }
|
35250 | }
|
35251 | forEachOperation(fn) {
|
35252 | let nextIt = this._itHead;
|
35253 | let nextRemove = this._removalsHead;
|
35254 | let addRemoveOffset = 0;
|
35255 | let moveOffsets = null;
|
35256 | while (nextIt || nextRemove) {
|
35257 | // Figure out which is the next record to process
|
35258 | // Order: remove, add, move
|
35259 | const record = !nextRemove ||
|
35260 | nextIt &&
|
35261 | nextIt.currentIndex <
|
35262 | getPreviousIndex(nextRemove, addRemoveOffset, moveOffsets) ?
|
35263 | nextIt :
|
35264 | nextRemove;
|
35265 | const adjPreviousIndex = getPreviousIndex(record, addRemoveOffset, moveOffsets);
|
35266 | const currentIndex = record.currentIndex;
|
35267 | // consume the item, and adjust the addRemoveOffset and update moveDistance if necessary
|
35268 | if (record === nextRemove) {
|
35269 | addRemoveOffset--;
|
35270 | nextRemove = nextRemove._nextRemoved;
|
35271 | }
|
35272 | else {
|
35273 | nextIt = nextIt._next;
|
35274 | if (record.previousIndex == null) {
|
35275 | addRemoveOffset++;
|
35276 | }
|
35277 | else {
|
35278 | // INVARIANT: currentIndex < previousIndex
|
35279 | if (!moveOffsets)
|
35280 | moveOffsets = [];
|
35281 | const localMovePreviousIndex = adjPreviousIndex - addRemoveOffset;
|
35282 | const localCurrentIndex = currentIndex - addRemoveOffset;
|
35283 | if (localMovePreviousIndex != localCurrentIndex) {
|
35284 | for (let i = 0; i < localMovePreviousIndex; i++) {
|
35285 | const offset = i < moveOffsets.length ? moveOffsets[i] : (moveOffsets[i] = 0);
|
35286 | const index = offset + i;
|
35287 | if (localCurrentIndex <= index && index < localMovePreviousIndex) {
|
35288 | moveOffsets[i] = offset + 1;
|
35289 | }
|
35290 | }
|
35291 | const previousIndex = record.previousIndex;
|
35292 | moveOffsets[previousIndex] = localCurrentIndex - localMovePreviousIndex;
|
35293 | }
|
35294 | }
|
35295 | }
|
35296 | if (adjPreviousIndex !== currentIndex) {
|
35297 | fn(record, adjPreviousIndex, currentIndex);
|
35298 | }
|
35299 | }
|
35300 | }
|
35301 | forEachPreviousItem(fn) {
|
35302 | let record;
|
35303 | for (record = this._previousItHead; record !== null; record = record._nextPrevious) {
|
35304 | fn(record);
|
35305 | }
|
35306 | }
|
35307 | forEachAddedItem(fn) {
|
35308 | let record;
|
35309 | for (record = this._additionsHead; record !== null; record = record._nextAdded) {
|
35310 | fn(record);
|
35311 | }
|
35312 | }
|
35313 | forEachMovedItem(fn) {
|
35314 | let record;
|
35315 | for (record = this._movesHead; record !== null; record = record._nextMoved) {
|
35316 | fn(record);
|
35317 | }
|
35318 | }
|
35319 | forEachRemovedItem(fn) {
|
35320 | let record;
|
35321 | for (record = this._removalsHead; record !== null; record = record._nextRemoved) {
|
35322 | fn(record);
|
35323 | }
|
35324 | }
|
35325 | forEachIdentityChange(fn) {
|
35326 | let record;
|
35327 | for (record = this._identityChangesHead; record !== null; record = record._nextIdentityChange) {
|
35328 | fn(record);
|
35329 | }
|
35330 | }
|
35331 | diff(collection) {
|
35332 | if (collection == null)
|
35333 | collection = [];
|
35334 | if (!isListLikeIterable(collection)) {
|
35335 | throw new Error(`Error trying to diff '${stringify$1(collection)}'. Only arrays and iterables are allowed`);
|
35336 | }
|
35337 | if (this.check(collection)) {
|
35338 | return this;
|
35339 | }
|
35340 | else {
|
35341 | return null;
|
35342 | }
|
35343 | }
|
35344 | onDestroy() { }
|
35345 | check(collection) {
|
35346 | this._reset();
|
35347 | let record = this._itHead;
|
35348 | let mayBeDirty = false;
|
35349 | let index;
|
35350 | let item;
|
35351 | let itemTrackBy;
|
35352 | if (Array.isArray(collection)) {
|
35353 | this.length = collection.length;
|
35354 | for (let index = 0; index < this.length; index++) {
|
35355 | item = collection[index];
|
35356 | itemTrackBy = this._trackByFn(index, item);
|
35357 | if (record === null || !Object.is(record.trackById, itemTrackBy)) {
|
35358 | record = this._mismatch(record, item, itemTrackBy, index);
|
35359 | mayBeDirty = true;
|
35360 | }
|
35361 | else {
|
35362 | if (mayBeDirty) {
|
35363 | // TODO(misko): can we limit this to duplicates only?
|
35364 | record = this._verifyReinsertion(record, item, itemTrackBy, index);
|
35365 | }
|
35366 | if (!Object.is(record.item, item))
|
35367 | this._addIdentityChange(record, item);
|
35368 | }
|
35369 | record = record._next;
|
35370 | }
|
35371 | }
|
35372 | else {
|
35373 | index = 0;
|
35374 | iterateListLike(collection, (item) => {
|
35375 | itemTrackBy = this._trackByFn(index, item);
|
35376 | if (record === null || !Object.is(record.trackById, itemTrackBy)) {
|
35377 | record = this._mismatch(record, item, itemTrackBy, index);
|
35378 | mayBeDirty = true;
|
35379 | }
|
35380 | else {
|
35381 | if (mayBeDirty) {
|
35382 | // TODO(misko): can we limit this to duplicates only?
|
35383 | record = this._verifyReinsertion(record, item, itemTrackBy, index);
|
35384 | }
|
35385 | if (!Object.is(record.item, item))
|
35386 | this._addIdentityChange(record, item);
|
35387 | }
|
35388 | record = record._next;
|
35389 | index++;
|
35390 | });
|
35391 | this.length = index;
|
35392 | }
|
35393 | this._truncate(record);
|
35394 | this.collection = collection;
|
35395 | return this.isDirty;
|
35396 | }
|
35397 | /* CollectionChanges is considered dirty if it has any additions, moves, removals, or identity
|
35398 | * changes.
|
35399 | */
|
35400 | get isDirty() {
|
35401 | return this._additionsHead !== null || this._movesHead !== null ||
|
35402 | this._removalsHead !== null || this._identityChangesHead !== null;
|
35403 | }
|
35404 | /**
|
35405 | * Reset the state of the change objects to show no changes. This means set previousKey to
|
35406 | * currentKey, and clear all of the queues (additions, moves, removals).
|
35407 | * Set the previousIndexes of moved and added items to their currentIndexes
|
35408 | * Reset the list of additions, moves and removals
|
35409 | *
|
35410 | * @internal
|
35411 | */
|
35412 | _reset() {
|
35413 | if (this.isDirty) {
|
35414 | let record;
|
35415 | for (record = this._previousItHead = this._itHead; record !== null; record = record._next) {
|
35416 | record._nextPrevious = record._next;
|
35417 | }
|
35418 | for (record = this._additionsHead; record !== null; record = record._nextAdded) {
|
35419 | record.previousIndex = record.currentIndex;
|
35420 | }
|
35421 | this._additionsHead = this._additionsTail = null;
|
35422 | for (record = this._movesHead; record !== null; record = record._nextMoved) {
|
35423 | record.previousIndex = record.currentIndex;
|
35424 | }
|
35425 | this._movesHead = this._movesTail = null;
|
35426 | this._removalsHead = this._removalsTail = null;
|
35427 | this._identityChangesHead = this._identityChangesTail = null;
|
35428 | // TODO(vicb): when assert gets supported
|
35429 | // assert(!this.isDirty);
|
35430 | }
|
35431 | }
|
35432 | /**
|
35433 | * This is the core function which handles differences between collections.
|
35434 | *
|
35435 | * - `record` is the record which we saw at this position last time. If null then it is a new
|
35436 | * item.
|
35437 | * - `item` is the current item in the collection
|
35438 | * - `index` is the position of the item in the collection
|
35439 | *
|
35440 | * @internal
|
35441 | */
|
35442 | _mismatch(record, item, itemTrackBy, index) {
|
35443 | // The previous record after which we will append the current one.
|
35444 | let previousRecord;
|
35445 | if (record === null) {
|
35446 | previousRecord = this._itTail;
|
35447 | }
|
35448 | else {
|
35449 | previousRecord = record._prev;
|
35450 | // Remove the record from the collection since we know it does not match the item.
|
35451 | this._remove(record);
|
35452 | }
|
35453 | // See if we have evicted the item, which used to be at some anterior position of _itHead list.
|
35454 | record = this._unlinkedRecords === null ? null : this._unlinkedRecords.get(itemTrackBy, null);
|
35455 | if (record !== null) {
|
35456 | // It is an item which we have evicted earlier: reinsert it back into the list.
|
35457 | // But first we need to check if identity changed, so we can update in view if necessary.
|
35458 | if (!Object.is(record.item, item))
|
35459 | this._addIdentityChange(record, item);
|
35460 | this._reinsertAfter(record, previousRecord, index);
|
35461 | }
|
35462 | else {
|
35463 | // Attempt to see if the item is at some posterior position of _itHead list.
|
35464 | record = this._linkedRecords === null ? null : this._linkedRecords.get(itemTrackBy, index);
|
35465 | if (record !== null) {
|
35466 | // We have the item in _itHead at/after `index` position. We need to move it forward in the
|
35467 | // collection.
|
35468 | // But first we need to check if identity changed, so we can update in view if necessary.
|
35469 | if (!Object.is(record.item, item))
|
35470 | this._addIdentityChange(record, item);
|
35471 | this._moveAfter(record, previousRecord, index);
|
35472 | }
|
35473 | else {
|
35474 | // It is a new item: add it.
|
35475 | record =
|
35476 | this._addAfter(new IterableChangeRecord_(item, itemTrackBy), previousRecord, index);
|
35477 | }
|
35478 | }
|
35479 | return record;
|
35480 | }
|
35481 | /**
|
35482 | * This check is only needed if an array contains duplicates. (Short circuit of nothing dirty)
|
35483 | *
|
35484 | * Use case: `[a, a]` => `[b, a, a]`
|
35485 | *
|
35486 | * If we did not have this check then the insertion of `b` would:
|
35487 | * 1) evict first `a`
|
35488 | * 2) insert `b` at `0` index.
|
35489 | * 3) leave `a` at index `1` as is. <-- this is wrong!
|
35490 | * 3) reinsert `a` at index 2. <-- this is wrong!
|
35491 | *
|
35492 | * The correct behavior is:
|
35493 | * 1) evict first `a`
|
35494 | * 2) insert `b` at `0` index.
|
35495 | * 3) reinsert `a` at index 1.
|
35496 | * 3) move `a` at from `1` to `2`.
|
35497 | *
|
35498 | *
|
35499 | * Double check that we have not evicted a duplicate item. We need to check if the item type may
|
35500 | * have already been removed:
|
35501 | * The insertion of b will evict the first 'a'. If we don't reinsert it now it will be reinserted
|
35502 | * at the end. Which will show up as the two 'a's switching position. This is incorrect, since a
|
35503 | * better way to think of it is as insert of 'b' rather then switch 'a' with 'b' and then add 'a'
|
35504 | * at the end.
|
35505 | *
|
35506 | * @internal
|
35507 | */
|
35508 | _verifyReinsertion(record, item, itemTrackBy, index) {
|
35509 | let reinsertRecord = this._unlinkedRecords === null ? null : this._unlinkedRecords.get(itemTrackBy, null);
|
35510 | if (reinsertRecord !== null) {
|
35511 | record = this._reinsertAfter(reinsertRecord, record._prev, index);
|
35512 | }
|
35513 | else if (record.currentIndex != index) {
|
35514 | record.currentIndex = index;
|
35515 | this._addToMoves(record, index);
|
35516 | }
|
35517 | return record;
|
35518 | }
|
35519 | /**
|
35520 | * Get rid of any excess {@link IterableChangeRecord_}s from the previous collection
|
35521 | *
|
35522 | * - `record` The first excess {@link IterableChangeRecord_}.
|
35523 | *
|
35524 | * @internal
|
35525 | */
|
35526 | _truncate(record) {
|
35527 | // Anything after that needs to be removed;
|
35528 | while (record !== null) {
|
35529 | const nextRecord = record._next;
|
35530 | this._addToRemovals(this._unlink(record));
|
35531 | record = nextRecord;
|
35532 | }
|
35533 | if (this._unlinkedRecords !== null) {
|
35534 | this._unlinkedRecords.clear();
|
35535 | }
|
35536 | if (this._additionsTail !== null) {
|
35537 | this._additionsTail._nextAdded = null;
|
35538 | }
|
35539 | if (this._movesTail !== null) {
|
35540 | this._movesTail._nextMoved = null;
|
35541 | }
|
35542 | if (this._itTail !== null) {
|
35543 | this._itTail._next = null;
|
35544 | }
|
35545 | if (this._removalsTail !== null) {
|
35546 | this._removalsTail._nextRemoved = null;
|
35547 | }
|
35548 | if (this._identityChangesTail !== null) {
|
35549 | this._identityChangesTail._nextIdentityChange = null;
|
35550 | }
|
35551 | }
|
35552 | /** @internal */
|
35553 | _reinsertAfter(record, prevRecord, index) {
|
35554 | if (this._unlinkedRecords !== null) {
|
35555 | this._unlinkedRecords.remove(record);
|
35556 | }
|
35557 | const prev = record._prevRemoved;
|
35558 | const next = record._nextRemoved;
|
35559 | if (prev === null) {
|
35560 | this._removalsHead = next;
|
35561 | }
|
35562 | else {
|
35563 | prev._nextRemoved = next;
|
35564 | }
|
35565 | if (next === null) {
|
35566 | this._removalsTail = prev;
|
35567 | }
|
35568 | else {
|
35569 | next._prevRemoved = prev;
|
35570 | }
|
35571 | this._insertAfter(record, prevRecord, index);
|
35572 | this._addToMoves(record, index);
|
35573 | return record;
|
35574 | }
|
35575 | /** @internal */
|
35576 | _moveAfter(record, prevRecord, index) {
|
35577 | this._unlink(record);
|
35578 | this._insertAfter(record, prevRecord, index);
|
35579 | this._addToMoves(record, index);
|
35580 | return record;
|
35581 | }
|
35582 | /** @internal */
|
35583 | _addAfter(record, prevRecord, index) {
|
35584 | this._insertAfter(record, prevRecord, index);
|
35585 | if (this._additionsTail === null) {
|
35586 | // TODO(vicb):
|
35587 | // assert(this._additionsHead === null);
|
35588 | this._additionsTail = this._additionsHead = record;
|
35589 | }
|
35590 | else {
|
35591 | // TODO(vicb):
|
35592 | // assert(_additionsTail._nextAdded === null);
|
35593 | // assert(record._nextAdded === null);
|
35594 | this._additionsTail = this._additionsTail._nextAdded = record;
|
35595 | }
|
35596 | return record;
|
35597 | }
|
35598 | /** @internal */
|
35599 | _insertAfter(record, prevRecord, index) {
|
35600 | // TODO(vicb):
|
35601 | // assert(record != prevRecord);
|
35602 | // assert(record._next === null);
|
35603 | // assert(record._prev === null);
|
35604 | const next = prevRecord === null ? this._itHead : prevRecord._next;
|
35605 | // TODO(vicb):
|
35606 | // assert(next != record);
|
35607 | // assert(prevRecord != record);
|
35608 | record._next = next;
|
35609 | record._prev = prevRecord;
|
35610 | if (next === null) {
|
35611 | this._itTail = record;
|
35612 | }
|
35613 | else {
|
35614 | next._prev = record;
|
35615 | }
|
35616 | if (prevRecord === null) {
|
35617 | this._itHead = record;
|
35618 | }
|
35619 | else {
|
35620 | prevRecord._next = record;
|
35621 | }
|
35622 | if (this._linkedRecords === null) {
|
35623 | this._linkedRecords = new _DuplicateMap();
|
35624 | }
|
35625 | this._linkedRecords.put(record);
|
35626 | record.currentIndex = index;
|
35627 | return record;
|
35628 | }
|
35629 | /** @internal */
|
35630 | _remove(record) {
|
35631 | return this._addToRemovals(this._unlink(record));
|
35632 | }
|
35633 | /** @internal */
|
35634 | _unlink(record) {
|
35635 | if (this._linkedRecords !== null) {
|
35636 | this._linkedRecords.remove(record);
|
35637 | }
|
35638 | const prev = record._prev;
|
35639 | const next = record._next;
|
35640 | // TODO(vicb):
|
35641 | // assert((record._prev = null) === null);
|
35642 | // assert((record._next = null) === null);
|
35643 | if (prev === null) {
|
35644 | this._itHead = next;
|
35645 | }
|
35646 | else {
|
35647 | prev._next = next;
|
35648 | }
|
35649 | if (next === null) {
|
35650 | this._itTail = prev;
|
35651 | }
|
35652 | else {
|
35653 | next._prev = prev;
|
35654 | }
|
35655 | return record;
|
35656 | }
|
35657 | /** @internal */
|
35658 | _addToMoves(record, toIndex) {
|
35659 | // TODO(vicb):
|
35660 | // assert(record._nextMoved === null);
|
35661 | if (record.previousIndex === toIndex) {
|
35662 | return record;
|
35663 | }
|
35664 | if (this._movesTail === null) {
|
35665 | // TODO(vicb):
|
35666 | // assert(_movesHead === null);
|
35667 | this._movesTail = this._movesHead = record;
|
35668 | }
|
35669 | else {
|
35670 | // TODO(vicb):
|
35671 | // assert(_movesTail._nextMoved === null);
|
35672 | this._movesTail = this._movesTail._nextMoved = record;
|
35673 | }
|
35674 | return record;
|
35675 | }
|
35676 | _addToRemovals(record) {
|
35677 | if (this._unlinkedRecords === null) {
|
35678 | this._unlinkedRecords = new _DuplicateMap();
|
35679 | }
|
35680 | this._unlinkedRecords.put(record);
|
35681 | record.currentIndex = null;
|
35682 | record._nextRemoved = null;
|
35683 | if (this._removalsTail === null) {
|
35684 | // TODO(vicb):
|
35685 | // assert(_removalsHead === null);
|
35686 | this._removalsTail = this._removalsHead = record;
|
35687 | record._prevRemoved = null;
|
35688 | }
|
35689 | else {
|
35690 | // TODO(vicb):
|
35691 | // assert(_removalsTail._nextRemoved === null);
|
35692 | // assert(record._nextRemoved === null);
|
35693 | record._prevRemoved = this._removalsTail;
|
35694 | this._removalsTail = this._removalsTail._nextRemoved = record;
|
35695 | }
|
35696 | return record;
|
35697 | }
|
35698 | /** @internal */
|
35699 | _addIdentityChange(record, item) {
|
35700 | record.item = item;
|
35701 | if (this._identityChangesTail === null) {
|
35702 | this._identityChangesTail = this._identityChangesHead = record;
|
35703 | }
|
35704 | else {
|
35705 | this._identityChangesTail = this._identityChangesTail._nextIdentityChange = record;
|
35706 | }
|
35707 | return record;
|
35708 | }
|
35709 | }
|
35710 | class IterableChangeRecord_ {
|
35711 | constructor(item, trackById) {
|
35712 | this.item = item;
|
35713 | this.trackById = trackById;
|
35714 | this.currentIndex = null;
|
35715 | this.previousIndex = null;
|
35716 | /** @internal */
|
35717 | this._nextPrevious = null;
|
35718 | /** @internal */
|
35719 | this._prev = null;
|
35720 | /** @internal */
|
35721 | this._next = null;
|
35722 | /** @internal */
|
35723 | this._prevDup = null;
|
35724 | /** @internal */
|
35725 | this._nextDup = null;
|
35726 | /** @internal */
|
35727 | this._prevRemoved = null;
|
35728 | /** @internal */
|
35729 | this._nextRemoved = null;
|
35730 | /** @internal */
|
35731 | this._nextAdded = null;
|
35732 | /** @internal */
|
35733 | this._nextMoved = null;
|
35734 | /** @internal */
|
35735 | this._nextIdentityChange = null;
|
35736 | }
|
35737 | }
|
35738 | // A linked list of IterableChangeRecords with the same IterableChangeRecord_.item
|
35739 | class _DuplicateItemRecordList {
|
35740 | constructor() {
|
35741 | /** @internal */
|
35742 | this._head = null;
|
35743 | /** @internal */
|
35744 | this._tail = null;
|
35745 | }
|
35746 | /**
|
35747 | * Append the record to the list of duplicates.
|
35748 | *
|
35749 | * Note: by design all records in the list of duplicates hold the same value in record.item.
|
35750 | */
|
35751 | add(record) {
|
35752 | if (this._head === null) {
|
35753 | this._head = this._tail = record;
|
35754 | record._nextDup = null;
|
35755 | record._prevDup = null;
|
35756 | }
|
35757 | else {
|
35758 | // TODO(vicb):
|
35759 | // assert(record.item == _head.item ||
|
35760 | // record.item is num && record.item.isNaN && _head.item is num && _head.item.isNaN);
|
35761 | this._tail._nextDup = record;
|
35762 | record._prevDup = this._tail;
|
35763 | record._nextDup = null;
|
35764 | this._tail = record;
|
35765 | }
|
35766 | }
|
35767 | // Returns a IterableChangeRecord_ having IterableChangeRecord_.trackById == trackById and
|
35768 | // IterableChangeRecord_.currentIndex >= atOrAfterIndex
|
35769 | get(trackById, atOrAfterIndex) {
|
35770 | let record;
|
35771 | for (record = this._head; record !== null; record = record._nextDup) {
|
35772 | if ((atOrAfterIndex === null || atOrAfterIndex <= record.currentIndex) &&
|
35773 | Object.is(record.trackById, trackById)) {
|
35774 | return record;
|
35775 | }
|
35776 | }
|
35777 | return null;
|
35778 | }
|
35779 | /**
|
35780 | * Remove one {@link IterableChangeRecord_} from the list of duplicates.
|
35781 | *
|
35782 | * Returns whether the list of duplicates is empty.
|
35783 | */
|
35784 | remove(record) {
|
35785 | // TODO(vicb):
|
35786 | // assert(() {
|
35787 | // // verify that the record being removed is in the list.
|
35788 | // for (IterableChangeRecord_ cursor = _head; cursor != null; cursor = cursor._nextDup) {
|
35789 | // if (identical(cursor, record)) return true;
|
35790 | // }
|
35791 | // return false;
|
35792 | //});
|
35793 | const prev = record._prevDup;
|
35794 | const next = record._nextDup;
|
35795 | if (prev === null) {
|
35796 | this._head = next;
|
35797 | }
|
35798 | else {
|
35799 | prev._nextDup = next;
|
35800 | }
|
35801 | if (next === null) {
|
35802 | this._tail = prev;
|
35803 | }
|
35804 | else {
|
35805 | next._prevDup = prev;
|
35806 | }
|
35807 | return this._head === null;
|
35808 | }
|
35809 | }
|
35810 | class _DuplicateMap {
|
35811 | constructor() {
|
35812 | this.map = new Map();
|
35813 | }
|
35814 | put(record) {
|
35815 | const key = record.trackById;
|
35816 | let duplicates = this.map.get(key);
|
35817 | if (!duplicates) {
|
35818 | duplicates = new _DuplicateItemRecordList();
|
35819 | this.map.set(key, duplicates);
|
35820 | }
|
35821 | duplicates.add(record);
|
35822 | }
|
35823 | /**
|
35824 | * Retrieve the `value` using key. Because the IterableChangeRecord_ value may be one which we
|
35825 | * have already iterated over, we use the `atOrAfterIndex` to pretend it is not there.
|
35826 | *
|
35827 | * Use case: `[a, b, c, a, a]` if we are at index `3` which is the second `a` then asking if we
|
35828 | * have any more `a`s needs to return the second `a`.
|
35829 | */
|
35830 | get(trackById, atOrAfterIndex) {
|
35831 | const key = trackById;
|
35832 | const recordList = this.map.get(key);
|
35833 | return recordList ? recordList.get(trackById, atOrAfterIndex) : null;
|
35834 | }
|
35835 | /**
|
35836 | * Removes a {@link IterableChangeRecord_} from the list of duplicates.
|
35837 | *
|
35838 | * The list of duplicates also is removed from the map if it gets empty.
|
35839 | */
|
35840 | remove(record) {
|
35841 | const key = record.trackById;
|
35842 | const recordList = this.map.get(key);
|
35843 | // Remove the list of duplicates when it gets empty
|
35844 | if (recordList.remove(record)) {
|
35845 | this.map.delete(key);
|
35846 | }
|
35847 | return record;
|
35848 | }
|
35849 | get isEmpty() {
|
35850 | return this.map.size === 0;
|
35851 | }
|
35852 | clear() {
|
35853 | this.map.clear();
|
35854 | }
|
35855 | }
|
35856 | function getPreviousIndex(item, addRemoveOffset, moveOffsets) {
|
35857 | const previousIndex = item.previousIndex;
|
35858 | if (previousIndex === null)
|
35859 | return previousIndex;
|
35860 | let moveOffset = 0;
|
35861 | if (moveOffsets && previousIndex < moveOffsets.length) {
|
35862 | moveOffset = moveOffsets[previousIndex];
|
35863 | }
|
35864 | return previousIndex + addRemoveOffset + moveOffset;
|
35865 | }
|
35866 |
|
35867 | /**
|
35868 | * @license
|
35869 | * Copyright Google LLC All Rights Reserved.
|
35870 | *
|
35871 | * Use of this source code is governed by an MIT-style license that can be
|
35872 | * found in the LICENSE file at https://angular.io/license
|
35873 | */
|
35874 | class DefaultKeyValueDifferFactory {
|
35875 | constructor() { }
|
35876 | supports(obj) {
|
35877 | return obj instanceof Map || isJsObject(obj);
|
35878 | }
|
35879 | create() {
|
35880 | return new DefaultKeyValueDiffer();
|
35881 | }
|
35882 | }
|
35883 | class DefaultKeyValueDiffer {
|
35884 | constructor() {
|
35885 | this._records = new Map();
|
35886 | this._mapHead = null;
|
35887 | // _appendAfter is used in the check loop
|
35888 | this._appendAfter = null;
|
35889 | this._previousMapHead = null;
|
35890 | this._changesHead = null;
|
35891 | this._changesTail = null;
|
35892 | this._additionsHead = null;
|
35893 | this._additionsTail = null;
|
35894 | this._removalsHead = null;
|
35895 | this._removalsTail = null;
|
35896 | }
|
35897 | get isDirty() {
|
35898 | return this._additionsHead !== null || this._changesHead !== null ||
|
35899 | this._removalsHead !== null;
|
35900 | }
|
35901 | forEachItem(fn) {
|
35902 | let record;
|
35903 | for (record = this._mapHead; record !== null; record = record._next) {
|
35904 | fn(record);
|
35905 | }
|
35906 | }
|
35907 | forEachPreviousItem(fn) {
|
35908 | let record;
|
35909 | for (record = this._previousMapHead; record !== null; record = record._nextPrevious) {
|
35910 | fn(record);
|
35911 | }
|
35912 | }
|
35913 | forEachChangedItem(fn) {
|
35914 | let record;
|
35915 | for (record = this._changesHead; record !== null; record = record._nextChanged) {
|
35916 | fn(record);
|
35917 | }
|
35918 | }
|
35919 | forEachAddedItem(fn) {
|
35920 | let record;
|
35921 | for (record = this._additionsHead; record !== null; record = record._nextAdded) {
|
35922 | fn(record);
|
35923 | }
|
35924 | }
|
35925 | forEachRemovedItem(fn) {
|
35926 | let record;
|
35927 | for (record = this._removalsHead; record !== null; record = record._nextRemoved) {
|
35928 | fn(record);
|
35929 | }
|
35930 | }
|
35931 | diff(map) {
|
35932 | if (!map) {
|
35933 | map = new Map();
|
35934 | }
|
35935 | else if (!(map instanceof Map || isJsObject(map))) {
|
35936 | throw new Error(`Error trying to diff '${stringify$1(map)}'. Only maps and objects are allowed`);
|
35937 | }
|
35938 | return this.check(map) ? this : null;
|
35939 | }
|
35940 | onDestroy() { }
|
35941 | /**
|
35942 | * Check the current state of the map vs the previous.
|
35943 | * The algorithm is optimised for when the keys do no change.
|
35944 | */
|
35945 | check(map) {
|
35946 | this._reset();
|
35947 | let insertBefore = this._mapHead;
|
35948 | this._appendAfter = null;
|
35949 | this._forEach(map, (value, key) => {
|
35950 | if (insertBefore && insertBefore.key === key) {
|
35951 | this._maybeAddToChanges(insertBefore, value);
|
35952 | this._appendAfter = insertBefore;
|
35953 | insertBefore = insertBefore._next;
|
35954 | }
|
35955 | else {
|
35956 | const record = this._getOrCreateRecordForKey(key, value);
|
35957 | insertBefore = this._insertBeforeOrAppend(insertBefore, record);
|
35958 | }
|
35959 | });
|
35960 | // Items remaining at the end of the list have been deleted
|
35961 | if (insertBefore) {
|
35962 | if (insertBefore._prev) {
|
35963 | insertBefore._prev._next = null;
|
35964 | }
|
35965 | this._removalsHead = insertBefore;
|
35966 | for (let record = insertBefore; record !== null; record = record._nextRemoved) {
|
35967 | if (record === this._mapHead) {
|
35968 | this._mapHead = null;
|
35969 | }
|
35970 | this._records.delete(record.key);
|
35971 | record._nextRemoved = record._next;
|
35972 | record.previousValue = record.currentValue;
|
35973 | record.currentValue = null;
|
35974 | record._prev = null;
|
35975 | record._next = null;
|
35976 | }
|
35977 | }
|
35978 | // Make sure tails have no next records from previous runs
|
35979 | if (this._changesTail)
|
35980 | this._changesTail._nextChanged = null;
|
35981 | if (this._additionsTail)
|
35982 | this._additionsTail._nextAdded = null;
|
35983 | return this.isDirty;
|
35984 | }
|
35985 | /**
|
35986 | * Inserts a record before `before` or append at the end of the list when `before` is null.
|
35987 | *
|
35988 | * Notes:
|
35989 | * - This method appends at `this._appendAfter`,
|
35990 | * - This method updates `this._appendAfter`,
|
35991 | * - The return value is the new value for the insertion pointer.
|
35992 | */
|
35993 | _insertBeforeOrAppend(before, record) {
|
35994 | if (before) {
|
35995 | const prev = before._prev;
|
35996 | record._next = before;
|
35997 | record._prev = prev;
|
35998 | before._prev = record;
|
35999 | if (prev) {
|
36000 | prev._next = record;
|
36001 | }
|
36002 | if (before === this._mapHead) {
|
36003 | this._mapHead = record;
|
36004 | }
|
36005 | this._appendAfter = before;
|
36006 | return before;
|
36007 | }
|
36008 | if (this._appendAfter) {
|
36009 | this._appendAfter._next = record;
|
36010 | record._prev = this._appendAfter;
|
36011 | }
|
36012 | else {
|
36013 | this._mapHead = record;
|
36014 | }
|
36015 | this._appendAfter = record;
|
36016 | return null;
|
36017 | }
|
36018 | _getOrCreateRecordForKey(key, value) {
|
36019 | if (this._records.has(key)) {
|
36020 | const record = this._records.get(key);
|
36021 | this._maybeAddToChanges(record, value);
|
36022 | const prev = record._prev;
|
36023 | const next = record._next;
|
36024 | if (prev) {
|
36025 | prev._next = next;
|
36026 | }
|
36027 | if (next) {
|
36028 | next._prev = prev;
|
36029 | }
|
36030 | record._next = null;
|
36031 | record._prev = null;
|
36032 | return record;
|
36033 | }
|
36034 | const record = new KeyValueChangeRecord_(key);
|
36035 | this._records.set(key, record);
|
36036 | record.currentValue = value;
|
36037 | this._addToAdditions(record);
|
36038 | return record;
|
36039 | }
|
36040 | /** @internal */
|
36041 | _reset() {
|
36042 | if (this.isDirty) {
|
36043 | let record;
|
36044 | // let `_previousMapHead` contain the state of the map before the changes
|
36045 | this._previousMapHead = this._mapHead;
|
36046 | for (record = this._previousMapHead; record !== null; record = record._next) {
|
36047 | record._nextPrevious = record._next;
|
36048 | }
|
36049 | // Update `record.previousValue` with the value of the item before the changes
|
36050 | // We need to update all changed items (that's those which have been added and changed)
|
36051 | for (record = this._changesHead; record !== null; record = record._nextChanged) {
|
36052 | record.previousValue = record.currentValue;
|
36053 | }
|
36054 | for (record = this._additionsHead; record != null; record = record._nextAdded) {
|
36055 | record.previousValue = record.currentValue;
|
36056 | }
|
36057 | this._changesHead = this._changesTail = null;
|
36058 | this._additionsHead = this._additionsTail = null;
|
36059 | this._removalsHead = null;
|
36060 | }
|
36061 | }
|
36062 | // Add the record or a given key to the list of changes only when the value has actually changed
|
36063 | _maybeAddToChanges(record, newValue) {
|
36064 | if (!Object.is(newValue, record.currentValue)) {
|
36065 | record.previousValue = record.currentValue;
|
36066 | record.currentValue = newValue;
|
36067 | this._addToChanges(record);
|
36068 | }
|
36069 | }
|
36070 | _addToAdditions(record) {
|
36071 | if (this._additionsHead === null) {
|
36072 | this._additionsHead = this._additionsTail = record;
|
36073 | }
|
36074 | else {
|
36075 | this._additionsTail._nextAdded = record;
|
36076 | this._additionsTail = record;
|
36077 | }
|
36078 | }
|
36079 | _addToChanges(record) {
|
36080 | if (this._changesHead === null) {
|
36081 | this._changesHead = this._changesTail = record;
|
36082 | }
|
36083 | else {
|
36084 | this._changesTail._nextChanged = record;
|
36085 | this._changesTail = record;
|
36086 | }
|
36087 | }
|
36088 | /** @internal */
|
36089 | _forEach(obj, fn) {
|
36090 | if (obj instanceof Map) {
|
36091 | obj.forEach(fn);
|
36092 | }
|
36093 | else {
|
36094 | Object.keys(obj).forEach(k => fn(obj[k], k));
|
36095 | }
|
36096 | }
|
36097 | }
|
36098 | class KeyValueChangeRecord_ {
|
36099 | constructor(key) {
|
36100 | this.key = key;
|
36101 | this.previousValue = null;
|
36102 | this.currentValue = null;
|
36103 | /** @internal */
|
36104 | this._nextPrevious = null;
|
36105 | /** @internal */
|
36106 | this._next = null;
|
36107 | /** @internal */
|
36108 | this._prev = null;
|
36109 | /** @internal */
|
36110 | this._nextAdded = null;
|
36111 | /** @internal */
|
36112 | this._nextRemoved = null;
|
36113 | /** @internal */
|
36114 | this._nextChanged = null;
|
36115 | }
|
36116 | }
|
36117 |
|
36118 | /**
|
36119 | * @license
|
36120 | * Copyright Google LLC All Rights Reserved.
|
36121 | *
|
36122 | * Use of this source code is governed by an MIT-style license that can be
|
36123 | * found in the LICENSE file at https://angular.io/license
|
36124 | */
|
36125 | function defaultIterableDiffersFactory() {
|
36126 | return new IterableDiffers([new DefaultIterableDifferFactory()]);
|
36127 | }
|
36128 | /**
|
36129 | * A repository of different iterable diffing strategies used by NgFor, NgClass, and others.
|
36130 | *
|
36131 | * @publicApi
|
36132 | */
|
36133 | class IterableDiffers {
|
36134 | constructor(factories) {
|
36135 | this.factories = factories;
|
36136 | }
|
36137 | static create(factories, parent) {
|
36138 | if (parent != null) {
|
36139 | const copied = parent.factories.slice();
|
36140 | factories = factories.concat(copied);
|
36141 | }
|
36142 | return new IterableDiffers(factories);
|
36143 | }
|
36144 | /**
|
36145 | * Takes an array of {@link IterableDifferFactory} and returns a provider used to extend the
|
36146 | * inherited {@link IterableDiffers} instance with the provided factories and return a new
|
36147 | * {@link IterableDiffers} instance.
|
36148 | *
|
36149 | * @usageNotes
|
36150 | * ### Example
|
36151 | *
|
36152 | * The following example shows how to extend an existing list of factories,
|
36153 | * which will only be applied to the injector for this component and its children.
|
36154 | * This step is all that's required to make a new {@link IterableDiffer} available.
|
36155 | *
|
36156 | * ```
|
36157 | * @Component({
|
36158 | * viewProviders: [
|
36159 | * IterableDiffers.extend([new ImmutableListDiffer()])
|
36160 | * ]
|
36161 | * })
|
36162 | * ```
|
36163 | */
|
36164 | static extend(factories) {
|
36165 | return {
|
36166 | provide: IterableDiffers,
|
36167 | useFactory: (parent) => {
|
36168 | // if parent is null, it means that we are in the root injector and we have just overridden
|
36169 | // the default injection mechanism for IterableDiffers, in such a case just assume
|
36170 | // `defaultIterableDiffersFactory`.
|
36171 | return IterableDiffers.create(factories, parent || defaultIterableDiffersFactory());
|
36172 | },
|
36173 | // Dependency technically isn't optional, but we can provide a better error message this way.
|
36174 | deps: [[IterableDiffers, new SkipSelf(), new Optional()]]
|
36175 | };
|
36176 | }
|
36177 | find(iterable) {
|
36178 | const factory = this.factories.find(f => f.supports(iterable));
|
36179 | if (factory != null) {
|
36180 | return factory;
|
36181 | }
|
36182 | else {
|
36183 | throw new Error(`Cannot find a differ supporting object '${iterable}' of type '${getTypeNameForDebugging(iterable)}'`);
|
36184 | }
|
36185 | }
|
36186 | }
|
36187 | /** @nocollapse */
|
36188 | IterableDiffers.ɵprov = ɵɵdefineInjectable({ token: IterableDiffers, providedIn: 'root', factory: defaultIterableDiffersFactory });
|
36189 | function getTypeNameForDebugging(type) {
|
36190 | return type['name'] || typeof type;
|
36191 | }
|
36192 |
|
36193 | /**
|
36194 | * @license
|
36195 | * Copyright Google LLC All Rights Reserved.
|
36196 | *
|
36197 | * Use of this source code is governed by an MIT-style license that can be
|
36198 | * found in the LICENSE file at https://angular.io/license
|
36199 | */
|
36200 | function defaultKeyValueDiffersFactory() {
|
36201 | return new KeyValueDiffers([new DefaultKeyValueDifferFactory()]);
|
36202 | }
|
36203 | /**
|
36204 | * A repository of different Map diffing strategies used by NgClass, NgStyle, and others.
|
36205 | *
|
36206 | * @publicApi
|
36207 | */
|
36208 | class KeyValueDiffers {
|
36209 | constructor(factories) {
|
36210 | this.factories = factories;
|
36211 | }
|
36212 | static create(factories, parent) {
|
36213 | if (parent) {
|
36214 | const copied = parent.factories.slice();
|
36215 | factories = factories.concat(copied);
|
36216 | }
|
36217 | return new KeyValueDiffers(factories);
|
36218 | }
|
36219 | /**
|
36220 | * Takes an array of {@link KeyValueDifferFactory} and returns a provider used to extend the
|
36221 | * inherited {@link KeyValueDiffers} instance with the provided factories and return a new
|
36222 | * {@link KeyValueDiffers} instance.
|
36223 | *
|
36224 | * @usageNotes
|
36225 | * ### Example
|
36226 | *
|
36227 | * The following example shows how to extend an existing list of factories,
|
36228 | * which will only be applied to the injector for this component and its children.
|
36229 | * This step is all that's required to make a new {@link KeyValueDiffer} available.
|
36230 | *
|
36231 | * ```
|
36232 | * @Component({
|
36233 | * viewProviders: [
|
36234 | * KeyValueDiffers.extend([new ImmutableMapDiffer()])
|
36235 | * ]
|
36236 | * })
|
36237 | * ```
|
36238 | */
|
36239 | static extend(factories) {
|
36240 | return {
|
36241 | provide: KeyValueDiffers,
|
36242 | useFactory: (parent) => {
|
36243 | // if parent is null, it means that we are in the root injector and we have just overridden
|
36244 | // the default injection mechanism for KeyValueDiffers, in such a case just assume
|
36245 | // `defaultKeyValueDiffersFactory`.
|
36246 | return KeyValueDiffers.create(factories, parent || defaultKeyValueDiffersFactory());
|
36247 | },
|
36248 | // Dependency technically isn't optional, but we can provide a better error message this way.
|
36249 | deps: [[KeyValueDiffers, new SkipSelf(), new Optional()]]
|
36250 | };
|
36251 | }
|
36252 | find(kv) {
|
36253 | const factory = this.factories.find(f => f.supports(kv));
|
36254 | if (factory) {
|
36255 | return factory;
|
36256 | }
|
36257 | throw new Error(`Cannot find a differ supporting object '${kv}'`);
|
36258 | }
|
36259 | }
|
36260 | /** @nocollapse */
|
36261 | KeyValueDiffers.ɵprov = ɵɵdefineInjectable({ token: KeyValueDiffers, providedIn: 'root', factory: defaultKeyValueDiffersFactory });
|
36262 |
|
36263 | /**
|
36264 | * @license
|
36265 | * Copyright Google LLC All Rights Reserved.
|
36266 | *
|
36267 | * Use of this source code is governed by an MIT-style license that can be
|
36268 | * found in the LICENSE file at https://angular.io/license
|
36269 | */
|
36270 | function collectNativeNodes(tView, lView, tNode, result, isProjection = false) {
|
36271 | while (tNode !== null) {
|
36272 | ngDevMode &&
|
36273 | assertTNodeType(tNode, 3 /* AnyRNode */ | 12 /* AnyContainer */ | 16 /* Projection */ | 32 /* Icu */);
|
36274 | const lNode = lView[tNode.index];
|
36275 | if (lNode !== null) {
|
36276 | result.push(unwrapRNode(lNode));
|
36277 | }
|
36278 | // A given lNode can represent either a native node or a LContainer (when it is a host of a
|
36279 | // ViewContainerRef). When we find a LContainer we need to descend into it to collect root nodes
|
36280 | // from the views in this container.
|
36281 | if (isLContainer(lNode)) {
|
36282 | for (let i = CONTAINER_HEADER_OFFSET; i < lNode.length; i++) {
|
36283 | const lViewInAContainer = lNode[i];
|
36284 | const lViewFirstChildTNode = lViewInAContainer[TVIEW].firstChild;
|
36285 | if (lViewFirstChildTNode !== null) {
|
36286 | collectNativeNodes(lViewInAContainer[TVIEW], lViewInAContainer, lViewFirstChildTNode, result);
|
36287 | }
|
36288 | }
|
36289 | }
|
36290 | const tNodeType = tNode.type;
|
36291 | if (tNodeType & 8 /* ElementContainer */) {
|
36292 | collectNativeNodes(tView, lView, tNode.child, result);
|
36293 | }
|
36294 | else if (tNodeType & 32 /* Icu */) {
|
36295 | const nextRNode = icuContainerIterate();
|
36296 | let rNode;
|
36297 | while (rNode = nextRNode()) {
|
36298 | result.push(rNode);
|
36299 | }
|
36300 | }
|
36301 | else if (tNodeType & 16 /* Projection */) {
|
36302 | const nodesInSlot = getProjectionNodes(lView, tNode);
|
36303 | if (Array.isArray(nodesInSlot)) {
|
36304 | result.push(...nodesInSlot);
|
36305 | }
|
36306 | else {
|
36307 | const parentView = getLViewParent(lView[DECLARATION_COMPONENT_VIEW]);
|
36308 | ngDevMode && assertParentView(parentView);
|
36309 | collectNativeNodes(parentView[TVIEW], parentView, nodesInSlot, result, true);
|
36310 | }
|
36311 | }
|
36312 | tNode = isProjection ? tNode.projectionNext : tNode.next;
|
36313 | }
|
36314 | return result;
|
36315 | }
|
36316 |
|
36317 | /**
|
36318 | * @license
|
36319 | * Copyright Google LLC All Rights Reserved.
|
36320 | *
|
36321 | * Use of this source code is governed by an MIT-style license that can be
|
36322 | * found in the LICENSE file at https://angular.io/license
|
36323 | */
|
36324 | class ViewRef {
|
36325 | constructor(
|
36326 | /**
|
36327 | * This represents `LView` associated with the component when ViewRef is a ChangeDetectorRef.
|
36328 | *
|
36329 | * When ViewRef is created for a dynamic component, this also represents the `LView` for the
|
36330 | * component.
|
36331 | *
|
36332 | * For a "regular" ViewRef created for an embedded view, this is the `LView` for the embedded
|
36333 | * view.
|
36334 | *
|
36335 | * @internal
|
36336 | */
|
36337 | _lView,
|
36338 | /**
|
36339 | * This represents the `LView` associated with the point where `ChangeDetectorRef` was
|
36340 | * requested.
|
36341 | *
|
36342 | * This may be different from `_lView` if the `_cdRefInjectingView` is an embedded view.
|
36343 | */
|
36344 | _cdRefInjectingView) {
|
36345 | this._lView = _lView;
|
36346 | this._cdRefInjectingView = _cdRefInjectingView;
|
36347 | this._appRef = null;
|
36348 | this._attachedToViewContainer = false;
|
36349 | }
|
36350 | get rootNodes() {
|
36351 | const lView = this._lView;
|
36352 | const tView = lView[TVIEW];
|
36353 | return collectNativeNodes(tView, lView, tView.firstChild, []);
|
36354 | }
|
36355 | get context() {
|
36356 | return this._lView[CONTEXT];
|
36357 | }
|
36358 | get destroyed() {
|
36359 | return (this._lView[FLAGS] & 256 /* Destroyed */) === 256 /* Destroyed */;
|
36360 | }
|
36361 | destroy() {
|
36362 | if (this._appRef) {
|
36363 | this._appRef.detachView(this);
|
36364 | }
|
36365 | else if (this._attachedToViewContainer) {
|
36366 | const parent = this._lView[PARENT];
|
36367 | if (isLContainer(parent)) {
|
36368 | const viewRefs = parent[VIEW_REFS];
|
36369 | const index = viewRefs ? viewRefs.indexOf(this) : -1;
|
36370 | if (index > -1) {
|
36371 | ngDevMode &&
|
36372 | assertEqual(index, parent.indexOf(this._lView) - CONTAINER_HEADER_OFFSET, 'An attached view should be in the same position within its container as its ViewRef in the VIEW_REFS array.');
|
36373 | detachView(parent, index);
|
36374 | removeFromArray(viewRefs, index);
|
36375 | }
|
36376 | }
|
36377 | this._attachedToViewContainer = false;
|
36378 | }
|
36379 | destroyLView(this._lView[TVIEW], this._lView);
|
36380 | }
|
36381 | onDestroy(callback) {
|
36382 | storeCleanupWithContext(this._lView[TVIEW], this._lView, null, callback);
|
36383 | }
|
36384 | /**
|
36385 | * Marks a view and all of its ancestors dirty.
|
36386 | *
|
36387 | * It also triggers change detection by calling `scheduleTick` internally, which coalesces
|
36388 | * multiple `markForCheck` calls to into one change detection run.
|
36389 | *
|
36390 | * This can be used to ensure an {@link ChangeDetectionStrategy#OnPush OnPush} component is
|
36391 | * checked when it needs to be re-rendered but the two normal triggers haven't marked it
|
36392 | * dirty (i.e. inputs haven't changed and events haven't fired in the view).
|
36393 | *
|
36394 | * <!-- TODO: Add a link to a chapter on OnPush components -->
|
36395 | *
|
36396 | * @usageNotes
|
36397 | * ### Example
|
36398 | *
|
36399 | * ```typescript
|
36400 | * @Component({
|
36401 | * selector: 'my-app',
|
36402 | * template: `Number of ticks: {{numberOfTicks}}`
|
36403 | * changeDetection: ChangeDetectionStrategy.OnPush,
|
36404 | * })
|
36405 | * class AppComponent {
|
36406 | * numberOfTicks = 0;
|
36407 | *
|
36408 | * constructor(private ref: ChangeDetectorRef) {
|
36409 | * setInterval(() => {
|
36410 | * this.numberOfTicks++;
|
36411 | * // the following is required, otherwise the view will not be updated
|
36412 | * this.ref.markForCheck();
|
36413 | * }, 1000);
|
36414 | * }
|
36415 | * }
|
36416 | * ```
|
36417 | */
|
36418 | markForCheck() {
|
36419 | markViewDirty(this._cdRefInjectingView || this._lView);
|
36420 | }
|
36421 | /**
|
36422 | * Detaches the view from the change detection tree.
|
36423 | *
|
36424 | * Detached views will not be checked during change detection runs until they are
|
36425 | * re-attached, even if they are dirty. `detach` can be used in combination with
|
36426 | * {@link ChangeDetectorRef#detectChanges detectChanges} to implement local change
|
36427 | * detection checks.
|
36428 | *
|
36429 | * <!-- TODO: Add a link to a chapter on detach/reattach/local digest -->
|
36430 | * <!-- TODO: Add a live demo once ref.detectChanges is merged into master -->
|
36431 | *
|
36432 | * @usageNotes
|
36433 | * ### Example
|
36434 | *
|
36435 | * The following example defines a component with a large list of readonly data.
|
36436 | * Imagine the data changes constantly, many times per second. For performance reasons,
|
36437 | * we want to check and update the list every five seconds. We can do that by detaching
|
36438 | * the component's change detector and doing a local check every five seconds.
|
36439 | *
|
36440 | * ```typescript
|
36441 | * class DataProvider {
|
36442 | * // in a real application the returned data will be different every time
|
36443 | * get data() {
|
36444 | * return [1,2,3,4,5];
|
36445 | * }
|
36446 | * }
|
36447 | *
|
36448 | * @Component({
|
36449 | * selector: 'giant-list',
|
36450 | * template: `
|
36451 | * <li *ngFor="let d of dataProvider.data">Data {{d}}</li>
|
36452 | * `,
|
36453 | * })
|
36454 | * class GiantList {
|
36455 | * constructor(private ref: ChangeDetectorRef, private dataProvider: DataProvider) {
|
36456 | * ref.detach();
|
36457 | * setInterval(() => {
|
36458 | * this.ref.detectChanges();
|
36459 | * }, 5000);
|
36460 | * }
|
36461 | * }
|
36462 | *
|
36463 | * @Component({
|
36464 | * selector: 'app',
|
36465 | * providers: [DataProvider],
|
36466 | * template: `
|
36467 | * <giant-list><giant-list>
|
36468 | * `,
|
36469 | * })
|
36470 | * class App {
|
36471 | * }
|
36472 | * ```
|
36473 | */
|
36474 | detach() {
|
36475 | this._lView[FLAGS] &= ~128 /* Attached */;
|
36476 | }
|
36477 | /**
|
36478 | * Re-attaches a view to the change detection tree.
|
36479 | *
|
36480 | * This can be used to re-attach views that were previously detached from the tree
|
36481 | * using {@link ChangeDetectorRef#detach detach}. Views are attached to the tree by default.
|
36482 | *
|
36483 | * <!-- TODO: Add a link to a chapter on detach/reattach/local digest -->
|
36484 | *
|
36485 | * @usageNotes
|
36486 | * ### Example
|
36487 | *
|
36488 | * The following example creates a component displaying `live` data. The component will detach
|
36489 | * its change detector from the main change detector tree when the component's live property
|
36490 | * is set to false.
|
36491 | *
|
36492 | * ```typescript
|
36493 | * class DataProvider {
|
36494 | * data = 1;
|
36495 | *
|
36496 | * constructor() {
|
36497 | * setInterval(() => {
|
36498 | * this.data = this.data * 2;
|
36499 | * }, 500);
|
36500 | * }
|
36501 | * }
|
36502 | *
|
36503 | * @Component({
|
36504 | * selector: 'live-data',
|
36505 | * inputs: ['live'],
|
36506 | * template: 'Data: {{dataProvider.data}}'
|
36507 | * })
|
36508 | * class LiveData {
|
36509 | * constructor(private ref: ChangeDetectorRef, private dataProvider: DataProvider) {}
|
36510 | *
|
36511 | * set live(value) {
|
36512 | * if (value) {
|
36513 | * this.ref.reattach();
|
36514 | * } else {
|
36515 | * this.ref.detach();
|
36516 | * }
|
36517 | * }
|
36518 | * }
|
36519 | *
|
36520 | * @Component({
|
36521 | * selector: 'my-app',
|
36522 | * providers: [DataProvider],
|
36523 | * template: `
|
36524 | * Live Update: <input type="checkbox" [(ngModel)]="live">
|
36525 | * <live-data [live]="live"><live-data>
|
36526 | * `,
|
36527 | * })
|
36528 | * class AppComponent {
|
36529 | * live = true;
|
36530 | * }
|
36531 | * ```
|
36532 | */
|
36533 | reattach() {
|
36534 | this._lView[FLAGS] |= 128 /* Attached */;
|
36535 | }
|
36536 | /**
|
36537 | * Checks the view and its children.
|
36538 | *
|
36539 | * This can also be used in combination with {@link ChangeDetectorRef#detach detach} to implement
|
36540 | * local change detection checks.
|
36541 | *
|
36542 | * <!-- TODO: Add a link to a chapter on detach/reattach/local digest -->
|
36543 | * <!-- TODO: Add a live demo once ref.detectChanges is merged into master -->
|
36544 | *
|
36545 | * @usageNotes
|
36546 | * ### Example
|
36547 | *
|
36548 | * The following example defines a component with a large list of readonly data.
|
36549 | * Imagine, the data changes constantly, many times per second. For performance reasons,
|
36550 | * we want to check and update the list every five seconds.
|
36551 | *
|
36552 | * We can do that by detaching the component's change detector and doing a local change detection
|
36553 | * check every five seconds.
|
36554 | *
|
36555 | * See {@link ChangeDetectorRef#detach detach} for more information.
|
36556 | */
|
36557 | detectChanges() {
|
36558 | detectChangesInternal(this._lView[TVIEW], this._lView, this.context);
|
36559 | }
|
36560 | /**
|
36561 | * Checks the change detector and its children, and throws if any changes are detected.
|
36562 | *
|
36563 | * This is used in development mode to verify that running change detection doesn't
|
36564 | * introduce other changes.
|
36565 | */
|
36566 | checkNoChanges() {
|
36567 | checkNoChangesInternal(this._lView[TVIEW], this._lView, this.context);
|
36568 | }
|
36569 | attachToViewContainerRef() {
|
36570 | if (this._appRef) {
|
36571 | throw new Error('This view is already attached directly to the ApplicationRef!');
|
36572 | }
|
36573 | this._attachedToViewContainer = true;
|
36574 | }
|
36575 | detachFromAppRef() {
|
36576 | this._appRef = null;
|
36577 | renderDetachView(this._lView[TVIEW], this._lView);
|
36578 | }
|
36579 | attachToAppRef(appRef) {
|
36580 | if (this._attachedToViewContainer) {
|
36581 | throw new Error('This view is already attached to a ViewContainer!');
|
36582 | }
|
36583 | this._appRef = appRef;
|
36584 | }
|
36585 | }
|
36586 | /** @internal */
|
36587 | class RootViewRef extends ViewRef {
|
36588 | constructor(_view) {
|
36589 | super(_view);
|
36590 | this._view = _view;
|
36591 | }
|
36592 | detectChanges() {
|
36593 | detectChangesInRootView(this._view);
|
36594 | }
|
36595 | checkNoChanges() {
|
36596 | checkNoChangesInRootView(this._view);
|
36597 | }
|
36598 | get context() {
|
36599 | return null;
|
36600 | }
|
36601 | }
|
36602 |
|
36603 | /**
|
36604 | * @license
|
36605 | * Copyright Google LLC All Rights Reserved.
|
36606 | *
|
36607 | * Use of this source code is governed by an MIT-style license that can be
|
36608 | * found in the LICENSE file at https://angular.io/license
|
36609 | */
|
36610 | const SWITCH_CHANGE_DETECTOR_REF_FACTORY__PRE_R3__ = noop;
|
36611 | const SWITCH_CHANGE_DETECTOR_REF_FACTORY = SWITCH_CHANGE_DETECTOR_REF_FACTORY__PRE_R3__;
|
36612 | /**
|
36613 | * Base class that provides change detection functionality.
|
36614 | * A change-detection tree collects all views that are to be checked for changes.
|
36615 | * Use the methods to add and remove views from the tree, initiate change-detection,
|
36616 | * and explicitly mark views as _dirty_, meaning that they have changed and need to be re-rendered.
|
36617 | *
|
36618 | * @see [Using change detection hooks](guide/lifecycle-hooks#using-change-detection-hooks)
|
36619 | * @see [Defining custom change detection](guide/lifecycle-hooks#defining-custom-change-detection)
|
36620 | *
|
36621 | * @usageNotes
|
36622 | *
|
36623 | * The following examples demonstrate how to modify default change-detection behavior
|
36624 | * to perform explicit detection when needed.
|
36625 | *
|
36626 | * ### Use `markForCheck()` with `CheckOnce` strategy
|
36627 | *
|
36628 | * The following example sets the `OnPush` change-detection strategy for a component
|
36629 | * (`CheckOnce`, rather than the default `CheckAlways`), then forces a second check
|
36630 | * after an interval. See [live demo](https://plnkr.co/edit/GC512b?p=preview).
|
36631 | *
|
36632 | * <code-example path="core/ts/change_detect/change-detection.ts"
|
36633 | * region="mark-for-check"></code-example>
|
36634 | *
|
36635 | * ### Detach change detector to limit how often check occurs
|
36636 | *
|
36637 | * The following example defines a component with a large list of read-only data
|
36638 | * that is expected to change constantly, many times per second.
|
36639 | * To improve performance, we want to check and update the list
|
36640 | * less often than the changes actually occur. To do that, we detach
|
36641 | * the component's change detector and perform an explicit local check every five seconds.
|
36642 | *
|
36643 | * <code-example path="core/ts/change_detect/change-detection.ts" region="detach"></code-example>
|
36644 | *
|
36645 | *
|
36646 | * ### Reattaching a detached component
|
36647 | *
|
36648 | * The following example creates a component displaying live data.
|
36649 | * The component detaches its change detector from the main change detector tree
|
36650 | * when the `live` property is set to false, and reattaches it when the property
|
36651 | * becomes true.
|
36652 | *
|
36653 | * <code-example path="core/ts/change_detect/change-detection.ts" region="reattach"></code-example>
|
36654 | *
|
36655 | * @publicApi
|
36656 | */
|
36657 | class ChangeDetectorRef {
|
36658 | }
|
36659 | /**
|
36660 | * @internal
|
36661 | * @nocollapse
|
36662 | */
|
36663 | ChangeDetectorRef.__NG_ELEMENT_ID__ = SWITCH_CHANGE_DETECTOR_REF_FACTORY;
|
36664 | /**
|
36665 | * This marker is need so that the JIT compiler can correctly identify this class as special.
|
36666 | *
|
36667 | * @internal
|
36668 | * @nocollapse
|
36669 | */
|
36670 | ChangeDetectorRef.__ChangeDetectorRef__ = true;
|
36671 |
|
36672 | /**
|
36673 | * @license
|
36674 | * Copyright Google LLC All Rights Reserved.
|
36675 | *
|
36676 | * Use of this source code is governed by an MIT-style license that can be
|
36677 | * found in the LICENSE file at https://angular.io/license
|
36678 | */
|
36679 | /**
|
36680 | * Structural diffing for `Object`s and `Map`s.
|
36681 | */
|
36682 | const keyValDiff = [new DefaultKeyValueDifferFactory()];
|
36683 | /**
|
36684 | * Structural diffing for `Iterable` types such as `Array`s.
|
36685 | */
|
36686 | const iterableDiff = [new DefaultIterableDifferFactory()];
|
36687 | const defaultIterableDiffers = new IterableDiffers(iterableDiff);
|
36688 | const defaultKeyValueDiffers = new KeyValueDiffers(keyValDiff);
|
36689 |
|
36690 | /**
|
36691 | * @license
|
36692 | * Copyright Google LLC All Rights Reserved.
|
36693 | *
|
36694 | * Use of this source code is governed by an MIT-style license that can be
|
36695 | * found in the LICENSE file at https://angular.io/license
|
36696 | */
|
36697 | const SWITCH_TEMPLATE_REF_FACTORY__PRE_R3__ = noop;
|
36698 | const SWITCH_TEMPLATE_REF_FACTORY = SWITCH_TEMPLATE_REF_FACTORY__PRE_R3__;
|
36699 | /**
|
36700 | * Represents an embedded template that can be used to instantiate embedded views.
|
36701 | * To instantiate embedded views based on a template, use the `ViewContainerRef`
|
36702 | * method `createEmbeddedView()`.
|
36703 | *
|
36704 | * Access a `TemplateRef` instance by placing a directive on an `<ng-template>`
|
36705 | * element (or directive prefixed with `*`). The `TemplateRef` for the embedded view
|
36706 | * is injected into the constructor of the directive,
|
36707 | * using the `TemplateRef` token.
|
36708 | *
|
36709 | * You can also use a `Query` to find a `TemplateRef` associated with
|
36710 | * a component or a directive.
|
36711 | *
|
36712 | * @see `ViewContainerRef`
|
36713 | * @see [Navigate the Component Tree with DI](guide/dependency-injection-navtree)
|
36714 | *
|
36715 | * @publicApi
|
36716 | */
|
36717 | class TemplateRef {
|
36718 | }
|
36719 | /**
|
36720 | * @internal
|
36721 | * @nocollapse
|
36722 | */
|
36723 | TemplateRef.__NG_ELEMENT_ID__ = SWITCH_TEMPLATE_REF_FACTORY;
|
36724 |
|
36725 | /**
|
36726 | * @license
|
36727 | * Copyright Google LLC All Rights Reserved.
|
36728 | *
|
36729 | * Use of this source code is governed by an MIT-style license that can be
|
36730 | * found in the LICENSE file at https://angular.io/license
|
36731 | */
|
36732 | /**
|
36733 | * Represents an instance of an `NgModule` created by an `NgModuleFactory`.
|
36734 | * Provides access to the `NgModule` instance and related objects.
|
36735 | *
|
36736 | * @publicApi
|
36737 | */
|
36738 | class NgModuleRef {
|
36739 | }
|
36740 |
|
36741 | /**
|
36742 | * @license
|
36743 | * Copyright Google LLC All Rights Reserved.
|
36744 | *
|
36745 | * Use of this source code is governed by an MIT-style license that can be
|
36746 | * found in the LICENSE file at https://angular.io/license
|
36747 | */
|
36748 | const SWITCH_VIEW_CONTAINER_REF_FACTORY__PRE_R3__ = noop;
|
36749 | const SWITCH_VIEW_CONTAINER_REF_FACTORY = SWITCH_VIEW_CONTAINER_REF_FACTORY__PRE_R3__;
|
36750 | /**
|
36751 | * Represents a container where one or more views can be attached to a component.
|
36752 | *
|
36753 | * Can contain *host views* (created by instantiating a
|
36754 | * component with the `createComponent()` method), and *embedded views*
|
36755 | * (created by instantiating a `TemplateRef` with the `createEmbeddedView()` method).
|
36756 | *
|
36757 | * A view container instance can contain other view containers,
|
36758 | * creating a [view hierarchy](guide/glossary#view-tree).
|
36759 | *
|
36760 | * @see `ComponentRef`
|
36761 | * @see `EmbeddedViewRef`
|
36762 | *
|
36763 | * @publicApi
|
36764 | */
|
36765 | class ViewContainerRef {
|
36766 | }
|
36767 | /**
|
36768 | * @internal
|
36769 | * @nocollapse
|
36770 | */
|
36771 | ViewContainerRef.__NG_ELEMENT_ID__ = SWITCH_VIEW_CONTAINER_REF_FACTORY;
|
36772 |
|
36773 | /**
|
36774 | * @license
|
36775 | * Copyright Google LLC All Rights Reserved.
|
36776 | *
|
36777 | * Use of this source code is governed by an MIT-style license that can be
|
36778 | * found in the LICENSE file at https://angular.io/license
|
36779 | */
|
36780 | const _tokenKeyCache = new Map();
|
36781 | function tokenKey(token) {
|
36782 | let key = _tokenKeyCache.get(token);
|
36783 | if (!key) {
|
36784 | key = stringify$1(token) + '_' + _tokenKeyCache.size;
|
36785 | _tokenKeyCache.set(token, key);
|
36786 | }
|
36787 | return key;
|
36788 | }
|
36789 |
|
36790 | /**
|
36791 | * @license
|
36792 | * Copyright Google LLC All Rights Reserved.
|
36793 | *
|
36794 | * Use of this source code is governed by an MIT-style license that can be
|
36795 | * found in the LICENSE file at https://angular.io/license
|
36796 | */
|
36797 | const InjectorRefTokenKey = tokenKey(Injector);
|
36798 | const INJECTORRefTokenKey = tokenKey(INJECTOR$1);
|
36799 | const NgModuleRefTokenKey = tokenKey(NgModuleRef);
|
36800 |
|
36801 | /**
|
36802 | * @license
|
36803 | * Copyright Google LLC All Rights Reserved.
|
36804 | *
|
36805 | * Use of this source code is governed by an MIT-style license that can be
|
36806 | * found in the LICENSE file at https://angular.io/license
|
36807 | */
|
36808 | const Renderer2TokenKey = tokenKey(Renderer2);
|
36809 | const ElementRefTokenKey = tokenKey(ElementRef);
|
36810 | const ViewContainerRefTokenKey = tokenKey(ViewContainerRef);
|
36811 | const TemplateRefTokenKey = tokenKey(TemplateRef);
|
36812 | const ChangeDetectorRefTokenKey = tokenKey(ChangeDetectorRef);
|
36813 | const InjectorRefTokenKey$1 = tokenKey(Injector);
|
36814 | const INJECTORRefTokenKey$1 = tokenKey(INJECTOR$1);
|
36815 | // This default value is when checking the hierarchy for a token.
|
36816 | //
|
36817 | // It means both:
|
36818 | // - the token is not provided by the current injector,
|
36819 | // - only the element injectors should be checked (ie do not check module injectors
|
36820 | //
|
36821 | // mod1
|
36822 | // /
|
36823 | // el1 mod2
|
36824 | // \ /
|
36825 | // el2
|
36826 | //
|
36827 | // When requesting el2.injector.get(token), we should check in the following order and return the
|
36828 | // first found value:
|
36829 | // - el2.injector.get(token, default)
|
36830 | // - el1.injector.get(token, NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR) -> do not check the module
|
36831 | // - mod2.injector.get(token, default)
|
36832 | const NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR = {};
|
36833 |
|
36834 | /**
|
36835 | * @license
|
36836 | * Copyright Google LLC All Rights Reserved.
|
36837 | *
|
36838 | * Use of this source code is governed by an MIT-style license that can be
|
36839 | * found in the LICENSE file at https://angular.io/license
|
36840 | */
|
36841 | class ComponentFactoryResolver$1 extends ComponentFactoryResolver {
|
36842 | /**
|
36843 | * @param ngModule The NgModuleRef to which all resolved factories are bound.
|
36844 | */
|
36845 | constructor(ngModule) {
|
36846 | super();
|
36847 | this.ngModule = ngModule;
|
36848 | }
|
36849 | resolveComponentFactory(component) {
|
36850 | ngDevMode && assertComponentType(component);
|
36851 | const componentDef = getComponentDef(component);
|
36852 | return new ComponentFactory$1(componentDef, this.ngModule);
|
36853 | }
|
36854 | }
|
36855 | function toRefArray(map) {
|
36856 | const array = [];
|
36857 | for (let nonMinified in map) {
|
36858 | if (map.hasOwnProperty(nonMinified)) {
|
36859 | const minified = map[nonMinified];
|
36860 | array.push({ propName: minified, templateName: nonMinified });
|
36861 | }
|
36862 | }
|
36863 | return array;
|
36864 | }
|
36865 | function getNamespace(elementName) {
|
36866 | const name = elementName.toLowerCase();
|
36867 | return name === 'svg' ? SVG_NAMESPACE : (name === 'math' ? MATH_ML_NAMESPACE : null);
|
36868 | }
|
36869 | /**
|
36870 | * A change detection scheduler token for {@link RootContext}. This token is the default value used
|
36871 | * for the default `RootContext` found in the {@link ROOT_CONTEXT} token.
|
36872 | */
|
36873 | const SCHEDULER = new InjectionToken('SCHEDULER_TOKEN', {
|
36874 | providedIn: 'root',
|
36875 | factory: () => defaultScheduler,
|
36876 | });
|
36877 | function createChainedInjector(rootViewInjector, moduleInjector) {
|
36878 | return {
|
36879 | get: (token, notFoundValue, flags) => {
|
36880 | const value = rootViewInjector.get(token, NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR, flags);
|
36881 | if (value !== NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR ||
|
36882 | notFoundValue === NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR) {
|
36883 | // Return the value from the root element injector when
|
36884 | // - it provides it
|
36885 | // (value !== NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR)
|
36886 | // - the module injector should not be checked
|
36887 | // (notFoundValue === NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR)
|
36888 | return value;
|
36889 | }
|
36890 | return moduleInjector.get(token, notFoundValue, flags);
|
36891 | }
|
36892 | };
|
36893 | }
|
36894 | /**
|
36895 | * Render3 implementation of {@link viewEngine_ComponentFactory}.
|
36896 | */
|
36897 | class ComponentFactory$1 extends ComponentFactory {
|
36898 | /**
|
36899 | * @param componentDef The component definition.
|
36900 | * @param ngModule The NgModuleRef to which the factory is bound.
|
36901 | */
|
36902 | constructor(componentDef, ngModule) {
|
36903 | super();
|
36904 | this.componentDef = componentDef;
|
36905 | this.ngModule = ngModule;
|
36906 | this.componentType = componentDef.type;
|
36907 | this.selector = stringifyCSSSelectorList(componentDef.selectors);
|
36908 | this.ngContentSelectors =
|
36909 | componentDef.ngContentSelectors ? componentDef.ngContentSelectors : [];
|
36910 | this.isBoundToModule = !!ngModule;
|
36911 | }
|
36912 | get inputs() {
|
36913 | return toRefArray(this.componentDef.inputs);
|
36914 | }
|
36915 | get outputs() {
|
36916 | return toRefArray(this.componentDef.outputs);
|
36917 | }
|
36918 | create(injector, projectableNodes, rootSelectorOrNode, ngModule) {
|
36919 | ngModule = ngModule || this.ngModule;
|
36920 | const rootViewInjector = ngModule ? createChainedInjector(injector, ngModule.injector) : injector;
|
36921 | const rendererFactory = rootViewInjector.get(RendererFactory2, domRendererFactory3);
|
36922 | const sanitizer = rootViewInjector.get(Sanitizer, null);
|
36923 | const hostRenderer = rendererFactory.createRenderer(null, this.componentDef);
|
36924 | // Determine a tag name used for creating host elements when this component is created
|
36925 | // dynamically. Default to 'div' if this component did not specify any tag name in its selector.
|
36926 | const elementName = this.componentDef.selectors[0][0] || 'div';
|
36927 | const hostRNode = rootSelectorOrNode ?
|
36928 | locateHostElement(hostRenderer, rootSelectorOrNode, this.componentDef.encapsulation) :
|
36929 | createElementNode(rendererFactory.createRenderer(null, this.componentDef), elementName, getNamespace(elementName));
|
36930 | const rootFlags = this.componentDef.onPush ? 64 /* Dirty */ | 512 /* IsRoot */ :
|
36931 | 16 /* CheckAlways */ | 512 /* IsRoot */;
|
36932 | const rootContext = createRootContext();
|
36933 | // Create the root view. Uses empty TView and ContentTemplate.
|
36934 | const rootTView = createTView(0 /* Root */, null, null, 1, 0, null, null, null, null, null);
|
36935 | const rootLView = createLView(null, rootTView, rootContext, rootFlags, null, null, rendererFactory, hostRenderer, sanitizer, rootViewInjector);
|
36936 | // rootView is the parent when bootstrapping
|
36937 | // TODO(misko): it looks like we are entering view here but we don't really need to as
|
36938 | // `renderView` does that. However as the code is written it is needed because
|
36939 | // `createRootComponentView` and `createRootComponent` both read global state. Fixing those
|
36940 | // issues would allow us to drop this.
|
36941 | enterView(rootLView);
|
36942 | let component;
|
36943 | let tElementNode;
|
36944 | try {
|
36945 | const componentView = createRootComponentView(hostRNode, this.componentDef, rootLView, rendererFactory, hostRenderer);
|
36946 | if (hostRNode) {
|
36947 | if (rootSelectorOrNode) {
|
36948 | setUpAttributes(hostRenderer, hostRNode, ['ng-version', VERSION$2.full]);
|
36949 | }
|
36950 | else {
|
36951 | // If host element is created as a part of this function call (i.e. `rootSelectorOrNode`
|
36952 | // is not defined), also apply attributes and classes extracted from component selector.
|
36953 | // Extract attributes and classes from the first selector only to match VE behavior.
|
36954 | const { attrs, classes } = extractAttrsAndClassesFromSelector(this.componentDef.selectors[0]);
|
36955 | if (attrs) {
|
36956 | setUpAttributes(hostRenderer, hostRNode, attrs);
|
36957 | }
|
36958 | if (classes && classes.length > 0) {
|
36959 | writeDirectClass(hostRenderer, hostRNode, classes.join(' '));
|
36960 | }
|
36961 | }
|
36962 | }
|
36963 | tElementNode = getTNode(rootTView, HEADER_OFFSET);
|
36964 | if (projectableNodes !== undefined) {
|
36965 | const projection = tElementNode.projection = [];
|
36966 | for (let i = 0; i < this.ngContentSelectors.length; i++) {
|
36967 | const nodesforSlot = projectableNodes[i];
|
36968 | // Projectable nodes can be passed as array of arrays or an array of iterables (ngUpgrade
|
36969 | // case). Here we do normalize passed data structure to be an array of arrays to avoid
|
36970 | // complex checks down the line.
|
36971 | // We also normalize the length of the passed in projectable nodes (to match the number of
|
36972 | // <ng-container> slots defined by a component).
|
36973 | projection.push(nodesforSlot != null ? Array.from(nodesforSlot) : null);
|
36974 | }
|
36975 | }
|
36976 | // TODO: should LifecycleHooksFeature and other host features be generated by the compiler and
|
36977 | // executed here?
|
36978 | // Angular 5 reference: https://stackblitz.com/edit/lifecycle-hooks-vcref
|
36979 | component = createRootComponent(componentView, this.componentDef, rootLView, rootContext, [LifecycleHooksFeature]);
|
36980 | renderView(rootTView, rootLView, null);
|
36981 | }
|
36982 | finally {
|
36983 | leaveView();
|
36984 | }
|
36985 | return new ComponentRef$1(this.componentType, component, createElementRef(tElementNode, rootLView), rootLView, tElementNode);
|
36986 | }
|
36987 | }
|
36988 | const componentFactoryResolver = new ComponentFactoryResolver$1();
|
36989 | /**
|
36990 | * Represents an instance of a Component created via a {@link ComponentFactory}.
|
36991 | *
|
36992 | * `ComponentRef` provides access to the Component Instance as well other objects related to this
|
36993 | * Component Instance and allows you to destroy the Component Instance via the {@link #destroy}
|
36994 | * method.
|
36995 | *
|
36996 | */
|
36997 | class ComponentRef$1 extends ComponentRef {
|
36998 | constructor(componentType, instance, location, _rootLView, _tNode) {
|
36999 | super();
|
37000 | this.location = location;
|
37001 | this._rootLView = _rootLView;
|
37002 | this._tNode = _tNode;
|
37003 | this.instance = instance;
|
37004 | this.hostView = this.changeDetectorRef = new RootViewRef(_rootLView);
|
37005 | this.componentType = componentType;
|
37006 | }
|
37007 | get injector() {
|
37008 | return new NodeInjector(this._tNode, this._rootLView);
|
37009 | }
|
37010 | destroy() {
|
37011 | this.hostView.destroy();
|
37012 | }
|
37013 | onDestroy(callback) {
|
37014 | this.hostView.onDestroy(callback);
|
37015 | }
|
37016 | }
|
37017 |
|
37018 | /*! *****************************************************************************
|
37019 | Copyright (c) Microsoft Corporation. All rights reserved.
|
37020 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
37021 | this file except in compliance with the License. You may obtain a copy of the
|
37022 | License at http://www.apache.org/licenses/LICENSE-2.0
|
37023 |
|
37024 | THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
37025 | KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
|
37026 | WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
|
37027 | MERCHANTABLITY OR NON-INFRINGEMENT.
|
37028 |
|
37029 | See the Apache Version 2.0 License for specific language governing permissions
|
37030 | and limitations under the License.
|
37031 | ***************************************************************************** */
|
37032 | /* global Reflect, Promise */
|
37033 |
|
37034 | var extendStatics = function(d, b) {
|
37035 | extendStatics = Object.setPrototypeOf ||
|
37036 | ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
37037 | function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
|
37038 | return extendStatics(d, b);
|
37039 | };
|
37040 |
|
37041 | function __extends(d, b) {
|
37042 | extendStatics(d, b);
|
37043 | function __() { this.constructor = d; }
|
37044 | d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
37045 | }
|
37046 |
|
37047 | /** PURE_IMPORTS_START PURE_IMPORTS_END */
|
37048 | function isFunction(x) {
|
37049 | return typeof x === 'function';
|
37050 | }
|
37051 |
|
37052 | /** PURE_IMPORTS_START PURE_IMPORTS_END */
|
37053 | var _enable_super_gross_mode_that_will_cause_bad_things = false;
|
37054 | var config = {
|
37055 | Promise: undefined,
|
37056 | set useDeprecatedSynchronousErrorHandling(value) {
|
37057 | if (value) {
|
37058 | var error = /*@__PURE__*/ new Error();
|
37059 | /*@__PURE__*/ console.warn('DEPRECATED! RxJS was set to use deprecated synchronous error handling behavior by code at: \n' + error.stack);
|
37060 | }
|
37061 | _enable_super_gross_mode_that_will_cause_bad_things = value;
|
37062 | },
|
37063 | get useDeprecatedSynchronousErrorHandling() {
|
37064 | return _enable_super_gross_mode_that_will_cause_bad_things;
|
37065 | },
|
37066 | };
|
37067 |
|
37068 | /** PURE_IMPORTS_START PURE_IMPORTS_END */
|
37069 | function hostReportError(err) {
|
37070 | setTimeout(function () { throw err; }, 0);
|
37071 | }
|
37072 |
|
37073 | /** PURE_IMPORTS_START _config,_util_hostReportError PURE_IMPORTS_END */
|
37074 | var empty = {
|
37075 | closed: true,
|
37076 | next: function (value) { },
|
37077 | error: function (err) {
|
37078 | if (config.useDeprecatedSynchronousErrorHandling) {
|
37079 | throw err;
|
37080 | }
|
37081 | else {
|
37082 | hostReportError(err);
|
37083 | }
|
37084 | },
|
37085 | complete: function () { }
|
37086 | };
|
37087 |
|
37088 | /** PURE_IMPORTS_START PURE_IMPORTS_END */
|
37089 | var isArray = /*@__PURE__*/ (function () { return Array.isArray || (function (x) { return x && typeof x.length === 'number'; }); })();
|
37090 |
|
37091 | /** PURE_IMPORTS_START PURE_IMPORTS_END */
|
37092 | function isObject(x) {
|
37093 | return x !== null && typeof x === 'object';
|
37094 | }
|
37095 |
|
37096 | /** PURE_IMPORTS_START PURE_IMPORTS_END */
|
37097 | var UnsubscriptionErrorImpl = /*@__PURE__*/ (function () {
|
37098 | function UnsubscriptionErrorImpl(errors) {
|
37099 | Error.call(this);
|
37100 | this.message = errors ?
|
37101 | errors.length + " errors occurred during unsubscription:\n" + errors.map(function (err, i) { return i + 1 + ") " + err.toString(); }).join('\n ') : '';
|
37102 | this.name = 'UnsubscriptionError';
|
37103 | this.errors = errors;
|
37104 | return this;
|
37105 | }
|
37106 | UnsubscriptionErrorImpl.prototype = /*@__PURE__*/ Object.create(Error.prototype);
|
37107 | return UnsubscriptionErrorImpl;
|
37108 | })();
|
37109 | var UnsubscriptionError = UnsubscriptionErrorImpl;
|
37110 |
|
37111 | /** PURE_IMPORTS_START _util_isArray,_util_isObject,_util_isFunction,_util_UnsubscriptionError PURE_IMPORTS_END */
|
37112 | var Subscription = /*@__PURE__*/ (function () {
|
37113 | function Subscription(unsubscribe) {
|
37114 | this.closed = false;
|
37115 | this._parentOrParents = null;
|
37116 | this._subscriptions = null;
|
37117 | if (unsubscribe) {
|
37118 | this._unsubscribe = unsubscribe;
|
37119 | }
|
37120 | }
|
37121 | Subscription.prototype.unsubscribe = function () {
|
37122 | var errors;
|
37123 | if (this.closed) {
|
37124 | return;
|
37125 | }
|
37126 | var _a = this, _parentOrParents = _a._parentOrParents, _unsubscribe = _a._unsubscribe, _subscriptions = _a._subscriptions;
|
37127 | this.closed = true;
|
37128 | this._parentOrParents = null;
|
37129 | this._subscriptions = null;
|
37130 | if (_parentOrParents instanceof Subscription) {
|
37131 | _parentOrParents.remove(this);
|
37132 | }
|
37133 | else if (_parentOrParents !== null) {
|
37134 | for (var index = 0; index < _parentOrParents.length; ++index) {
|
37135 | var parent_1 = _parentOrParents[index];
|
37136 | parent_1.remove(this);
|
37137 | }
|
37138 | }
|
37139 | if (isFunction(_unsubscribe)) {
|
37140 | try {
|
37141 | _unsubscribe.call(this);
|
37142 | }
|
37143 | catch (e) {
|
37144 | errors = e instanceof UnsubscriptionError ? flattenUnsubscriptionErrors(e.errors) : [e];
|
37145 | }
|
37146 | }
|
37147 | if (isArray(_subscriptions)) {
|
37148 | var index = -1;
|
37149 | var len = _subscriptions.length;
|
37150 | while (++index < len) {
|
37151 | var sub = _subscriptions[index];
|
37152 | if (isObject(sub)) {
|
37153 | try {
|
37154 | sub.unsubscribe();
|
37155 | }
|
37156 | catch (e) {
|
37157 | errors = errors || [];
|
37158 | if (e instanceof UnsubscriptionError) {
|
37159 | errors = errors.concat(flattenUnsubscriptionErrors(e.errors));
|
37160 | }
|
37161 | else {
|
37162 | errors.push(e);
|
37163 | }
|
37164 | }
|
37165 | }
|
37166 | }
|
37167 | }
|
37168 | if (errors) {
|
37169 | throw new UnsubscriptionError(errors);
|
37170 | }
|
37171 | };
|
37172 | Subscription.prototype.add = function (teardown) {
|
37173 | var subscription = teardown;
|
37174 | if (!teardown) {
|
37175 | return Subscription.EMPTY;
|
37176 | }
|
37177 | switch (typeof teardown) {
|
37178 | case 'function':
|
37179 | subscription = new Subscription(teardown);
|
37180 | case 'object':
|
37181 | if (subscription === this || subscription.closed || typeof subscription.unsubscribe !== 'function') {
|
37182 | return subscription;
|
37183 | }
|
37184 | else if (this.closed) {
|
37185 | subscription.unsubscribe();
|
37186 | return subscription;
|
37187 | }
|
37188 | else if (!(subscription instanceof Subscription)) {
|
37189 | var tmp = subscription;
|
37190 | subscription = new Subscription();
|
37191 | subscription._subscriptions = [tmp];
|
37192 | }
|
37193 | break;
|
37194 | default: {
|
37195 | throw new Error('unrecognized teardown ' + teardown + ' added to Subscription.');
|
37196 | }
|
37197 | }
|
37198 | var _parentOrParents = subscription._parentOrParents;
|
37199 | if (_parentOrParents === null) {
|
37200 | subscription._parentOrParents = this;
|
37201 | }
|
37202 | else if (_parentOrParents instanceof Subscription) {
|
37203 | if (_parentOrParents === this) {
|
37204 | return subscription;
|
37205 | }
|
37206 | subscription._parentOrParents = [_parentOrParents, this];
|
37207 | }
|
37208 | else if (_parentOrParents.indexOf(this) === -1) {
|
37209 | _parentOrParents.push(this);
|
37210 | }
|
37211 | else {
|
37212 | return subscription;
|
37213 | }
|
37214 | var subscriptions = this._subscriptions;
|
37215 | if (subscriptions === null) {
|
37216 | this._subscriptions = [subscription];
|
37217 | }
|
37218 | else {
|
37219 | subscriptions.push(subscription);
|
37220 | }
|
37221 | return subscription;
|
37222 | };
|
37223 | Subscription.prototype.remove = function (subscription) {
|
37224 | var subscriptions = this._subscriptions;
|
37225 | if (subscriptions) {
|
37226 | var subscriptionIndex = subscriptions.indexOf(subscription);
|
37227 | if (subscriptionIndex !== -1) {
|
37228 | subscriptions.splice(subscriptionIndex, 1);
|
37229 | }
|
37230 | }
|
37231 | };
|
37232 | Subscription.EMPTY = (function (empty) {
|
37233 | empty.closed = true;
|
37234 | return empty;
|
37235 | }(new Subscription()));
|
37236 | return Subscription;
|
37237 | }());
|
37238 | function flattenUnsubscriptionErrors(errors) {
|
37239 | return errors.reduce(function (errs, err) { return errs.concat((err instanceof UnsubscriptionError) ? err.errors : err); }, []);
|
37240 | }
|
37241 |
|
37242 | /** PURE_IMPORTS_START PURE_IMPORTS_END */
|
37243 | var rxSubscriber = /*@__PURE__*/ (function () {
|
37244 | return typeof Symbol === 'function'
|
37245 | ? /*@__PURE__*/ Symbol('rxSubscriber')
|
37246 | : '@@rxSubscriber_' + /*@__PURE__*/ Math.random();
|
37247 | })();
|
37248 |
|
37249 | /** PURE_IMPORTS_START tslib,_util_isFunction,_Observer,_Subscription,_internal_symbol_rxSubscriber,_config,_util_hostReportError PURE_IMPORTS_END */
|
37250 | var Subscriber = /*@__PURE__*/ (function (_super) {
|
37251 | __extends(Subscriber, _super);
|
37252 | function Subscriber(destinationOrNext, error, complete) {
|
37253 | var _this = _super.call(this) || this;
|
37254 | _this.syncErrorValue = null;
|
37255 | _this.syncErrorThrown = false;
|
37256 | _this.syncErrorThrowable = false;
|
37257 | _this.isStopped = false;
|
37258 | switch (arguments.length) {
|
37259 | case 0:
|
37260 | _this.destination = empty;
|
37261 | break;
|
37262 | case 1:
|
37263 | if (!destinationOrNext) {
|
37264 | _this.destination = empty;
|
37265 | break;
|
37266 | }
|
37267 | if (typeof destinationOrNext === 'object') {
|
37268 | if (destinationOrNext instanceof Subscriber) {
|
37269 | _this.syncErrorThrowable = destinationOrNext.syncErrorThrowable;
|
37270 | _this.destination = destinationOrNext;
|
37271 | destinationOrNext.add(_this);
|
37272 | }
|
37273 | else {
|
37274 | _this.syncErrorThrowable = true;
|
37275 | _this.destination = new SafeSubscriber(_this, destinationOrNext);
|
37276 | }
|
37277 | break;
|
37278 | }
|
37279 | default:
|
37280 | _this.syncErrorThrowable = true;
|
37281 | _this.destination = new SafeSubscriber(_this, destinationOrNext, error, complete);
|
37282 | break;
|
37283 | }
|
37284 | return _this;
|
37285 | }
|
37286 | Subscriber.prototype[rxSubscriber] = function () { return this; };
|
37287 | Subscriber.create = function (next, error, complete) {
|
37288 | var subscriber = new Subscriber(next, error, complete);
|
37289 | subscriber.syncErrorThrowable = false;
|
37290 | return subscriber;
|
37291 | };
|
37292 | Subscriber.prototype.next = function (value) {
|
37293 | if (!this.isStopped) {
|
37294 | this._next(value);
|
37295 | }
|
37296 | };
|
37297 | Subscriber.prototype.error = function (err) {
|
37298 | if (!this.isStopped) {
|
37299 | this.isStopped = true;
|
37300 | this._error(err);
|
37301 | }
|
37302 | };
|
37303 | Subscriber.prototype.complete = function () {
|
37304 | if (!this.isStopped) {
|
37305 | this.isStopped = true;
|
37306 | this._complete();
|
37307 | }
|
37308 | };
|
37309 | Subscriber.prototype.unsubscribe = function () {
|
37310 | if (this.closed) {
|
37311 | return;
|
37312 | }
|
37313 | this.isStopped = true;
|
37314 | _super.prototype.unsubscribe.call(this);
|
37315 | };
|
37316 | Subscriber.prototype._next = function (value) {
|
37317 | this.destination.next(value);
|
37318 | };
|
37319 | Subscriber.prototype._error = function (err) {
|
37320 | this.destination.error(err);
|
37321 | this.unsubscribe();
|
37322 | };
|
37323 | Subscriber.prototype._complete = function () {
|
37324 | this.destination.complete();
|
37325 | this.unsubscribe();
|
37326 | };
|
37327 | Subscriber.prototype._unsubscribeAndRecycle = function () {
|
37328 | var _parentOrParents = this._parentOrParents;
|
37329 | this._parentOrParents = null;
|
37330 | this.unsubscribe();
|
37331 | this.closed = false;
|
37332 | this.isStopped = false;
|
37333 | this._parentOrParents = _parentOrParents;
|
37334 | return this;
|
37335 | };
|
37336 | return Subscriber;
|
37337 | }(Subscription));
|
37338 | var SafeSubscriber = /*@__PURE__*/ (function (_super) {
|
37339 | __extends(SafeSubscriber, _super);
|
37340 | function SafeSubscriber(_parentSubscriber, observerOrNext, error, complete) {
|
37341 | var _this = _super.call(this) || this;
|
37342 | _this._parentSubscriber = _parentSubscriber;
|
37343 | var next;
|
37344 | var context = _this;
|
37345 | if (isFunction(observerOrNext)) {
|
37346 | next = observerOrNext;
|
37347 | }
|
37348 | else if (observerOrNext) {
|
37349 | next = observerOrNext.next;
|
37350 | error = observerOrNext.error;
|
37351 | complete = observerOrNext.complete;
|
37352 | if (observerOrNext !== empty) {
|
37353 | context = Object.create(observerOrNext);
|
37354 | if (isFunction(context.unsubscribe)) {
|
37355 | _this.add(context.unsubscribe.bind(context));
|
37356 | }
|
37357 | context.unsubscribe = _this.unsubscribe.bind(_this);
|
37358 | }
|
37359 | }
|
37360 | _this._context = context;
|
37361 | _this._next = next;
|
37362 | _this._error = error;
|
37363 | _this._complete = complete;
|
37364 | return _this;
|
37365 | }
|
37366 | SafeSubscriber.prototype.next = function (value) {
|
37367 | if (!this.isStopped && this._next) {
|
37368 | var _parentSubscriber = this._parentSubscriber;
|
37369 | if (!config.useDeprecatedSynchronousErrorHandling || !_parentSubscriber.syncErrorThrowable) {
|
37370 | this.__tryOrUnsub(this._next, value);
|
37371 | }
|
37372 | else if (this.__tryOrSetError(_parentSubscriber, this._next, value)) {
|
37373 | this.unsubscribe();
|
37374 | }
|
37375 | }
|
37376 | };
|
37377 | SafeSubscriber.prototype.error = function (err) {
|
37378 | if (!this.isStopped) {
|
37379 | var _parentSubscriber = this._parentSubscriber;
|
37380 | var useDeprecatedSynchronousErrorHandling = config.useDeprecatedSynchronousErrorHandling;
|
37381 | if (this._error) {
|
37382 | if (!useDeprecatedSynchronousErrorHandling || !_parentSubscriber.syncErrorThrowable) {
|
37383 | this.__tryOrUnsub(this._error, err);
|
37384 | this.unsubscribe();
|
37385 | }
|
37386 | else {
|
37387 | this.__tryOrSetError(_parentSubscriber, this._error, err);
|
37388 | this.unsubscribe();
|
37389 | }
|
37390 | }
|
37391 | else if (!_parentSubscriber.syncErrorThrowable) {
|
37392 | this.unsubscribe();
|
37393 | if (useDeprecatedSynchronousErrorHandling) {
|
37394 | throw err;
|
37395 | }
|
37396 | hostReportError(err);
|
37397 | }
|
37398 | else {
|
37399 | if (useDeprecatedSynchronousErrorHandling) {
|
37400 | _parentSubscriber.syncErrorValue = err;
|
37401 | _parentSubscriber.syncErrorThrown = true;
|
37402 | }
|
37403 | else {
|
37404 | hostReportError(err);
|
37405 | }
|
37406 | this.unsubscribe();
|
37407 | }
|
37408 | }
|
37409 | };
|
37410 | SafeSubscriber.prototype.complete = function () {
|
37411 | var _this = this;
|
37412 | if (!this.isStopped) {
|
37413 | var _parentSubscriber = this._parentSubscriber;
|
37414 | if (this._complete) {
|
37415 | var wrappedComplete = function () { return _this._complete.call(_this._context); };
|
37416 | if (!config.useDeprecatedSynchronousErrorHandling || !_parentSubscriber.syncErrorThrowable) {
|
37417 | this.__tryOrUnsub(wrappedComplete);
|
37418 | this.unsubscribe();
|
37419 | }
|
37420 | else {
|
37421 | this.__tryOrSetError(_parentSubscriber, wrappedComplete);
|
37422 | this.unsubscribe();
|
37423 | }
|
37424 | }
|
37425 | else {
|
37426 | this.unsubscribe();
|
37427 | }
|
37428 | }
|
37429 | };
|
37430 | SafeSubscriber.prototype.__tryOrUnsub = function (fn, value) {
|
37431 | try {
|
37432 | fn.call(this._context, value);
|
37433 | }
|
37434 | catch (err) {
|
37435 | this.unsubscribe();
|
37436 | if (config.useDeprecatedSynchronousErrorHandling) {
|
37437 | throw err;
|
37438 | }
|
37439 | else {
|
37440 | hostReportError(err);
|
37441 | }
|
37442 | }
|
37443 | };
|
37444 | SafeSubscriber.prototype.__tryOrSetError = function (parent, fn, value) {
|
37445 | if (!config.useDeprecatedSynchronousErrorHandling) {
|
37446 | throw new Error('bad call');
|
37447 | }
|
37448 | try {
|
37449 | fn.call(this._context, value);
|
37450 | }
|
37451 | catch (err) {
|
37452 | if (config.useDeprecatedSynchronousErrorHandling) {
|
37453 | parent.syncErrorValue = err;
|
37454 | parent.syncErrorThrown = true;
|
37455 | return true;
|
37456 | }
|
37457 | else {
|
37458 | hostReportError(err);
|
37459 | return true;
|
37460 | }
|
37461 | }
|
37462 | return false;
|
37463 | };
|
37464 | SafeSubscriber.prototype._unsubscribe = function () {
|
37465 | var _parentSubscriber = this._parentSubscriber;
|
37466 | this._context = null;
|
37467 | this._parentSubscriber = null;
|
37468 | _parentSubscriber.unsubscribe();
|
37469 | };
|
37470 | return SafeSubscriber;
|
37471 | }(Subscriber));
|
37472 |
|
37473 | /** PURE_IMPORTS_START _Subscriber PURE_IMPORTS_END */
|
37474 | function canReportError(observer) {
|
37475 | while (observer) {
|
37476 | var _a = observer, closed_1 = _a.closed, destination = _a.destination, isStopped = _a.isStopped;
|
37477 | if (closed_1 || isStopped) {
|
37478 | return false;
|
37479 | }
|
37480 | else if (destination && destination instanceof Subscriber) {
|
37481 | observer = destination;
|
37482 | }
|
37483 | else {
|
37484 | observer = null;
|
37485 | }
|
37486 | }
|
37487 | return true;
|
37488 | }
|
37489 |
|
37490 | /** PURE_IMPORTS_START _Subscriber,_symbol_rxSubscriber,_Observer PURE_IMPORTS_END */
|
37491 | function toSubscriber(nextOrObserver, error, complete) {
|
37492 | if (nextOrObserver) {
|
37493 | if (nextOrObserver instanceof Subscriber) {
|
37494 | return nextOrObserver;
|
37495 | }
|
37496 | if (nextOrObserver[rxSubscriber]) {
|
37497 | return nextOrObserver[rxSubscriber]();
|
37498 | }
|
37499 | }
|
37500 | if (!nextOrObserver && !error && !complete) {
|
37501 | return new Subscriber(empty);
|
37502 | }
|
37503 | return new Subscriber(nextOrObserver, error, complete);
|
37504 | }
|
37505 |
|
37506 | /** PURE_IMPORTS_START PURE_IMPORTS_END */
|
37507 | var observable = /*@__PURE__*/ (function () { return typeof Symbol === 'function' && Symbol.observable || '@@observable'; })();
|
37508 |
|
37509 | /** PURE_IMPORTS_START PURE_IMPORTS_END */
|
37510 | function noop$1() { }
|
37511 |
|
37512 | /** PURE_IMPORTS_START _noop PURE_IMPORTS_END */
|
37513 | function pipeFromArray(fns) {
|
37514 | if (!fns) {
|
37515 | return noop$1;
|
37516 | }
|
37517 | if (fns.length === 1) {
|
37518 | return fns[0];
|
37519 | }
|
37520 | return function piped(input) {
|
37521 | return fns.reduce(function (prev, fn) { return fn(prev); }, input);
|
37522 | };
|
37523 | }
|
37524 |
|
37525 | /** PURE_IMPORTS_START _util_canReportError,_util_toSubscriber,_symbol_observable,_util_pipe,_config PURE_IMPORTS_END */
|
37526 | var Observable = /*@__PURE__*/ (function () {
|
37527 | function Observable(subscribe) {
|
37528 | this._isScalar = false;
|
37529 | if (subscribe) {
|
37530 | this._subscribe = subscribe;
|
37531 | }
|
37532 | }
|
37533 | Observable.prototype.lift = function (operator) {
|
37534 | var observable = new Observable();
|
37535 | observable.source = this;
|
37536 | observable.operator = operator;
|
37537 | return observable;
|
37538 | };
|
37539 | Observable.prototype.subscribe = function (observerOrNext, error, complete) {
|
37540 | var operator = this.operator;
|
37541 | var sink = toSubscriber(observerOrNext, error, complete);
|
37542 | if (operator) {
|
37543 | sink.add(operator.call(sink, this.source));
|
37544 | }
|
37545 | else {
|
37546 | sink.add(this.source || (config.useDeprecatedSynchronousErrorHandling && !sink.syncErrorThrowable) ?
|
37547 | this._subscribe(sink) :
|
37548 | this._trySubscribe(sink));
|
37549 | }
|
37550 | if (config.useDeprecatedSynchronousErrorHandling) {
|
37551 | if (sink.syncErrorThrowable) {
|
37552 | sink.syncErrorThrowable = false;
|
37553 | if (sink.syncErrorThrown) {
|
37554 | throw sink.syncErrorValue;
|
37555 | }
|
37556 | }
|
37557 | }
|
37558 | return sink;
|
37559 | };
|
37560 | Observable.prototype._trySubscribe = function (sink) {
|
37561 | try {
|
37562 | return this._subscribe(sink);
|
37563 | }
|
37564 | catch (err) {
|
37565 | if (config.useDeprecatedSynchronousErrorHandling) {
|
37566 | sink.syncErrorThrown = true;
|
37567 | sink.syncErrorValue = err;
|
37568 | }
|
37569 | if (canReportError(sink)) {
|
37570 | sink.error(err);
|
37571 | }
|
37572 | else {
|
37573 | console.warn(err);
|
37574 | }
|
37575 | }
|
37576 | };
|
37577 | Observable.prototype.forEach = function (next, promiseCtor) {
|
37578 | var _this = this;
|
37579 | promiseCtor = getPromiseCtor(promiseCtor);
|
37580 | return new promiseCtor(function (resolve, reject) {
|
37581 | var subscription;
|
37582 | subscription = _this.subscribe(function (value) {
|
37583 | try {
|
37584 | next(value);
|
37585 | }
|
37586 | catch (err) {
|
37587 | reject(err);
|
37588 | if (subscription) {
|
37589 | subscription.unsubscribe();
|
37590 | }
|
37591 | }
|
37592 | }, reject, resolve);
|
37593 | });
|
37594 | };
|
37595 | Observable.prototype._subscribe = function (subscriber) {
|
37596 | var source = this.source;
|
37597 | return source && source.subscribe(subscriber);
|
37598 | };
|
37599 | Observable.prototype[observable] = function () {
|
37600 | return this;
|
37601 | };
|
37602 | Observable.prototype.pipe = function () {
|
37603 | var operations = [];
|
37604 | for (var _i = 0; _i < arguments.length; _i++) {
|
37605 | operations[_i] = arguments[_i];
|
37606 | }
|
37607 | if (operations.length === 0) {
|
37608 | return this;
|
37609 | }
|
37610 | return pipeFromArray(operations)(this);
|
37611 | };
|
37612 | Observable.prototype.toPromise = function (promiseCtor) {
|
37613 | var _this = this;
|
37614 | promiseCtor = getPromiseCtor(promiseCtor);
|
37615 | return new promiseCtor(function (resolve, reject) {
|
37616 | var value;
|
37617 | _this.subscribe(function (x) { return value = x; }, function (err) { return reject(err); }, function () { return resolve(value); });
|
37618 | });
|
37619 | };
|
37620 | Observable.create = function (subscribe) {
|
37621 | return new Observable(subscribe);
|
37622 | };
|
37623 | return Observable;
|
37624 | }());
|
37625 | function getPromiseCtor(promiseCtor) {
|
37626 | if (!promiseCtor) {
|
37627 | promiseCtor = Promise;
|
37628 | }
|
37629 | if (!promiseCtor) {
|
37630 | throw new Error('no Promise impl found');
|
37631 | }
|
37632 | return promiseCtor;
|
37633 | }
|
37634 |
|
37635 | /** PURE_IMPORTS_START PURE_IMPORTS_END */
|
37636 | var ObjectUnsubscribedErrorImpl = /*@__PURE__*/ (function () {
|
37637 | function ObjectUnsubscribedErrorImpl() {
|
37638 | Error.call(this);
|
37639 | this.message = 'object unsubscribed';
|
37640 | this.name = 'ObjectUnsubscribedError';
|
37641 | return this;
|
37642 | }
|
37643 | ObjectUnsubscribedErrorImpl.prototype = /*@__PURE__*/ Object.create(Error.prototype);
|
37644 | return ObjectUnsubscribedErrorImpl;
|
37645 | })();
|
37646 | var ObjectUnsubscribedError = ObjectUnsubscribedErrorImpl;
|
37647 |
|
37648 | /** PURE_IMPORTS_START tslib,_Subscription PURE_IMPORTS_END */
|
37649 | var SubjectSubscription = /*@__PURE__*/ (function (_super) {
|
37650 | __extends(SubjectSubscription, _super);
|
37651 | function SubjectSubscription(subject, subscriber) {
|
37652 | var _this = _super.call(this) || this;
|
37653 | _this.subject = subject;
|
37654 | _this.subscriber = subscriber;
|
37655 | _this.closed = false;
|
37656 | return _this;
|
37657 | }
|
37658 | SubjectSubscription.prototype.unsubscribe = function () {
|
37659 | if (this.closed) {
|
37660 | return;
|
37661 | }
|
37662 | this.closed = true;
|
37663 | var subject = this.subject;
|
37664 | var observers = subject.observers;
|
37665 | this.subject = null;
|
37666 | if (!observers || observers.length === 0 || subject.isStopped || subject.closed) {
|
37667 | return;
|
37668 | }
|
37669 | var subscriberIndex = observers.indexOf(this.subscriber);
|
37670 | if (subscriberIndex !== -1) {
|
37671 | observers.splice(subscriberIndex, 1);
|
37672 | }
|
37673 | };
|
37674 | return SubjectSubscription;
|
37675 | }(Subscription));
|
37676 |
|
37677 | /** PURE_IMPORTS_START tslib,_Observable,_Subscriber,_Subscription,_util_ObjectUnsubscribedError,_SubjectSubscription,_internal_symbol_rxSubscriber PURE_IMPORTS_END */
|
37678 | var SubjectSubscriber = /*@__PURE__*/ (function (_super) {
|
37679 | __extends(SubjectSubscriber, _super);
|
37680 | function SubjectSubscriber(destination) {
|
37681 | var _this = _super.call(this, destination) || this;
|
37682 | _this.destination = destination;
|
37683 | return _this;
|
37684 | }
|
37685 | return SubjectSubscriber;
|
37686 | }(Subscriber));
|
37687 | var Subject = /*@__PURE__*/ (function (_super) {
|
37688 | __extends(Subject, _super);
|
37689 | function Subject() {
|
37690 | var _this = _super.call(this) || this;
|
37691 | _this.observers = [];
|
37692 | _this.closed = false;
|
37693 | _this.isStopped = false;
|
37694 | _this.hasError = false;
|
37695 | _this.thrownError = null;
|
37696 | return _this;
|
37697 | }
|
37698 | Subject.prototype[rxSubscriber] = function () {
|
37699 | return new SubjectSubscriber(this);
|
37700 | };
|
37701 | Subject.prototype.lift = function (operator) {
|
37702 | var subject = new AnonymousSubject(this, this);
|
37703 | subject.operator = operator;
|
37704 | return subject;
|
37705 | };
|
37706 | Subject.prototype.next = function (value) {
|
37707 | if (this.closed) {
|
37708 | throw new ObjectUnsubscribedError();
|
37709 | }
|
37710 | if (!this.isStopped) {
|
37711 | var observers = this.observers;
|
37712 | var len = observers.length;
|
37713 | var copy = observers.slice();
|
37714 | for (var i = 0; i < len; i++) {
|
37715 | copy[i].next(value);
|
37716 | }
|
37717 | }
|
37718 | };
|
37719 | Subject.prototype.error = function (err) {
|
37720 | if (this.closed) {
|
37721 | throw new ObjectUnsubscribedError();
|
37722 | }
|
37723 | this.hasError = true;
|
37724 | this.thrownError = err;
|
37725 | this.isStopped = true;
|
37726 | var observers = this.observers;
|
37727 | var len = observers.length;
|
37728 | var copy = observers.slice();
|
37729 | for (var i = 0; i < len; i++) {
|
37730 | copy[i].error(err);
|
37731 | }
|
37732 | this.observers.length = 0;
|
37733 | };
|
37734 | Subject.prototype.complete = function () {
|
37735 | if (this.closed) {
|
37736 | throw new ObjectUnsubscribedError();
|
37737 | }
|
37738 | this.isStopped = true;
|
37739 | var observers = this.observers;
|
37740 | var len = observers.length;
|
37741 | var copy = observers.slice();
|
37742 | for (var i = 0; i < len; i++) {
|
37743 | copy[i].complete();
|
37744 | }
|
37745 | this.observers.length = 0;
|
37746 | };
|
37747 | Subject.prototype.unsubscribe = function () {
|
37748 | this.isStopped = true;
|
37749 | this.closed = true;
|
37750 | this.observers = null;
|
37751 | };
|
37752 | Subject.prototype._trySubscribe = function (subscriber) {
|
37753 | if (this.closed) {
|
37754 | throw new ObjectUnsubscribedError();
|
37755 | }
|
37756 | else {
|
37757 | return _super.prototype._trySubscribe.call(this, subscriber);
|
37758 | }
|
37759 | };
|
37760 | Subject.prototype._subscribe = function (subscriber) {
|
37761 | if (this.closed) {
|
37762 | throw new ObjectUnsubscribedError();
|
37763 | }
|
37764 | else if (this.hasError) {
|
37765 | subscriber.error(this.thrownError);
|
37766 | return Subscription.EMPTY;
|
37767 | }
|
37768 | else if (this.isStopped) {
|
37769 | subscriber.complete();
|
37770 | return Subscription.EMPTY;
|
37771 | }
|
37772 | else {
|
37773 | this.observers.push(subscriber);
|
37774 | return new SubjectSubscription(this, subscriber);
|
37775 | }
|
37776 | };
|
37777 | Subject.prototype.asObservable = function () {
|
37778 | var observable = new Observable();
|
37779 | observable.source = this;
|
37780 | return observable;
|
37781 | };
|
37782 | Subject.create = function (destination, source) {
|
37783 | return new AnonymousSubject(destination, source);
|
37784 | };
|
37785 | return Subject;
|
37786 | }(Observable));
|
37787 | var AnonymousSubject = /*@__PURE__*/ (function (_super) {
|
37788 | __extends(AnonymousSubject, _super);
|
37789 | function AnonymousSubject(destination, source) {
|
37790 | var _this = _super.call(this) || this;
|
37791 | _this.destination = destination;
|
37792 | _this.source = source;
|
37793 | return _this;
|
37794 | }
|
37795 | AnonymousSubject.prototype.next = function (value) {
|
37796 | var destination = this.destination;
|
37797 | if (destination && destination.next) {
|
37798 | destination.next(value);
|
37799 | }
|
37800 | };
|
37801 | AnonymousSubject.prototype.error = function (err) {
|
37802 | var destination = this.destination;
|
37803 | if (destination && destination.error) {
|
37804 | this.destination.error(err);
|
37805 | }
|
37806 | };
|
37807 | AnonymousSubject.prototype.complete = function () {
|
37808 | var destination = this.destination;
|
37809 | if (destination && destination.complete) {
|
37810 | this.destination.complete();
|
37811 | }
|
37812 | };
|
37813 | AnonymousSubject.prototype._subscribe = function (subscriber) {
|
37814 | var source = this.source;
|
37815 | if (source) {
|
37816 | return this.source.subscribe(subscriber);
|
37817 | }
|
37818 | else {
|
37819 | return Subscription.EMPTY;
|
37820 | }
|
37821 | };
|
37822 | return AnonymousSubject;
|
37823 | }(Subject));
|
37824 |
|
37825 | /** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */
|
37826 | function refCount() {
|
37827 | return function refCountOperatorFunction(source) {
|
37828 | return source.lift(new RefCountOperator(source));
|
37829 | };
|
37830 | }
|
37831 | var RefCountOperator = /*@__PURE__*/ (function () {
|
37832 | function RefCountOperator(connectable) {
|
37833 | this.connectable = connectable;
|
37834 | }
|
37835 | RefCountOperator.prototype.call = function (subscriber, source) {
|
37836 | var connectable = this.connectable;
|
37837 | connectable._refCount++;
|
37838 | var refCounter = new RefCountSubscriber(subscriber, connectable);
|
37839 | var subscription = source.subscribe(refCounter);
|
37840 | if (!refCounter.closed) {
|
37841 | refCounter.connection = connectable.connect();
|
37842 | }
|
37843 | return subscription;
|
37844 | };
|
37845 | return RefCountOperator;
|
37846 | }());
|
37847 | var RefCountSubscriber = /*@__PURE__*/ (function (_super) {
|
37848 | __extends(RefCountSubscriber, _super);
|
37849 | function RefCountSubscriber(destination, connectable) {
|
37850 | var _this = _super.call(this, destination) || this;
|
37851 | _this.connectable = connectable;
|
37852 | return _this;
|
37853 | }
|
37854 | RefCountSubscriber.prototype._unsubscribe = function () {
|
37855 | var connectable = this.connectable;
|
37856 | if (!connectable) {
|
37857 | this.connection = null;
|
37858 | return;
|
37859 | }
|
37860 | this.connectable = null;
|
37861 | var refCount = connectable._refCount;
|
37862 | if (refCount <= 0) {
|
37863 | this.connection = null;
|
37864 | return;
|
37865 | }
|
37866 | connectable._refCount = refCount - 1;
|
37867 | if (refCount > 1) {
|
37868 | this.connection = null;
|
37869 | return;
|
37870 | }
|
37871 | var connection = this.connection;
|
37872 | var sharedConnection = connectable._connection;
|
37873 | this.connection = null;
|
37874 | if (sharedConnection && (!connection || sharedConnection === connection)) {
|
37875 | sharedConnection.unsubscribe();
|
37876 | }
|
37877 | };
|
37878 | return RefCountSubscriber;
|
37879 | }(Subscriber));
|
37880 |
|
37881 | /** PURE_IMPORTS_START tslib,_Subject,_Observable,_Subscriber,_Subscription,_operators_refCount PURE_IMPORTS_END */
|
37882 | var ConnectableObservable = /*@__PURE__*/ (function (_super) {
|
37883 | __extends(ConnectableObservable, _super);
|
37884 | function ConnectableObservable(source, subjectFactory) {
|
37885 | var _this = _super.call(this) || this;
|
37886 | _this.source = source;
|
37887 | _this.subjectFactory = subjectFactory;
|
37888 | _this._refCount = 0;
|
37889 | _this._isComplete = false;
|
37890 | return _this;
|
37891 | }
|
37892 | ConnectableObservable.prototype._subscribe = function (subscriber) {
|
37893 | return this.getSubject().subscribe(subscriber);
|
37894 | };
|
37895 | ConnectableObservable.prototype.getSubject = function () {
|
37896 | var subject = this._subject;
|
37897 | if (!subject || subject.isStopped) {
|
37898 | this._subject = this.subjectFactory();
|
37899 | }
|
37900 | return this._subject;
|
37901 | };
|
37902 | ConnectableObservable.prototype.connect = function () {
|
37903 | var connection = this._connection;
|
37904 | if (!connection) {
|
37905 | this._isComplete = false;
|
37906 | connection = this._connection = new Subscription();
|
37907 | connection.add(this.source
|
37908 | .subscribe(new ConnectableSubscriber(this.getSubject(), this)));
|
37909 | if (connection.closed) {
|
37910 | this._connection = null;
|
37911 | connection = Subscription.EMPTY;
|
37912 | }
|
37913 | }
|
37914 | return connection;
|
37915 | };
|
37916 | ConnectableObservable.prototype.refCount = function () {
|
37917 | return refCount()(this);
|
37918 | };
|
37919 | return ConnectableObservable;
|
37920 | }(Observable));
|
37921 | var connectableObservableDescriptor = /*@__PURE__*/ (function () {
|
37922 | var connectableProto = ConnectableObservable.prototype;
|
37923 | return {
|
37924 | operator: { value: null },
|
37925 | _refCount: { value: 0, writable: true },
|
37926 | _subject: { value: null, writable: true },
|
37927 | _connection: { value: null, writable: true },
|
37928 | _subscribe: { value: connectableProto._subscribe },
|
37929 | _isComplete: { value: connectableProto._isComplete, writable: true },
|
37930 | getSubject: { value: connectableProto.getSubject },
|
37931 | connect: { value: connectableProto.connect },
|
37932 | refCount: { value: connectableProto.refCount }
|
37933 | };
|
37934 | })();
|
37935 | var ConnectableSubscriber = /*@__PURE__*/ (function (_super) {
|
37936 | __extends(ConnectableSubscriber, _super);
|
37937 | function ConnectableSubscriber(destination, connectable) {
|
37938 | var _this = _super.call(this, destination) || this;
|
37939 | _this.connectable = connectable;
|
37940 | return _this;
|
37941 | }
|
37942 | ConnectableSubscriber.prototype._error = function (err) {
|
37943 | this._unsubscribe();
|
37944 | _super.prototype._error.call(this, err);
|
37945 | };
|
37946 | ConnectableSubscriber.prototype._complete = function () {
|
37947 | this.connectable._isComplete = true;
|
37948 | this._unsubscribe();
|
37949 | _super.prototype._complete.call(this);
|
37950 | };
|
37951 | ConnectableSubscriber.prototype._unsubscribe = function () {
|
37952 | var connectable = this.connectable;
|
37953 | if (connectable) {
|
37954 | this.connectable = null;
|
37955 | var connection = connectable._connection;
|
37956 | connectable._refCount = 0;
|
37957 | connectable._subject = null;
|
37958 | connectable._connection = null;
|
37959 | if (connection) {
|
37960 | connection.unsubscribe();
|
37961 | }
|
37962 | }
|
37963 | };
|
37964 | return ConnectableSubscriber;
|
37965 | }(SubjectSubscriber));
|
37966 |
|
37967 | /** PURE_IMPORTS_START PURE_IMPORTS_END */
|
37968 | function isScheduler(value) {
|
37969 | return value && typeof value.schedule === 'function';
|
37970 | }
|
37971 |
|
37972 | /** PURE_IMPORTS_START PURE_IMPORTS_END */
|
37973 | var subscribeToArray = function (array) {
|
37974 | return function (subscriber) {
|
37975 | for (var i = 0, len = array.length; i < len && !subscriber.closed; i++) {
|
37976 | subscriber.next(array[i]);
|
37977 | }
|
37978 | subscriber.complete();
|
37979 | };
|
37980 | };
|
37981 |
|
37982 | /** PURE_IMPORTS_START _Observable,_Subscription PURE_IMPORTS_END */
|
37983 | function scheduleArray(input, scheduler) {
|
37984 | return new Observable(function (subscriber) {
|
37985 | var sub = new Subscription();
|
37986 | var i = 0;
|
37987 | sub.add(scheduler.schedule(function () {
|
37988 | if (i === input.length) {
|
37989 | subscriber.complete();
|
37990 | return;
|
37991 | }
|
37992 | subscriber.next(input[i++]);
|
37993 | if (!subscriber.closed) {
|
37994 | sub.add(this.schedule());
|
37995 | }
|
37996 | }));
|
37997 | return sub;
|
37998 | });
|
37999 | }
|
38000 |
|
38001 | /** PURE_IMPORTS_START _Observable,_util_subscribeToArray,_scheduled_scheduleArray PURE_IMPORTS_END */
|
38002 | function fromArray(input, scheduler) {
|
38003 | if (!scheduler) {
|
38004 | return new Observable(subscribeToArray(input));
|
38005 | }
|
38006 | else {
|
38007 | return scheduleArray(input, scheduler);
|
38008 | }
|
38009 | }
|
38010 |
|
38011 | /** PURE_IMPORTS_START PURE_IMPORTS_END */
|
38012 | function identity(x) {
|
38013 | return x;
|
38014 | }
|
38015 |
|
38016 | /** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */
|
38017 | function map(project, thisArg) {
|
38018 | return function mapOperation(source) {
|
38019 | if (typeof project !== 'function') {
|
38020 | throw new TypeError('argument is not a function. Are you looking for `mapTo()`?');
|
38021 | }
|
38022 | return source.lift(new MapOperator(project, thisArg));
|
38023 | };
|
38024 | }
|
38025 | var MapOperator = /*@__PURE__*/ (function () {
|
38026 | function MapOperator(project, thisArg) {
|
38027 | this.project = project;
|
38028 | this.thisArg = thisArg;
|
38029 | }
|
38030 | MapOperator.prototype.call = function (subscriber, source) {
|
38031 | return source.subscribe(new MapSubscriber(subscriber, this.project, this.thisArg));
|
38032 | };
|
38033 | return MapOperator;
|
38034 | }());
|
38035 | var MapSubscriber = /*@__PURE__*/ (function (_super) {
|
38036 | __extends(MapSubscriber, _super);
|
38037 | function MapSubscriber(destination, project, thisArg) {
|
38038 | var _this = _super.call(this, destination) || this;
|
38039 | _this.project = project;
|
38040 | _this.count = 0;
|
38041 | _this.thisArg = thisArg || _this;
|
38042 | return _this;
|
38043 | }
|
38044 | MapSubscriber.prototype._next = function (value) {
|
38045 | var result;
|
38046 | try {
|
38047 | result = this.project.call(this.thisArg, value, this.count++);
|
38048 | }
|
38049 | catch (err) {
|
38050 | this.destination.error(err);
|
38051 | return;
|
38052 | }
|
38053 | this.destination.next(result);
|
38054 | };
|
38055 | return MapSubscriber;
|
38056 | }(Subscriber));
|
38057 |
|
38058 | /** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */
|
38059 | var OuterSubscriber = /*@__PURE__*/ (function (_super) {
|
38060 | __extends(OuterSubscriber, _super);
|
38061 | function OuterSubscriber() {
|
38062 | return _super !== null && _super.apply(this, arguments) || this;
|
38063 | }
|
38064 | OuterSubscriber.prototype.notifyNext = function (outerValue, innerValue, outerIndex, innerIndex, innerSub) {
|
38065 | this.destination.next(innerValue);
|
38066 | };
|
38067 | OuterSubscriber.prototype.notifyError = function (error, innerSub) {
|
38068 | this.destination.error(error);
|
38069 | };
|
38070 | OuterSubscriber.prototype.notifyComplete = function (innerSub) {
|
38071 | this.destination.complete();
|
38072 | };
|
38073 | return OuterSubscriber;
|
38074 | }(Subscriber));
|
38075 |
|
38076 | /** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */
|
38077 | var InnerSubscriber = /*@__PURE__*/ (function (_super) {
|
38078 | __extends(InnerSubscriber, _super);
|
38079 | function InnerSubscriber(parent, outerValue, outerIndex) {
|
38080 | var _this = _super.call(this) || this;
|
38081 | _this.parent = parent;
|
38082 | _this.outerValue = outerValue;
|
38083 | _this.outerIndex = outerIndex;
|
38084 | _this.index = 0;
|
38085 | return _this;
|
38086 | }
|
38087 | InnerSubscriber.prototype._next = function (value) {
|
38088 | this.parent.notifyNext(this.outerValue, value, this.outerIndex, this.index++, this);
|
38089 | };
|
38090 | InnerSubscriber.prototype._error = function (error) {
|
38091 | this.parent.notifyError(error, this);
|
38092 | this.unsubscribe();
|
38093 | };
|
38094 | InnerSubscriber.prototype._complete = function () {
|
38095 | this.parent.notifyComplete(this);
|
38096 | this.unsubscribe();
|
38097 | };
|
38098 | return InnerSubscriber;
|
38099 | }(Subscriber));
|
38100 |
|
38101 | /** PURE_IMPORTS_START _hostReportError PURE_IMPORTS_END */
|
38102 | var subscribeToPromise = function (promise) {
|
38103 | return function (subscriber) {
|
38104 | promise.then(function (value) {
|
38105 | if (!subscriber.closed) {
|
38106 | subscriber.next(value);
|
38107 | subscriber.complete();
|
38108 | }
|
38109 | }, function (err) { return subscriber.error(err); })
|
38110 | .then(null, hostReportError);
|
38111 | return subscriber;
|
38112 | };
|
38113 | };
|
38114 |
|
38115 | /** PURE_IMPORTS_START PURE_IMPORTS_END */
|
38116 | function getSymbolIterator$1() {
|
38117 | if (typeof Symbol !== 'function' || !Symbol.iterator) {
|
38118 | return '@@iterator';
|
38119 | }
|
38120 | return Symbol.iterator;
|
38121 | }
|
38122 | var iterator = /*@__PURE__*/ getSymbolIterator$1();
|
38123 |
|
38124 | /** PURE_IMPORTS_START _symbol_iterator PURE_IMPORTS_END */
|
38125 | var subscribeToIterable = function (iterable) {
|
38126 | return function (subscriber) {
|
38127 | var iterator$1 = iterable[iterator]();
|
38128 | do {
|
38129 | var item = iterator$1.next();
|
38130 | if (item.done) {
|
38131 | subscriber.complete();
|
38132 | break;
|
38133 | }
|
38134 | subscriber.next(item.value);
|
38135 | if (subscriber.closed) {
|
38136 | break;
|
38137 | }
|
38138 | } while (true);
|
38139 | if (typeof iterator$1.return === 'function') {
|
38140 | subscriber.add(function () {
|
38141 | if (iterator$1.return) {
|
38142 | iterator$1.return();
|
38143 | }
|
38144 | });
|
38145 | }
|
38146 | return subscriber;
|
38147 | };
|
38148 | };
|
38149 |
|
38150 | /** PURE_IMPORTS_START _symbol_observable PURE_IMPORTS_END */
|
38151 | var subscribeToObservable = function (obj) {
|
38152 | return function (subscriber) {
|
38153 | var obs = obj[observable]();
|
38154 | if (typeof obs.subscribe !== 'function') {
|
38155 | throw new TypeError('Provided object does not correctly implement Symbol.observable');
|
38156 | }
|
38157 | else {
|
38158 | return obs.subscribe(subscriber);
|
38159 | }
|
38160 | };
|
38161 | };
|
38162 |
|
38163 | /** PURE_IMPORTS_START PURE_IMPORTS_END */
|
38164 | var isArrayLike = (function (x) { return x && typeof x.length === 'number' && typeof x !== 'function'; });
|
38165 |
|
38166 | /** PURE_IMPORTS_START PURE_IMPORTS_END */
|
38167 | function isPromise$2(value) {
|
38168 | return !!value && typeof value.subscribe !== 'function' && typeof value.then === 'function';
|
38169 | }
|
38170 |
|
38171 | /** PURE_IMPORTS_START _subscribeToArray,_subscribeToPromise,_subscribeToIterable,_subscribeToObservable,_isArrayLike,_isPromise,_isObject,_symbol_iterator,_symbol_observable PURE_IMPORTS_END */
|
38172 | var subscribeTo = function (result) {
|
38173 | if (!!result && typeof result[observable] === 'function') {
|
38174 | return subscribeToObservable(result);
|
38175 | }
|
38176 | else if (isArrayLike(result)) {
|
38177 | return subscribeToArray(result);
|
38178 | }
|
38179 | else if (isPromise$2(result)) {
|
38180 | return subscribeToPromise(result);
|
38181 | }
|
38182 | else if (!!result && typeof result[iterator] === 'function') {
|
38183 | return subscribeToIterable(result);
|
38184 | }
|
38185 | else {
|
38186 | var value = isObject(result) ? 'an invalid object' : "'" + result + "'";
|
38187 | var msg = "You provided " + value + " where a stream was expected."
|
38188 | + ' You can provide an Observable, Promise, Array, or Iterable.';
|
38189 | throw new TypeError(msg);
|
38190 | }
|
38191 | };
|
38192 |
|
38193 | /** PURE_IMPORTS_START _InnerSubscriber,_subscribeTo,_Observable PURE_IMPORTS_END */
|
38194 | function subscribeToResult(outerSubscriber, result, outerValue, outerIndex, innerSubscriber) {
|
38195 | if (innerSubscriber === void 0) {
|
38196 | innerSubscriber = new InnerSubscriber(outerSubscriber, outerValue, outerIndex);
|
38197 | }
|
38198 | if (innerSubscriber.closed) {
|
38199 | return undefined;
|
38200 | }
|
38201 | if (result instanceof Observable) {
|
38202 | return result.subscribe(innerSubscriber);
|
38203 | }
|
38204 | return subscribeTo(result)(innerSubscriber);
|
38205 | }
|
38206 |
|
38207 | /** PURE_IMPORTS_START _Observable,_Subscription,_symbol_observable PURE_IMPORTS_END */
|
38208 | function scheduleObservable(input, scheduler) {
|
38209 | return new Observable(function (subscriber) {
|
38210 | var sub = new Subscription();
|
38211 | sub.add(scheduler.schedule(function () {
|
38212 | var observable$1 = input[observable]();
|
38213 | sub.add(observable$1.subscribe({
|
38214 | next: function (value) { sub.add(scheduler.schedule(function () { return subscriber.next(value); })); },
|
38215 | error: function (err) { sub.add(scheduler.schedule(function () { return subscriber.error(err); })); },
|
38216 | complete: function () { sub.add(scheduler.schedule(function () { return subscriber.complete(); })); },
|
38217 | }));
|
38218 | }));
|
38219 | return sub;
|
38220 | });
|
38221 | }
|
38222 |
|
38223 | /** PURE_IMPORTS_START _Observable,_Subscription PURE_IMPORTS_END */
|
38224 | function schedulePromise(input, scheduler) {
|
38225 | return new Observable(function (subscriber) {
|
38226 | var sub = new Subscription();
|
38227 | sub.add(scheduler.schedule(function () {
|
38228 | return input.then(function (value) {
|
38229 | sub.add(scheduler.schedule(function () {
|
38230 | subscriber.next(value);
|
38231 | sub.add(scheduler.schedule(function () { return subscriber.complete(); }));
|
38232 | }));
|
38233 | }, function (err) {
|
38234 | sub.add(scheduler.schedule(function () { return subscriber.error(err); }));
|
38235 | });
|
38236 | }));
|
38237 | return sub;
|
38238 | });
|
38239 | }
|
38240 |
|
38241 | /** PURE_IMPORTS_START _Observable,_Subscription,_symbol_iterator PURE_IMPORTS_END */
|
38242 | function scheduleIterable(input, scheduler) {
|
38243 | if (!input) {
|
38244 | throw new Error('Iterable cannot be null');
|
38245 | }
|
38246 | return new Observable(function (subscriber) {
|
38247 | var sub = new Subscription();
|
38248 | var iterator$1;
|
38249 | sub.add(function () {
|
38250 | if (iterator$1 && typeof iterator$1.return === 'function') {
|
38251 | iterator$1.return();
|
38252 | }
|
38253 | });
|
38254 | sub.add(scheduler.schedule(function () {
|
38255 | iterator$1 = input[iterator]();
|
38256 | sub.add(scheduler.schedule(function () {
|
38257 | if (subscriber.closed) {
|
38258 | return;
|
38259 | }
|
38260 | var value;
|
38261 | var done;
|
38262 | try {
|
38263 | var result = iterator$1.next();
|
38264 | value = result.value;
|
38265 | done = result.done;
|
38266 | }
|
38267 | catch (err) {
|
38268 | subscriber.error(err);
|
38269 | return;
|
38270 | }
|
38271 | if (done) {
|
38272 | subscriber.complete();
|
38273 | }
|
38274 | else {
|
38275 | subscriber.next(value);
|
38276 | this.schedule();
|
38277 | }
|
38278 | }));
|
38279 | }));
|
38280 | return sub;
|
38281 | });
|
38282 | }
|
38283 |
|
38284 | /** PURE_IMPORTS_START _symbol_observable PURE_IMPORTS_END */
|
38285 | function isInteropObservable(input) {
|
38286 | return input && typeof input[observable] === 'function';
|
38287 | }
|
38288 |
|
38289 | /** PURE_IMPORTS_START _symbol_iterator PURE_IMPORTS_END */
|
38290 | function isIterable(input) {
|
38291 | return input && typeof input[iterator] === 'function';
|
38292 | }
|
38293 |
|
38294 | /** PURE_IMPORTS_START _scheduleObservable,_schedulePromise,_scheduleArray,_scheduleIterable,_util_isInteropObservable,_util_isPromise,_util_isArrayLike,_util_isIterable PURE_IMPORTS_END */
|
38295 | function scheduled(input, scheduler) {
|
38296 | if (input != null) {
|
38297 | if (isInteropObservable(input)) {
|
38298 | return scheduleObservable(input, scheduler);
|
38299 | }
|
38300 | else if (isPromise$2(input)) {
|
38301 | return schedulePromise(input, scheduler);
|
38302 | }
|
38303 | else if (isArrayLike(input)) {
|
38304 | return scheduleArray(input, scheduler);
|
38305 | }
|
38306 | else if (isIterable(input) || typeof input === 'string') {
|
38307 | return scheduleIterable(input, scheduler);
|
38308 | }
|
38309 | }
|
38310 | throw new TypeError((input !== null && typeof input || input) + ' is not observable');
|
38311 | }
|
38312 |
|
38313 | /** PURE_IMPORTS_START _Observable,_util_subscribeTo,_scheduled_scheduled PURE_IMPORTS_END */
|
38314 | function from(input, scheduler) {
|
38315 | if (!scheduler) {
|
38316 | if (input instanceof Observable) {
|
38317 | return input;
|
38318 | }
|
38319 | return new Observable(subscribeTo(input));
|
38320 | }
|
38321 | else {
|
38322 | return scheduled(input, scheduler);
|
38323 | }
|
38324 | }
|
38325 |
|
38326 | /** PURE_IMPORTS_START tslib,_util_subscribeToResult,_OuterSubscriber,_InnerSubscriber,_map,_observable_from PURE_IMPORTS_END */
|
38327 | function mergeMap(project, resultSelector, concurrent) {
|
38328 | if (concurrent === void 0) {
|
38329 | concurrent = Number.POSITIVE_INFINITY;
|
38330 | }
|
38331 | if (typeof resultSelector === 'function') {
|
38332 | return function (source) { return source.pipe(mergeMap(function (a, i) { return from(project(a, i)).pipe(map(function (b, ii) { return resultSelector(a, b, i, ii); })); }, concurrent)); };
|
38333 | }
|
38334 | else if (typeof resultSelector === 'number') {
|
38335 | concurrent = resultSelector;
|
38336 | }
|
38337 | return function (source) { return source.lift(new MergeMapOperator(project, concurrent)); };
|
38338 | }
|
38339 | var MergeMapOperator = /*@__PURE__*/ (function () {
|
38340 | function MergeMapOperator(project, concurrent) {
|
38341 | if (concurrent === void 0) {
|
38342 | concurrent = Number.POSITIVE_INFINITY;
|
38343 | }
|
38344 | this.project = project;
|
38345 | this.concurrent = concurrent;
|
38346 | }
|
38347 | MergeMapOperator.prototype.call = function (observer, source) {
|
38348 | return source.subscribe(new MergeMapSubscriber(observer, this.project, this.concurrent));
|
38349 | };
|
38350 | return MergeMapOperator;
|
38351 | }());
|
38352 | var MergeMapSubscriber = /*@__PURE__*/ (function (_super) {
|
38353 | __extends(MergeMapSubscriber, _super);
|
38354 | function MergeMapSubscriber(destination, project, concurrent) {
|
38355 | if (concurrent === void 0) {
|
38356 | concurrent = Number.POSITIVE_INFINITY;
|
38357 | }
|
38358 | var _this = _super.call(this, destination) || this;
|
38359 | _this.project = project;
|
38360 | _this.concurrent = concurrent;
|
38361 | _this.hasCompleted = false;
|
38362 | _this.buffer = [];
|
38363 | _this.active = 0;
|
38364 | _this.index = 0;
|
38365 | return _this;
|
38366 | }
|
38367 | MergeMapSubscriber.prototype._next = function (value) {
|
38368 | if (this.active < this.concurrent) {
|
38369 | this._tryNext(value);
|
38370 | }
|
38371 | else {
|
38372 | this.buffer.push(value);
|
38373 | }
|
38374 | };
|
38375 | MergeMapSubscriber.prototype._tryNext = function (value) {
|
38376 | var result;
|
38377 | var index = this.index++;
|
38378 | try {
|
38379 | result = this.project(value, index);
|
38380 | }
|
38381 | catch (err) {
|
38382 | this.destination.error(err);
|
38383 | return;
|
38384 | }
|
38385 | this.active++;
|
38386 | this._innerSub(result, value, index);
|
38387 | };
|
38388 | MergeMapSubscriber.prototype._innerSub = function (ish, value, index) {
|
38389 | var innerSubscriber = new InnerSubscriber(this, value, index);
|
38390 | var destination = this.destination;
|
38391 | destination.add(innerSubscriber);
|
38392 | var innerSubscription = subscribeToResult(this, ish, undefined, undefined, innerSubscriber);
|
38393 | if (innerSubscription !== innerSubscriber) {
|
38394 | destination.add(innerSubscription);
|
38395 | }
|
38396 | };
|
38397 | MergeMapSubscriber.prototype._complete = function () {
|
38398 | this.hasCompleted = true;
|
38399 | if (this.active === 0 && this.buffer.length === 0) {
|
38400 | this.destination.complete();
|
38401 | }
|
38402 | this.unsubscribe();
|
38403 | };
|
38404 | MergeMapSubscriber.prototype.notifyNext = function (outerValue, innerValue, outerIndex, innerIndex, innerSub) {
|
38405 | this.destination.next(innerValue);
|
38406 | };
|
38407 | MergeMapSubscriber.prototype.notifyComplete = function (innerSub) {
|
38408 | var buffer = this.buffer;
|
38409 | this.remove(innerSub);
|
38410 | this.active--;
|
38411 | if (buffer.length > 0) {
|
38412 | this._next(buffer.shift());
|
38413 | }
|
38414 | else if (this.active === 0 && this.hasCompleted) {
|
38415 | this.destination.complete();
|
38416 | }
|
38417 | };
|
38418 | return MergeMapSubscriber;
|
38419 | }(OuterSubscriber));
|
38420 |
|
38421 | /** PURE_IMPORTS_START _mergeMap,_util_identity PURE_IMPORTS_END */
|
38422 | function mergeAll(concurrent) {
|
38423 | if (concurrent === void 0) {
|
38424 | concurrent = Number.POSITIVE_INFINITY;
|
38425 | }
|
38426 | return mergeMap(identity, concurrent);
|
38427 | }
|
38428 |
|
38429 | /** PURE_IMPORTS_START _Observable,_util_isScheduler,_operators_mergeAll,_fromArray PURE_IMPORTS_END */
|
38430 | function merge$1() {
|
38431 | var observables = [];
|
38432 | for (var _i = 0; _i < arguments.length; _i++) {
|
38433 | observables[_i] = arguments[_i];
|
38434 | }
|
38435 | var concurrent = Number.POSITIVE_INFINITY;
|
38436 | var scheduler = null;
|
38437 | var last = observables[observables.length - 1];
|
38438 | if (isScheduler(last)) {
|
38439 | scheduler = observables.pop();
|
38440 | if (observables.length > 1 && typeof observables[observables.length - 1] === 'number') {
|
38441 | concurrent = observables.pop();
|
38442 | }
|
38443 | }
|
38444 | else if (typeof last === 'number') {
|
38445 | concurrent = observables.pop();
|
38446 | }
|
38447 | if (scheduler === null && observables.length === 1 && observables[0] instanceof Observable) {
|
38448 | return observables[0];
|
38449 | }
|
38450 | return mergeAll(concurrent)(fromArray(observables, scheduler));
|
38451 | }
|
38452 |
|
38453 | /**
|
38454 | * @license
|
38455 | * Copyright Google LLC All Rights Reserved.
|
38456 | *
|
38457 | * Use of this source code is governed by an MIT-style license that can be
|
38458 | * found in the LICENSE file at https://angular.io/license
|
38459 | */
|
38460 | class EventEmitter_ extends Subject {
|
38461 | constructor(isAsync = false) {
|
38462 | super();
|
38463 | this.__isAsync = isAsync;
|
38464 | }
|
38465 | emit(value) {
|
38466 | super.next(value);
|
38467 | }
|
38468 | subscribe(observerOrNext, error, complete) {
|
38469 | let schedulerFn;
|
38470 | let errorFn = (err) => null;
|
38471 | let completeFn = () => null;
|
38472 | if (observerOrNext && typeof observerOrNext === 'object') {
|
38473 | schedulerFn = this.__isAsync ? (value) => {
|
38474 | setTimeout(() => observerOrNext.next(value));
|
38475 | } : (value) => {
|
38476 | observerOrNext.next(value);
|
38477 | };
|
38478 | if (observerOrNext.error) {
|
38479 | errorFn = this.__isAsync ? (err) => {
|
38480 | setTimeout(() => observerOrNext.error(err));
|
38481 | } : (err) => {
|
38482 | observerOrNext.error(err);
|
38483 | };
|
38484 | }
|
38485 | if (observerOrNext.complete) {
|
38486 | completeFn = this.__isAsync ? () => {
|
38487 | setTimeout(() => observerOrNext.complete());
|
38488 | } : () => {
|
38489 | observerOrNext.complete();
|
38490 | };
|
38491 | }
|
38492 | }
|
38493 | else {
|
38494 | schedulerFn = this.__isAsync ? (value) => {
|
38495 | setTimeout(() => observerOrNext(value));
|
38496 | } : (value) => {
|
38497 | observerOrNext(value);
|
38498 | };
|
38499 | if (error) {
|
38500 | errorFn = this.__isAsync ? (err) => {
|
38501 | setTimeout(() => error(err));
|
38502 | } : (err) => {
|
38503 | error(err);
|
38504 | };
|
38505 | }
|
38506 | if (complete) {
|
38507 | completeFn = this.__isAsync ? () => {
|
38508 | setTimeout(() => complete());
|
38509 | } : () => {
|
38510 | complete();
|
38511 | };
|
38512 | }
|
38513 | }
|
38514 | const sink = super.subscribe(schedulerFn, errorFn, completeFn);
|
38515 | if (observerOrNext instanceof Subscription) {
|
38516 | observerOrNext.add(sink);
|
38517 | }
|
38518 | return sink;
|
38519 | }
|
38520 | }
|
38521 | /**
|
38522 | * @publicApi
|
38523 | */
|
38524 | const EventEmitter = EventEmitter_;
|
38525 |
|
38526 | /**
|
38527 | * @license
|
38528 | * Copyright Google LLC All Rights Reserved.
|
38529 | *
|
38530 | * Use of this source code is governed by an MIT-style license that can be
|
38531 | * found in the LICENSE file at https://angular.io/license
|
38532 | */
|
38533 | const ɵ0$9 = (dir = {}) => dir, ɵ1$1 = (type, meta) => SWITCH_COMPILE_DIRECTIVE(type, meta);
|
38534 | /**
|
38535 | * Type of the Directive metadata.
|
38536 | *
|
38537 | * @publicApi
|
38538 | */
|
38539 | const Directive = makeDecorator('Directive', ɵ0$9, undefined, undefined, ɵ1$1);
|
38540 | const ɵ2$1 = (c = {}) => (Object.assign({ changeDetection: ChangeDetectionStrategy$1.Default }, c)), ɵ3$1 = (type, meta) => SWITCH_COMPILE_COMPONENT(type, meta);
|
38541 | /**
|
38542 | * Component decorator and metadata.
|
38543 | *
|
38544 | * @Annotation
|
38545 | * @publicApi
|
38546 | */
|
38547 | const Component = makeDecorator('Component', ɵ2$1, Directive, undefined, ɵ3$1);
|
38548 | const ɵ4 = (p) => (Object.assign({ pure: true }, p)), ɵ5 = (type, meta) => SWITCH_COMPILE_PIPE(type, meta);
|
38549 | /**
|
38550 | * @Annotation
|
38551 | * @publicApi
|
38552 | */
|
38553 | const Pipe = makeDecorator('Pipe', ɵ4, undefined, undefined, ɵ5);
|
38554 | const ɵ6 = (bindingPropertyName) => ({ bindingPropertyName });
|
38555 | /**
|
38556 | * @Annotation
|
38557 | * @publicApi
|
38558 | */
|
38559 | const Input = makePropDecorator('Input', ɵ6);
|
38560 | const ɵ7 = (bindingPropertyName) => ({ bindingPropertyName });
|
38561 | /**
|
38562 | * @Annotation
|
38563 | * @publicApi
|
38564 | */
|
38565 | const Output = makePropDecorator('Output', ɵ7);
|
38566 | const ɵ8 = (hostPropertyName) => ({ hostPropertyName });
|
38567 | /**
|
38568 | * @Annotation
|
38569 | * @publicApi
|
38570 | */
|
38571 | const HostBinding = makePropDecorator('HostBinding', ɵ8);
|
38572 | const ɵ9 = (eventName, args) => ({ eventName, args });
|
38573 | /**
|
38574 | * Decorator that binds a DOM event to a host listener and supplies configuration metadata.
|
38575 | * Angular invokes the supplied handler method when the host element emits the specified event,
|
38576 | * and updates the bound element with the result.
|
38577 | *
|
38578 | * If the handler method returns false, applies `preventDefault` on the bound element.
|
38579 | *
|
38580 | * @usageNotes
|
38581 | *
|
38582 | * The following example declares a directive
|
38583 | * that attaches a click listener to a button and counts clicks.
|
38584 | *
|
38585 | * ```ts
|
38586 | * @Directive({selector: 'button[counting]'})
|
38587 | * class CountClicks {
|
38588 | * numberOfClicks = 0;
|
38589 | *
|
38590 | * @HostListener('click', ['$event.target'])
|
38591 | * onClick(btn) {
|
38592 | * console.log('button', btn, 'number of clicks:', this.numberOfClicks++);
|
38593 | * }
|
38594 | * }
|
38595 | *
|
38596 | * @Component({
|
38597 | * selector: 'app',
|
38598 | * template: '<button counting>Increment</button>',
|
38599 | * })
|
38600 | * class App {}
|
38601 | *
|
38602 | * ```
|
38603 | *
|
38604 | * The following example registers another DOM event handler that listens for key-press events.
|
38605 | * ``` ts
|
38606 | * import { HostListener, Component } from "@angular/core";
|
38607 | *
|
38608 | * @Component({
|
38609 | * selector: 'app',
|
38610 | * template: `<h1>Hello, you have pressed keys {{counter}} number of times!</h1> Press any key to
|
38611 | * increment the counter.
|
38612 | * <button (click)="resetCounter()">Reset Counter</button>`
|
38613 | * })
|
38614 | * class AppComponent {
|
38615 | * counter = 0;
|
38616 | * @HostListener('window:keydown', ['$event'])
|
38617 | * handleKeyDown(event: KeyboardEvent) {
|
38618 | * this.counter++;
|
38619 | * }
|
38620 | * resetCounter() {
|
38621 | * this.counter = 0;
|
38622 | * }
|
38623 | * }
|
38624 | * ```
|
38625 | *
|
38626 | * @Annotation
|
38627 | * @publicApi
|
38628 | */
|
38629 | const HostListener = makePropDecorator('HostListener', ɵ9);
|
38630 | const SWITCH_COMPILE_COMPONENT__PRE_R3__ = noop;
|
38631 | const SWITCH_COMPILE_DIRECTIVE__PRE_R3__ = noop;
|
38632 | const SWITCH_COMPILE_PIPE__PRE_R3__ = noop;
|
38633 | const SWITCH_COMPILE_COMPONENT = SWITCH_COMPILE_COMPONENT__PRE_R3__;
|
38634 | const SWITCH_COMPILE_DIRECTIVE = SWITCH_COMPILE_DIRECTIVE__PRE_R3__;
|
38635 | const SWITCH_COMPILE_PIPE = SWITCH_COMPILE_PIPE__PRE_R3__;
|
38636 |
|
38637 | /**
|
38638 | * @license
|
38639 | * Copyright Google LLC All Rights Reserved.
|
38640 | *
|
38641 | * Use of this source code is governed by an MIT-style license that can be
|
38642 | * found in the LICENSE file at https://angular.io/license
|
38643 | */
|
38644 | const ɵ0$a = (ngModule) => ngModule, ɵ1$2 =
|
38645 | /**
|
38646 | * Decorator that marks the following class as an NgModule, and supplies
|
38647 | * configuration metadata for it.
|
38648 | *
|
38649 | * * The `declarations` and `entryComponents` options configure the compiler
|
38650 | * with information about what belongs to the NgModule.
|
38651 | * * The `providers` options configures the NgModule's injector to provide
|
38652 | * dependencies the NgModule members.
|
38653 | * * The `imports` and `exports` options bring in members from other modules, and make
|
38654 | * this module's members available to others.
|
38655 | */
|
38656 | (type, meta) => SWITCH_COMPILE_NGMODULE(type, meta);
|
38657 | /**
|
38658 | * @Annotation
|
38659 | * @publicApi
|
38660 | */
|
38661 | const NgModule = makeDecorator('NgModule', ɵ0$a, undefined, undefined, ɵ1$2);
|
38662 | function preR3NgModuleCompile(moduleType, metadata) {
|
38663 | let imports = (metadata && metadata.imports) || [];
|
38664 | if (metadata && metadata.exports) {
|
38665 | imports = [...imports, metadata.exports];
|
38666 | }
|
38667 | moduleType.ɵinj = ɵɵdefineInjector({
|
38668 | factory: convertInjectableProviderToFactory(moduleType, { useClass: moduleType }),
|
38669 | providers: metadata && metadata.providers,
|
38670 | imports: imports,
|
38671 | });
|
38672 | }
|
38673 | const SWITCH_COMPILE_NGMODULE__PRE_R3__ = preR3NgModuleCompile;
|
38674 | const SWITCH_COMPILE_NGMODULE = SWITCH_COMPILE_NGMODULE__PRE_R3__;
|
38675 |
|
38676 | /** PURE_IMPORTS_START _observable_ConnectableObservable PURE_IMPORTS_END */
|
38677 | function multicast(subjectOrSubjectFactory, selector) {
|
38678 | return function multicastOperatorFunction(source) {
|
38679 | var subjectFactory;
|
38680 | if (typeof subjectOrSubjectFactory === 'function') {
|
38681 | subjectFactory = subjectOrSubjectFactory;
|
38682 | }
|
38683 | else {
|
38684 | subjectFactory = function subjectFactory() {
|
38685 | return subjectOrSubjectFactory;
|
38686 | };
|
38687 | }
|
38688 | if (typeof selector === 'function') {
|
38689 | return source.lift(new MulticastOperator(subjectFactory, selector));
|
38690 | }
|
38691 | var connectable = Object.create(source, connectableObservableDescriptor);
|
38692 | connectable.source = source;
|
38693 | connectable.subjectFactory = subjectFactory;
|
38694 | return connectable;
|
38695 | };
|
38696 | }
|
38697 | var MulticastOperator = /*@__PURE__*/ (function () {
|
38698 | function MulticastOperator(subjectFactory, selector) {
|
38699 | this.subjectFactory = subjectFactory;
|
38700 | this.selector = selector;
|
38701 | }
|
38702 | MulticastOperator.prototype.call = function (subscriber, source) {
|
38703 | var selector = this.selector;
|
38704 | var subject = this.subjectFactory();
|
38705 | var subscription = selector(subject).subscribe(subscriber);
|
38706 | subscription.add(source.subscribe(subject));
|
38707 | return subscription;
|
38708 | };
|
38709 | return MulticastOperator;
|
38710 | }());
|
38711 |
|
38712 | /** PURE_IMPORTS_START _multicast,_refCount,_Subject PURE_IMPORTS_END */
|
38713 | function shareSubjectFactory() {
|
38714 | return new Subject();
|
38715 | }
|
38716 | function share() {
|
38717 | return function (source) { return refCount()(multicast(shareSubjectFactory)(source)); };
|
38718 | }
|
38719 |
|
38720 | /**
|
38721 | * @license
|
38722 | * Copyright Google LLC All Rights Reserved.
|
38723 | *
|
38724 | * Use of this source code is governed by an MIT-style license that can be
|
38725 | * found in the LICENSE file at https://angular.io/license
|
38726 | */
|
38727 | /**
|
38728 | * A [DI token](guide/glossary#di-token "DI token definition") that you can use to provide
|
38729 | * one or more initialization functions.
|
38730 | *
|
38731 | * The provided functions are injected at application startup and executed during
|
38732 | * app initialization. If any of these functions returns a Promise, initialization
|
38733 | * does not complete until the Promise is resolved.
|
38734 | *
|
38735 | * You can, for example, create a factory function that loads language data
|
38736 | * or an external configuration, and provide that function to the `APP_INITIALIZER` token.
|
38737 | * The function is executed during the application bootstrap process,
|
38738 | * and the needed data is available on startup.
|
38739 | *
|
38740 | * @see `ApplicationInitStatus`
|
38741 | *
|
38742 | * @publicApi
|
38743 | */
|
38744 | const APP_INITIALIZER = new InjectionToken('Application Initializer');
|
38745 | /**
|
38746 | * A class that reflects the state of running {@link APP_INITIALIZER} functions.
|
38747 | *
|
38748 | * @publicApi
|
38749 | */
|
38750 | class ApplicationInitStatus {
|
38751 | constructor(appInits) {
|
38752 | this.appInits = appInits;
|
38753 | this.resolve = noop;
|
38754 | this.reject = noop;
|
38755 | this.initialized = false;
|
38756 | this.done = false;
|
38757 | this.donePromise = new Promise((res, rej) => {
|
38758 | this.resolve = res;
|
38759 | this.reject = rej;
|
38760 | });
|
38761 | }
|
38762 | /** @internal */
|
38763 | runInitializers() {
|
38764 | if (this.initialized) {
|
38765 | return;
|
38766 | }
|
38767 | const asyncInitPromises = [];
|
38768 | const complete = () => {
|
38769 | this.done = true;
|
38770 | this.resolve();
|
38771 | };
|
38772 | if (this.appInits) {
|
38773 | for (let i = 0; i < this.appInits.length; i++) {
|
38774 | const initResult = this.appInits[i]();
|
38775 | if (isPromise$1(initResult)) {
|
38776 | asyncInitPromises.push(initResult);
|
38777 | }
|
38778 | }
|
38779 | }
|
38780 | Promise.all(asyncInitPromises)
|
38781 | .then(() => {
|
38782 | complete();
|
38783 | })
|
38784 | .catch(e => {
|
38785 | this.reject(e);
|
38786 | });
|
38787 | if (asyncInitPromises.length === 0) {
|
38788 | complete();
|
38789 | }
|
38790 | this.initialized = true;
|
38791 | }
|
38792 | }
|
38793 | ApplicationInitStatus.decorators = [
|
38794 | { type: Injectable }
|
38795 | ];
|
38796 | ApplicationInitStatus.ctorParameters = () => [
|
38797 | { type: Array, decorators: [{ type: Inject, args: [APP_INITIALIZER,] }, { type: Optional }] }
|
38798 | ];
|
38799 |
|
38800 | /**
|
38801 | * @license
|
38802 | * Copyright Google LLC All Rights Reserved.
|
38803 | *
|
38804 | * Use of this source code is governed by an MIT-style license that can be
|
38805 | * found in the LICENSE file at https://angular.io/license
|
38806 | */
|
38807 | /**
|
38808 | * A [DI token](guide/glossary#di-token "DI token definition") representing a unique string ID, used
|
38809 | * primarily for prefixing application attributes and CSS styles when
|
38810 | * {@link ViewEncapsulation#Emulated ViewEncapsulation.Emulated} is being used.
|
38811 | *
|
38812 | * BY default, the value is randomly generated and assigned to the application by Angular.
|
38813 | * To provide a custom ID value, use a DI provider <!-- TODO: provider --> to configure
|
38814 | * the root {@link Injector} that uses this token.
|
38815 | *
|
38816 | * @publicApi
|
38817 | */
|
38818 | const APP_ID = new InjectionToken('AppId');
|
38819 | function _appIdRandomProviderFactory() {
|
38820 | return `${_randomChar()}${_randomChar()}${_randomChar()}`;
|
38821 | }
|
38822 | /**
|
38823 | * Providers that generate a random `APP_ID_TOKEN`.
|
38824 | * @publicApi
|
38825 | */
|
38826 | const APP_ID_RANDOM_PROVIDER = {
|
38827 | provide: APP_ID,
|
38828 | useFactory: _appIdRandomProviderFactory,
|
38829 | deps: [],
|
38830 | };
|
38831 | function _randomChar() {
|
38832 | return String.fromCharCode(97 + Math.floor(Math.random() * 25));
|
38833 | }
|
38834 | /**
|
38835 | * A function that is executed when a platform is initialized.
|
38836 | * @publicApi
|
38837 | */
|
38838 | const PLATFORM_INITIALIZER = new InjectionToken('Platform Initializer');
|
38839 | /**
|
38840 | * A token that indicates an opaque platform ID.
|
38841 | * @publicApi
|
38842 | */
|
38843 | const PLATFORM_ID = new InjectionToken('Platform ID');
|
38844 | /**
|
38845 | * A [DI token](guide/glossary#di-token "DI token definition") that provides a set of callbacks to
|
38846 | * be called for every component that is bootstrapped.
|
38847 | *
|
38848 | * Each callback must take a `ComponentRef` instance and return nothing.
|
38849 | *
|
38850 | * `(componentRef: ComponentRef) => void`
|
38851 | *
|
38852 | * @publicApi
|
38853 | */
|
38854 | const APP_BOOTSTRAP_LISTENER = new InjectionToken('appBootstrapListener');
|
38855 | /**
|
38856 | * A [DI token](guide/glossary#di-token "DI token definition") that indicates the root directory of
|
38857 | * the application
|
38858 | * @publicApi
|
38859 | */
|
38860 | const PACKAGE_ROOT_URL = new InjectionToken('Application Packages Root URL');
|
38861 |
|
38862 | /**
|
38863 | * @license
|
38864 | * Copyright Google LLC All Rights Reserved.
|
38865 | *
|
38866 | * Use of this source code is governed by an MIT-style license that can be
|
38867 | * found in the LICENSE file at https://angular.io/license
|
38868 | */
|
38869 | class Console {
|
38870 | log(message) {
|
38871 | // tslint:disable-next-line:no-console
|
38872 | console.log(message);
|
38873 | }
|
38874 | // Note: for reporting errors use `DOM.logError()` as it is platform specific
|
38875 | warn(message) {
|
38876 | // tslint:disable-next-line:no-console
|
38877 | console.warn(message);
|
38878 | }
|
38879 | }
|
38880 | Console.decorators = [
|
38881 | { type: Injectable }
|
38882 | ];
|
38883 |
|
38884 | /**
|
38885 | * @license
|
38886 | * Copyright Google LLC All Rights Reserved.
|
38887 | *
|
38888 | * Use of this source code is governed by an MIT-style license that can be
|
38889 | * found in the LICENSE file at https://angular.io/license
|
38890 | */
|
38891 | /**
|
38892 | * Provide this token to set the locale of your application.
|
38893 | * It is used for i18n extraction, by i18n pipes (DatePipe, I18nPluralPipe, CurrencyPipe,
|
38894 | * DecimalPipe and PercentPipe) and by ICU expressions.
|
38895 | *
|
38896 | * See the [i18n guide](guide/i18n#setting-up-locale) for more information.
|
38897 | *
|
38898 | * @usageNotes
|
38899 | * ### Example
|
38900 | *
|
38901 | * ```typescript
|
38902 | * import { LOCALE_ID } from '@angular/core';
|
38903 | * import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
38904 | * import { AppModule } from './app/app.module';
|
38905 | *
|
38906 | * platformBrowserDynamic().bootstrapModule(AppModule, {
|
38907 | * providers: [{provide: LOCALE_ID, useValue: 'en-US' }]
|
38908 | * });
|
38909 | * ```
|
38910 | *
|
38911 | * @publicApi
|
38912 | */
|
38913 | const LOCALE_ID$1 = new InjectionToken('LocaleId');
|
38914 | /**
|
38915 | * Provide this token to set the default currency code your application uses for
|
38916 | * CurrencyPipe when there is no currency code passed into it. This is only used by
|
38917 | * CurrencyPipe and has no relation to locale currency. Defaults to USD if not configured.
|
38918 | *
|
38919 | * See the [i18n guide](guide/i18n#setting-up-locale) for more information.
|
38920 | *
|
38921 | * <div class="alert is-helpful">
|
38922 | *
|
38923 | * **Deprecation notice:**
|
38924 | *
|
38925 | * The default currency code is currently always `USD` but this is deprecated from v9.
|
38926 | *
|
38927 | * **In v10 the default currency code will be taken from the current locale.**
|
38928 | *
|
38929 | * If you need the previous behavior then set it by creating a `DEFAULT_CURRENCY_CODE` provider in
|
38930 | * your application `NgModule`:
|
38931 | *
|
38932 | * ```ts
|
38933 | * {provide: DEFAULT_CURRENCY_CODE, useValue: 'USD'}
|
38934 | * ```
|
38935 | *
|
38936 | * </div>
|
38937 | *
|
38938 | * @usageNotes
|
38939 | * ### Example
|
38940 | *
|
38941 | * ```typescript
|
38942 | * import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
38943 | * import { AppModule } from './app/app.module';
|
38944 | *
|
38945 | * platformBrowserDynamic().bootstrapModule(AppModule, {
|
38946 | * providers: [{provide: DEFAULT_CURRENCY_CODE, useValue: 'EUR' }]
|
38947 | * });
|
38948 | * ```
|
38949 | *
|
38950 | * @publicApi
|
38951 | */
|
38952 | const DEFAULT_CURRENCY_CODE = new InjectionToken('DefaultCurrencyCode');
|
38953 | /**
|
38954 | * Use this token at bootstrap to provide the content of your translation file (`xtb`,
|
38955 | * `xlf` or `xlf2`) when you want to translate your application in another language.
|
38956 | *
|
38957 | * See the [i18n guide](guide/i18n#merge) for more information.
|
38958 | *
|
38959 | * @usageNotes
|
38960 | * ### Example
|
38961 | *
|
38962 | * ```typescript
|
38963 | * import { TRANSLATIONS } from '@angular/core';
|
38964 | * import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
38965 | * import { AppModule } from './app/app.module';
|
38966 | *
|
38967 | * // content of your translation file
|
38968 | * const translations = '....';
|
38969 | *
|
38970 | * platformBrowserDynamic().bootstrapModule(AppModule, {
|
38971 | * providers: [{provide: TRANSLATIONS, useValue: translations }]
|
38972 | * });
|
38973 | * ```
|
38974 | *
|
38975 | * @publicApi
|
38976 | */
|
38977 | const TRANSLATIONS = new InjectionToken('Translations');
|
38978 | /**
|
38979 | * Provide this token at bootstrap to set the format of your {@link TRANSLATIONS}: `xtb`,
|
38980 | * `xlf` or `xlf2`.
|
38981 | *
|
38982 | * See the [i18n guide](guide/i18n#merge) for more information.
|
38983 | *
|
38984 | * @usageNotes
|
38985 | * ### Example
|
38986 | *
|
38987 | * ```typescript
|
38988 | * import { TRANSLATIONS_FORMAT } from '@angular/core';
|
38989 | * import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
38990 | * import { AppModule } from './app/app.module';
|
38991 | *
|
38992 | * platformBrowserDynamic().bootstrapModule(AppModule, {
|
38993 | * providers: [{provide: TRANSLATIONS_FORMAT, useValue: 'xlf' }]
|
38994 | * });
|
38995 | * ```
|
38996 | *
|
38997 | * @publicApi
|
38998 | */
|
38999 | const TRANSLATIONS_FORMAT = new InjectionToken('TranslationsFormat');
|
39000 | /**
|
39001 | * Use this enum at bootstrap as an option of `bootstrapModule` to define the strategy
|
39002 | * that the compiler should use in case of missing translations:
|
39003 | * - Error: throw if you have missing translations.
|
39004 | * - Warning (default): show a warning in the console and/or shell.
|
39005 | * - Ignore: do nothing.
|
39006 | *
|
39007 | * See the [i18n guide](guide/i18n#missing-translation) for more information.
|
39008 | *
|
39009 | * @usageNotes
|
39010 | * ### Example
|
39011 | * ```typescript
|
39012 | * import { MissingTranslationStrategy } from '@angular/core';
|
39013 | * import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
39014 | * import { AppModule } from './app/app.module';
|
39015 | *
|
39016 | * platformBrowserDynamic().bootstrapModule(AppModule, {
|
39017 | * missingTranslation: MissingTranslationStrategy.Error
|
39018 | * });
|
39019 | * ```
|
39020 | *
|
39021 | * @publicApi
|
39022 | */
|
39023 | var MissingTranslationStrategy$1;
|
39024 | (function (MissingTranslationStrategy) {
|
39025 | MissingTranslationStrategy[MissingTranslationStrategy["Error"] = 0] = "Error";
|
39026 | MissingTranslationStrategy[MissingTranslationStrategy["Warning"] = 1] = "Warning";
|
39027 | MissingTranslationStrategy[MissingTranslationStrategy["Ignore"] = 2] = "Ignore";
|
39028 | })(MissingTranslationStrategy$1 || (MissingTranslationStrategy$1 = {}));
|
39029 |
|
39030 | /**
|
39031 | * @license
|
39032 | * Copyright Google LLC All Rights Reserved.
|
39033 | *
|
39034 | * Use of this source code is governed by an MIT-style license that can be
|
39035 | * found in the LICENSE file at https://angular.io/license
|
39036 | */
|
39037 | const SWITCH_IVY_ENABLED__PRE_R3__ = false;
|
39038 | const ivyEnabled = SWITCH_IVY_ENABLED__PRE_R3__;
|
39039 |
|
39040 | /**
|
39041 | * @license
|
39042 | * Copyright Google LLC All Rights Reserved.
|
39043 | *
|
39044 | * Use of this source code is governed by an MIT-style license that can be
|
39045 | * found in the LICENSE file at https://angular.io/license
|
39046 | */
|
39047 | function _throwError() {
|
39048 | throw new Error(`Runtime compiler is not loaded`);
|
39049 | }
|
39050 | const Compiler_compileModuleSync__PRE_R3__ = _throwError;
|
39051 | const Compiler_compileModuleSync = Compiler_compileModuleSync__PRE_R3__;
|
39052 | const Compiler_compileModuleAsync__PRE_R3__ = _throwError;
|
39053 | const Compiler_compileModuleAsync = Compiler_compileModuleAsync__PRE_R3__;
|
39054 | const Compiler_compileModuleAndAllComponentsSync__PRE_R3__ = _throwError;
|
39055 | const Compiler_compileModuleAndAllComponentsSync = Compiler_compileModuleAndAllComponentsSync__PRE_R3__;
|
39056 | const Compiler_compileModuleAndAllComponentsAsync__PRE_R3__ = _throwError;
|
39057 | const Compiler_compileModuleAndAllComponentsAsync = Compiler_compileModuleAndAllComponentsAsync__PRE_R3__;
|
39058 | /**
|
39059 | * Low-level service for running the angular compiler during runtime
|
39060 | * to create {@link ComponentFactory}s, which
|
39061 | * can later be used to create and render a Component instance.
|
39062 | *
|
39063 | * Each `@NgModule` provides an own `Compiler` to its injector,
|
39064 | * that will use the directives/pipes of the ng module for compilation
|
39065 | * of components.
|
39066 | *
|
39067 | * @publicApi
|
39068 | */
|
39069 | class Compiler {
|
39070 | constructor() {
|
39071 | /**
|
39072 | * Compiles the given NgModule and all of its components. All templates of the components listed
|
39073 | * in `entryComponents` have to be inlined.
|
39074 | */
|
39075 | this.compileModuleSync = Compiler_compileModuleSync;
|
39076 | /**
|
39077 | * Compiles the given NgModule and all of its components
|
39078 | */
|
39079 | this.compileModuleAsync = Compiler_compileModuleAsync;
|
39080 | /**
|
39081 | * Same as {@link #compileModuleSync} but also creates ComponentFactories for all components.
|
39082 | */
|
39083 | this.compileModuleAndAllComponentsSync = Compiler_compileModuleAndAllComponentsSync;
|
39084 | /**
|
39085 | * Same as {@link #compileModuleAsync} but also creates ComponentFactories for all components.
|
39086 | */
|
39087 | this.compileModuleAndAllComponentsAsync = Compiler_compileModuleAndAllComponentsAsync;
|
39088 | }
|
39089 | /**
|
39090 | * Clears all caches.
|
39091 | */
|
39092 | clearCache() { }
|
39093 | /**
|
39094 | * Clears the cache for the given component/ngModule.
|
39095 | */
|
39096 | clearCacheFor(type) { }
|
39097 | /**
|
39098 | * Returns the id for a given NgModule, if one is defined and known to the compiler.
|
39099 | */
|
39100 | getModuleId(moduleType) {
|
39101 | return undefined;
|
39102 | }
|
39103 | }
|
39104 | Compiler.decorators = [
|
39105 | { type: Injectable }
|
39106 | ];
|
39107 | /**
|
39108 | * Token to provide CompilerOptions in the platform injector.
|
39109 | *
|
39110 | * @publicApi
|
39111 | */
|
39112 | const COMPILER_OPTIONS = new InjectionToken('compilerOptions');
|
39113 | /**
|
39114 | * A factory for creating a Compiler
|
39115 | *
|
39116 | * @publicApi
|
39117 | */
|
39118 | class CompilerFactory {
|
39119 | }
|
39120 |
|
39121 | /**
|
39122 | * @license
|
39123 | * Copyright Google LLC All Rights Reserved.
|
39124 | *
|
39125 | * Use of this source code is governed by an MIT-style license that can be
|
39126 | * found in the LICENSE file at https://angular.io/license
|
39127 | */
|
39128 | const promise = (() => Promise.resolve(0))();
|
39129 | function scheduleMicroTask(fn) {
|
39130 | if (typeof Zone === 'undefined') {
|
39131 | // use promise to schedule microTask instead of use Zone
|
39132 | promise.then(() => {
|
39133 | fn && fn.apply(null, null);
|
39134 | });
|
39135 | }
|
39136 | else {
|
39137 | Zone.current.scheduleMicroTask('scheduleMicrotask', fn);
|
39138 | }
|
39139 | }
|
39140 |
|
39141 | /**
|
39142 | * @license
|
39143 | * Copyright Google LLC All Rights Reserved.
|
39144 | *
|
39145 | * Use of this source code is governed by an MIT-style license that can be
|
39146 | * found in the LICENSE file at https://angular.io/license
|
39147 | */
|
39148 | function getNativeRequestAnimationFrame() {
|
39149 | let nativeRequestAnimationFrame = _global$1['requestAnimationFrame'];
|
39150 | let nativeCancelAnimationFrame = _global$1['cancelAnimationFrame'];
|
39151 | if (typeof Zone !== 'undefined' && nativeRequestAnimationFrame && nativeCancelAnimationFrame) {
|
39152 | // use unpatched version of requestAnimationFrame(native delegate) if possible
|
39153 | // to avoid another Change detection
|
39154 | const unpatchedRequestAnimationFrame = nativeRequestAnimationFrame[Zone.__symbol__('OriginalDelegate')];
|
39155 | if (unpatchedRequestAnimationFrame) {
|
39156 | nativeRequestAnimationFrame = unpatchedRequestAnimationFrame;
|
39157 | }
|
39158 | const unpatchedCancelAnimationFrame = nativeCancelAnimationFrame[Zone.__symbol__('OriginalDelegate')];
|
39159 | if (unpatchedCancelAnimationFrame) {
|
39160 | nativeCancelAnimationFrame = unpatchedCancelAnimationFrame;
|
39161 | }
|
39162 | }
|
39163 | return { nativeRequestAnimationFrame, nativeCancelAnimationFrame };
|
39164 | }
|
39165 |
|
39166 | /**
|
39167 | * @license
|
39168 | * Copyright Google LLC All Rights Reserved.
|
39169 | *
|
39170 | * Use of this source code is governed by an MIT-style license that can be
|
39171 | * found in the LICENSE file at https://angular.io/license
|
39172 | */
|
39173 | /**
|
39174 | * An injectable service for executing work inside or outside of the Angular zone.
|
39175 | *
|
39176 | * The most common use of this service is to optimize performance when starting a work consisting of
|
39177 | * one or more asynchronous tasks that don't require UI updates or error handling to be handled by
|
39178 | * Angular. Such tasks can be kicked off via {@link #runOutsideAngular} and if needed, these tasks
|
39179 | * can reenter the Angular zone via {@link #run}.
|
39180 | *
|
39181 | * <!-- TODO: add/fix links to:
|
39182 | * - docs explaining zones and the use of zones in Angular and change-detection
|
39183 | * - link to runOutsideAngular/run (throughout this file!)
|
39184 | * -->
|
39185 | *
|
39186 | * @usageNotes
|
39187 | * ### Example
|
39188 | *
|
39189 | * ```
|
39190 | * import {Component, NgZone} from '@angular/core';
|
39191 | * import {NgIf} from '@angular/common';
|
39192 | *
|
39193 | * @Component({
|
39194 | * selector: 'ng-zone-demo',
|
39195 | * template: `
|
39196 | * <h2>Demo: NgZone</h2>
|
39197 | *
|
39198 | * <p>Progress: {{progress}}%</p>
|
39199 | * <p *ngIf="progress >= 100">Done processing {{label}} of Angular zone!</p>
|
39200 | *
|
39201 | * <button (click)="processWithinAngularZone()">Process within Angular zone</button>
|
39202 | * <button (click)="processOutsideOfAngularZone()">Process outside of Angular zone</button>
|
39203 | * `,
|
39204 | * })
|
39205 | * export class NgZoneDemo {
|
39206 | * progress: number = 0;
|
39207 | * label: string;
|
39208 | *
|
39209 | * constructor(private _ngZone: NgZone) {}
|
39210 | *
|
39211 | * // Loop inside the Angular zone
|
39212 | * // so the UI DOES refresh after each setTimeout cycle
|
39213 | * processWithinAngularZone() {
|
39214 | * this.label = 'inside';
|
39215 | * this.progress = 0;
|
39216 | * this._increaseProgress(() => console.log('Inside Done!'));
|
39217 | * }
|
39218 | *
|
39219 | * // Loop outside of the Angular zone
|
39220 | * // so the UI DOES NOT refresh after each setTimeout cycle
|
39221 | * processOutsideOfAngularZone() {
|
39222 | * this.label = 'outside';
|
39223 | * this.progress = 0;
|
39224 | * this._ngZone.runOutsideAngular(() => {
|
39225 | * this._increaseProgress(() => {
|
39226 | * // reenter the Angular zone and display done
|
39227 | * this._ngZone.run(() => { console.log('Outside Done!'); });
|
39228 | * });
|
39229 | * });
|
39230 | * }
|
39231 | *
|
39232 | * _increaseProgress(doneCallback: () => void) {
|
39233 | * this.progress += 1;
|
39234 | * console.log(`Current progress: ${this.progress}%`);
|
39235 | *
|
39236 | * if (this.progress < 100) {
|
39237 | * window.setTimeout(() => this._increaseProgress(doneCallback), 10);
|
39238 | * } else {
|
39239 | * doneCallback();
|
39240 | * }
|
39241 | * }
|
39242 | * }
|
39243 | * ```
|
39244 | *
|
39245 | * @publicApi
|
39246 | */
|
39247 | class NgZone {
|
39248 | constructor({ enableLongStackTrace = false, shouldCoalesceEventChangeDetection = false, shouldCoalesceRunChangeDetection = false }) {
|
39249 | this.hasPendingMacrotasks = false;
|
39250 | this.hasPendingMicrotasks = false;
|
39251 | /**
|
39252 | * Whether there are no outstanding microtasks or macrotasks.
|
39253 | */
|
39254 | this.isStable = true;
|
39255 | /**
|
39256 | * Notifies when code enters Angular Zone. This gets fired first on VM Turn.
|
39257 | */
|
39258 | this.onUnstable = new EventEmitter(false);
|
39259 | /**
|
39260 | * Notifies when there is no more microtasks enqueued in the current VM Turn.
|
39261 | * This is a hint for Angular to do change detection, which may enqueue more microtasks.
|
39262 | * For this reason this event can fire multiple times per VM Turn.
|
39263 | */
|
39264 | this.onMicrotaskEmpty = new EventEmitter(false);
|
39265 | /**
|
39266 | * Notifies when the last `onMicrotaskEmpty` has run and there are no more microtasks, which
|
39267 | * implies we are about to relinquish VM turn.
|
39268 | * This event gets called just once.
|
39269 | */
|
39270 | this.onStable = new EventEmitter(false);
|
39271 | /**
|
39272 | * Notifies that an error has been delivered.
|
39273 | */
|
39274 | this.onError = new EventEmitter(false);
|
39275 | if (typeof Zone == 'undefined') {
|
39276 | throw new Error(`In this configuration Angular requires Zone.js`);
|
39277 | }
|
39278 | Zone.assertZonePatched();
|
39279 | const self = this;
|
39280 | self._nesting = 0;
|
39281 | self._outer = self._inner = Zone.current;
|
39282 | if (Zone['TaskTrackingZoneSpec']) {
|
39283 | self._inner = self._inner.fork(new Zone['TaskTrackingZoneSpec']);
|
39284 | }
|
39285 | if (enableLongStackTrace && Zone['longStackTraceZoneSpec']) {
|
39286 | self._inner = self._inner.fork(Zone['longStackTraceZoneSpec']);
|
39287 | }
|
39288 | // if shouldCoalesceRunChangeDetection is true, all tasks including event tasks will be
|
39289 | // coalesced, so shouldCoalesceEventChangeDetection option is not necessary and can be skipped.
|
39290 | self.shouldCoalesceEventChangeDetection =
|
39291 | !shouldCoalesceRunChangeDetection && shouldCoalesceEventChangeDetection;
|
39292 | self.shouldCoalesceRunChangeDetection = shouldCoalesceRunChangeDetection;
|
39293 | self.lastRequestAnimationFrameId = -1;
|
39294 | self.nativeRequestAnimationFrame = getNativeRequestAnimationFrame().nativeRequestAnimationFrame;
|
39295 | forkInnerZoneWithAngularBehavior(self);
|
39296 | }
|
39297 | static isInAngularZone() {
|
39298 | return Zone.current.get('isAngularZone') === true;
|
39299 | }
|
39300 | static assertInAngularZone() {
|
39301 | if (!NgZone.isInAngularZone()) {
|
39302 | throw new Error('Expected to be in Angular Zone, but it is not!');
|
39303 | }
|
39304 | }
|
39305 | static assertNotInAngularZone() {
|
39306 | if (NgZone.isInAngularZone()) {
|
39307 | throw new Error('Expected to not be in Angular Zone, but it is!');
|
39308 | }
|
39309 | }
|
39310 | /**
|
39311 | * Executes the `fn` function synchronously within the Angular zone and returns value returned by
|
39312 | * the function.
|
39313 | *
|
39314 | * Running functions via `run` allows you to reenter Angular zone from a task that was executed
|
39315 | * outside of the Angular zone (typically started via {@link #runOutsideAngular}).
|
39316 | *
|
39317 | * Any future tasks or microtasks scheduled from within this function will continue executing from
|
39318 | * within the Angular zone.
|
39319 | *
|
39320 | * If a synchronous error happens it will be rethrown and not reported via `onError`.
|
39321 | */
|
39322 | run(fn, applyThis, applyArgs) {
|
39323 | return this._inner.run(fn, applyThis, applyArgs);
|
39324 | }
|
39325 | /**
|
39326 | * Executes the `fn` function synchronously within the Angular zone as a task and returns value
|
39327 | * returned by the function.
|
39328 | *
|
39329 | * Running functions via `run` allows you to reenter Angular zone from a task that was executed
|
39330 | * outside of the Angular zone (typically started via {@link #runOutsideAngular}).
|
39331 | *
|
39332 | * Any future tasks or microtasks scheduled from within this function will continue executing from
|
39333 | * within the Angular zone.
|
39334 | *
|
39335 | * If a synchronous error happens it will be rethrown and not reported via `onError`.
|
39336 | */
|
39337 | runTask(fn, applyThis, applyArgs, name) {
|
39338 | const zone = this._inner;
|
39339 | const task = zone.scheduleEventTask('NgZoneEvent: ' + name, fn, EMPTY_PAYLOAD, noop, noop);
|
39340 | try {
|
39341 | return zone.runTask(task, applyThis, applyArgs);
|
39342 | }
|
39343 | finally {
|
39344 | zone.cancelTask(task);
|
39345 | }
|
39346 | }
|
39347 | /**
|
39348 | * Same as `run`, except that synchronous errors are caught and forwarded via `onError` and not
|
39349 | * rethrown.
|
39350 | */
|
39351 | runGuarded(fn, applyThis, applyArgs) {
|
39352 | return this._inner.runGuarded(fn, applyThis, applyArgs);
|
39353 | }
|
39354 | /**
|
39355 | * Executes the `fn` function synchronously in Angular's parent zone and returns value returned by
|
39356 | * the function.
|
39357 | *
|
39358 | * Running functions via {@link #runOutsideAngular} allows you to escape Angular's zone and do
|
39359 | * work that
|
39360 | * doesn't trigger Angular change-detection or is subject to Angular's error handling.
|
39361 | *
|
39362 | * Any future tasks or microtasks scheduled from within this function will continue executing from
|
39363 | * outside of the Angular zone.
|
39364 | *
|
39365 | * Use {@link #run} to reenter the Angular zone and do work that updates the application model.
|
39366 | */
|
39367 | runOutsideAngular(fn) {
|
39368 | return this._outer.run(fn);
|
39369 | }
|
39370 | }
|
39371 | const EMPTY_PAYLOAD = {};
|
39372 | function checkStable(zone) {
|
39373 | if (zone._nesting == 0 && !zone.hasPendingMicrotasks && !zone.isStable) {
|
39374 | try {
|
39375 | zone._nesting++;
|
39376 | zone.onMicrotaskEmpty.emit(null);
|
39377 | }
|
39378 | finally {
|
39379 | zone._nesting--;
|
39380 | if (!zone.hasPendingMicrotasks) {
|
39381 | try {
|
39382 | zone.runOutsideAngular(() => zone.onStable.emit(null));
|
39383 | }
|
39384 | finally {
|
39385 | zone.isStable = true;
|
39386 | }
|
39387 | }
|
39388 | }
|
39389 | }
|
39390 | }
|
39391 | function delayChangeDetectionForEvents(zone) {
|
39392 | if (zone.lastRequestAnimationFrameId !== -1) {
|
39393 | return;
|
39394 | }
|
39395 | zone.lastRequestAnimationFrameId = zone.nativeRequestAnimationFrame.call(_global$1, () => {
|
39396 | // This is a work around for https://github.com/angular/angular/issues/36839.
|
39397 | // The core issue is that when event coalescing is enabled it is possible for microtasks
|
39398 | // to get flushed too early (As is the case with `Promise.then`) between the
|
39399 | // coalescing eventTasks.
|
39400 | //
|
39401 | // To workaround this we schedule a "fake" eventTask before we process the
|
39402 | // coalescing eventTasks. The benefit of this is that the "fake" container eventTask
|
39403 | // will prevent the microtasks queue from getting drained in between the coalescing
|
39404 | // eventTask execution.
|
39405 | if (!zone.fakeTopEventTask) {
|
39406 | zone.fakeTopEventTask = Zone.root.scheduleEventTask('fakeTopEventTask', () => {
|
39407 | zone.lastRequestAnimationFrameId = -1;
|
39408 | updateMicroTaskStatus(zone);
|
39409 | checkStable(zone);
|
39410 | }, undefined, () => { }, () => { });
|
39411 | }
|
39412 | zone.fakeTopEventTask.invoke();
|
39413 | });
|
39414 | updateMicroTaskStatus(zone);
|
39415 | }
|
39416 | function forkInnerZoneWithAngularBehavior(zone) {
|
39417 | const delayChangeDetectionForEventsDelegate = () => {
|
39418 | delayChangeDetectionForEvents(zone);
|
39419 | };
|
39420 | zone._inner = zone._inner.fork({
|
39421 | name: 'angular',
|
39422 | properties: { 'isAngularZone': true },
|
39423 | onInvokeTask: (delegate, current, target, task, applyThis, applyArgs) => {
|
39424 | try {
|
39425 | onEnter(zone);
|
39426 | return delegate.invokeTask(target, task, applyThis, applyArgs);
|
39427 | }
|
39428 | finally {
|
39429 | if ((zone.shouldCoalesceEventChangeDetection && task.type === 'eventTask') ||
|
39430 | zone.shouldCoalesceRunChangeDetection) {
|
39431 | delayChangeDetectionForEventsDelegate();
|
39432 | }
|
39433 | onLeave(zone);
|
39434 | }
|
39435 | },
|
39436 | onInvoke: (delegate, current, target, callback, applyThis, applyArgs, source) => {
|
39437 | try {
|
39438 | onEnter(zone);
|
39439 | return delegate.invoke(target, callback, applyThis, applyArgs, source);
|
39440 | }
|
39441 | finally {
|
39442 | if (zone.shouldCoalesceRunChangeDetection) {
|
39443 | delayChangeDetectionForEventsDelegate();
|
39444 | }
|
39445 | onLeave(zone);
|
39446 | }
|
39447 | },
|
39448 | onHasTask: (delegate, current, target, hasTaskState) => {
|
39449 | delegate.hasTask(target, hasTaskState);
|
39450 | if (current === target) {
|
39451 | // We are only interested in hasTask events which originate from our zone
|
39452 | // (A child hasTask event is not interesting to us)
|
39453 | if (hasTaskState.change == 'microTask') {
|
39454 | zone._hasPendingMicrotasks = hasTaskState.microTask;
|
39455 | updateMicroTaskStatus(zone);
|
39456 | checkStable(zone);
|
39457 | }
|
39458 | else if (hasTaskState.change == 'macroTask') {
|
39459 | zone.hasPendingMacrotasks = hasTaskState.macroTask;
|
39460 | }
|
39461 | }
|
39462 | },
|
39463 | onHandleError: (delegate, current, target, error) => {
|
39464 | delegate.handleError(target, error);
|
39465 | zone.runOutsideAngular(() => zone.onError.emit(error));
|
39466 | return false;
|
39467 | }
|
39468 | });
|
39469 | }
|
39470 | function updateMicroTaskStatus(zone) {
|
39471 | if (zone._hasPendingMicrotasks ||
|
39472 | ((zone.shouldCoalesceEventChangeDetection || zone.shouldCoalesceRunChangeDetection) &&
|
39473 | zone.lastRequestAnimationFrameId !== -1)) {
|
39474 | zone.hasPendingMicrotasks = true;
|
39475 | }
|
39476 | else {
|
39477 | zone.hasPendingMicrotasks = false;
|
39478 | }
|
39479 | }
|
39480 | function onEnter(zone) {
|
39481 | zone._nesting++;
|
39482 | if (zone.isStable) {
|
39483 | zone.isStable = false;
|
39484 | zone.onUnstable.emit(null);
|
39485 | }
|
39486 | }
|
39487 | function onLeave(zone) {
|
39488 | zone._nesting--;
|
39489 | checkStable(zone);
|
39490 | }
|
39491 | /**
|
39492 | * Provides a noop implementation of `NgZone` which does nothing. This zone requires explicit calls
|
39493 | * to framework to perform rendering.
|
39494 | */
|
39495 | class NoopNgZone {
|
39496 | constructor() {
|
39497 | this.hasPendingMicrotasks = false;
|
39498 | this.hasPendingMacrotasks = false;
|
39499 | this.isStable = true;
|
39500 | this.onUnstable = new EventEmitter();
|
39501 | this.onMicrotaskEmpty = new EventEmitter();
|
39502 | this.onStable = new EventEmitter();
|
39503 | this.onError = new EventEmitter();
|
39504 | }
|
39505 | run(fn, applyThis, applyArgs) {
|
39506 | return fn.apply(applyThis, applyArgs);
|
39507 | }
|
39508 | runGuarded(fn, applyThis, applyArgs) {
|
39509 | return fn.apply(applyThis, applyArgs);
|
39510 | }
|
39511 | runOutsideAngular(fn) {
|
39512 | return fn();
|
39513 | }
|
39514 | runTask(fn, applyThis, applyArgs, name) {
|
39515 | return fn.apply(applyThis, applyArgs);
|
39516 | }
|
39517 | }
|
39518 |
|
39519 | /**
|
39520 | * @license
|
39521 | * Copyright Google LLC All Rights Reserved.
|
39522 | *
|
39523 | * Use of this source code is governed by an MIT-style license that can be
|
39524 | * found in the LICENSE file at https://angular.io/license
|
39525 | */
|
39526 | /**
|
39527 | * The Testability service provides testing hooks that can be accessed from
|
39528 | * the browser and by services such as Protractor. Each bootstrapped Angular
|
39529 | * application on the page will have an instance of Testability.
|
39530 | * @publicApi
|
39531 | */
|
39532 | class Testability {
|
39533 | constructor(_ngZone) {
|
39534 | this._ngZone = _ngZone;
|
39535 | this._pendingCount = 0;
|
39536 | this._isZoneStable = true;
|
39537 | /**
|
39538 | * Whether any work was done since the last 'whenStable' callback. This is
|
39539 | * useful to detect if this could have potentially destabilized another
|
39540 | * component while it is stabilizing.
|
39541 | * @internal
|
39542 | */
|
39543 | this._didWork = false;
|
39544 | this._callbacks = [];
|
39545 | this.taskTrackingZone = null;
|
39546 | this._watchAngularEvents();
|
39547 | _ngZone.run(() => {
|
39548 | this.taskTrackingZone =
|
39549 | typeof Zone == 'undefined' ? null : Zone.current.get('TaskTrackingZone');
|
39550 | });
|
39551 | }
|
39552 | _watchAngularEvents() {
|
39553 | this._ngZone.onUnstable.subscribe({
|
39554 | next: () => {
|
39555 | this._didWork = true;
|
39556 | this._isZoneStable = false;
|
39557 | }
|
39558 | });
|
39559 | this._ngZone.runOutsideAngular(() => {
|
39560 | this._ngZone.onStable.subscribe({
|
39561 | next: () => {
|
39562 | NgZone.assertNotInAngularZone();
|
39563 | scheduleMicroTask(() => {
|
39564 | this._isZoneStable = true;
|
39565 | this._runCallbacksIfReady();
|
39566 | });
|
39567 | }
|
39568 | });
|
39569 | });
|
39570 | }
|
39571 | /**
|
39572 | * Increases the number of pending request
|
39573 | * @deprecated pending requests are now tracked with zones.
|
39574 | */
|
39575 | increasePendingRequestCount() {
|
39576 | this._pendingCount += 1;
|
39577 | this._didWork = true;
|
39578 | return this._pendingCount;
|
39579 | }
|
39580 | /**
|
39581 | * Decreases the number of pending request
|
39582 | * @deprecated pending requests are now tracked with zones
|
39583 | */
|
39584 | decreasePendingRequestCount() {
|
39585 | this._pendingCount -= 1;
|
39586 | if (this._pendingCount < 0) {
|
39587 | throw new Error('pending async requests below zero');
|
39588 | }
|
39589 | this._runCallbacksIfReady();
|
39590 | return this._pendingCount;
|
39591 | }
|
39592 | /**
|
39593 | * Whether an associated application is stable
|
39594 | */
|
39595 | isStable() {
|
39596 | return this._isZoneStable && this._pendingCount === 0 && !this._ngZone.hasPendingMacrotasks;
|
39597 | }
|
39598 | _runCallbacksIfReady() {
|
39599 | if (this.isStable()) {
|
39600 | // Schedules the call backs in a new frame so that it is always async.
|
39601 | scheduleMicroTask(() => {
|
39602 | while (this._callbacks.length !== 0) {
|
39603 | let cb = this._callbacks.pop();
|
39604 | clearTimeout(cb.timeoutId);
|
39605 | cb.doneCb(this._didWork);
|
39606 | }
|
39607 | this._didWork = false;
|
39608 | });
|
39609 | }
|
39610 | else {
|
39611 | // Still not stable, send updates.
|
39612 | let pending = this.getPendingTasks();
|
39613 | this._callbacks = this._callbacks.filter((cb) => {
|
39614 | if (cb.updateCb && cb.updateCb(pending)) {
|
39615 | clearTimeout(cb.timeoutId);
|
39616 | return false;
|
39617 | }
|
39618 | return true;
|
39619 | });
|
39620 | this._didWork = true;
|
39621 | }
|
39622 | }
|
39623 | getPendingTasks() {
|
39624 | if (!this.taskTrackingZone) {
|
39625 | return [];
|
39626 | }
|
39627 | // Copy the tasks data so that we don't leak tasks.
|
39628 | return this.taskTrackingZone.macroTasks.map((t) => {
|
39629 | return {
|
39630 | source: t.source,
|
39631 | // From TaskTrackingZone:
|
39632 | // https://github.com/angular/zone.js/blob/master/lib/zone-spec/task-tracking.ts#L40
|
39633 | creationLocation: t.creationLocation,
|
39634 | data: t.data
|
39635 | };
|
39636 | });
|
39637 | }
|
39638 | addCallback(cb, timeout, updateCb) {
|
39639 | let timeoutId = -1;
|
39640 | if (timeout && timeout > 0) {
|
39641 | timeoutId = setTimeout(() => {
|
39642 | this._callbacks = this._callbacks.filter((cb) => cb.timeoutId !== timeoutId);
|
39643 | cb(this._didWork, this.getPendingTasks());
|
39644 | }, timeout);
|
39645 | }
|
39646 | this._callbacks.push({ doneCb: cb, timeoutId: timeoutId, updateCb: updateCb });
|
39647 | }
|
39648 | /**
|
39649 | * Wait for the application to be stable with a timeout. If the timeout is reached before that
|
39650 | * happens, the callback receives a list of the macro tasks that were pending, otherwise null.
|
39651 | *
|
39652 | * @param doneCb The callback to invoke when Angular is stable or the timeout expires
|
39653 | * whichever comes first.
|
39654 | * @param timeout Optional. The maximum time to wait for Angular to become stable. If not
|
39655 | * specified, whenStable() will wait forever.
|
39656 | * @param updateCb Optional. If specified, this callback will be invoked whenever the set of
|
39657 | * pending macrotasks changes. If this callback returns true doneCb will not be invoked
|
39658 | * and no further updates will be issued.
|
39659 | */
|
39660 | whenStable(doneCb, timeout, updateCb) {
|
39661 | if (updateCb && !this.taskTrackingZone) {
|
39662 | throw new Error('Task tracking zone is required when passing an update callback to ' +
|
39663 | 'whenStable(). Is "zone.js/dist/task-tracking.js" loaded?');
|
39664 | }
|
39665 | // These arguments are 'Function' above to keep the public API simple.
|
39666 | this.addCallback(doneCb, timeout, updateCb);
|
39667 | this._runCallbacksIfReady();
|
39668 | }
|
39669 | /**
|
39670 | * Get the number of pending requests
|
39671 | * @deprecated pending requests are now tracked with zones
|
39672 | */
|
39673 | getPendingRequestCount() {
|
39674 | return this._pendingCount;
|
39675 | }
|
39676 | /**
|
39677 | * Find providers by name
|
39678 | * @param using The root element to search from
|
39679 | * @param provider The name of binding variable
|
39680 | * @param exactMatch Whether using exactMatch
|
39681 | */
|
39682 | findProviders(using, provider, exactMatch) {
|
39683 | // TODO(juliemr): implement.
|
39684 | return [];
|
39685 | }
|
39686 | }
|
39687 | Testability.decorators = [
|
39688 | { type: Injectable }
|
39689 | ];
|
39690 | Testability.ctorParameters = () => [
|
39691 | { type: NgZone }
|
39692 | ];
|
39693 | /**
|
39694 | * A global registry of {@link Testability} instances for specific elements.
|
39695 | * @publicApi
|
39696 | */
|
39697 | class TestabilityRegistry {
|
39698 | constructor() {
|
39699 | /** @internal */
|
39700 | this._applications = new Map();
|
39701 | _testabilityGetter.addToWindow(this);
|
39702 | }
|
39703 | /**
|
39704 | * Registers an application with a testability hook so that it can be tracked
|
39705 | * @param token token of application, root element
|
39706 | * @param testability Testability hook
|
39707 | */
|
39708 | registerApplication(token, testability) {
|
39709 | this._applications.set(token, testability);
|
39710 | }
|
39711 | /**
|
39712 | * Unregisters an application.
|
39713 | * @param token token of application, root element
|
39714 | */
|
39715 | unregisterApplication(token) {
|
39716 | this._applications.delete(token);
|
39717 | }
|
39718 | /**
|
39719 | * Unregisters all applications
|
39720 | */
|
39721 | unregisterAllApplications() {
|
39722 | this._applications.clear();
|
39723 | }
|
39724 | /**
|
39725 | * Get a testability hook associated with the application
|
39726 | * @param elem root element
|
39727 | */
|
39728 | getTestability(elem) {
|
39729 | return this._applications.get(elem) || null;
|
39730 | }
|
39731 | /**
|
39732 | * Get all registered testabilities
|
39733 | */
|
39734 | getAllTestabilities() {
|
39735 | return Array.from(this._applications.values());
|
39736 | }
|
39737 | /**
|
39738 | * Get all registered applications(root elements)
|
39739 | */
|
39740 | getAllRootElements() {
|
39741 | return Array.from(this._applications.keys());
|
39742 | }
|
39743 | /**
|
39744 | * Find testability of a node in the Tree
|
39745 | * @param elem node
|
39746 | * @param findInAncestors whether finding testability in ancestors if testability was not found in
|
39747 | * current node
|
39748 | */
|
39749 | findTestabilityInTree(elem, findInAncestors = true) {
|
39750 | return _testabilityGetter.findTestabilityInTree(this, elem, findInAncestors);
|
39751 | }
|
39752 | }
|
39753 | TestabilityRegistry.decorators = [
|
39754 | { type: Injectable }
|
39755 | ];
|
39756 | TestabilityRegistry.ctorParameters = () => [];
|
39757 | class _NoopGetTestability {
|
39758 | addToWindow(registry) { }
|
39759 | findTestabilityInTree(registry, elem, findInAncestors) {
|
39760 | return null;
|
39761 | }
|
39762 | }
|
39763 | let _testabilityGetter = new _NoopGetTestability();
|
39764 |
|
39765 | /**
|
39766 | * @license
|
39767 | * Copyright Google LLC All Rights Reserved.
|
39768 | *
|
39769 | * Use of this source code is governed by an MIT-style license that can be
|
39770 | * found in the LICENSE file at https://angular.io/license
|
39771 | */
|
39772 | /**
|
39773 | * This file is used to control if the default rendering pipeline should be `ViewEngine` or `Ivy`.
|
39774 | *
|
39775 | * For more information on how to run and debug tests with either Ivy or View Engine (legacy),
|
39776 | * please see [BAZEL.md](./docs/BAZEL.md).
|
39777 | */
|
39778 | let _devMode = true;
|
39779 | /**
|
39780 | * Returns whether Angular is in development mode. After called once,
|
39781 | * the value is locked and won't change any more.
|
39782 | *
|
39783 | * By default, this is true, unless a user calls `enableProdMode` before calling this.
|
39784 | *
|
39785 | * @publicApi
|
39786 | */
|
39787 | function isDevMode() {
|
39788 | return _devMode;
|
39789 | }
|
39790 |
|
39791 | /**
|
39792 | * @license
|
39793 | * Copyright Google LLC All Rights Reserved.
|
39794 | *
|
39795 | * Use of this source code is governed by an MIT-style license that can be
|
39796 | * found in the LICENSE file at https://angular.io/license
|
39797 | */
|
39798 | let _platform;
|
39799 | let compileNgModuleFactory = compileNgModuleFactory__PRE_R3__;
|
39800 | function compileNgModuleFactory__PRE_R3__(injector, options, moduleType) {
|
39801 | const compilerFactory = injector.get(CompilerFactory);
|
39802 | const compiler = compilerFactory.createCompiler([options]);
|
39803 | return compiler.compileModuleAsync(moduleType);
|
39804 | }
|
39805 | let isBoundToModule = isBoundToModule__PRE_R3__;
|
39806 | function isBoundToModule__PRE_R3__(cf) {
|
39807 | return cf instanceof ComponentFactoryBoundToModule;
|
39808 | }
|
39809 | const ALLOW_MULTIPLE_PLATFORMS = new InjectionToken('AllowMultipleToken');
|
39810 | /**
|
39811 | * Creates a platform.
|
39812 | * Platforms must be created on launch using this function.
|
39813 | *
|
39814 | * @publicApi
|
39815 | */
|
39816 | function createPlatform(injector) {
|
39817 | if (_platform && !_platform.destroyed &&
|
39818 | !_platform.injector.get(ALLOW_MULTIPLE_PLATFORMS, false)) {
|
39819 | throw new Error('There can be only one platform. Destroy the previous one to create a new one.');
|
39820 | }
|
39821 | _platform = injector.get(PlatformRef);
|
39822 | const inits = injector.get(PLATFORM_INITIALIZER, null);
|
39823 | if (inits)
|
39824 | inits.forEach((init) => init());
|
39825 | return _platform;
|
39826 | }
|
39827 | /**
|
39828 | * Creates a factory for a platform. Can be used to provide or override `Providers` specific to
|
39829 | * your application's runtime needs, such as `PLATFORM_INITIALIZER` and `PLATFORM_ID`.
|
39830 | * @param parentPlatformFactory Another platform factory to modify. Allows you to compose factories
|
39831 | * to build up configurations that might be required by different libraries or parts of the
|
39832 | * application.
|
39833 | * @param name Identifies the new platform factory.
|
39834 | * @param providers A set of dependency providers for platforms created with the new factory.
|
39835 | *
|
39836 | * @publicApi
|
39837 | */
|
39838 | function createPlatformFactory(parentPlatformFactory, name, providers = []) {
|
39839 | const desc = `Platform: ${name}`;
|
39840 | const marker = new InjectionToken(desc);
|
39841 | return (extraProviders = []) => {
|
39842 | let platform = getPlatform();
|
39843 | if (!platform || platform.injector.get(ALLOW_MULTIPLE_PLATFORMS, false)) {
|
39844 | if (parentPlatformFactory) {
|
39845 | parentPlatformFactory(providers.concat(extraProviders).concat({ provide: marker, useValue: true }));
|
39846 | }
|
39847 | else {
|
39848 | const injectedProviders = providers.concat(extraProviders).concat({ provide: marker, useValue: true }, {
|
39849 | provide: INJECTOR_SCOPE,
|
39850 | useValue: 'platform'
|
39851 | });
|
39852 | createPlatform(Injector.create({ providers: injectedProviders, name: desc }));
|
39853 | }
|
39854 | }
|
39855 | return assertPlatform(marker);
|
39856 | };
|
39857 | }
|
39858 | /**
|
39859 | * Checks that there is currently a platform that contains the given token as a provider.
|
39860 | *
|
39861 | * @publicApi
|
39862 | */
|
39863 | function assertPlatform(requiredToken) {
|
39864 | const platform = getPlatform();
|
39865 | if (!platform) {
|
39866 | throw new Error('No platform exists!');
|
39867 | }
|
39868 | if (!platform.injector.get(requiredToken, null)) {
|
39869 | throw new Error('A platform with a different configuration has been created. Please destroy it first.');
|
39870 | }
|
39871 | return platform;
|
39872 | }
|
39873 | /**
|
39874 | * Returns the current platform.
|
39875 | *
|
39876 | * @publicApi
|
39877 | */
|
39878 | function getPlatform() {
|
39879 | return _platform && !_platform.destroyed ? _platform : null;
|
39880 | }
|
39881 | /**
|
39882 | * The Angular platform is the entry point for Angular on a web page.
|
39883 | * Each page has exactly one platform. Services (such as reflection) which are common
|
39884 | * to every Angular application running on the page are bound in its scope.
|
39885 | * A page's platform is initialized implicitly when a platform is created using a platform
|
39886 | * factory such as `PlatformBrowser`, or explicitly by calling the `createPlatform()` function.
|
39887 | *
|
39888 | * @publicApi
|
39889 | */
|
39890 | class PlatformRef {
|
39891 | /** @internal */
|
39892 | constructor(_injector) {
|
39893 | this._injector = _injector;
|
39894 | this._modules = [];
|
39895 | this._destroyListeners = [];
|
39896 | this._destroyed = false;
|
39897 | }
|
39898 | /**
|
39899 | * Creates an instance of an `@NgModule` for the given platform for offline compilation.
|
39900 | *
|
39901 | * @usageNotes
|
39902 | *
|
39903 | * The following example creates the NgModule for a browser platform.
|
39904 | *
|
39905 | * ```typescript
|
39906 | * my_module.ts:
|
39907 | *
|
39908 | * @NgModule({
|
39909 | * imports: [BrowserModule]
|
39910 | * })
|
39911 | * class MyModule {}
|
39912 | *
|
39913 | * main.ts:
|
39914 | * import {MyModuleNgFactory} from './my_module.ngfactory';
|
39915 | * import {platformBrowser} from '@angular/platform-browser';
|
39916 | *
|
39917 | * let moduleRef = platformBrowser().bootstrapModuleFactory(MyModuleNgFactory);
|
39918 | * ```
|
39919 | */
|
39920 | bootstrapModuleFactory(moduleFactory, options) {
|
39921 | // Note: We need to create the NgZone _before_ we instantiate the module,
|
39922 | // as instantiating the module creates some providers eagerly.
|
39923 | // So we create a mini parent injector that just contains the new NgZone and
|
39924 | // pass that as parent to the NgModuleFactory.
|
39925 | const ngZoneOption = options ? options.ngZone : undefined;
|
39926 | const ngZoneEventCoalescing = (options && options.ngZoneEventCoalescing) || false;
|
39927 | const ngZoneRunCoalescing = (options && options.ngZoneRunCoalescing) || false;
|
39928 | const ngZone = getNgZone(ngZoneOption, { ngZoneEventCoalescing, ngZoneRunCoalescing });
|
39929 | const providers = [{ provide: NgZone, useValue: ngZone }];
|
39930 | // Note: Create ngZoneInjector within ngZone.run so that all of the instantiated services are
|
39931 | // created within the Angular zone
|
39932 | // Do not try to replace ngZone.run with ApplicationRef#run because ApplicationRef would then be
|
39933 | // created outside of the Angular zone.
|
39934 | return ngZone.run(() => {
|
39935 | const ngZoneInjector = Injector.create({ providers: providers, parent: this.injector, name: moduleFactory.moduleType.name });
|
39936 | const moduleRef = moduleFactory.create(ngZoneInjector);
|
39937 | const exceptionHandler = moduleRef.injector.get(ErrorHandler, null);
|
39938 | if (!exceptionHandler) {
|
39939 | throw new Error('No ErrorHandler. Is platform module (BrowserModule) included?');
|
39940 | }
|
39941 | ngZone.runOutsideAngular(() => {
|
39942 | const subscription = ngZone.onError.subscribe({
|
39943 | next: (error) => {
|
39944 | exceptionHandler.handleError(error);
|
39945 | }
|
39946 | });
|
39947 | moduleRef.onDestroy(() => {
|
39948 | remove(this._modules, moduleRef);
|
39949 | subscription.unsubscribe();
|
39950 | });
|
39951 | });
|
39952 | return _callAndReportToErrorHandler(exceptionHandler, ngZone, () => {
|
39953 | const initStatus = moduleRef.injector.get(ApplicationInitStatus);
|
39954 | initStatus.runInitializers();
|
39955 | return initStatus.donePromise.then(() => {
|
39956 | if (ivyEnabled) {
|
39957 | // If the `LOCALE_ID` provider is defined at bootstrap then we set the value for ivy
|
39958 | const localeId = moduleRef.injector.get(LOCALE_ID$1, DEFAULT_LOCALE_ID);
|
39959 | setLocaleId(localeId || DEFAULT_LOCALE_ID);
|
39960 | }
|
39961 | this._moduleDoBootstrap(moduleRef);
|
39962 | return moduleRef;
|
39963 | });
|
39964 | });
|
39965 | });
|
39966 | }
|
39967 | /**
|
39968 | * Creates an instance of an `@NgModule` for a given platform using the given runtime compiler.
|
39969 | *
|
39970 | * @usageNotes
|
39971 | * ### Simple Example
|
39972 | *
|
39973 | * ```typescript
|
39974 | * @NgModule({
|
39975 | * imports: [BrowserModule]
|
39976 | * })
|
39977 | * class MyModule {}
|
39978 | *
|
39979 | * let moduleRef = platformBrowser().bootstrapModule(MyModule);
|
39980 | * ```
|
39981 | *
|
39982 | */
|
39983 | bootstrapModule(moduleType, compilerOptions = []) {
|
39984 | const options = optionsReducer({}, compilerOptions);
|
39985 | return compileNgModuleFactory(this.injector, options, moduleType)
|
39986 | .then(moduleFactory => this.bootstrapModuleFactory(moduleFactory, options));
|
39987 | }
|
39988 | _moduleDoBootstrap(moduleRef) {
|
39989 | const appRef = moduleRef.injector.get(ApplicationRef);
|
39990 | if (moduleRef._bootstrapComponents.length > 0) {
|
39991 | moduleRef._bootstrapComponents.forEach(f => appRef.bootstrap(f));
|
39992 | }
|
39993 | else if (moduleRef.instance.ngDoBootstrap) {
|
39994 | moduleRef.instance.ngDoBootstrap(appRef);
|
39995 | }
|
39996 | else {
|
39997 | throw new Error(`The module ${stringify$1(moduleRef.instance
|
39998 | .constructor)} was bootstrapped, but it does not declare "@NgModule.bootstrap" components nor a "ngDoBootstrap" method. ` +
|
39999 | `Please define one of these.`);
|
40000 | }
|
40001 | this._modules.push(moduleRef);
|
40002 | }
|
40003 | /**
|
40004 | * Registers a listener to be called when the platform is destroyed.
|
40005 | */
|
40006 | onDestroy(callback) {
|
40007 | this._destroyListeners.push(callback);
|
40008 | }
|
40009 | /**
|
40010 | * Retrieves the platform {@link Injector}, which is the parent injector for
|
40011 | * every Angular application on the page and provides singleton providers.
|
40012 | */
|
40013 | get injector() {
|
40014 | return this._injector;
|
40015 | }
|
40016 | /**
|
40017 | * Destroys the current Angular platform and all Angular applications on the page.
|
40018 | * Destroys all modules and listeners registered with the platform.
|
40019 | */
|
40020 | destroy() {
|
40021 | if (this._destroyed) {
|
40022 | throw new Error('The platform has already been destroyed!');
|
40023 | }
|
40024 | this._modules.slice().forEach(module => module.destroy());
|
40025 | this._destroyListeners.forEach(listener => listener());
|
40026 | this._destroyed = true;
|
40027 | }
|
40028 | get destroyed() {
|
40029 | return this._destroyed;
|
40030 | }
|
40031 | }
|
40032 | PlatformRef.decorators = [
|
40033 | { type: Injectable }
|
40034 | ];
|
40035 | PlatformRef.ctorParameters = () => [
|
40036 | { type: Injector }
|
40037 | ];
|
40038 | function getNgZone(ngZoneOption, extra) {
|
40039 | let ngZone;
|
40040 | if (ngZoneOption === 'noop') {
|
40041 | ngZone = new NoopNgZone();
|
40042 | }
|
40043 | else {
|
40044 | ngZone = (ngZoneOption === 'zone.js' ? undefined : ngZoneOption) || new NgZone({
|
40045 | enableLongStackTrace: isDevMode(),
|
40046 | shouldCoalesceEventChangeDetection: !!(extra === null || extra === void 0 ? void 0 : extra.ngZoneEventCoalescing),
|
40047 | shouldCoalesceRunChangeDetection: !!(extra === null || extra === void 0 ? void 0 : extra.ngZoneRunCoalescing)
|
40048 | });
|
40049 | }
|
40050 | return ngZone;
|
40051 | }
|
40052 | function _callAndReportToErrorHandler(errorHandler, ngZone, callback) {
|
40053 | try {
|
40054 | const result = callback();
|
40055 | if (isPromise$1(result)) {
|
40056 | return result.catch((e) => {
|
40057 | ngZone.runOutsideAngular(() => errorHandler.handleError(e));
|
40058 | // rethrow as the exception handler might not do it
|
40059 | throw e;
|
40060 | });
|
40061 | }
|
40062 | return result;
|
40063 | }
|
40064 | catch (e) {
|
40065 | ngZone.runOutsideAngular(() => errorHandler.handleError(e));
|
40066 | // rethrow as the exception handler might not do it
|
40067 | throw e;
|
40068 | }
|
40069 | }
|
40070 | function optionsReducer(dst, objs) {
|
40071 | if (Array.isArray(objs)) {
|
40072 | dst = objs.reduce(optionsReducer, dst);
|
40073 | }
|
40074 | else {
|
40075 | dst = Object.assign(Object.assign({}, dst), objs);
|
40076 | }
|
40077 | return dst;
|
40078 | }
|
40079 | /**
|
40080 | * A reference to an Angular application running on a page.
|
40081 | *
|
40082 | * @usageNotes
|
40083 | *
|
40084 | * {@a is-stable-examples}
|
40085 | * ### isStable examples and caveats
|
40086 | *
|
40087 | * Note two important points about `isStable`, demonstrated in the examples below:
|
40088 | * - the application will never be stable if you start any kind
|
40089 | * of recurrent asynchronous task when the application starts
|
40090 | * (for example for a polling process, started with a `setInterval`, a `setTimeout`
|
40091 | * or using RxJS operators like `interval`);
|
40092 | * - the `isStable` Observable runs outside of the Angular zone.
|
40093 | *
|
40094 | * Let's imagine that you start a recurrent task
|
40095 | * (here incrementing a counter, using RxJS `interval`),
|
40096 | * and at the same time subscribe to `isStable`.
|
40097 | *
|
40098 | * ```
|
40099 | * constructor(appRef: ApplicationRef) {
|
40100 | * appRef.isStable.pipe(
|
40101 | * filter(stable => stable)
|
40102 | * ).subscribe(() => console.log('App is stable now');
|
40103 | * interval(1000).subscribe(counter => console.log(counter));
|
40104 | * }
|
40105 | * ```
|
40106 | * In this example, `isStable` will never emit `true`,
|
40107 | * and the trace "App is stable now" will never get logged.
|
40108 | *
|
40109 | * If you want to execute something when the app is stable,
|
40110 | * you have to wait for the application to be stable
|
40111 | * before starting your polling process.
|
40112 | *
|
40113 | * ```
|
40114 | * constructor(appRef: ApplicationRef) {
|
40115 | * appRef.isStable.pipe(
|
40116 | * first(stable => stable),
|
40117 | * tap(stable => console.log('App is stable now')),
|
40118 | * switchMap(() => interval(1000))
|
40119 | * ).subscribe(counter => console.log(counter));
|
40120 | * }
|
40121 | * ```
|
40122 | * In this example, the trace "App is stable now" will be logged
|
40123 | * and then the counter starts incrementing every second.
|
40124 | *
|
40125 | * Note also that this Observable runs outside of the Angular zone,
|
40126 | * which means that the code in the subscription
|
40127 | * to this Observable will not trigger the change detection.
|
40128 | *
|
40129 | * Let's imagine that instead of logging the counter value,
|
40130 | * you update a field of your component
|
40131 | * and display it in its template.
|
40132 | *
|
40133 | * ```
|
40134 | * constructor(appRef: ApplicationRef) {
|
40135 | * appRef.isStable.pipe(
|
40136 | * first(stable => stable),
|
40137 | * switchMap(() => interval(1000))
|
40138 | * ).subscribe(counter => this.value = counter);
|
40139 | * }
|
40140 | * ```
|
40141 | * As the `isStable` Observable runs outside the zone,
|
40142 | * the `value` field will be updated properly,
|
40143 | * but the template will not be refreshed!
|
40144 | *
|
40145 | * You'll have to manually trigger the change detection to update the template.
|
40146 | *
|
40147 | * ```
|
40148 | * constructor(appRef: ApplicationRef, cd: ChangeDetectorRef) {
|
40149 | * appRef.isStable.pipe(
|
40150 | * first(stable => stable),
|
40151 | * switchMap(() => interval(1000))
|
40152 | * ).subscribe(counter => {
|
40153 | * this.value = counter;
|
40154 | * cd.detectChanges();
|
40155 | * });
|
40156 | * }
|
40157 | * ```
|
40158 | *
|
40159 | * Or make the subscription callback run inside the zone.
|
40160 | *
|
40161 | * ```
|
40162 | * constructor(appRef: ApplicationRef, zone: NgZone) {
|
40163 | * appRef.isStable.pipe(
|
40164 | * first(stable => stable),
|
40165 | * switchMap(() => interval(1000))
|
40166 | * ).subscribe(counter => zone.run(() => this.value = counter));
|
40167 | * }
|
40168 | * ```
|
40169 | *
|
40170 | * @publicApi
|
40171 | */
|
40172 | class ApplicationRef {
|
40173 | /** @internal */
|
40174 | constructor(_zone, _injector, _exceptionHandler, _componentFactoryResolver, _initStatus) {
|
40175 | this._zone = _zone;
|
40176 | this._injector = _injector;
|
40177 | this._exceptionHandler = _exceptionHandler;
|
40178 | this._componentFactoryResolver = _componentFactoryResolver;
|
40179 | this._initStatus = _initStatus;
|
40180 | /** @internal */
|
40181 | this._bootstrapListeners = [];
|
40182 | this._views = [];
|
40183 | this._runningTick = false;
|
40184 | this._stable = true;
|
40185 | /**
|
40186 | * Get a list of component types registered to this application.
|
40187 | * This list is populated even before the component is created.
|
40188 | */
|
40189 | this.componentTypes = [];
|
40190 | /**
|
40191 | * Get a list of components registered to this application.
|
40192 | */
|
40193 | this.components = [];
|
40194 | this._onMicrotaskEmptySubscription = this._zone.onMicrotaskEmpty.subscribe({
|
40195 | next: () => {
|
40196 | this._zone.run(() => {
|
40197 | this.tick();
|
40198 | });
|
40199 | }
|
40200 | });
|
40201 | const isCurrentlyStable = new Observable((observer) => {
|
40202 | this._stable = this._zone.isStable && !this._zone.hasPendingMacrotasks &&
|
40203 | !this._zone.hasPendingMicrotasks;
|
40204 | this._zone.runOutsideAngular(() => {
|
40205 | observer.next(this._stable);
|
40206 | observer.complete();
|
40207 | });
|
40208 | });
|
40209 | const isStable = new Observable((observer) => {
|
40210 | // Create the subscription to onStable outside the Angular Zone so that
|
40211 | // the callback is run outside the Angular Zone.
|
40212 | let stableSub;
|
40213 | this._zone.runOutsideAngular(() => {
|
40214 | stableSub = this._zone.onStable.subscribe(() => {
|
40215 | NgZone.assertNotInAngularZone();
|
40216 | // Check whether there are no pending macro/micro tasks in the next tick
|
40217 | // to allow for NgZone to update the state.
|
40218 | scheduleMicroTask(() => {
|
40219 | if (!this._stable && !this._zone.hasPendingMacrotasks &&
|
40220 | !this._zone.hasPendingMicrotasks) {
|
40221 | this._stable = true;
|
40222 | observer.next(true);
|
40223 | }
|
40224 | });
|
40225 | });
|
40226 | });
|
40227 | const unstableSub = this._zone.onUnstable.subscribe(() => {
|
40228 | NgZone.assertInAngularZone();
|
40229 | if (this._stable) {
|
40230 | this._stable = false;
|
40231 | this._zone.runOutsideAngular(() => {
|
40232 | observer.next(false);
|
40233 | });
|
40234 | }
|
40235 | });
|
40236 | return () => {
|
40237 | stableSub.unsubscribe();
|
40238 | unstableSub.unsubscribe();
|
40239 | };
|
40240 | });
|
40241 | this.isStable =
|
40242 | merge$1(isCurrentlyStable, isStable.pipe(share()));
|
40243 | }
|
40244 | /**
|
40245 | * Bootstrap a new component at the root level of the application.
|
40246 | *
|
40247 | * @usageNotes
|
40248 | * ### Bootstrap process
|
40249 | *
|
40250 | * When bootstrapping a new root component into an application, Angular mounts the
|
40251 | * specified application component onto DOM elements identified by the componentType's
|
40252 | * selector and kicks off automatic change detection to finish initializing the component.
|
40253 | *
|
40254 | * Optionally, a component can be mounted onto a DOM element that does not match the
|
40255 | * componentType's selector.
|
40256 | *
|
40257 | * ### Example
|
40258 | * {@example core/ts/platform/platform.ts region='longform'}
|
40259 | */
|
40260 | bootstrap(componentOrFactory, rootSelectorOrNode) {
|
40261 | if (!this._initStatus.done) {
|
40262 | throw new Error('Cannot bootstrap as there are still asynchronous initializers running. Bootstrap components in the `ngDoBootstrap` method of the root module.');
|
40263 | }
|
40264 | let componentFactory;
|
40265 | if (componentOrFactory instanceof ComponentFactory) {
|
40266 | componentFactory = componentOrFactory;
|
40267 | }
|
40268 | else {
|
40269 | componentFactory =
|
40270 | this._componentFactoryResolver.resolveComponentFactory(componentOrFactory);
|
40271 | }
|
40272 | this.componentTypes.push(componentFactory.componentType);
|
40273 | // Create a factory associated with the current module if it's not bound to some other
|
40274 | const ngModule = isBoundToModule(componentFactory) ? undefined : this._injector.get(NgModuleRef);
|
40275 | const selectorOrNode = rootSelectorOrNode || componentFactory.selector;
|
40276 | const compRef = componentFactory.create(Injector.NULL, [], selectorOrNode, ngModule);
|
40277 | const nativeElement = compRef.location.nativeElement;
|
40278 | const testability = compRef.injector.get(Testability, null);
|
40279 | const testabilityRegistry = testability && compRef.injector.get(TestabilityRegistry);
|
40280 | if (testability && testabilityRegistry) {
|
40281 | testabilityRegistry.registerApplication(nativeElement, testability);
|
40282 | }
|
40283 | compRef.onDestroy(() => {
|
40284 | this.detachView(compRef.hostView);
|
40285 | remove(this.components, compRef);
|
40286 | if (testabilityRegistry) {
|
40287 | testabilityRegistry.unregisterApplication(nativeElement);
|
40288 | }
|
40289 | });
|
40290 | this._loadComponent(compRef);
|
40291 | // Note that we have still left the `isDevMode()` condition in order to avoid
|
40292 | // creating a breaking change for projects that still use the View Engine.
|
40293 | if ((typeof ngDevMode === 'undefined' || ngDevMode) && isDevMode()) {
|
40294 | const _console = this._injector.get(Console);
|
40295 | _console.log(`Angular is running in development mode. Call enableProdMode() to enable production mode.`);
|
40296 | }
|
40297 | return compRef;
|
40298 | }
|
40299 | /**
|
40300 | * Invoke this method to explicitly process change detection and its side-effects.
|
40301 | *
|
40302 | * In development mode, `tick()` also performs a second change detection cycle to ensure that no
|
40303 | * further changes are detected. If additional changes are picked up during this second cycle,
|
40304 | * bindings in the app have side-effects that cannot be resolved in a single change detection
|
40305 | * pass.
|
40306 | * In this case, Angular throws an error, since an Angular application can only have one change
|
40307 | * detection pass during which all change detection must complete.
|
40308 | */
|
40309 | tick() {
|
40310 | if (this._runningTick) {
|
40311 | throw new Error('ApplicationRef.tick is called recursively');
|
40312 | }
|
40313 | try {
|
40314 | this._runningTick = true;
|
40315 | for (let view of this._views) {
|
40316 | view.detectChanges();
|
40317 | }
|
40318 | // Note that we have still left the `isDevMode()` condition in order to avoid
|
40319 | // creating a breaking change for projects that still use the View Engine.
|
40320 | if ((typeof ngDevMode === 'undefined' || ngDevMode) && isDevMode()) {
|
40321 | for (let view of this._views) {
|
40322 | view.checkNoChanges();
|
40323 | }
|
40324 | }
|
40325 | }
|
40326 | catch (e) {
|
40327 | // Attention: Don't rethrow as it could cancel subscriptions to Observables!
|
40328 | this._zone.runOutsideAngular(() => this._exceptionHandler.handleError(e));
|
40329 | }
|
40330 | finally {
|
40331 | this._runningTick = false;
|
40332 | }
|
40333 | }
|
40334 | /**
|
40335 | * Attaches a view so that it will be dirty checked.
|
40336 | * The view will be automatically detached when it is destroyed.
|
40337 | * This will throw if the view is already attached to a ViewContainer.
|
40338 | */
|
40339 | attachView(viewRef) {
|
40340 | const view = viewRef;
|
40341 | this._views.push(view);
|
40342 | view.attachToAppRef(this);
|
40343 | }
|
40344 | /**
|
40345 | * Detaches a view from dirty checking again.
|
40346 | */
|
40347 | detachView(viewRef) {
|
40348 | const view = viewRef;
|
40349 | remove(this._views, view);
|
40350 | view.detachFromAppRef();
|
40351 | }
|
40352 | _loadComponent(componentRef) {
|
40353 | this.attachView(componentRef.hostView);
|
40354 | this.tick();
|
40355 | this.components.push(componentRef);
|
40356 | // Get the listeners lazily to prevent DI cycles.
|
40357 | const listeners = this._injector.get(APP_BOOTSTRAP_LISTENER, []).concat(this._bootstrapListeners);
|
40358 | listeners.forEach((listener) => listener(componentRef));
|
40359 | }
|
40360 | /** @internal */
|
40361 | ngOnDestroy() {
|
40362 | this._views.slice().forEach((view) => view.destroy());
|
40363 | this._onMicrotaskEmptySubscription.unsubscribe();
|
40364 | }
|
40365 | /**
|
40366 | * Returns the number of attached views.
|
40367 | */
|
40368 | get viewCount() {
|
40369 | return this._views.length;
|
40370 | }
|
40371 | }
|
40372 | ApplicationRef.decorators = [
|
40373 | { type: Injectable }
|
40374 | ];
|
40375 | ApplicationRef.ctorParameters = () => [
|
40376 | { type: NgZone },
|
40377 | { type: Injector },
|
40378 | { type: ErrorHandler },
|
40379 | { type: ComponentFactoryResolver },
|
40380 | { type: ApplicationInitStatus }
|
40381 | ];
|
40382 | function remove(list, el) {
|
40383 | const index = list.indexOf(el);
|
40384 | if (index > -1) {
|
40385 | list.splice(index, 1);
|
40386 | }
|
40387 | }
|
40388 |
|
40389 | /**
|
40390 | * @license
|
40391 | * Copyright Google LLC All Rights Reserved.
|
40392 | *
|
40393 | * Use of this source code is governed by an MIT-style license that can be
|
40394 | * found in the LICENSE file at https://angular.io/license
|
40395 | */
|
40396 | const _CORE_PLATFORM_PROVIDERS = [
|
40397 | // Set a default platform name for platforms that don't set it explicitly.
|
40398 | { provide: PLATFORM_ID, useValue: 'unknown' },
|
40399 | { provide: PlatformRef, deps: [Injector] },
|
40400 | { provide: TestabilityRegistry, deps: [] },
|
40401 | { provide: Console, deps: [] },
|
40402 | ];
|
40403 | /**
|
40404 | * This platform has to be included in any other platform
|
40405 | *
|
40406 | * @publicApi
|
40407 | */
|
40408 | const platformCore = createPlatformFactory(null, 'core', _CORE_PLATFORM_PROVIDERS);
|
40409 |
|
40410 | /**
|
40411 | * @license
|
40412 | * Copyright Google LLC All Rights Reserved.
|
40413 | *
|
40414 | * Use of this source code is governed by an MIT-style license that can be
|
40415 | * found in the LICENSE file at https://angular.io/license
|
40416 | */
|
40417 | function _iterableDiffersFactory() {
|
40418 | return defaultIterableDiffers;
|
40419 | }
|
40420 | function _keyValueDiffersFactory() {
|
40421 | return defaultKeyValueDiffers;
|
40422 | }
|
40423 | function _localeFactory(locale) {
|
40424 | locale = locale || getGlobalLocale();
|
40425 | return locale;
|
40426 | }
|
40427 | /**
|
40428 | * Work out the locale from the potential global properties.
|
40429 | *
|
40430 | * * Closure Compiler: use `goog.LOCALE`.
|
40431 | * * Ivy enabled: use `$localize.locale`
|
40432 | */
|
40433 | function getGlobalLocale() {
|
40434 | if (typeof ngI18nClosureMode !== 'undefined' && ngI18nClosureMode &&
|
40435 | typeof goog !== 'undefined' && goog.LOCALE !== 'en') {
|
40436 | // * The default `goog.LOCALE` value is `en`, while Angular used `en-US`.
|
40437 | // * In order to preserve backwards compatibility, we use Angular default value over
|
40438 | // Closure Compiler's one.
|
40439 | return goog.LOCALE;
|
40440 | }
|
40441 | else {
|
40442 | // KEEP `typeof $localize !== 'undefined' && $localize.locale` IN SYNC WITH THE LOCALIZE
|
40443 | // COMPILE-TIME INLINER.
|
40444 | //
|
40445 | // * During compile time inlining of translations the expression will be replaced
|
40446 | // with a string literal that is the current locale. Other forms of this expression are not
|
40447 | // guaranteed to be replaced.
|
40448 | //
|
40449 | // * During runtime translation evaluation, the developer is required to set `$localize.locale`
|
40450 | // if required, or just to provide their own `LOCALE_ID` provider.
|
40451 | return DEFAULT_LOCALE_ID;
|
40452 | }
|
40453 | }
|
40454 | const ɵ0$b = USD_CURRENCY_CODE;
|
40455 | /**
|
40456 | * A built-in [dependency injection token](guide/glossary#di-token)
|
40457 | * that is used to configure the root injector for bootstrapping.
|
40458 | */
|
40459 | const APPLICATION_MODULE_PROVIDERS = [
|
40460 | {
|
40461 | provide: ApplicationRef,
|
40462 | useClass: ApplicationRef,
|
40463 | deps: [NgZone, Injector, ErrorHandler, ComponentFactoryResolver, ApplicationInitStatus]
|
40464 | },
|
40465 | { provide: SCHEDULER, deps: [NgZone], useFactory: zoneSchedulerFactory },
|
40466 | {
|
40467 | provide: ApplicationInitStatus,
|
40468 | useClass: ApplicationInitStatus,
|
40469 | deps: [[new Optional(), APP_INITIALIZER]]
|
40470 | },
|
40471 | { provide: Compiler, useClass: Compiler, deps: [] },
|
40472 | APP_ID_RANDOM_PROVIDER,
|
40473 | { provide: IterableDiffers, useFactory: _iterableDiffersFactory, deps: [] },
|
40474 | { provide: KeyValueDiffers, useFactory: _keyValueDiffersFactory, deps: [] },
|
40475 | {
|
40476 | provide: LOCALE_ID$1,
|
40477 | useFactory: _localeFactory,
|
40478 | deps: [[new Inject(LOCALE_ID$1), new Optional(), new SkipSelf()]]
|
40479 | },
|
40480 | { provide: DEFAULT_CURRENCY_CODE, useValue: ɵ0$b },
|
40481 | ];
|
40482 | /**
|
40483 | * Schedule work at next available slot.
|
40484 | *
|
40485 | * In Ivy this is just `requestAnimationFrame`. For compatibility reasons when bootstrapped
|
40486 | * using `platformRef.bootstrap` we need to use `NgZone.onStable` as the scheduling mechanism.
|
40487 | * This overrides the scheduling mechanism in Ivy to `NgZone.onStable`.
|
40488 | *
|
40489 | * @param ngZone NgZone to use for scheduling.
|
40490 | */
|
40491 | function zoneSchedulerFactory(ngZone) {
|
40492 | let queue = [];
|
40493 | ngZone.onStable.subscribe(() => {
|
40494 | while (queue.length) {
|
40495 | queue.pop()();
|
40496 | }
|
40497 | });
|
40498 | return function (fn) {
|
40499 | queue.push(fn);
|
40500 | };
|
40501 | }
|
40502 |
|
40503 | /**
|
40504 | * @license
|
40505 | * Copyright Google LLC All Rights Reserved.
|
40506 | *
|
40507 | * Use of this source code is governed by an MIT-style license that can be
|
40508 | * found in the LICENSE file at https://angular.io/license
|
40509 | */
|
40510 | var ViewAction;
|
40511 | (function (ViewAction) {
|
40512 | ViewAction[ViewAction["CreateViewNodes"] = 0] = "CreateViewNodes";
|
40513 | ViewAction[ViewAction["CheckNoChanges"] = 1] = "CheckNoChanges";
|
40514 | ViewAction[ViewAction["CheckNoChangesProjectedViews"] = 2] = "CheckNoChangesProjectedViews";
|
40515 | ViewAction[ViewAction["CheckAndUpdate"] = 3] = "CheckAndUpdate";
|
40516 | ViewAction[ViewAction["CheckAndUpdateProjectedViews"] = 4] = "CheckAndUpdateProjectedViews";
|
40517 | ViewAction[ViewAction["Destroy"] = 5] = "Destroy";
|
40518 | })(ViewAction || (ViewAction = {}));
|
40519 |
|
40520 | /**
|
40521 | * @license
|
40522 | * Copyright Google LLC All Rights Reserved.
|
40523 | *
|
40524 | * Use of this source code is governed by an MIT-style license that can be
|
40525 | * found in the LICENSE file at https://angular.io/license
|
40526 | */
|
40527 | var DebugAction;
|
40528 | (function (DebugAction) {
|
40529 | DebugAction[DebugAction["create"] = 0] = "create";
|
40530 | DebugAction[DebugAction["detectChanges"] = 1] = "detectChanges";
|
40531 | DebugAction[DebugAction["checkNoChanges"] = 2] = "checkNoChanges";
|
40532 | DebugAction[DebugAction["destroy"] = 3] = "destroy";
|
40533 | DebugAction[DebugAction["handleEvent"] = 4] = "handleEvent";
|
40534 | })(DebugAction || (DebugAction = {}));
|
40535 |
|
40536 | /**
|
40537 | * @license
|
40538 | * Copyright Google LLC All Rights Reserved.
|
40539 | *
|
40540 | * Use of this source code is governed by an MIT-style license that can be
|
40541 | * found in the LICENSE file at https://angular.io/license
|
40542 | */
|
40543 | if (typeof ngDevMode !== 'undefined' && ngDevMode) {
|
40544 | // This helper is to give a reasonable error message to people upgrading to v9 that have not yet
|
40545 | // installed `@angular/localize` in their app.
|
40546 | // tslint:disable-next-line: no-toplevel-property-access
|
40547 | _global$1.$localize = _global$1.$localize || function () {
|
40548 | throw new Error('It looks like your application or one of its dependencies is using i18n.\n' +
|
40549 | 'Angular 9 introduced a global `$localize()` function that needs to be loaded.\n' +
|
40550 | 'Please run `ng add @angular/localize` from the Angular CLI.\n' +
|
40551 | '(For non-CLI projects, add `import \'@angular/localize/init\';` to your `polyfills.ts` file.\n' +
|
40552 | 'For server-side rendering applications add the import to your `main.server.ts` file.)');
|
40553 | };
|
40554 | }
|
40555 |
|
40556 | /**
|
40557 | * @license
|
40558 | * Copyright Google LLC All Rights Reserved.
|
40559 | *
|
40560 | * Use of this source code is governed by an MIT-style license that can be
|
40561 | * found in the LICENSE file at https://angular.io/license
|
40562 | */
|
40563 | // Metadata Schema
|
40564 | // If you make a backwards incompatible change to the schema, increment the METADTA_VERSION number.
|
40565 | // If you make a backwards compatible change to the metadata (such as adding an option field) then
|
40566 | // leave METADATA_VERSION the same. If possible, supply as many versions of the metadata that can
|
40567 | // represent the semantics of the file in an array. For example, when generating a version 2 file,
|
40568 | // if version 1 can accurately represent the metadata, generate both version 1 and version 2 in
|
40569 | // an array.
|
40570 | const METADATA_VERSION = 4;
|
40571 | function isClassMetadata(value) {
|
40572 | return value && value.__symbolic === 'class';
|
40573 | }
|
40574 | function isMethodMetadata(value) {
|
40575 | return value && (value.__symbolic === 'constructor' || value.__symbolic === 'method');
|
40576 | }
|
40577 | function isConstructorMetadata(value) {
|
40578 | return value && value.__symbolic === 'constructor';
|
40579 | }
|
40580 | function isFunctionMetadata(value) {
|
40581 | return value && value.__symbolic === 'function';
|
40582 | }
|
40583 | function isMetadataSymbolicExpression(value) {
|
40584 | if (value) {
|
40585 | switch (value.__symbolic) {
|
40586 | case 'binary':
|
40587 | case 'call':
|
40588 | case 'index':
|
40589 | case 'new':
|
40590 | case 'pre':
|
40591 | case 'reference':
|
40592 | case 'select':
|
40593 | case 'spread':
|
40594 | case 'if':
|
40595 | return true;
|
40596 | }
|
40597 | }
|
40598 | return false;
|
40599 | }
|
40600 | function isMetadataGlobalReferenceExpression(value) {
|
40601 | return value && value.name && !value.module && isMetadataSymbolicReferenceExpression(value);
|
40602 | }
|
40603 | function isMetadataModuleReferenceExpression(value) {
|
40604 | return value && value.module && !value.name && !value.default &&
|
40605 | isMetadataSymbolicReferenceExpression(value);
|
40606 | }
|
40607 | function isMetadataImportedSymbolReferenceExpression(value) {
|
40608 | return value && value.module && !!value.name && isMetadataSymbolicReferenceExpression(value);
|
40609 | }
|
40610 | function isMetadataImportDefaultReference(value) {
|
40611 | return value && value.module && value.default && isMetadataSymbolicReferenceExpression(value);
|
40612 | }
|
40613 | function isMetadataSymbolicReferenceExpression(value) {
|
40614 | return value && value.__symbolic === 'reference';
|
40615 | }
|
40616 | function isMetadataSymbolicSelectExpression(value) {
|
40617 | return value && value.__symbolic === 'select';
|
40618 | }
|
40619 | function isMetadataSymbolicSpreadExpression(value) {
|
40620 | return value && value.__symbolic === 'spread';
|
40621 | }
|
40622 | function isMetadataError$1(value) {
|
40623 | return value && value.__symbolic === 'error';
|
40624 | }
|
40625 |
|
40626 | /**
|
40627 | * @license
|
40628 | * Copyright Google LLC All Rights Reserved.
|
40629 | *
|
40630 | * Use of this source code is governed by an MIT-style license that can be
|
40631 | * found in the LICENSE file at https://angular.io/license
|
40632 | */
|
40633 | // In TypeScript 2.1 the spread element kind was renamed.
|
40634 | const spreadElementSyntaxKind = ts.SyntaxKind.SpreadElement || ts.SyntaxKind.SpreadElementExpression;
|
40635 | function isMethodCallOf(callExpression, memberName) {
|
40636 | const expression = callExpression.expression;
|
40637 | if (expression.kind === ts.SyntaxKind.PropertyAccessExpression) {
|
40638 | const propertyAccessExpression = expression;
|
40639 | const name = propertyAccessExpression.name;
|
40640 | if (name.kind == ts.SyntaxKind.Identifier) {
|
40641 | return name.text === memberName;
|
40642 | }
|
40643 | }
|
40644 | return false;
|
40645 | }
|
40646 | function isCallOf(callExpression, ident) {
|
40647 | const expression = callExpression.expression;
|
40648 | if (expression.kind === ts.SyntaxKind.Identifier) {
|
40649 | const identifier = expression;
|
40650 | return identifier.text === ident;
|
40651 | }
|
40652 | return false;
|
40653 | }
|
40654 | /* @internal */
|
40655 | function recordMapEntry(entry, node, nodeMap, sourceFile) {
|
40656 | if (!nodeMap.has(entry)) {
|
40657 | nodeMap.set(entry, node);
|
40658 | if (node &&
|
40659 | (isMetadataImportedSymbolReferenceExpression(entry) ||
|
40660 | isMetadataImportDefaultReference(entry)) &&
|
40661 | entry.line == null) {
|
40662 | const info = sourceInfo(node, sourceFile);
|
40663 | if (info.line != null)
|
40664 | entry.line = info.line;
|
40665 | if (info.character != null)
|
40666 | entry.character = info.character;
|
40667 | }
|
40668 | }
|
40669 | return entry;
|
40670 | }
|
40671 | /**
|
40672 | * ts.forEachChild stops iterating children when the callback return a truthy value.
|
40673 | * This method inverts this to implement an `every` style iterator. It will return
|
40674 | * true if every call to `cb` returns `true`.
|
40675 | */
|
40676 | function everyNodeChild(node, cb) {
|
40677 | return !ts.forEachChild(node, node => !cb(node));
|
40678 | }
|
40679 | function isPrimitive$1(value) {
|
40680 | return Object(value) !== value;
|
40681 | }
|
40682 | function isDefined$1(obj) {
|
40683 | return obj !== undefined;
|
40684 | }
|
40685 | function getSourceFileOfNode(node) {
|
40686 | while (node && node.kind != ts.SyntaxKind.SourceFile) {
|
40687 | node = node.parent;
|
40688 | }
|
40689 | return node;
|
40690 | }
|
40691 | /* @internal */
|
40692 | function sourceInfo(node, sourceFile) {
|
40693 | if (node) {
|
40694 | sourceFile = sourceFile || getSourceFileOfNode(node);
|
40695 | if (sourceFile) {
|
40696 | return ts.getLineAndCharacterOfPosition(sourceFile, node.getStart(sourceFile));
|
40697 | }
|
40698 | }
|
40699 | return {};
|
40700 | }
|
40701 | /* @internal */
|
40702 | function errorSymbol(message, node, context, sourceFile) {
|
40703 | const result = Object.assign({ __symbolic: 'error', message }, sourceInfo(node, sourceFile));
|
40704 | if (context) {
|
40705 | result.context = context;
|
40706 | }
|
40707 | return result;
|
40708 | }
|
40709 | /**
|
40710 | * Produce a symbolic representation of an expression folding values into their final value when
|
40711 | * possible.
|
40712 | */
|
40713 | class Evaluator {
|
40714 | constructor(symbols, nodeMap, options = {}, recordExport) {
|
40715 | this.symbols = symbols;
|
40716 | this.nodeMap = nodeMap;
|
40717 | this.options = options;
|
40718 | this.recordExport = recordExport;
|
40719 | }
|
40720 | nameOf(node) {
|
40721 | if (node && node.kind == ts.SyntaxKind.Identifier) {
|
40722 | return node.text;
|
40723 | }
|
40724 | const result = node && this.evaluateNode(node);
|
40725 | if (isMetadataError$1(result) || typeof result === 'string') {
|
40726 | return result;
|
40727 | }
|
40728 | else {
|
40729 | return errorSymbol('Name expected', node, { received: (node && node.getText()) || '<missing>' });
|
40730 | }
|
40731 | }
|
40732 | /**
|
40733 | * Returns true if the expression represented by `node` can be folded into a literal expression.
|
40734 | *
|
40735 | * For example, a literal is always foldable. This means that literal expressions such as `1.2`
|
40736 | * `"Some value"` `true` `false` are foldable.
|
40737 | *
|
40738 | * - An object literal is foldable if all the properties in the literal are foldable.
|
40739 | * - An array literal is foldable if all the elements are foldable.
|
40740 | * - A call is foldable if it is a call to a Array.prototype.concat or a call to CONST_EXPR.
|
40741 | * - A property access is foldable if the object is foldable.
|
40742 | * - A array index is foldable if index expression is foldable and the array is foldable.
|
40743 | * - Binary operator expressions are foldable if the left and right expressions are foldable and
|
40744 | * it is one of '+', '-', '*', '/', '%', '||', and '&&'.
|
40745 | * - An identifier is foldable if a value can be found for its symbol in the evaluator symbol
|
40746 | * table.
|
40747 | */
|
40748 | isFoldable(node) {
|
40749 | return this.isFoldableWorker(node, new Map());
|
40750 | }
|
40751 | isFoldableWorker(node, folding) {
|
40752 | if (node) {
|
40753 | switch (node.kind) {
|
40754 | case ts.SyntaxKind.ObjectLiteralExpression:
|
40755 | return everyNodeChild(node, child => {
|
40756 | if (child.kind === ts.SyntaxKind.PropertyAssignment) {
|
40757 | const propertyAssignment = child;
|
40758 | return this.isFoldableWorker(propertyAssignment.initializer, folding);
|
40759 | }
|
40760 | return false;
|
40761 | });
|
40762 | case ts.SyntaxKind.ArrayLiteralExpression:
|
40763 | return everyNodeChild(node, child => this.isFoldableWorker(child, folding));
|
40764 | case ts.SyntaxKind.CallExpression:
|
40765 | const callExpression = node;
|
40766 | // We can fold a <array>.concat(<v>).
|
40767 | if (isMethodCallOf(callExpression, 'concat') &&
|
40768 | arrayOrEmpty(callExpression.arguments).length === 1) {
|
40769 | const arrayNode = callExpression.expression.expression;
|
40770 | if (this.isFoldableWorker(arrayNode, folding) &&
|
40771 | this.isFoldableWorker(callExpression.arguments[0], folding)) {
|
40772 | // It needs to be an array.
|
40773 | const arrayValue = this.evaluateNode(arrayNode);
|
40774 | if (arrayValue && Array.isArray(arrayValue)) {
|
40775 | return true;
|
40776 | }
|
40777 | }
|
40778 | }
|
40779 | // We can fold a call to CONST_EXPR
|
40780 | if (isCallOf(callExpression, 'CONST_EXPR') &&
|
40781 | arrayOrEmpty(callExpression.arguments).length === 1)
|
40782 | return this.isFoldableWorker(callExpression.arguments[0], folding);
|
40783 | return false;
|
40784 | case ts.SyntaxKind.NoSubstitutionTemplateLiteral:
|
40785 | case ts.SyntaxKind.StringLiteral:
|
40786 | case ts.SyntaxKind.NumericLiteral:
|
40787 | case ts.SyntaxKind.NullKeyword:
|
40788 | case ts.SyntaxKind.TrueKeyword:
|
40789 | case ts.SyntaxKind.FalseKeyword:
|
40790 | case ts.SyntaxKind.TemplateHead:
|
40791 | case ts.SyntaxKind.TemplateMiddle:
|
40792 | case ts.SyntaxKind.TemplateTail:
|
40793 | return true;
|
40794 | case ts.SyntaxKind.ParenthesizedExpression:
|
40795 | const parenthesizedExpression = node;
|
40796 | return this.isFoldableWorker(parenthesizedExpression.expression, folding);
|
40797 | case ts.SyntaxKind.BinaryExpression:
|
40798 | const binaryExpression = node;
|
40799 | switch (binaryExpression.operatorToken.kind) {
|
40800 | case ts.SyntaxKind.PlusToken:
|
40801 | case ts.SyntaxKind.MinusToken:
|
40802 | case ts.SyntaxKind.AsteriskToken:
|
40803 | case ts.SyntaxKind.SlashToken:
|
40804 | case ts.SyntaxKind.PercentToken:
|
40805 | case ts.SyntaxKind.AmpersandAmpersandToken:
|
40806 | case ts.SyntaxKind.BarBarToken:
|
40807 | return this.isFoldableWorker(binaryExpression.left, folding) &&
|
40808 | this.isFoldableWorker(binaryExpression.right, folding);
|
40809 | default:
|
40810 | return false;
|
40811 | }
|
40812 | case ts.SyntaxKind.PropertyAccessExpression:
|
40813 | const propertyAccessExpression = node;
|
40814 | return this.isFoldableWorker(propertyAccessExpression.expression, folding);
|
40815 | case ts.SyntaxKind.ElementAccessExpression:
|
40816 | const elementAccessExpression = node;
|
40817 | return this.isFoldableWorker(elementAccessExpression.expression, folding) &&
|
40818 | this.isFoldableWorker(elementAccessExpression.argumentExpression, folding);
|
40819 | case ts.SyntaxKind.Identifier:
|
40820 | let identifier = node;
|
40821 | let reference = this.symbols.resolve(identifier.text);
|
40822 | if (reference !== undefined && isPrimitive$1(reference)) {
|
40823 | return true;
|
40824 | }
|
40825 | break;
|
40826 | case ts.SyntaxKind.TemplateExpression:
|
40827 | const templateExpression = node;
|
40828 | return templateExpression.templateSpans.every(span => this.isFoldableWorker(span.expression, folding));
|
40829 | }
|
40830 | }
|
40831 | return false;
|
40832 | }
|
40833 | /**
|
40834 | * Produce a JSON serialiable object representing `node`. The foldable values in the expression
|
40835 | * tree are folded. For example, a node representing `1 + 2` is folded into `3`.
|
40836 | */
|
40837 | evaluateNode(node, preferReference) {
|
40838 | const t = this;
|
40839 | let error;
|
40840 | function recordEntry(entry, node) {
|
40841 | if (t.options.substituteExpression) {
|
40842 | const newEntry = t.options.substituteExpression(entry, node);
|
40843 | if (t.recordExport && newEntry != entry && isMetadataGlobalReferenceExpression(newEntry)) {
|
40844 | t.recordExport(newEntry.name, entry);
|
40845 | }
|
40846 | entry = newEntry;
|
40847 | }
|
40848 | return recordMapEntry(entry, node, t.nodeMap);
|
40849 | }
|
40850 | function isFoldableError(value) {
|
40851 | return !t.options.verboseInvalidExpression && isMetadataError$1(value);
|
40852 | }
|
40853 | const resolveName = (name, preferReference) => {
|
40854 | const reference = this.symbols.resolve(name, preferReference);
|
40855 | if (reference === undefined) {
|
40856 | // Encode as a global reference. StaticReflector will check the reference.
|
40857 | return recordEntry({ __symbolic: 'reference', name }, node);
|
40858 | }
|
40859 | if (reference && isMetadataSymbolicReferenceExpression(reference)) {
|
40860 | return recordEntry(Object.assign({}, reference), node);
|
40861 | }
|
40862 | return reference;
|
40863 | };
|
40864 | switch (node.kind) {
|
40865 | case ts.SyntaxKind.ObjectLiteralExpression:
|
40866 | let obj = {};
|
40867 | let quoted = [];
|
40868 | ts.forEachChild(node, child => {
|
40869 | switch (child.kind) {
|
40870 | case ts.SyntaxKind.ShorthandPropertyAssignment:
|
40871 | case ts.SyntaxKind.PropertyAssignment:
|
40872 | const assignment = child;
|
40873 | if (assignment.name.kind == ts.SyntaxKind.StringLiteral) {
|
40874 | const name = assignment.name.text;
|
40875 | quoted.push(name);
|
40876 | }
|
40877 | const propertyName = this.nameOf(assignment.name);
|
40878 | if (isFoldableError(propertyName)) {
|
40879 | error = propertyName;
|
40880 | return true;
|
40881 | }
|
40882 | const propertyValue = isPropertyAssignment(assignment) ?
|
40883 | this.evaluateNode(assignment.initializer, /* preferReference */ true) :
|
40884 | resolveName(propertyName, /* preferReference */ true);
|
40885 | if (isFoldableError(propertyValue)) {
|
40886 | error = propertyValue;
|
40887 | return true; // Stop the forEachChild.
|
40888 | }
|
40889 | else {
|
40890 | obj[propertyName] = isPropertyAssignment(assignment) ?
|
40891 | recordEntry(propertyValue, assignment.initializer) :
|
40892 | propertyValue;
|
40893 | }
|
40894 | }
|
40895 | });
|
40896 | if (error)
|
40897 | return error;
|
40898 | if (this.options.quotedNames && quoted.length) {
|
40899 | obj['$quoted$'] = quoted;
|
40900 | }
|
40901 | return recordEntry(obj, node);
|
40902 | case ts.SyntaxKind.ArrayLiteralExpression:
|
40903 | let arr = [];
|
40904 | ts.forEachChild(node, child => {
|
40905 | const value = this.evaluateNode(child, /* preferReference */ true);
|
40906 | // Check for error
|
40907 | if (isFoldableError(value)) {
|
40908 | error = value;
|
40909 | return true; // Stop the forEachChild.
|
40910 | }
|
40911 | // Handle spread expressions
|
40912 | if (isMetadataSymbolicSpreadExpression(value)) {
|
40913 | if (Array.isArray(value.expression)) {
|
40914 | for (const spreadValue of value.expression) {
|
40915 | arr.push(spreadValue);
|
40916 | }
|
40917 | return;
|
40918 | }
|
40919 | }
|
40920 | arr.push(value);
|
40921 | });
|
40922 | if (error)
|
40923 | return error;
|
40924 | return recordEntry(arr, node);
|
40925 | case spreadElementSyntaxKind:
|
40926 | let spreadExpression = this.evaluateNode(node.expression);
|
40927 | return recordEntry({ __symbolic: 'spread', expression: spreadExpression }, node);
|
40928 | case ts.SyntaxKind.CallExpression:
|
40929 | const callExpression = node;
|
40930 | if (isCallOf(callExpression, 'forwardRef') &&
|
40931 | arrayOrEmpty(callExpression.arguments).length === 1) {
|
40932 | const firstArgument = callExpression.arguments[0];
|
40933 | if (firstArgument.kind == ts.SyntaxKind.ArrowFunction) {
|
40934 | const arrowFunction = firstArgument;
|
40935 | return recordEntry(this.evaluateNode(arrowFunction.body), node);
|
40936 | }
|
40937 | }
|
40938 | const args = arrayOrEmpty(callExpression.arguments).map(arg => this.evaluateNode(arg));
|
40939 | if (this.isFoldable(callExpression)) {
|
40940 | if (isMethodCallOf(callExpression, 'concat')) {
|
40941 | const arrayValue = this.evaluateNode(callExpression.expression.expression);
|
40942 | if (isFoldableError(arrayValue))
|
40943 | return arrayValue;
|
40944 | return arrayValue.concat(args[0]);
|
40945 | }
|
40946 | }
|
40947 | // Always fold a CONST_EXPR even if the argument is not foldable.
|
40948 | if (isCallOf(callExpression, 'CONST_EXPR') &&
|
40949 | arrayOrEmpty(callExpression.arguments).length === 1) {
|
40950 | return recordEntry(args[0], node);
|
40951 | }
|
40952 | const expression = this.evaluateNode(callExpression.expression);
|
40953 | if (isFoldableError(expression)) {
|
40954 | return recordEntry(expression, node);
|
40955 | }
|
40956 | let result = { __symbolic: 'call', expression: expression };
|
40957 | if (args && args.length) {
|
40958 | result.arguments = args;
|
40959 | }
|
40960 | return recordEntry(result, node);
|
40961 | case ts.SyntaxKind.NewExpression:
|
40962 | const newExpression = node;
|
40963 | const newArgs = arrayOrEmpty(newExpression.arguments).map(arg => this.evaluateNode(arg));
|
40964 | const newTarget = this.evaluateNode(newExpression.expression);
|
40965 | if (isMetadataError$1(newTarget)) {
|
40966 | return recordEntry(newTarget, node);
|
40967 | }
|
40968 | const call = { __symbolic: 'new', expression: newTarget };
|
40969 | if (newArgs.length) {
|
40970 | call.arguments = newArgs;
|
40971 | }
|
40972 | return recordEntry(call, node);
|
40973 | case ts.SyntaxKind.PropertyAccessExpression: {
|
40974 | const propertyAccessExpression = node;
|
40975 | const expression = this.evaluateNode(propertyAccessExpression.expression);
|
40976 | if (isFoldableError(expression)) {
|
40977 | return recordEntry(expression, node);
|
40978 | }
|
40979 | const member = this.nameOf(propertyAccessExpression.name);
|
40980 | if (isFoldableError(member)) {
|
40981 | return recordEntry(member, node);
|
40982 | }
|
40983 | if (expression && this.isFoldable(propertyAccessExpression.expression))
|
40984 | return expression[member];
|
40985 | if (isMetadataModuleReferenceExpression(expression)) {
|
40986 | // A select into a module reference and be converted into a reference to the symbol
|
40987 | // in the module
|
40988 | return recordEntry({ __symbolic: 'reference', module: expression.module, name: member }, node);
|
40989 | }
|
40990 | return recordEntry({ __symbolic: 'select', expression, member }, node);
|
40991 | }
|
40992 | case ts.SyntaxKind.ElementAccessExpression: {
|
40993 | const elementAccessExpression = node;
|
40994 | const expression = this.evaluateNode(elementAccessExpression.expression);
|
40995 | if (isFoldableError(expression)) {
|
40996 | return recordEntry(expression, node);
|
40997 | }
|
40998 | if (!elementAccessExpression.argumentExpression) {
|
40999 | return recordEntry(errorSymbol('Expression form not supported', node), node);
|
41000 | }
|
41001 | const index = this.evaluateNode(elementAccessExpression.argumentExpression);
|
41002 | if (isFoldableError(expression)) {
|
41003 | return recordEntry(expression, node);
|
41004 | }
|
41005 | if (this.isFoldable(elementAccessExpression.expression) &&
|
41006 | this.isFoldable(elementAccessExpression.argumentExpression))
|
41007 | return expression[index];
|
41008 | return recordEntry({ __symbolic: 'index', expression, index }, node);
|
41009 | }
|
41010 | case ts.SyntaxKind.Identifier:
|
41011 | const identifier = node;
|
41012 | const name = identifier.text;
|
41013 | return resolveName(name, preferReference);
|
41014 | case ts.SyntaxKind.TypeReference:
|
41015 | const typeReferenceNode = node;
|
41016 | const typeNameNode = typeReferenceNode.typeName;
|
41017 | const getReference = node => {
|
41018 | if (typeNameNode.kind === ts.SyntaxKind.QualifiedName) {
|
41019 | const qualifiedName = node;
|
41020 | const left = this.evaluateNode(qualifiedName.left);
|
41021 | if (isMetadataModuleReferenceExpression(left)) {
|
41022 | return recordEntry({
|
41023 | __symbolic: 'reference',
|
41024 | module: left.module,
|
41025 | name: qualifiedName.right.text
|
41026 | }, node);
|
41027 | }
|
41028 | // Record a type reference to a declared type as a select.
|
41029 | return { __symbolic: 'select', expression: left, member: qualifiedName.right.text };
|
41030 | }
|
41031 | else {
|
41032 | const identifier = typeNameNode;
|
41033 | const symbol = this.symbols.resolve(identifier.text);
|
41034 | if (isFoldableError(symbol) || isMetadataSymbolicReferenceExpression(symbol)) {
|
41035 | return recordEntry(symbol, node);
|
41036 | }
|
41037 | return recordEntry(errorSymbol('Could not resolve type', node, { typeName: identifier.text }), node);
|
41038 | }
|
41039 | };
|
41040 | const typeReference = getReference(typeNameNode);
|
41041 | if (isFoldableError(typeReference)) {
|
41042 | return recordEntry(typeReference, node);
|
41043 | }
|
41044 | if (!isMetadataModuleReferenceExpression(typeReference) &&
|
41045 | typeReferenceNode.typeArguments && typeReferenceNode.typeArguments.length) {
|
41046 | const args = typeReferenceNode.typeArguments.map(element => this.evaluateNode(element));
|
41047 | // TODO: Remove typecast when upgraded to 2.0 as it will be correctly inferred.
|
41048 | // Some versions of 1.9 do not infer this correctly.
|
41049 | typeReference.arguments = args;
|
41050 | }
|
41051 | return recordEntry(typeReference, node);
|
41052 | case ts.SyntaxKind.UnionType:
|
41053 | const unionType = node;
|
41054 | // Remove null and undefined from the list of unions.
|
41055 | const references = unionType.types
|
41056 | .filter(n => n.kind !== ts.SyntaxKind.UndefinedKeyword &&
|
41057 | !(ts.isLiteralTypeNode(n) && n.literal.kind === ts.SyntaxKind.NullKeyword))
|
41058 | .map(n => this.evaluateNode(n));
|
41059 | // The remmaining reference must be the same. If two have type arguments consider them
|
41060 | // different even if the type arguments are the same.
|
41061 | let candidate = null;
|
41062 | for (let i = 0; i < references.length; i++) {
|
41063 | const reference = references[i];
|
41064 | if (isMetadataSymbolicReferenceExpression(reference)) {
|
41065 | if (candidate) {
|
41066 | if (reference.name == candidate.name &&
|
41067 | reference.module == candidate.module && !reference.arguments) {
|
41068 | candidate = reference;
|
41069 | }
|
41070 | }
|
41071 | else {
|
41072 | candidate = reference;
|
41073 | }
|
41074 | }
|
41075 | else {
|
41076 | return reference;
|
41077 | }
|
41078 | }
|
41079 | if (candidate)
|
41080 | return candidate;
|
41081 | break;
|
41082 | case ts.SyntaxKind.NoSubstitutionTemplateLiteral:
|
41083 | case ts.SyntaxKind.StringLiteral:
|
41084 | case ts.SyntaxKind.TemplateHead:
|
41085 | case ts.SyntaxKind.TemplateTail:
|
41086 | case ts.SyntaxKind.TemplateMiddle:
|
41087 | return node.text;
|
41088 | case ts.SyntaxKind.NumericLiteral:
|
41089 | return parseFloat(node.text);
|
41090 | case ts.SyntaxKind.AnyKeyword:
|
41091 | return recordEntry({ __symbolic: 'reference', name: 'any' }, node);
|
41092 | case ts.SyntaxKind.StringKeyword:
|
41093 | return recordEntry({ __symbolic: 'reference', name: 'string' }, node);
|
41094 | case ts.SyntaxKind.NumberKeyword:
|
41095 | return recordEntry({ __symbolic: 'reference', name: 'number' }, node);
|
41096 | case ts.SyntaxKind.BooleanKeyword:
|
41097 | return recordEntry({ __symbolic: 'reference', name: 'boolean' }, node);
|
41098 | case ts.SyntaxKind.ArrayType:
|
41099 | const arrayTypeNode = node;
|
41100 | return recordEntry({
|
41101 | __symbolic: 'reference',
|
41102 | name: 'Array',
|
41103 | arguments: [this.evaluateNode(arrayTypeNode.elementType)]
|
41104 | }, node);
|
41105 | case ts.SyntaxKind.NullKeyword:
|
41106 | return null;
|
41107 | case ts.SyntaxKind.TrueKeyword:
|
41108 | return true;
|
41109 | case ts.SyntaxKind.FalseKeyword:
|
41110 | return false;
|
41111 | case ts.SyntaxKind.ParenthesizedExpression:
|
41112 | const parenthesizedExpression = node;
|
41113 | return this.evaluateNode(parenthesizedExpression.expression);
|
41114 | case ts.SyntaxKind.TypeAssertionExpression:
|
41115 | const typeAssertion = node;
|
41116 | return this.evaluateNode(typeAssertion.expression);
|
41117 | case ts.SyntaxKind.PrefixUnaryExpression:
|
41118 | const prefixUnaryExpression = node;
|
41119 | const operand = this.evaluateNode(prefixUnaryExpression.operand);
|
41120 | if (isDefined$1(operand) && isPrimitive$1(operand)) {
|
41121 | switch (prefixUnaryExpression.operator) {
|
41122 | case ts.SyntaxKind.PlusToken:
|
41123 | return +operand;
|
41124 | case ts.SyntaxKind.MinusToken:
|
41125 | return -operand;
|
41126 | case ts.SyntaxKind.TildeToken:
|
41127 | return ~operand;
|
41128 | case ts.SyntaxKind.ExclamationToken:
|
41129 | return !operand;
|
41130 | }
|
41131 | }
|
41132 | let operatorText;
|
41133 | switch (prefixUnaryExpression.operator) {
|
41134 | case ts.SyntaxKind.PlusToken:
|
41135 | operatorText = '+';
|
41136 | break;
|
41137 | case ts.SyntaxKind.MinusToken:
|
41138 | operatorText = '-';
|
41139 | break;
|
41140 | case ts.SyntaxKind.TildeToken:
|
41141 | operatorText = '~';
|
41142 | break;
|
41143 | case ts.SyntaxKind.ExclamationToken:
|
41144 | operatorText = '!';
|
41145 | break;
|
41146 | default:
|
41147 | return undefined;
|
41148 | }
|
41149 | return recordEntry({ __symbolic: 'pre', operator: operatorText, operand: operand }, node);
|
41150 | case ts.SyntaxKind.BinaryExpression:
|
41151 | const binaryExpression = node;
|
41152 | const left = this.evaluateNode(binaryExpression.left);
|
41153 | const right = this.evaluateNode(binaryExpression.right);
|
41154 | if (isDefined$1(left) && isDefined$1(right)) {
|
41155 | if (isPrimitive$1(left) && isPrimitive$1(right))
|
41156 | switch (binaryExpression.operatorToken.kind) {
|
41157 | case ts.SyntaxKind.BarBarToken:
|
41158 | return left || right;
|
41159 | case ts.SyntaxKind.AmpersandAmpersandToken:
|
41160 | return left && right;
|
41161 | case ts.SyntaxKind.AmpersandToken:
|
41162 | return left & right;
|
41163 | case ts.SyntaxKind.BarToken:
|
41164 | return left | right;
|
41165 | case ts.SyntaxKind.CaretToken:
|
41166 | return left ^ right;
|
41167 | case ts.SyntaxKind.EqualsEqualsToken:
|
41168 | return left == right;
|
41169 | case ts.SyntaxKind.ExclamationEqualsToken:
|
41170 | return left != right;
|
41171 | case ts.SyntaxKind.EqualsEqualsEqualsToken:
|
41172 | return left === right;
|
41173 | case ts.SyntaxKind.ExclamationEqualsEqualsToken:
|
41174 | return left !== right;
|
41175 | case ts.SyntaxKind.LessThanToken:
|
41176 | return left < right;
|
41177 | case ts.SyntaxKind.GreaterThanToken:
|
41178 | return left > right;
|
41179 | case ts.SyntaxKind.LessThanEqualsToken:
|
41180 | return left <= right;
|
41181 | case ts.SyntaxKind.GreaterThanEqualsToken:
|
41182 | return left >= right;
|
41183 | case ts.SyntaxKind.LessThanLessThanToken:
|
41184 | return left << right;
|
41185 | case ts.SyntaxKind.GreaterThanGreaterThanToken:
|
41186 | return left >> right;
|
41187 | case ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken:
|
41188 | return left >>> right;
|
41189 | case ts.SyntaxKind.PlusToken:
|
41190 | return left + right;
|
41191 | case ts.SyntaxKind.MinusToken:
|
41192 | return left - right;
|
41193 | case ts.SyntaxKind.AsteriskToken:
|
41194 | return left * right;
|
41195 | case ts.SyntaxKind.SlashToken:
|
41196 | return left / right;
|
41197 | case ts.SyntaxKind.PercentToken:
|
41198 | return left % right;
|
41199 | }
|
41200 | return recordEntry({
|
41201 | __symbolic: 'binop',
|
41202 | operator: binaryExpression.operatorToken.getText(),
|
41203 | left: left,
|
41204 | right: right
|
41205 | }, node);
|
41206 | }
|
41207 | break;
|
41208 | case ts.SyntaxKind.ConditionalExpression:
|
41209 | const conditionalExpression = node;
|
41210 | const condition = this.evaluateNode(conditionalExpression.condition);
|
41211 | const thenExpression = this.evaluateNode(conditionalExpression.whenTrue);
|
41212 | const elseExpression = this.evaluateNode(conditionalExpression.whenFalse);
|
41213 | if (isPrimitive$1(condition)) {
|
41214 | return condition ? thenExpression : elseExpression;
|
41215 | }
|
41216 | return recordEntry({ __symbolic: 'if', condition, thenExpression, elseExpression }, node);
|
41217 | case ts.SyntaxKind.FunctionExpression:
|
41218 | case ts.SyntaxKind.ArrowFunction:
|
41219 | return recordEntry(errorSymbol('Lambda not supported', node), node);
|
41220 | case ts.SyntaxKind.TaggedTemplateExpression:
|
41221 | return recordEntry(errorSymbol('Tagged template expressions are not supported in metadata', node), node);
|
41222 | case ts.SyntaxKind.TemplateExpression:
|
41223 | const templateExpression = node;
|
41224 | if (this.isFoldable(node)) {
|
41225 | return templateExpression.templateSpans.reduce((previous, current) => previous + this.evaluateNode(current.expression) +
|
41226 | this.evaluateNode(current.literal), this.evaluateNode(templateExpression.head));
|
41227 | }
|
41228 | else {
|
41229 | return templateExpression.templateSpans.reduce((previous, current) => {
|
41230 | const expr = this.evaluateNode(current.expression);
|
41231 | const literal = this.evaluateNode(current.literal);
|
41232 | if (isFoldableError(expr))
|
41233 | return expr;
|
41234 | if (isFoldableError(literal))
|
41235 | return literal;
|
41236 | if (typeof previous === 'string' && typeof expr === 'string' &&
|
41237 | typeof literal === 'string') {
|
41238 | return previous + expr + literal;
|
41239 | }
|
41240 | let result = expr;
|
41241 | if (previous !== '') {
|
41242 | result = { __symbolic: 'binop', operator: '+', left: previous, right: expr };
|
41243 | }
|
41244 | if (literal != '') {
|
41245 | result = { __symbolic: 'binop', operator: '+', left: result, right: literal };
|
41246 | }
|
41247 | return result;
|
41248 | }, this.evaluateNode(templateExpression.head));
|
41249 | }
|
41250 | case ts.SyntaxKind.AsExpression:
|
41251 | const asExpression = node;
|
41252 | return this.evaluateNode(asExpression.expression);
|
41253 | case ts.SyntaxKind.ClassExpression:
|
41254 | return { __symbolic: 'class' };
|
41255 | }
|
41256 | return recordEntry(errorSymbol('Expression form not supported', node), node);
|
41257 | }
|
41258 | }
|
41259 | function isPropertyAssignment(node) {
|
41260 | return node.kind == ts.SyntaxKind.PropertyAssignment;
|
41261 | }
|
41262 | const empty$1 = ts.createNodeArray();
|
41263 | function arrayOrEmpty(v) {
|
41264 | return v || empty$1;
|
41265 | }
|
41266 |
|
41267 | /**
|
41268 | * @license
|
41269 | * Copyright Google LLC All Rights Reserved.
|
41270 | *
|
41271 | * Use of this source code is governed by an MIT-style license that can be
|
41272 | * found in the LICENSE file at https://angular.io/license
|
41273 | */
|
41274 | class Symbols {
|
41275 | constructor(sourceFile) {
|
41276 | this.sourceFile = sourceFile;
|
41277 | this.references = new Map();
|
41278 | }
|
41279 | resolve(name, preferReference) {
|
41280 | return (preferReference && this.references.get(name)) || this.symbols.get(name);
|
41281 | }
|
41282 | define(name, value) {
|
41283 | this.symbols.set(name, value);
|
41284 | }
|
41285 | defineReference(name, value) {
|
41286 | this.references.set(name, value);
|
41287 | }
|
41288 | has(name) {
|
41289 | return this.symbols.has(name);
|
41290 | }
|
41291 | get symbols() {
|
41292 | let result = this._symbols;
|
41293 | if (!result) {
|
41294 | result = this._symbols = new Map();
|
41295 | populateBuiltins(result);
|
41296 | this.buildImports();
|
41297 | }
|
41298 | return result;
|
41299 | }
|
41300 | buildImports() {
|
41301 | const symbols = this._symbols;
|
41302 | // Collect the imported symbols into this.symbols
|
41303 | const stripQuotes = (s) => s.replace(/^['"]|['"]$/g, '');
|
41304 | const visit = (node) => {
|
41305 | switch (node.kind) {
|
41306 | case ts.SyntaxKind.ImportEqualsDeclaration:
|
41307 | const importEqualsDeclaration = node;
|
41308 | if (importEqualsDeclaration.moduleReference.kind ===
|
41309 | ts.SyntaxKind.ExternalModuleReference) {
|
41310 | const externalReference = importEqualsDeclaration.moduleReference;
|
41311 | if (externalReference.expression) {
|
41312 | // An `import <identifier> = require(<module-specifier>);
|
41313 | if (!externalReference.expression.parent) {
|
41314 | // The `parent` field of a node is set by the TypeScript binder (run as
|
41315 | // part of the type checker). Setting it here allows us to call `getText()`
|
41316 | // even if the `SourceFile` was not type checked (which looks for `SourceFile`
|
41317 | // in the parent chain). This doesn't damage the node as the binder unconditionally
|
41318 | // sets the parent.
|
41319 | externalReference.expression.parent = externalReference;
|
41320 | externalReference.parent = this.sourceFile;
|
41321 | }
|
41322 | const from = stripQuotes(externalReference.expression.getText());
|
41323 | symbols.set(importEqualsDeclaration.name.text, { __symbolic: 'reference', module: from });
|
41324 | break;
|
41325 | }
|
41326 | }
|
41327 | symbols.set(importEqualsDeclaration.name.text, { __symbolic: 'error', message: `Unsupported import syntax` });
|
41328 | break;
|
41329 | case ts.SyntaxKind.ImportDeclaration:
|
41330 | const importDecl = node;
|
41331 | if (!importDecl.importClause) {
|
41332 | // An `import <module-specifier>` clause which does not bring symbols into scope.
|
41333 | break;
|
41334 | }
|
41335 | if (!importDecl.moduleSpecifier.parent) {
|
41336 | // See note above in the `ImportEqualDeclaration` case.
|
41337 | importDecl.moduleSpecifier.parent = importDecl;
|
41338 | importDecl.parent = this.sourceFile;
|
41339 | }
|
41340 | const from = stripQuotes(importDecl.moduleSpecifier.getText());
|
41341 | if (importDecl.importClause.name) {
|
41342 | // An `import <identifier> form <module-specifier>` clause. Record the default symbol.
|
41343 | symbols.set(importDecl.importClause.name.text, { __symbolic: 'reference', module: from, default: true });
|
41344 | }
|
41345 | const bindings = importDecl.importClause.namedBindings;
|
41346 | if (bindings) {
|
41347 | switch (bindings.kind) {
|
41348 | case ts.SyntaxKind.NamedImports:
|
41349 | // An `import { [<identifier> [, <identifier>] } from <module-specifier>` clause
|
41350 | for (const binding of bindings.elements) {
|
41351 | symbols.set(binding.name.text, {
|
41352 | __symbolic: 'reference',
|
41353 | module: from,
|
41354 | name: binding.propertyName ? binding.propertyName.text : binding.name.text
|
41355 | });
|
41356 | }
|
41357 | break;
|
41358 | case ts.SyntaxKind.NamespaceImport:
|
41359 | // An `input * as <identifier> from <module-specifier>` clause.
|
41360 | symbols.set(bindings.name.text, { __symbolic: 'reference', module: from });
|
41361 | break;
|
41362 | }
|
41363 | }
|
41364 | break;
|
41365 | }
|
41366 | ts.forEachChild(node, visit);
|
41367 | };
|
41368 | if (this.sourceFile) {
|
41369 | ts.forEachChild(this.sourceFile, visit);
|
41370 | }
|
41371 | }
|
41372 | }
|
41373 | function populateBuiltins(symbols) {
|
41374 | // From lib.core.d.ts (all "define const")
|
41375 | ['Object', 'Function', 'String', 'Number', 'Array', 'Boolean', 'Map', 'NaN', 'Infinity', 'Math',
|
41376 | 'Date', 'RegExp', 'Error', 'Error', 'EvalError', 'RangeError', 'ReferenceError', 'SyntaxError',
|
41377 | 'TypeError', 'URIError', 'JSON', 'ArrayBuffer', 'DataView', 'Int8Array', 'Uint8Array',
|
41378 | 'Uint8ClampedArray', 'Uint16Array', 'Int16Array', 'Int32Array', 'Uint32Array', 'Float32Array',
|
41379 | 'Float64Array']
|
41380 | .forEach(name => symbols.set(name, { __symbolic: 'reference', name }));
|
41381 | }
|
41382 |
|
41383 | /**
|
41384 | * @license
|
41385 | * Copyright Google LLC All Rights Reserved.
|
41386 | *
|
41387 | * Use of this source code is governed by an MIT-style license that can be
|
41388 | * found in the LICENSE file at https://angular.io/license
|
41389 | */
|
41390 | const isStatic = (node) => ts.getCombinedModifierFlags(node) & ts.ModifierFlags.Static;
|
41391 | /**
|
41392 | * Collect decorator metadata from a TypeScript module.
|
41393 | */
|
41394 | class MetadataCollector {
|
41395 | constructor(options = {}) {
|
41396 | this.options = options;
|
41397 | }
|
41398 | /**
|
41399 | * Returns a JSON.stringify friendly form describing the decorators of the exported classes from
|
41400 | * the source file that is expected to correspond to a module.
|
41401 | */
|
41402 | getMetadata(sourceFile, strict = false, substituteExpression) {
|
41403 | const locals = new Symbols(sourceFile);
|
41404 | const nodeMap = new Map();
|
41405 | const composedSubstituter = substituteExpression && this.options.substituteExpression ?
|
41406 | (value, node) => this.options.substituteExpression(substituteExpression(value, node), node) :
|
41407 | substituteExpression;
|
41408 | const evaluatorOptions = substituteExpression ? Object.assign(Object.assign({}, this.options), { substituteExpression: composedSubstituter }) :
|
41409 | this.options;
|
41410 | let metadata;
|
41411 | const evaluator = new Evaluator(locals, nodeMap, evaluatorOptions, (name, value) => {
|
41412 | if (!metadata)
|
41413 | metadata = {};
|
41414 | metadata[name] = value;
|
41415 | });
|
41416 | let exports = undefined;
|
41417 | function objFromDecorator(decoratorNode) {
|
41418 | return evaluator.evaluateNode(decoratorNode.expression);
|
41419 | }
|
41420 | function recordEntry(entry, node) {
|
41421 | if (composedSubstituter) {
|
41422 | entry = composedSubstituter(entry, node);
|
41423 | }
|
41424 | return recordMapEntry(entry, node, nodeMap, sourceFile);
|
41425 | }
|
41426 | function errorSym(message, node, context) {
|
41427 | return errorSymbol(message, node, context, sourceFile);
|
41428 | }
|
41429 | function maybeGetSimpleFunction(functionDeclaration) {
|
41430 | if (functionDeclaration.name && functionDeclaration.name.kind == ts.SyntaxKind.Identifier) {
|
41431 | const nameNode = functionDeclaration.name;
|
41432 | const functionName = nameNode.text;
|
41433 | const functionBody = functionDeclaration.body;
|
41434 | if (functionBody && functionBody.statements.length == 1) {
|
41435 | const statement = functionBody.statements[0];
|
41436 | if (statement.kind === ts.SyntaxKind.ReturnStatement) {
|
41437 | const returnStatement = statement;
|
41438 | if (returnStatement.expression) {
|
41439 | const func = {
|
41440 | __symbolic: 'function',
|
41441 | parameters: namesOf(functionDeclaration.parameters),
|
41442 | value: evaluator.evaluateNode(returnStatement.expression)
|
41443 | };
|
41444 | if (functionDeclaration.parameters.some(p => p.initializer != null)) {
|
41445 | func.defaults = functionDeclaration.parameters.map(p => p.initializer && evaluator.evaluateNode(p.initializer));
|
41446 | }
|
41447 | return recordEntry({ func, name: functionName }, functionDeclaration);
|
41448 | }
|
41449 | }
|
41450 | }
|
41451 | }
|
41452 | }
|
41453 | function classMetadataOf(classDeclaration) {
|
41454 | const result = { __symbolic: 'class' };
|
41455 | function getDecorators(decorators) {
|
41456 | if (decorators && decorators.length)
|
41457 | return decorators.map(decorator => objFromDecorator(decorator));
|
41458 | return undefined;
|
41459 | }
|
41460 | function referenceFrom(node) {
|
41461 | const result = evaluator.evaluateNode(node);
|
41462 | if (isMetadataError$1(result) || isMetadataSymbolicReferenceExpression(result) ||
|
41463 | isMetadataSymbolicSelectExpression(result)) {
|
41464 | return result;
|
41465 | }
|
41466 | else {
|
41467 | return errorSym('Symbol reference expected', node);
|
41468 | }
|
41469 | }
|
41470 | // Add class parents
|
41471 | if (classDeclaration.heritageClauses) {
|
41472 | classDeclaration.heritageClauses.forEach((hc) => {
|
41473 | if (hc.token === ts.SyntaxKind.ExtendsKeyword && hc.types) {
|
41474 | hc.types.forEach(type => result.extends = referenceFrom(type.expression));
|
41475 | }
|
41476 | });
|
41477 | }
|
41478 | // Add arity if the type is generic
|
41479 | const typeParameters = classDeclaration.typeParameters;
|
41480 | if (typeParameters && typeParameters.length) {
|
41481 | result.arity = typeParameters.length;
|
41482 | }
|
41483 | // Add class decorators
|
41484 | if (classDeclaration.decorators) {
|
41485 | result.decorators = getDecorators(classDeclaration.decorators);
|
41486 | }
|
41487 | // member decorators
|
41488 | let members = null;
|
41489 | function recordMember(name, metadata) {
|
41490 | if (!members)
|
41491 | members = {};
|
41492 | const data = members.hasOwnProperty(name) ? members[name] : [];
|
41493 | data.push(metadata);
|
41494 | members[name] = data;
|
41495 | }
|
41496 | // static member
|
41497 | let statics = null;
|
41498 | function recordStaticMember(name, value) {
|
41499 | if (!statics)
|
41500 | statics = {};
|
41501 | statics[name] = value;
|
41502 | }
|
41503 | for (const member of classDeclaration.members) {
|
41504 | let isConstructor = false;
|
41505 | switch (member.kind) {
|
41506 | case ts.SyntaxKind.Constructor:
|
41507 | case ts.SyntaxKind.MethodDeclaration:
|
41508 | isConstructor = member.kind === ts.SyntaxKind.Constructor;
|
41509 | const method = member;
|
41510 | if (isStatic(method)) {
|
41511 | const maybeFunc = maybeGetSimpleFunction(method);
|
41512 | if (maybeFunc) {
|
41513 | recordStaticMember(maybeFunc.name, maybeFunc.func);
|
41514 | }
|
41515 | continue;
|
41516 | }
|
41517 | const methodDecorators = getDecorators(method.decorators);
|
41518 | const parameters = method.parameters;
|
41519 | const parameterDecoratorData = [];
|
41520 | const parametersData = [];
|
41521 | let hasDecoratorData = false;
|
41522 | let hasParameterData = false;
|
41523 | for (const parameter of parameters) {
|
41524 | const parameterData = getDecorators(parameter.decorators);
|
41525 | parameterDecoratorData.push(parameterData);
|
41526 | hasDecoratorData = hasDecoratorData || !!parameterData;
|
41527 | if (isConstructor) {
|
41528 | if (parameter.type) {
|
41529 | parametersData.push(referenceFrom(parameter.type));
|
41530 | }
|
41531 | else {
|
41532 | parametersData.push(null);
|
41533 | }
|
41534 | hasParameterData = true;
|
41535 | }
|
41536 | }
|
41537 | const data = { __symbolic: isConstructor ? 'constructor' : 'method' };
|
41538 | const name = isConstructor ? '__ctor__' : evaluator.nameOf(member.name);
|
41539 | if (methodDecorators) {
|
41540 | data.decorators = methodDecorators;
|
41541 | }
|
41542 | if (hasDecoratorData) {
|
41543 | data.parameterDecorators = parameterDecoratorData;
|
41544 | }
|
41545 | if (hasParameterData) {
|
41546 | data.parameters = parametersData;
|
41547 | }
|
41548 | if (!isMetadataError$1(name)) {
|
41549 | recordMember(name, data);
|
41550 | }
|
41551 | break;
|
41552 | case ts.SyntaxKind.PropertyDeclaration:
|
41553 | case ts.SyntaxKind.GetAccessor:
|
41554 | case ts.SyntaxKind.SetAccessor:
|
41555 | const property = member;
|
41556 | if (isStatic(property)) {
|
41557 | const name = evaluator.nameOf(property.name);
|
41558 | if (!isMetadataError$1(name) && !shouldIgnoreStaticMember(name)) {
|
41559 | if (property.initializer) {
|
41560 | const value = evaluator.evaluateNode(property.initializer);
|
41561 | recordStaticMember(name, value);
|
41562 | }
|
41563 | else {
|
41564 | recordStaticMember(name, errorSym('Variable not initialized', property.name));
|
41565 | }
|
41566 | }
|
41567 | }
|
41568 | const propertyDecorators = getDecorators(property.decorators);
|
41569 | if (propertyDecorators) {
|
41570 | const name = evaluator.nameOf(property.name);
|
41571 | if (!isMetadataError$1(name)) {
|
41572 | recordMember(name, { __symbolic: 'property', decorators: propertyDecorators });
|
41573 | }
|
41574 | }
|
41575 | break;
|
41576 | }
|
41577 | }
|
41578 | if (members) {
|
41579 | result.members = members;
|
41580 | }
|
41581 | if (statics) {
|
41582 | result.statics = statics;
|
41583 | }
|
41584 | return recordEntry(result, classDeclaration);
|
41585 | }
|
41586 | // Collect all exported symbols from an exports clause.
|
41587 | const exportMap = new Map();
|
41588 | ts.forEachChild(sourceFile, node => {
|
41589 | switch (node.kind) {
|
41590 | case ts.SyntaxKind.ExportDeclaration:
|
41591 | const exportDeclaration = node;
|
41592 | const { moduleSpecifier, exportClause } = exportDeclaration;
|
41593 | if (!moduleSpecifier && exportClause && ts.isNamedExports(exportClause)) {
|
41594 | // If there is a module specifier there is also an exportClause
|
41595 | exportClause.elements.forEach(spec => {
|
41596 | const exportedAs = spec.name.text;
|
41597 | const name = (spec.propertyName || spec.name).text;
|
41598 | exportMap.set(name, exportedAs);
|
41599 | });
|
41600 | }
|
41601 | }
|
41602 | });
|
41603 | const isExport = (node) => sourceFile.isDeclarationFile ||
|
41604 | ts.getCombinedModifierFlags(node) & ts.ModifierFlags.Export;
|
41605 | const isExportedIdentifier = (identifier) => identifier && exportMap.has(identifier.text);
|
41606 | const isExported = (node) => isExport(node) || isExportedIdentifier(node.name);
|
41607 | const exportedIdentifierName = (identifier) => identifier && (exportMap.get(identifier.text) || identifier.text);
|
41608 | const exportedName = (node) => exportedIdentifierName(node.name);
|
41609 | // Pre-declare classes and functions
|
41610 | ts.forEachChild(sourceFile, node => {
|
41611 | switch (node.kind) {
|
41612 | case ts.SyntaxKind.ClassDeclaration:
|
41613 | const classDeclaration = node;
|
41614 | if (classDeclaration.name) {
|
41615 | const className = classDeclaration.name.text;
|
41616 | if (isExported(classDeclaration)) {
|
41617 | locals.define(className, { __symbolic: 'reference', name: exportedName(classDeclaration) });
|
41618 | }
|
41619 | else {
|
41620 | locals.define(className, errorSym('Reference to non-exported class', node, { className }));
|
41621 | }
|
41622 | }
|
41623 | break;
|
41624 | case ts.SyntaxKind.InterfaceDeclaration:
|
41625 | const interfaceDeclaration = node;
|
41626 | if (interfaceDeclaration.name) {
|
41627 | const interfaceName = interfaceDeclaration.name.text;
|
41628 | // All references to interfaces should be converted to references to `any`.
|
41629 | locals.define(interfaceName, { __symbolic: 'reference', name: 'any' });
|
41630 | }
|
41631 | break;
|
41632 | case ts.SyntaxKind.FunctionDeclaration:
|
41633 | const functionDeclaration = node;
|
41634 | if (!isExported(functionDeclaration)) {
|
41635 | // Report references to this function as an error.
|
41636 | const nameNode = functionDeclaration.name;
|
41637 | if (nameNode && nameNode.text) {
|
41638 | locals.define(nameNode.text, errorSym('Reference to a non-exported function', nameNode, { name: nameNode.text }));
|
41639 | }
|
41640 | }
|
41641 | break;
|
41642 | }
|
41643 | });
|
41644 | ts.forEachChild(sourceFile, node => {
|
41645 | switch (node.kind) {
|
41646 | case ts.SyntaxKind.ExportDeclaration:
|
41647 | // Record export declarations
|
41648 | const exportDeclaration = node;
|
41649 | const { moduleSpecifier, exportClause } = exportDeclaration;
|
41650 | if (!moduleSpecifier) {
|
41651 | // no module specifier -> export {propName as name};
|
41652 | if (exportClause && ts.isNamedExports(exportClause)) {
|
41653 | exportClause.elements.forEach(spec => {
|
41654 | const name = spec.name.text;
|
41655 | // If the symbol was not already exported, export a reference since it is a
|
41656 | // reference to an import
|
41657 | if (!metadata || !metadata[name]) {
|
41658 | const propNode = spec.propertyName || spec.name;
|
41659 | const value = evaluator.evaluateNode(propNode);
|
41660 | if (!metadata)
|
41661 | metadata = {};
|
41662 | metadata[name] = recordEntry(value, node);
|
41663 | }
|
41664 | });
|
41665 | }
|
41666 | }
|
41667 | if (moduleSpecifier && moduleSpecifier.kind == ts.SyntaxKind.StringLiteral) {
|
41668 | // Ignore exports that don't have string literals as exports.
|
41669 | // This is allowed by the syntax but will be flagged as an error by the type checker.
|
41670 | const from = moduleSpecifier.text;
|
41671 | const moduleExport = { from };
|
41672 | if (exportClause && ts.isNamedExports(exportClause)) {
|
41673 | moduleExport.export = exportClause.elements.map(spec => spec.propertyName ? { name: spec.propertyName.text, as: spec.name.text } :
|
41674 | spec.name.text);
|
41675 | }
|
41676 | if (!exports)
|
41677 | exports = [];
|
41678 | exports.push(moduleExport);
|
41679 | }
|
41680 | break;
|
41681 | case ts.SyntaxKind.ClassDeclaration:
|
41682 | const classDeclaration = node;
|
41683 | if (classDeclaration.name) {
|
41684 | if (isExported(classDeclaration)) {
|
41685 | const name = exportedName(classDeclaration);
|
41686 | if (name) {
|
41687 | if (!metadata)
|
41688 | metadata = {};
|
41689 | metadata[name] = classMetadataOf(classDeclaration);
|
41690 | }
|
41691 | }
|
41692 | }
|
41693 | // Otherwise don't record metadata for the class.
|
41694 | break;
|
41695 | case ts.SyntaxKind.TypeAliasDeclaration:
|
41696 | const typeDeclaration = node;
|
41697 | if (typeDeclaration.name && isExported(typeDeclaration)) {
|
41698 | const name = exportedName(typeDeclaration);
|
41699 | if (name) {
|
41700 | if (!metadata)
|
41701 | metadata = {};
|
41702 | metadata[name] = { __symbolic: 'interface' };
|
41703 | }
|
41704 | }
|
41705 | break;
|
41706 | case ts.SyntaxKind.InterfaceDeclaration:
|
41707 | const interfaceDeclaration = node;
|
41708 | if (interfaceDeclaration.name && isExported(interfaceDeclaration)) {
|
41709 | const name = exportedName(interfaceDeclaration);
|
41710 | if (name) {
|
41711 | if (!metadata)
|
41712 | metadata = {};
|
41713 | metadata[name] = { __symbolic: 'interface' };
|
41714 | }
|
41715 | }
|
41716 | break;
|
41717 | case ts.SyntaxKind.FunctionDeclaration:
|
41718 | // Record functions that return a single value. Record the parameter
|
41719 | // names substitution will be performed by the StaticReflector.
|
41720 | const functionDeclaration = node;
|
41721 | if (isExported(functionDeclaration) && functionDeclaration.name) {
|
41722 | const name = exportedName(functionDeclaration);
|
41723 | const maybeFunc = maybeGetSimpleFunction(functionDeclaration);
|
41724 | if (name) {
|
41725 | if (!metadata)
|
41726 | metadata = {};
|
41727 | // TODO(alxhub): The literal here is not valid FunctionMetadata.
|
41728 | metadata[name] =
|
41729 | maybeFunc ? recordEntry(maybeFunc.func, node) : { __symbolic: 'function' };
|
41730 | }
|
41731 | }
|
41732 | break;
|
41733 | case ts.SyntaxKind.EnumDeclaration:
|
41734 | const enumDeclaration = node;
|
41735 | if (isExported(enumDeclaration)) {
|
41736 | const enumValueHolder = {};
|
41737 | const enumName = exportedName(enumDeclaration);
|
41738 | let nextDefaultValue = 0;
|
41739 | let writtenMembers = 0;
|
41740 | for (const member of enumDeclaration.members) {
|
41741 | let enumValue;
|
41742 | if (!member.initializer) {
|
41743 | enumValue = nextDefaultValue;
|
41744 | }
|
41745 | else {
|
41746 | enumValue = evaluator.evaluateNode(member.initializer);
|
41747 | }
|
41748 | let name = undefined;
|
41749 | if (member.name.kind == ts.SyntaxKind.Identifier) {
|
41750 | const identifier = member.name;
|
41751 | name = identifier.text;
|
41752 | enumValueHolder[name] = enumValue;
|
41753 | writtenMembers++;
|
41754 | }
|
41755 | if (typeof enumValue === 'number') {
|
41756 | nextDefaultValue = enumValue + 1;
|
41757 | }
|
41758 | else if (name) {
|
41759 | // TODO(alxhub): 'left' here has a name propery which is not valid for
|
41760 | // MetadataSymbolicSelectExpression.
|
41761 | nextDefaultValue = {
|
41762 | __symbolic: 'binary',
|
41763 | operator: '+',
|
41764 | left: {
|
41765 | __symbolic: 'select',
|
41766 | expression: recordEntry({ __symbolic: 'reference', name: enumName }, node),
|
41767 | name
|
41768 | },
|
41769 | };
|
41770 | }
|
41771 | else {
|
41772 | nextDefaultValue =
|
41773 | recordEntry(errorSym('Unsupported enum member name', member.name), node);
|
41774 | }
|
41775 | }
|
41776 | if (writtenMembers) {
|
41777 | if (enumName) {
|
41778 | if (!metadata)
|
41779 | metadata = {};
|
41780 | metadata[enumName] = recordEntry(enumValueHolder, node);
|
41781 | }
|
41782 | }
|
41783 | }
|
41784 | break;
|
41785 | case ts.SyntaxKind.VariableStatement:
|
41786 | const variableStatement = node;
|
41787 | for (const variableDeclaration of variableStatement.declarationList.declarations) {
|
41788 | if (variableDeclaration.name.kind == ts.SyntaxKind.Identifier) {
|
41789 | const nameNode = variableDeclaration.name;
|
41790 | let varValue;
|
41791 | if (variableDeclaration.initializer) {
|
41792 | varValue = evaluator.evaluateNode(variableDeclaration.initializer);
|
41793 | }
|
41794 | else {
|
41795 | varValue = recordEntry(errorSym('Variable not initialized', nameNode), nameNode);
|
41796 | }
|
41797 | let exported = false;
|
41798 | if (isExport(variableStatement) || isExport(variableDeclaration) ||
|
41799 | isExportedIdentifier(nameNode)) {
|
41800 | const name = exportedIdentifierName(nameNode);
|
41801 | if (name) {
|
41802 | if (!metadata)
|
41803 | metadata = {};
|
41804 | metadata[name] = recordEntry(varValue, node);
|
41805 | }
|
41806 | exported = true;
|
41807 | }
|
41808 | if (typeof varValue == 'string' || typeof varValue == 'number' ||
|
41809 | typeof varValue == 'boolean') {
|
41810 | locals.define(nameNode.text, varValue);
|
41811 | if (exported) {
|
41812 | locals.defineReference(nameNode.text, { __symbolic: 'reference', name: nameNode.text });
|
41813 | }
|
41814 | }
|
41815 | else if (!exported) {
|
41816 | if (varValue && !isMetadataError$1(varValue)) {
|
41817 | locals.define(nameNode.text, recordEntry(varValue, node));
|
41818 | }
|
41819 | else {
|
41820 | locals.define(nameNode.text, recordEntry(errorSym('Reference to a local symbol', nameNode, { name: nameNode.text }), node));
|
41821 | }
|
41822 | }
|
41823 | }
|
41824 | else {
|
41825 | // Destructuring (or binding) declarations are not supported,
|
41826 | // var {<identifier>[, <identifier>]+} = <expression>;
|
41827 | // or
|
41828 | // var [<identifier>[, <identifier}+] = <expression>;
|
41829 | // are not supported.
|
41830 | const report = (nameNode) => {
|
41831 | switch (nameNode.kind) {
|
41832 | case ts.SyntaxKind.Identifier:
|
41833 | const name = nameNode;
|
41834 | const varValue = errorSym('Destructuring not supported', name);
|
41835 | locals.define(name.text, varValue);
|
41836 | if (isExport(node)) {
|
41837 | if (!metadata)
|
41838 | metadata = {};
|
41839 | metadata[name.text] = varValue;
|
41840 | }
|
41841 | break;
|
41842 | case ts.SyntaxKind.BindingElement:
|
41843 | const bindingElement = nameNode;
|
41844 | report(bindingElement.name);
|
41845 | break;
|
41846 | case ts.SyntaxKind.ObjectBindingPattern:
|
41847 | case ts.SyntaxKind.ArrayBindingPattern:
|
41848 | const bindings = nameNode;
|
41849 | bindings.elements.forEach(report);
|
41850 | break;
|
41851 | }
|
41852 | };
|
41853 | report(variableDeclaration.name);
|
41854 | }
|
41855 | }
|
41856 | break;
|
41857 | }
|
41858 | });
|
41859 | if (metadata || exports) {
|
41860 | if (!metadata)
|
41861 | metadata = {};
|
41862 | else if (strict) {
|
41863 | validateMetadata(sourceFile, nodeMap, metadata);
|
41864 | }
|
41865 | const result = {
|
41866 | __symbolic: 'module',
|
41867 | version: this.options.version || METADATA_VERSION,
|
41868 | metadata
|
41869 | };
|
41870 | if (sourceFile.moduleName)
|
41871 | result.importAs = sourceFile.moduleName;
|
41872 | if (exports)
|
41873 | result.exports = exports;
|
41874 | return result;
|
41875 | }
|
41876 | }
|
41877 | }
|
41878 | // This will throw if the metadata entry given contains an error node.
|
41879 | function validateMetadata(sourceFile, nodeMap, metadata) {
|
41880 | let locals = new Set(['Array', 'Object', 'Set', 'Map', 'string', 'number', 'any']);
|
41881 | function validateExpression(expression) {
|
41882 | if (!expression) {
|
41883 | return;
|
41884 | }
|
41885 | else if (Array.isArray(expression)) {
|
41886 | expression.forEach(validateExpression);
|
41887 | }
|
41888 | else if (typeof expression === 'object' && !expression.hasOwnProperty('__symbolic')) {
|
41889 | Object.getOwnPropertyNames(expression).forEach(v => validateExpression(expression[v]));
|
41890 | }
|
41891 | else if (isMetadataError$1(expression)) {
|
41892 | reportError(expression);
|
41893 | }
|
41894 | else if (isMetadataGlobalReferenceExpression(expression)) {
|
41895 | if (!locals.has(expression.name)) {
|
41896 | const reference = metadata[expression.name];
|
41897 | if (reference) {
|
41898 | validateExpression(reference);
|
41899 | }
|
41900 | }
|
41901 | }
|
41902 | else if (isFunctionMetadata(expression)) {
|
41903 | validateFunction(expression);
|
41904 | }
|
41905 | else if (isMetadataSymbolicExpression(expression)) {
|
41906 | switch (expression.__symbolic) {
|
41907 | case 'binary':
|
41908 | const binaryExpression = expression;
|
41909 | validateExpression(binaryExpression.left);
|
41910 | validateExpression(binaryExpression.right);
|
41911 | break;
|
41912 | case 'call':
|
41913 | case 'new':
|
41914 | const callExpression = expression;
|
41915 | validateExpression(callExpression.expression);
|
41916 | if (callExpression.arguments)
|
41917 | callExpression.arguments.forEach(validateExpression);
|
41918 | break;
|
41919 | case 'index':
|
41920 | const indexExpression = expression;
|
41921 | validateExpression(indexExpression.expression);
|
41922 | validateExpression(indexExpression.index);
|
41923 | break;
|
41924 | case 'pre':
|
41925 | const prefixExpression = expression;
|
41926 | validateExpression(prefixExpression.operand);
|
41927 | break;
|
41928 | case 'select':
|
41929 | const selectExpression = expression;
|
41930 | validateExpression(selectExpression.expression);
|
41931 | break;
|
41932 | case 'spread':
|
41933 | const spreadExpression = expression;
|
41934 | validateExpression(spreadExpression.expression);
|
41935 | break;
|
41936 | case 'if':
|
41937 | const ifExpression = expression;
|
41938 | validateExpression(ifExpression.condition);
|
41939 | validateExpression(ifExpression.elseExpression);
|
41940 | validateExpression(ifExpression.thenExpression);
|
41941 | break;
|
41942 | }
|
41943 | }
|
41944 | }
|
41945 | function validateMember(classData, member) {
|
41946 | if (member.decorators) {
|
41947 | member.decorators.forEach(validateExpression);
|
41948 | }
|
41949 | if (isMethodMetadata(member) && member.parameterDecorators) {
|
41950 | member.parameterDecorators.forEach(validateExpression);
|
41951 | }
|
41952 | // Only validate parameters of classes for which we know that are used with our DI
|
41953 | if (classData.decorators && isConstructorMetadata(member) && member.parameters) {
|
41954 | member.parameters.forEach(validateExpression);
|
41955 | }
|
41956 | }
|
41957 | function validateClass(classData) {
|
41958 | if (classData.decorators) {
|
41959 | classData.decorators.forEach(validateExpression);
|
41960 | }
|
41961 | if (classData.members) {
|
41962 | Object.getOwnPropertyNames(classData.members)
|
41963 | .forEach(name => classData.members[name].forEach((m) => validateMember(classData, m)));
|
41964 | }
|
41965 | if (classData.statics) {
|
41966 | Object.getOwnPropertyNames(classData.statics).forEach(name => {
|
41967 | const staticMember = classData.statics[name];
|
41968 | if (isFunctionMetadata(staticMember)) {
|
41969 | validateExpression(staticMember.value);
|
41970 | }
|
41971 | else {
|
41972 | validateExpression(staticMember);
|
41973 | }
|
41974 | });
|
41975 | }
|
41976 | }
|
41977 | function validateFunction(functionDeclaration) {
|
41978 | if (functionDeclaration.value) {
|
41979 | const oldLocals = locals;
|
41980 | if (functionDeclaration.parameters) {
|
41981 | locals = new Set(oldLocals.values());
|
41982 | if (functionDeclaration.parameters)
|
41983 | functionDeclaration.parameters.forEach(n => locals.add(n));
|
41984 | }
|
41985 | validateExpression(functionDeclaration.value);
|
41986 | locals = oldLocals;
|
41987 | }
|
41988 | }
|
41989 | function shouldReportNode(node) {
|
41990 | if (node) {
|
41991 | const nodeStart = node.getStart();
|
41992 | return !(node.pos != nodeStart &&
|
41993 | sourceFile.text.substring(node.pos, nodeStart).indexOf('@dynamic') >= 0);
|
41994 | }
|
41995 | return true;
|
41996 | }
|
41997 | function reportError(error) {
|
41998 | const node = nodeMap.get(error);
|
41999 | if (shouldReportNode(node)) {
|
42000 | const lineInfo = error.line != undefined ? error.character != undefined ?
|
42001 | `:${error.line + 1}:${error.character + 1}` :
|
42002 | `:${error.line + 1}` :
|
42003 | '';
|
42004 | throw new Error(`${sourceFile.fileName}${lineInfo}: Metadata collected contains an error that will be reported at runtime: ${expandedMessage$1(error)}.\n ${JSON.stringify(error)}`);
|
42005 | }
|
42006 | }
|
42007 | Object.getOwnPropertyNames(metadata).forEach(name => {
|
42008 | const entry = metadata[name];
|
42009 | try {
|
42010 | if (isClassMetadata(entry)) {
|
42011 | validateClass(entry);
|
42012 | }
|
42013 | }
|
42014 | catch (e) {
|
42015 | const node = nodeMap.get(entry);
|
42016 | if (shouldReportNode(node)) {
|
42017 | if (node) {
|
42018 | const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
|
42019 | throw new Error(`${sourceFile.fileName}:${line + 1}:${character + 1}: Error encountered in metadata generated for exported symbol '${name}': \n ${e.message}`);
|
42020 | }
|
42021 | throw new Error(`Error encountered in metadata generated for exported symbol ${name}: \n ${e.message}`);
|
42022 | }
|
42023 | }
|
42024 | });
|
42025 | }
|
42026 | // Collect parameter names from a function.
|
42027 | function namesOf(parameters) {
|
42028 | const result = [];
|
42029 | function addNamesOf(name) {
|
42030 | if (name.kind == ts.SyntaxKind.Identifier) {
|
42031 | const identifier = name;
|
42032 | result.push(identifier.text);
|
42033 | }
|
42034 | else {
|
42035 | const bindingPattern = name;
|
42036 | for (const element of bindingPattern.elements) {
|
42037 | const name = element.name;
|
42038 | if (name) {
|
42039 | addNamesOf(name);
|
42040 | }
|
42041 | }
|
42042 | }
|
42043 | }
|
42044 | for (const parameter of parameters) {
|
42045 | addNamesOf(parameter.name);
|
42046 | }
|
42047 | return result;
|
42048 | }
|
42049 | function shouldIgnoreStaticMember(memberName) {
|
42050 | return memberName.startsWith('ngAcceptInputType_') || memberName.startsWith('ngTemplateGuard_');
|
42051 | }
|
42052 | function expandedMessage$1(error) {
|
42053 | switch (error.message) {
|
42054 | case 'Reference to non-exported class':
|
42055 | if (error.context && error.context.className) {
|
42056 | return `Reference to a non-exported class ${error.context.className}. Consider exporting the class`;
|
42057 | }
|
42058 | break;
|
42059 | case 'Variable not initialized':
|
42060 | return 'Only initialized variables and constants can be referenced because the value of this variable is needed by the template compiler';
|
42061 | case 'Destructuring not supported':
|
42062 | return 'Referencing an exported destructured variable or constant is not supported by the template compiler. Consider simplifying this to avoid destructuring';
|
42063 | case 'Could not resolve type':
|
42064 | if (error.context && error.context.typeName) {
|
42065 | return `Could not resolve type ${error.context.typeName}`;
|
42066 | }
|
42067 | break;
|
42068 | case 'Function call not supported':
|
42069 | let prefix = error.context && error.context.name ? `Calling function '${error.context.name}', f` : 'F';
|
42070 | return prefix +
|
42071 | 'unction calls are not supported. Consider replacing the function or lambda with a reference to an exported function';
|
42072 | case 'Reference to a local symbol':
|
42073 | if (error.context && error.context.name) {
|
42074 | return `Reference to a local (non-exported) symbol '${error.context.name}'. Consider exporting the symbol`;
|
42075 | }
|
42076 | }
|
42077 | return error.message;
|
42078 | }
|
42079 |
|
42080 | /**
|
42081 | * @license
|
42082 | * Copyright Google LLC All Rights Reserved.
|
42083 | *
|
42084 | * Use of this source code is governed by an MIT-style license that can be
|
42085 | * found in the LICENSE file at https://angular.io/license
|
42086 | */
|
42087 | var EmitFlags;
|
42088 | (function (EmitFlags) {
|
42089 | EmitFlags[EmitFlags["DTS"] = 1] = "DTS";
|
42090 | EmitFlags[EmitFlags["JS"] = 2] = "JS";
|
42091 | EmitFlags[EmitFlags["Metadata"] = 4] = "Metadata";
|
42092 | EmitFlags[EmitFlags["I18nBundle"] = 8] = "I18nBundle";
|
42093 | EmitFlags[EmitFlags["Codegen"] = 16] = "Codegen";
|
42094 | EmitFlags[EmitFlags["Default"] = 19] = "Default";
|
42095 | EmitFlags[EmitFlags["All"] = 31] = "All";
|
42096 | })(EmitFlags || (EmitFlags = {}));
|
42097 |
|
42098 | /**
|
42099 | * @license
|
42100 | * Copyright Google LLC All Rights Reserved.
|
42101 | *
|
42102 | * Use of this source code is governed by an MIT-style license that can be
|
42103 | * found in the LICENSE file at https://angular.io/license
|
42104 | */
|
42105 | const DTS = /\.d\.ts$/;
|
42106 |
|
42107 | /**
|
42108 | * @license
|
42109 | * Copyright Google LLC All Rights Reserved.
|
42110 | *
|
42111 | * Use of this source code is governed by an MIT-style license that can be
|
42112 | * found in the LICENSE file at https://angular.io/license
|
42113 | */
|
42114 | function createMetadataReaderCache() {
|
42115 | const data = new Map();
|
42116 | return { data };
|
42117 | }
|
42118 | function readMetadata(filePath, host, cache) {
|
42119 | let metadatas = cache && cache.data.get(filePath);
|
42120 | if (metadatas) {
|
42121 | return metadatas;
|
42122 | }
|
42123 | if (host.fileExists(filePath)) {
|
42124 | // If the file doesn't exists then we cannot return metadata for the file.
|
42125 | // This will occur if the user referenced a declared module for which no file
|
42126 | // exists for the module (i.e. jQuery or angularjs).
|
42127 | if (DTS.test(filePath)) {
|
42128 | metadatas = readMetadataFile(host, filePath);
|
42129 | if (!metadatas) {
|
42130 | // If there is a .d.ts file but no metadata file we need to produce a
|
42131 | // metadata from the .d.ts file as metadata files capture reexports
|
42132 | // (starting with v3).
|
42133 | metadatas = [upgradeMetadataWithDtsData(host, { '__symbolic': 'module', 'version': 1, 'metadata': {} }, filePath)];
|
42134 | }
|
42135 | }
|
42136 | else {
|
42137 | const metadata = host.getSourceFileMetadata(filePath);
|
42138 | metadatas = metadata ? [metadata] : [];
|
42139 | }
|
42140 | }
|
42141 | if (cache && (!host.cacheMetadata || host.cacheMetadata(filePath))) {
|
42142 | cache.data.set(filePath, metadatas);
|
42143 | }
|
42144 | return metadatas;
|
42145 | }
|
42146 | function readMetadataFile(host, dtsFilePath) {
|
42147 | const metadataPath = dtsFilePath.replace(DTS, '.metadata.json');
|
42148 | if (!host.fileExists(metadataPath)) {
|
42149 | return undefined;
|
42150 | }
|
42151 | try {
|
42152 | const metadataOrMetadatas = JSON.parse(host.readFile(metadataPath));
|
42153 | const metadatas = metadataOrMetadatas ?
|
42154 | (Array.isArray(metadataOrMetadatas) ? metadataOrMetadatas : [metadataOrMetadatas]) :
|
42155 | [];
|
42156 | if (metadatas.length) {
|
42157 | let maxMetadata = metadatas.reduce((p, c) => p.version > c.version ? p : c);
|
42158 | if (maxMetadata.version < METADATA_VERSION) {
|
42159 | metadatas.push(upgradeMetadataWithDtsData(host, maxMetadata, dtsFilePath));
|
42160 | }
|
42161 | }
|
42162 | return metadatas;
|
42163 | }
|
42164 | catch (e) {
|
42165 | console.error(`Failed to read JSON file ${metadataPath}`);
|
42166 | throw e;
|
42167 | }
|
42168 | }
|
42169 | function upgradeMetadataWithDtsData(host, oldMetadata, dtsFilePath) {
|
42170 | // patch v1 to v3 by adding exports and the `extends` clause.
|
42171 | // patch v3 to v4 by adding `interface` symbols for TypeAlias
|
42172 | let newMetadata = {
|
42173 | '__symbolic': 'module',
|
42174 | 'version': METADATA_VERSION,
|
42175 | 'metadata': Object.assign({}, oldMetadata.metadata),
|
42176 | };
|
42177 | if (oldMetadata.exports) {
|
42178 | newMetadata.exports = oldMetadata.exports;
|
42179 | }
|
42180 | if (oldMetadata.importAs) {
|
42181 | newMetadata.importAs = oldMetadata.importAs;
|
42182 | }
|
42183 | if (oldMetadata.origins) {
|
42184 | newMetadata.origins = oldMetadata.origins;
|
42185 | }
|
42186 | const dtsMetadata = host.getSourceFileMetadata(dtsFilePath);
|
42187 | if (dtsMetadata) {
|
42188 | for (let prop in dtsMetadata.metadata) {
|
42189 | if (!newMetadata.metadata[prop]) {
|
42190 | newMetadata.metadata[prop] = dtsMetadata.metadata[prop];
|
42191 | }
|
42192 | }
|
42193 | if (dtsMetadata['importAs'])
|
42194 | newMetadata['importAs'] = dtsMetadata['importAs'];
|
42195 | // Only copy exports from exports from metadata prior to version 3.
|
42196 | // Starting with version 3 the collector began collecting exports and
|
42197 | // this should be redundant. Also, with bundler will rewrite the exports
|
42198 | // which will hoist the exports from modules referenced indirectly causing
|
42199 | // the imports to be different than the .d.ts files and using the .d.ts file
|
42200 | // exports would cause the StaticSymbolResolver to redirect symbols to the
|
42201 | // incorrect location.
|
42202 | if ((!oldMetadata.version || oldMetadata.version < 3) && dtsMetadata.exports) {
|
42203 | newMetadata.exports = dtsMetadata.exports;
|
42204 | }
|
42205 | }
|
42206 | return newMetadata;
|
42207 | }
|
42208 |
|
42209 | /**
|
42210 | * @license
|
42211 | * Copyright Google LLC All Rights Reserved.
|
42212 | *
|
42213 | * Use of this source code is governed by an MIT-style license that can be
|
42214 | * found in the LICENSE file at https://angular.io/license
|
42215 | */
|
42216 | class ReflectorModuleModuleResolutionHost {
|
42217 | constructor(tsLSHost, getProgram) {
|
42218 | this.tsLSHost = tsLSHost;
|
42219 | this.getProgram = getProgram;
|
42220 | this.metadataCollector = new MetadataCollector({
|
42221 | // Note: verboseInvalidExpressions is important so that
|
42222 | // the collector will collect errors instead of throwing
|
42223 | verboseInvalidExpression: true,
|
42224 | });
|
42225 | if (tsLSHost.directoryExists) {
|
42226 | this.directoryExists = directoryName => tsLSHost.directoryExists(directoryName);
|
42227 | }
|
42228 | if (tsLSHost.realpath) {
|
42229 | this.realpath = path => tsLSHost.realpath(path);
|
42230 | }
|
42231 | }
|
42232 | fileExists(fileName) {
|
42233 | // TypeScript resolution logic walks through the following sequence in order:
|
42234 | // package.json (read "types" field) -> .ts -> .tsx -> .d.ts
|
42235 | // For more info, see
|
42236 | // https://www.typescriptlang.org/docs/handbook/module-resolution.html
|
42237 | // For Angular specifically, we can skip .tsx lookup
|
42238 | if (fileName.endsWith('.tsx')) {
|
42239 | return false;
|
42240 | }
|
42241 | if (this.tsLSHost.fileExists) {
|
42242 | return this.tsLSHost.fileExists(fileName);
|
42243 | }
|
42244 | return !!this.tsLSHost.getScriptSnapshot(fileName);
|
42245 | }
|
42246 | readFile(fileName) {
|
42247 | // readFile() is used by TypeScript to read package.json during module
|
42248 | // resolution, and it's used by Angular to read metadata.json during
|
42249 | // metadata resolution.
|
42250 | if (this.tsLSHost.readFile) {
|
42251 | return this.tsLSHost.readFile(fileName);
|
42252 | }
|
42253 | // As a fallback, read the JSON files from the editor snapshot.
|
42254 | const snapshot = this.tsLSHost.getScriptSnapshot(fileName);
|
42255 | if (!snapshot) {
|
42256 | // MetadataReaderHost readFile() declaration should be
|
42257 | // `readFile(fileName: string): string | undefined`
|
42258 | return undefined;
|
42259 | }
|
42260 | return snapshot.getText(0, snapshot.getLength());
|
42261 | }
|
42262 | getSourceFileMetadata(fileName) {
|
42263 | const sf = this.getProgram().getSourceFile(fileName);
|
42264 | return sf ? this.metadataCollector.getMetadata(sf) : undefined;
|
42265 | }
|
42266 | cacheMetadata(fileName) {
|
42267 | // Don't cache the metadata for .ts files as they might change in the editor!
|
42268 | return fileName.endsWith('.d.ts');
|
42269 | }
|
42270 | }
|
42271 | class ReflectorHost {
|
42272 | constructor(getProgram, tsLSHost) {
|
42273 | this.tsLSHost = tsLSHost;
|
42274 | this.metadataReaderCache = createMetadataReaderCache();
|
42275 | // tsLSHost.getCurrentDirectory() returns the directory where tsconfig.json
|
42276 | // is located. This is not the same as process.cwd() because the language
|
42277 | // service host sets the "project root path" as its current directory.
|
42278 | const currentDir = tsLSHost.getCurrentDirectory();
|
42279 | this.fakeContainingPath = currentDir ? path.join(currentDir, 'fakeContainingFile.ts') : '';
|
42280 | this.hostAdapter = new ReflectorModuleModuleResolutionHost(tsLSHost, getProgram);
|
42281 | this.moduleResolutionCache = ts.createModuleResolutionCache(currentDir, s => s, // getCanonicalFileName
|
42282 | tsLSHost.getCompilationSettings());
|
42283 | }
|
42284 | getMetadataFor(modulePath) {
|
42285 | return readMetadata(modulePath, this.hostAdapter, this.metadataReaderCache);
|
42286 | }
|
42287 | moduleNameToFileName(moduleName, containingFile) {
|
42288 | if (!containingFile) {
|
42289 | if (moduleName.startsWith('.')) {
|
42290 | throw new Error('Resolution of relative paths requires a containing file.');
|
42291 | }
|
42292 | if (!this.fakeContainingPath) {
|
42293 | // If current directory is empty then the file must belong to an inferred
|
42294 | // project (no tsconfig.json), in which case it's not possible to resolve
|
42295 | // the module without the caller explicitly providing a containing file.
|
42296 | throw new Error(`Could not resolve '${moduleName}' without a containing file.`);
|
42297 | }
|
42298 | containingFile = this.fakeContainingPath;
|
42299 | }
|
42300 | const compilerOptions = this.tsLSHost.getCompilationSettings();
|
42301 | const resolved = ts.resolveModuleName(moduleName, containingFile, compilerOptions, this.hostAdapter, this.moduleResolutionCache)
|
42302 | .resolvedModule;
|
42303 | return resolved ? resolved.resolvedFileName : null;
|
42304 | }
|
42305 | getOutputName(filePath) {
|
42306 | return filePath;
|
42307 | }
|
42308 | }
|
42309 |
|
42310 | /**
|
42311 | * @license
|
42312 | * Copyright Google LLC All Rights Reserved.
|
42313 | *
|
42314 | * Use of this source code is governed by an MIT-style license that can be
|
42315 | * found in the LICENSE file at https://angular.io/license
|
42316 | */
|
42317 | /**
|
42318 | * The language service never needs the normalized versions of the metadata. To avoid parsing
|
42319 | * the content and resolving references, return an empty file. This also allows normalizing
|
42320 | * template that are syntatically incorrect which is required to provide completions in
|
42321 | * syntactically incorrect templates.
|
42322 | */
|
42323 | class DummyHtmlParser extends HtmlParser {
|
42324 | parse() {
|
42325 | return new ParseTreeResult([], []);
|
42326 | }
|
42327 | }
|
42328 | /**
|
42329 | * Avoid loading resources in the language servcie by using a dummy loader.
|
42330 | */
|
42331 | class DummyResourceLoader extends ResourceLoader {
|
42332 | get(_url) {
|
42333 | return Promise.resolve('');
|
42334 | }
|
42335 | }
|
42336 | /**
|
42337 | * An implementation of a `LanguageServiceHost` for a TypeScript project.
|
42338 | *
|
42339 | * The `TypeScriptServiceHost` implements the Angular `LanguageServiceHost` using
|
42340 | * the TypeScript language services.
|
42341 | *
|
42342 | * @publicApi
|
42343 | */
|
42344 | class TypeScriptServiceHost {
|
42345 | constructor(tsLsHost, tsLS) {
|
42346 | this.tsLsHost = tsLsHost;
|
42347 | this.tsLS = tsLS;
|
42348 | this.staticSymbolCache = new StaticSymbolCache();
|
42349 | /**
|
42350 | * Key of the `fileToComponent` map must be TS internal normalized path (path
|
42351 | * separator must be `/`), value of the map is the StaticSymbol for the
|
42352 | * Component class declaration.
|
42353 | */
|
42354 | this.fileToComponent = new Map();
|
42355 | this.collectedErrors = new Map();
|
42356 | this.fileVersions = new Map();
|
42357 | this.lastProgram = undefined;
|
42358 | this.analyzedModules = {
|
42359 | files: [],
|
42360 | ngModuleByPipeOrDirective: new Map(),
|
42361 | ngModules: [],
|
42362 | };
|
42363 | this.summaryResolver = new AotSummaryResolver({
|
42364 | loadSummary(_filePath) {
|
42365 | return null;
|
42366 | },
|
42367 | isSourceFile(_sourceFilePath) {
|
42368 | return true;
|
42369 | },
|
42370 | toSummaryFileName(sourceFilePath) {
|
42371 | return sourceFilePath;
|
42372 | },
|
42373 | fromSummaryFileName(filePath) {
|
42374 | return filePath;
|
42375 | },
|
42376 | }, this.staticSymbolCache);
|
42377 | this.reflectorHost = new ReflectorHost(() => this.program, tsLsHost);
|
42378 | this.staticSymbolResolver = new StaticSymbolResolver(this.reflectorHost, this.staticSymbolCache, this.summaryResolver, (e, filePath) => this.collectError(e, filePath));
|
42379 | this.urlResolver = {
|
42380 | resolve: (baseUrl, url) => {
|
42381 | // In practice, `directoryExists` is always defined.
|
42382 | // https://github.com/microsoft/TypeScript/blob/0b6c9254a850dd07056259d4eefca7721745af75/src/server/project.ts#L1608-L1614
|
42383 | if (tsLsHost.directoryExists(baseUrl)) {
|
42384 | return path.resolve(baseUrl, url);
|
42385 | }
|
42386 | return path.resolve(path.dirname(baseUrl), url);
|
42387 | }
|
42388 | };
|
42389 | }
|
42390 | /**
|
42391 | * Return the singleton instance of the MetadataResolver.
|
42392 | */
|
42393 | get resolver() {
|
42394 | if (this._resolver) {
|
42395 | return this._resolver;
|
42396 | }
|
42397 | // StaticReflector keeps its own private caches that are not clearable.
|
42398 | // We have no choice but to create a new instance to invalidate the caches.
|
42399 | // TODO: Revisit this when language service gets rewritten for Ivy.
|
42400 | const staticReflector = new StaticReflector(this.summaryResolver, this.staticSymbolResolver, [], // knownMetadataClasses
|
42401 | [], // knownMetadataFunctions
|
42402 | (e, filePath) => this.collectError(e, filePath));
|
42403 | // Because static reflector above is changed, we need to create a new
|
42404 | // resolver.
|
42405 | const moduleResolver = new NgModuleResolver(staticReflector);
|
42406 | const directiveResolver = new DirectiveResolver(staticReflector);
|
42407 | const pipeResolver = new PipeResolver(staticReflector);
|
42408 | const elementSchemaRegistry = new DomElementSchemaRegistry();
|
42409 | const resourceLoader = new DummyResourceLoader();
|
42410 | const htmlParser = new DummyHtmlParser();
|
42411 | // This tracks the CompileConfig in codegen.ts. Currently these options
|
42412 | // are hard-coded.
|
42413 | const config = new CompilerConfig({
|
42414 | defaultEncapsulation: ViewEncapsulation$1.Emulated,
|
42415 | useJit: false,
|
42416 | });
|
42417 | const directiveNormalizer = new DirectiveNormalizer(resourceLoader, this.urlResolver, htmlParser, config);
|
42418 | this._resolver = new CompileMetadataResolver(config, htmlParser, moduleResolver, directiveResolver, pipeResolver, new JitSummaryResolver(), elementSchemaRegistry, directiveNormalizer, new Console(), this.staticSymbolCache, staticReflector, (error, type) => this.collectError(error, type && type.filePath));
|
42419 | return this._resolver;
|
42420 | }
|
42421 | /**
|
42422 | * Return the singleton instance of the StaticReflector hosted in the
|
42423 | * MetadataResolver.
|
42424 | */
|
42425 | get reflector() {
|
42426 | return this.resolver.getReflector();
|
42427 | }
|
42428 | /**
|
42429 | * Return all known external templates.
|
42430 | */
|
42431 | getExternalTemplates() {
|
42432 | return [...this.fileToComponent.keys()];
|
42433 | }
|
42434 | /**
|
42435 | * Checks whether the program has changed and returns all analyzed modules.
|
42436 | * If program has changed, invalidate all caches and update fileToComponent
|
42437 | * and templateReferences.
|
42438 | * In addition to returning information about NgModules, this method plays the
|
42439 | * same role as 'synchronizeHostData' in tsserver.
|
42440 | */
|
42441 | getAnalyzedModules() {
|
42442 | if (this.upToDate()) {
|
42443 | return this.analyzedModules;
|
42444 | }
|
42445 | // Invalidate caches
|
42446 | this.fileToComponent.clear();
|
42447 | this.collectedErrors.clear();
|
42448 | this.resolver.clearCache();
|
42449 | const analyzeHost = {
|
42450 | isSourceFile(_filePath) {
|
42451 | return true;
|
42452 | }
|
42453 | };
|
42454 | const programFiles = this.program.getSourceFiles().map(sf => sf.fileName);
|
42455 | try {
|
42456 | this.analyzedModules =
|
42457 | analyzeNgModules(programFiles, analyzeHost, this.staticSymbolResolver, this.resolver);
|
42458 | }
|
42459 | catch (e) {
|
42460 | // Analyzing modules may throw; in that case, reuse the old modules.
|
42461 | this.error(`Analyzing NgModules failed. ${e}`);
|
42462 | return this.analyzedModules;
|
42463 | }
|
42464 | // update template references and fileToComponent
|
42465 | for (const ngModule of this.analyzedModules.ngModules) {
|
42466 | for (const directive of ngModule.declaredDirectives) {
|
42467 | const { metadata } = this.resolver.getNonNormalizedDirectiveMetadata(directive.reference);
|
42468 | if (metadata.isComponent && metadata.template && metadata.template.templateUrl) {
|
42469 | const templateName = this.urlResolver.resolve(this.reflector.componentModuleUrl(directive.reference), metadata.template.templateUrl);
|
42470 | this.fileToComponent.set(tss.server.toNormalizedPath(templateName), directive.reference);
|
42471 | }
|
42472 | }
|
42473 | }
|
42474 | return this.analyzedModules;
|
42475 | }
|
42476 | /**
|
42477 | * Checks whether the program has changed, and invalidate static symbols in
|
42478 | * the source files that have changed.
|
42479 | * Returns true if modules are up-to-date, false otherwise.
|
42480 | * This should only be called by getAnalyzedModules().
|
42481 | */
|
42482 | upToDate() {
|
42483 | const { lastProgram, program } = this;
|
42484 | if (lastProgram === program) {
|
42485 | return true;
|
42486 | }
|
42487 | this.lastProgram = program;
|
42488 | // Even though the program has changed, it could be the case that none of
|
42489 | // the source files have changed. If all source files remain the same, then
|
42490 | // program is still up-to-date, and we should not invalidate caches.
|
42491 | let filesAdded = 0;
|
42492 | const filesChangedOrRemoved = [];
|
42493 | // Check if any source files have been added / changed since last computation.
|
42494 | const seen = new Set();
|
42495 | const ANGULAR_CORE = '@angular/core';
|
42496 | const corePath = this.reflectorHost.moduleNameToFileName(ANGULAR_CORE);
|
42497 | for (const { fileName } of program.getSourceFiles()) {
|
42498 | // If `@angular/core` is edited, the language service would have to be
|
42499 | // restarted, so ignore changes to `@angular/core`.
|
42500 | // When the StaticReflector is initialized at startup, it loads core
|
42501 | // symbols from @angular/core by calling initializeConversionMap(). This
|
42502 | // is only done once. If the file is invalidated, some of the core symbols
|
42503 | // will be lost permanently.
|
42504 | if (fileName === corePath) {
|
42505 | continue;
|
42506 | }
|
42507 | seen.add(fileName);
|
42508 | const version = this.tsLsHost.getScriptVersion(fileName);
|
42509 | const lastVersion = this.fileVersions.get(fileName);
|
42510 | if (lastVersion === undefined) {
|
42511 | filesAdded++;
|
42512 | this.fileVersions.set(fileName, version);
|
42513 | }
|
42514 | else if (version !== lastVersion) {
|
42515 | filesChangedOrRemoved.push(fileName); // changed
|
42516 | this.fileVersions.set(fileName, version);
|
42517 | }
|
42518 | }
|
42519 | // Check if any source files have been removed since last computation.
|
42520 | for (const [fileName] of this.fileVersions) {
|
42521 | if (!seen.has(fileName)) {
|
42522 | filesChangedOrRemoved.push(fileName); // removed
|
42523 | // Because Maps are iterated in insertion order, it is safe to delete
|
42524 | // entries from the same map while iterating.
|
42525 | // See https://stackoverflow.com/questions/35940216 and
|
42526 | // https://www.ecma-international.org/ecma-262/10.0/index.html#sec-map.prototype.foreach
|
42527 | this.fileVersions.delete(fileName);
|
42528 | }
|
42529 | }
|
42530 | for (const fileName of filesChangedOrRemoved) {
|
42531 | const symbols = this.staticSymbolResolver.invalidateFile(fileName);
|
42532 | this.reflector.invalidateSymbols(symbols);
|
42533 | }
|
42534 | // Program is up-to-date iff no files are added, changed, or removed.
|
42535 | return filesAdded === 0 && filesChangedOrRemoved.length === 0;
|
42536 | }
|
42537 | /**
|
42538 | * Find all templates in the specified `file`.
|
42539 | * @param fileName TS or HTML file
|
42540 | */
|
42541 | getTemplates(fileName) {
|
42542 | const results = [];
|
42543 | if (fileName.endsWith('.ts')) {
|
42544 | // Find every template string in the file
|
42545 | const visit = (child) => {
|
42546 | const template = this.getInternalTemplate(child);
|
42547 | if (template) {
|
42548 | results.push(template);
|
42549 | }
|
42550 | else {
|
42551 | tss.forEachChild(child, visit);
|
42552 | }
|
42553 | };
|
42554 | const sourceFile = this.getSourceFile(fileName);
|
42555 | if (sourceFile) {
|
42556 | tss.forEachChild(sourceFile, visit);
|
42557 | }
|
42558 | }
|
42559 | else {
|
42560 | const template = this.getExternalTemplate(fileName);
|
42561 | if (template) {
|
42562 | results.push(template);
|
42563 | }
|
42564 | }
|
42565 | return results;
|
42566 | }
|
42567 | /**
|
42568 | * Return metadata about all class declarations in the file that are Angular
|
42569 | * directives. Potential matches are `@NgModule`, `@Component`, `@Directive`,
|
42570 | * `@Pipes`, etc. class declarations.
|
42571 | *
|
42572 | * @param fileName TS file
|
42573 | */
|
42574 | getDeclarations(fileName) {
|
42575 | if (!fileName.endsWith('.ts')) {
|
42576 | return [];
|
42577 | }
|
42578 | const sourceFile = this.getSourceFile(fileName);
|
42579 | if (!sourceFile) {
|
42580 | return [];
|
42581 | }
|
42582 | const results = [];
|
42583 | const visit = (child) => {
|
42584 | const candidate = getDirectiveClassLike(child);
|
42585 | if (candidate) {
|
42586 | const { classId } = candidate;
|
42587 | const declarationSpan = spanOf$2(classId);
|
42588 | const className = classId.getText();
|
42589 | const classSymbol = this.reflector.getStaticSymbol(sourceFile.fileName, className);
|
42590 | // Ask the resolver to check if candidate is actually Angular directive
|
42591 | if (!this.resolver.isDirective(classSymbol)) {
|
42592 | return;
|
42593 | }
|
42594 | const data = this.resolver.getNonNormalizedDirectiveMetadata(classSymbol);
|
42595 | if (!data) {
|
42596 | return;
|
42597 | }
|
42598 | results.push({
|
42599 | type: classSymbol,
|
42600 | declarationSpan,
|
42601 | metadata: data.metadata,
|
42602 | errors: this.getCollectedErrors(declarationSpan, sourceFile),
|
42603 | });
|
42604 | }
|
42605 | else {
|
42606 | child.forEachChild(visit);
|
42607 | }
|
42608 | };
|
42609 | tss.forEachChild(sourceFile, visit);
|
42610 | return results;
|
42611 | }
|
42612 | getSourceFile(fileName) {
|
42613 | if (!fileName.endsWith('.ts')) {
|
42614 | throw new Error(`Non-TS source file requested: ${fileName}`);
|
42615 | }
|
42616 | return this.program.getSourceFile(fileName);
|
42617 | }
|
42618 | get program() {
|
42619 | const program = this.tsLS.getProgram();
|
42620 | if (!program) {
|
42621 | // Program is very very unlikely to be undefined.
|
42622 | throw new Error('No program in language service!');
|
42623 | }
|
42624 | return program;
|
42625 | }
|
42626 | /**
|
42627 | * Return the TemplateSource if `node` is a template node.
|
42628 | *
|
42629 | * For example,
|
42630 | *
|
42631 | * @Component({
|
42632 | * template: '<div></div>' <-- template node
|
42633 | * })
|
42634 | * class AppComponent {}
|
42635 | * ^---- class declaration node
|
42636 | *
|
42637 | * @param node Potential template node
|
42638 | */
|
42639 | getInternalTemplate(node) {
|
42640 | if (!tss.isStringLiteralLike(node)) {
|
42641 | return;
|
42642 | }
|
42643 | const tmplAsgn = getPropertyAssignmentFromValue(node, 'template');
|
42644 | if (!tmplAsgn) {
|
42645 | return;
|
42646 | }
|
42647 | const classDecl = getClassDeclFromDecoratorProp(tmplAsgn);
|
42648 | if (!classDecl || !classDecl.name) { // Does not handle anonymous class
|
42649 | return;
|
42650 | }
|
42651 | const fileName = node.getSourceFile().fileName;
|
42652 | const classSymbol = this.reflector.getStaticSymbol(fileName, classDecl.name.text);
|
42653 | return new InlineTemplate(node, classDecl, classSymbol, this);
|
42654 | }
|
42655 | /**
|
42656 | * Return the external template for `fileName`.
|
42657 | * @param fileName HTML file
|
42658 | */
|
42659 | getExternalTemplate(fileName) {
|
42660 | // First get the text for the template
|
42661 | const snapshot = this.tsLsHost.getScriptSnapshot(fileName);
|
42662 | if (!snapshot) {
|
42663 | return;
|
42664 | }
|
42665 | const source = snapshot.getText(0, snapshot.getLength());
|
42666 | // Next find the component class symbol
|
42667 | const classSymbol = this.fileToComponent.get(tss.server.toNormalizedPath(fileName));
|
42668 | if (!classSymbol) {
|
42669 | return;
|
42670 | }
|
42671 | // Then use the class symbol to find the actual ts.ClassDeclaration node
|
42672 | const sourceFile = this.getSourceFile(classSymbol.filePath);
|
42673 | if (!sourceFile) {
|
42674 | return;
|
42675 | }
|
42676 | // TODO: This only considers top-level class declarations in a source file.
|
42677 | // This would not find a class declaration in a namespace, for example.
|
42678 | const classDecl = sourceFile.forEachChild((child) => {
|
42679 | if (tss.isClassDeclaration(child) && child.name && child.name.text === classSymbol.name) {
|
42680 | return child;
|
42681 | }
|
42682 | });
|
42683 | if (!classDecl) {
|
42684 | return;
|
42685 | }
|
42686 | return new ExternalTemplate(source, fileName, classDecl, classSymbol, this);
|
42687 | }
|
42688 | collectError(error, filePath) {
|
42689 | if (filePath) {
|
42690 | let errors = this.collectedErrors.get(filePath);
|
42691 | if (!errors) {
|
42692 | errors = [];
|
42693 | this.collectedErrors.set(filePath, errors);
|
42694 | }
|
42695 | errors.push(error);
|
42696 | }
|
42697 | }
|
42698 | getCollectedErrors(defaultSpan, sourceFile) {
|
42699 | const errors = this.collectedErrors.get(sourceFile.fileName);
|
42700 | if (!errors) {
|
42701 | return [];
|
42702 | }
|
42703 | // TODO: Add better typings for the errors
|
42704 | return errors.map((e) => {
|
42705 | const line = e.line || (e.position && e.position.line);
|
42706 | const column = e.column || (e.position && e.position.column);
|
42707 | const span = spanAt$1(sourceFile, line, column) || defaultSpan;
|
42708 | if (isFormattedError(e)) {
|
42709 | return errorToDiagnosticWithChain(e, span);
|
42710 | }
|
42711 | return { message: e.message, span };
|
42712 | });
|
42713 | }
|
42714 | /**
|
42715 | * Return the parsed template for the template at the specified `position`.
|
42716 | * @param fileName TS or HTML file
|
42717 | * @param position Position of the template in the TS file, otherwise ignored.
|
42718 | */
|
42719 | getTemplateAstAtPosition(fileName, position) {
|
42720 | let template;
|
42721 | if (fileName.endsWith('.ts')) {
|
42722 | const sourceFile = this.getSourceFile(fileName);
|
42723 | if (!sourceFile) {
|
42724 | return;
|
42725 | }
|
42726 | // Find the node that most closely matches the position
|
42727 | const node = findTightestNode(sourceFile, position);
|
42728 | if (!node) {
|
42729 | return;
|
42730 | }
|
42731 | template = this.getInternalTemplate(node);
|
42732 | }
|
42733 | else {
|
42734 | template = this.getExternalTemplate(fileName);
|
42735 | }
|
42736 | if (!template) {
|
42737 | return;
|
42738 | }
|
42739 | return this.getTemplateAst(template);
|
42740 | }
|
42741 | /**
|
42742 | * Find the NgModule which the directive associated with the `classSymbol`
|
42743 | * belongs to, then return its schema and transitive directives and pipes.
|
42744 | * @param classSymbol Angular Symbol that defines a directive
|
42745 | */
|
42746 | getModuleMetadataForDirective(classSymbol) {
|
42747 | const result = {
|
42748 | directives: [],
|
42749 | pipes: [],
|
42750 | schemas: [],
|
42751 | };
|
42752 | // First find which NgModule the directive belongs to.
|
42753 | const ngModule = this.analyzedModules.ngModuleByPipeOrDirective.get(classSymbol) ||
|
42754 | findSuitableDefaultModule(this.analyzedModules);
|
42755 | if (!ngModule) {
|
42756 | return result;
|
42757 | }
|
42758 | // Then gather all transitive directives and pipes.
|
42759 | const { directives, pipes } = ngModule.transitiveModule;
|
42760 | for (const directive of directives) {
|
42761 | const data = this.resolver.getNonNormalizedDirectiveMetadata(directive.reference);
|
42762 | if (data) {
|
42763 | result.directives.push(data.metadata.toSummary());
|
42764 | }
|
42765 | }
|
42766 | for (const pipe of pipes) {
|
42767 | const metadata = this.resolver.getOrLoadPipeMetadata(pipe.reference);
|
42768 | result.pipes.push(metadata.toSummary());
|
42769 | }
|
42770 | result.schemas.push(...ngModule.schemas);
|
42771 | return result;
|
42772 | }
|
42773 | /**
|
42774 | * Parse the `template` and return its AST, if any.
|
42775 | * @param template template to be parsed
|
42776 | */
|
42777 | getTemplateAst(template) {
|
42778 | const { type: classSymbol, fileName } = template;
|
42779 | const data = this.resolver.getNonNormalizedDirectiveMetadata(classSymbol);
|
42780 | if (!data) {
|
42781 | return;
|
42782 | }
|
42783 | const htmlParser = new HtmlParser();
|
42784 | const expressionParser = new Parser$1(new Lexer());
|
42785 | const parser = new TemplateParser(new CompilerConfig(), this.reflector, expressionParser, new DomElementSchemaRegistry(), htmlParser, null, // console
|
42786 | [] // tranforms
|
42787 | );
|
42788 | const htmlResult = htmlParser.parse(template.source, fileName, {
|
42789 | tokenizeExpansionForms: true,
|
42790 | preserveLineEndings: true,
|
42791 | });
|
42792 | const { directives, pipes, schemas } = this.getModuleMetadataForDirective(classSymbol);
|
42793 | const parseResult = parser.tryParseHtml(htmlResult, data.metadata, directives, pipes, schemas);
|
42794 | if (!parseResult.templateAst) {
|
42795 | return;
|
42796 | }
|
42797 | return {
|
42798 | htmlAst: htmlResult.rootNodes,
|
42799 | templateAst: parseResult.templateAst,
|
42800 | directive: data.metadata,
|
42801 | directives,
|
42802 | pipes,
|
42803 | parseErrors: parseResult.errors,
|
42804 | expressionParser,
|
42805 | template,
|
42806 | };
|
42807 | }
|
42808 | /**
|
42809 | * Log the specified `msg` to file at INFO level. If logging is not enabled
|
42810 | * this method is a no-op.
|
42811 | * @param msg Log message
|
42812 | */
|
42813 | log(msg) {
|
42814 | if (this.tsLsHost.log) {
|
42815 | this.tsLsHost.log(msg);
|
42816 | }
|
42817 | }
|
42818 | /**
|
42819 | * Log the specified `msg` to file at ERROR level. If logging is not enabled
|
42820 | * this method is a no-op.
|
42821 | * @param msg error message
|
42822 | */
|
42823 | error(msg) {
|
42824 | if (this.tsLsHost.error) {
|
42825 | this.tsLsHost.error(msg);
|
42826 | }
|
42827 | }
|
42828 | /**
|
42829 | * Log debugging info to file at INFO level, only if verbose setting is turned
|
42830 | * on. Otherwise, this method is a no-op.
|
42831 | * @param msg debugging message
|
42832 | */
|
42833 | debug(msg) {
|
42834 | const project = this.tsLsHost;
|
42835 | if (!project.projectService) {
|
42836 | // tsLsHost is not a Project
|
42837 | return;
|
42838 | }
|
42839 | const { logger } = project.projectService;
|
42840 | if (logger.hasLevel(tss.server.LogLevel.verbose)) {
|
42841 | logger.info(msg);
|
42842 | }
|
42843 | }
|
42844 | }
|
42845 | function findSuitableDefaultModule(modules) {
|
42846 | let result = undefined;
|
42847 | let resultSize = 0;
|
42848 | for (const module of modules.ngModules) {
|
42849 | const moduleSize = module.transitiveModule.directives.length;
|
42850 | if (moduleSize > resultSize) {
|
42851 | result = module;
|
42852 | resultSize = moduleSize;
|
42853 | }
|
42854 | }
|
42855 | return result;
|
42856 | }
|
42857 | function spanOf$2(node) {
|
42858 | return { start: node.getStart(), end: node.getEnd() };
|
42859 | }
|
42860 | function spanAt$1(sourceFile, line, column) {
|
42861 | if (line != null && column != null) {
|
42862 | const position = tss.getPositionOfLineAndCharacter(sourceFile, line, column);
|
42863 | const findChild = function findChild(node) {
|
42864 | if (node.kind > tss.SyntaxKind.LastToken && node.pos <= position && node.end > position) {
|
42865 | const betterNode = tss.forEachChild(node, findChild);
|
42866 | return betterNode || node;
|
42867 | }
|
42868 | };
|
42869 | const node = tss.forEachChild(sourceFile, findChild);
|
42870 | if (node) {
|
42871 | return { start: node.getStart(), end: node.getEnd() };
|
42872 | }
|
42873 | }
|
42874 | }
|
42875 | function convertChain(chain) {
|
42876 | return { message: chain.message, next: chain.next ? chain.next.map(convertChain) : undefined };
|
42877 | }
|
42878 | function errorToDiagnosticWithChain(error, span) {
|
42879 | return { message: error.chain ? convertChain(error.chain) : error.message, span };
|
42880 | }
|
42881 |
|
42882 | /**
|
42883 | * @license
|
42884 | * Copyright Google LLC All Rights Reserved.
|
42885 | *
|
42886 | * Use of this source code is governed by an MIT-style license that can be
|
42887 | * found in the LICENSE file at https://angular.io/license
|
42888 | */
|
42889 | // Use a WeakMap to keep track of Project to Host mapping so that when Project
|
42890 | // is deleted Host could be garbage collected.
|
42891 | const PROJECT_MAP = new WeakMap();
|
42892 | /**
|
42893 | * This function is called by tsserver to retrieve the external (non-TS) files
|
42894 | * that should belong to the specified `project`. For Angular, these files are
|
42895 | * external templates. This is called once when the project is loaded, then
|
42896 | * every time when the program is updated.
|
42897 | * @param project Project for which external files should be retrieved.
|
42898 | */
|
42899 | function getExternalFiles(project) {
|
42900 | if (!project.hasRoots()) {
|
42901 | // During project initialization where there is no root files yet we should
|
42902 | // not do any work.
|
42903 | return [];
|
42904 | }
|
42905 | const ngLsHost = PROJECT_MAP.get(project);
|
42906 | if (ngLsHost === undefined) {
|
42907 | return [];
|
42908 | }
|
42909 | ngLsHost.getAnalyzedModules();
|
42910 | return ngLsHost.getExternalTemplates().filter(fileName => {
|
42911 | // TODO(kyliau): Remove this when the following PR lands on the version of
|
42912 | // TypeScript used in this repo.
|
42913 | // https://github.com/microsoft/TypeScript/pull/41737
|
42914 | return project.fileExists(fileName);
|
42915 | });
|
42916 | }
|
42917 | function create(info) {
|
42918 | const { languageService: tsLS, languageServiceHost: tsLSHost, config, project } = info;
|
42919 | // This plugin could operate under two different modes:
|
42920 | // 1. TS + Angular
|
42921 | // Plugin augments TS language service to provide additional Angular
|
42922 | // information. This only works with inline templates and is meant to be
|
42923 | // used as a local plugin (configured via tsconfig.json)
|
42924 | // 2. Angular only
|
42925 | // Plugin only provides information on Angular templates, no TS info at all.
|
42926 | // This effectively disables native TS features and is meant for internal
|
42927 | // use only.
|
42928 | const angularOnly = config ? config.angularOnly === true : false;
|
42929 | const ngLSHost = new TypeScriptServiceHost(tsLSHost, tsLS);
|
42930 | const ngLS = createLanguageService(ngLSHost);
|
42931 | PROJECT_MAP.set(project, ngLSHost);
|
42932 | function getCompletionsAtPosition(fileName, position, options) {
|
42933 | if (!angularOnly) {
|
42934 | const results = tsLS.getCompletionsAtPosition(fileName, position, options);
|
42935 | if (results && results.entries.length) {
|
42936 | // If TS could answer the query, then return results immediately.
|
42937 | return results;
|
42938 | }
|
42939 | }
|
42940 | return ngLS.getCompletionsAtPosition(fileName, position, options);
|
42941 | }
|
42942 | function getQuickInfoAtPosition(fileName, position) {
|
42943 | if (!angularOnly) {
|
42944 | const result = tsLS.getQuickInfoAtPosition(fileName, position);
|
42945 | if (result) {
|
42946 | // If TS could answer the query, then return results immediately.
|
42947 | return result;
|
42948 | }
|
42949 | }
|
42950 | return ngLS.getQuickInfoAtPosition(fileName, position);
|
42951 | }
|
42952 | function getSemanticDiagnostics(fileName) {
|
42953 | const results = [];
|
42954 | if (!angularOnly) {
|
42955 | results.push(...tsLS.getSemanticDiagnostics(fileName));
|
42956 | }
|
42957 | // For semantic diagnostics we need to combine both TS + Angular results
|
42958 | results.push(...ngLS.getSemanticDiagnostics(fileName));
|
42959 | return results;
|
42960 | }
|
42961 | function getDefinitionAtPosition(fileName, position) {
|
42962 | if (!angularOnly) {
|
42963 | const results = tsLS.getDefinitionAtPosition(fileName, position);
|
42964 | if (results) {
|
42965 | // If TS could answer the query, then return results immediately.
|
42966 | return results;
|
42967 | }
|
42968 | }
|
42969 | const result = ngLS.getDefinitionAndBoundSpan(fileName, position);
|
42970 | if (!result || !result.definitions || !result.definitions.length) {
|
42971 | return;
|
42972 | }
|
42973 | return result.definitions;
|
42974 | }
|
42975 | function getDefinitionAndBoundSpan(fileName, position) {
|
42976 | if (!angularOnly) {
|
42977 | const result = tsLS.getDefinitionAndBoundSpan(fileName, position);
|
42978 | if (result) {
|
42979 | // If TS could answer the query, then return results immediately.
|
42980 | return result;
|
42981 | }
|
42982 | }
|
42983 | return ngLS.getDefinitionAndBoundSpan(fileName, position);
|
42984 | }
|
42985 | function getTypeDefinitionAtPosition(fileName, position) {
|
42986 | // Not implemented in VE Language Service
|
42987 | return undefined;
|
42988 | }
|
42989 | function getReferencesAtPosition(fileName, position) {
|
42990 | // Not implemented in VE Language Service
|
42991 | return undefined;
|
42992 | }
|
42993 | function findRenameLocations(fileName, position, findInStrings, findInComments, providePrefixAndSuffixTextForRename) {
|
42994 | // not implemented in VE Language Service
|
42995 | return undefined;
|
42996 | }
|
42997 | function getTcb(fileName, position) {
|
42998 | // Not implemented in VE Language Service
|
42999 | return undefined;
|
43000 | }
|
43001 | return Object.assign(Object.assign({}, tsLS), {
|
43002 | // Then override the methods supported by Angular language service
|
43003 | getCompletionsAtPosition,
|
43004 | getQuickInfoAtPosition,
|
43005 | getSemanticDiagnostics,
|
43006 | getDefinitionAtPosition,
|
43007 | getDefinitionAndBoundSpan,
|
43008 | getTypeDefinitionAtPosition,
|
43009 | getReferencesAtPosition,
|
43010 | findRenameLocations,
|
43011 | getTcb });
|
43012 | }
|
43013 |
|
43014 | exports.create = create;
|
43015 | exports.getExternalFiles = getExternalFiles;
|
43016 | exports.isNgLanguageService = isNgLanguageService;
|
43017 |
|
43018 | Object.defineProperty(exports, '__esModule', { value: true });
|
43019 |
|
43020 | });
|
43021 | //# sourceMappingURL=language-service.js.map
|