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 | var TagContentType;
|
40 | (function (TagContentType) {
|
41 | TagContentType[TagContentType["RAW_TEXT"] = 0] = "RAW_TEXT";
|
42 | TagContentType[TagContentType["ESCAPABLE_RAW_TEXT"] = 1] = "ESCAPABLE_RAW_TEXT";
|
43 | TagContentType[TagContentType["PARSABLE_DATA"] = 2] = "PARSABLE_DATA";
|
44 | })(TagContentType || (TagContentType = {}));
|
45 | function splitNsName(elementName) {
|
46 | if (elementName[0] != ':') {
|
47 | return [null, elementName];
|
48 | }
|
49 | const colonIndex = elementName.indexOf(':', 1);
|
50 | if (colonIndex == -1) {
|
51 | throw new Error(`Unsupported format "${elementName}" expecting ":namespace:name"`);
|
52 | }
|
53 | return [elementName.slice(1, colonIndex), elementName.slice(colonIndex + 1)];
|
54 | }
|
55 |
|
56 | function isNgContainer(tagName) {
|
57 | return splitNsName(tagName)[1] === 'ng-container';
|
58 | }
|
59 |
|
60 | function isNgContent(tagName) {
|
61 | return splitNsName(tagName)[1] === 'ng-content';
|
62 | }
|
63 |
|
64 | function isNgTemplate(tagName) {
|
65 | return splitNsName(tagName)[1] === 'ng-template';
|
66 | }
|
67 | function getNsPrefix(fullName) {
|
68 | return fullName === null ? null : splitNsName(fullName)[0];
|
69 | }
|
70 | function mergeNsAndName(prefix, localName) {
|
71 | return prefix ? `:${prefix}:${localName}` : localName;
|
72 | }
|
73 |
|
74 |
|
75 |
|
76 |
|
77 |
|
78 | const NAMED_ENTITIES = {
|
79 | 'Aacute': '\u00C1',
|
80 | 'aacute': '\u00E1',
|
81 | 'Acirc': '\u00C2',
|
82 | 'acirc': '\u00E2',
|
83 | 'acute': '\u00B4',
|
84 | 'AElig': '\u00C6',
|
85 | 'aelig': '\u00E6',
|
86 | 'Agrave': '\u00C0',
|
87 | 'agrave': '\u00E0',
|
88 | 'alefsym': '\u2135',
|
89 | 'Alpha': '\u0391',
|
90 | 'alpha': '\u03B1',
|
91 | 'amp': '&',
|
92 | 'and': '\u2227',
|
93 | 'ang': '\u2220',
|
94 | 'apos': '\u0027',
|
95 | 'Aring': '\u00C5',
|
96 | 'aring': '\u00E5',
|
97 | 'asymp': '\u2248',
|
98 | 'Atilde': '\u00C3',
|
99 | 'atilde': '\u00E3',
|
100 | 'Auml': '\u00C4',
|
101 | 'auml': '\u00E4',
|
102 | 'bdquo': '\u201E',
|
103 | 'Beta': '\u0392',
|
104 | 'beta': '\u03B2',
|
105 | 'brvbar': '\u00A6',
|
106 | 'bull': '\u2022',
|
107 | 'cap': '\u2229',
|
108 | 'Ccedil': '\u00C7',
|
109 | 'ccedil': '\u00E7',
|
110 | 'cedil': '\u00B8',
|
111 | 'cent': '\u00A2',
|
112 | 'Chi': '\u03A7',
|
113 | 'chi': '\u03C7',
|
114 | 'circ': '\u02C6',
|
115 | 'clubs': '\u2663',
|
116 | 'cong': '\u2245',
|
117 | 'copy': '\u00A9',
|
118 | 'crarr': '\u21B5',
|
119 | 'cup': '\u222A',
|
120 | 'curren': '\u00A4',
|
121 | 'dagger': '\u2020',
|
122 | 'Dagger': '\u2021',
|
123 | 'darr': '\u2193',
|
124 | 'dArr': '\u21D3',
|
125 | 'deg': '\u00B0',
|
126 | 'Delta': '\u0394',
|
127 | 'delta': '\u03B4',
|
128 | 'diams': '\u2666',
|
129 | 'divide': '\u00F7',
|
130 | 'Eacute': '\u00C9',
|
131 | 'eacute': '\u00E9',
|
132 | 'Ecirc': '\u00CA',
|
133 | 'ecirc': '\u00EA',
|
134 | 'Egrave': '\u00C8',
|
135 | 'egrave': '\u00E8',
|
136 | 'empty': '\u2205',
|
137 | 'emsp': '\u2003',
|
138 | 'ensp': '\u2002',
|
139 | 'Epsilon': '\u0395',
|
140 | 'epsilon': '\u03B5',
|
141 | 'equiv': '\u2261',
|
142 | 'Eta': '\u0397',
|
143 | 'eta': '\u03B7',
|
144 | 'ETH': '\u00D0',
|
145 | 'eth': '\u00F0',
|
146 | 'Euml': '\u00CB',
|
147 | 'euml': '\u00EB',
|
148 | 'euro': '\u20AC',
|
149 | 'exist': '\u2203',
|
150 | 'fnof': '\u0192',
|
151 | 'forall': '\u2200',
|
152 | 'frac12': '\u00BD',
|
153 | 'frac14': '\u00BC',
|
154 | 'frac34': '\u00BE',
|
155 | 'frasl': '\u2044',
|
156 | 'Gamma': '\u0393',
|
157 | 'gamma': '\u03B3',
|
158 | 'ge': '\u2265',
|
159 | 'gt': '>',
|
160 | 'harr': '\u2194',
|
161 | 'hArr': '\u21D4',
|
162 | 'hearts': '\u2665',
|
163 | 'hellip': '\u2026',
|
164 | 'Iacute': '\u00CD',
|
165 | 'iacute': '\u00ED',
|
166 | 'Icirc': '\u00CE',
|
167 | 'icirc': '\u00EE',
|
168 | 'iexcl': '\u00A1',
|
169 | 'Igrave': '\u00CC',
|
170 | 'igrave': '\u00EC',
|
171 | 'image': '\u2111',
|
172 | 'infin': '\u221E',
|
173 | 'int': '\u222B',
|
174 | 'Iota': '\u0399',
|
175 | 'iota': '\u03B9',
|
176 | 'iquest': '\u00BF',
|
177 | 'isin': '\u2208',
|
178 | 'Iuml': '\u00CF',
|
179 | 'iuml': '\u00EF',
|
180 | 'Kappa': '\u039A',
|
181 | 'kappa': '\u03BA',
|
182 | 'Lambda': '\u039B',
|
183 | 'lambda': '\u03BB',
|
184 | 'lang': '\u27E8',
|
185 | 'laquo': '\u00AB',
|
186 | 'larr': '\u2190',
|
187 | 'lArr': '\u21D0',
|
188 | 'lceil': '\u2308',
|
189 | 'ldquo': '\u201C',
|
190 | 'le': '\u2264',
|
191 | 'lfloor': '\u230A',
|
192 | 'lowast': '\u2217',
|
193 | 'loz': '\u25CA',
|
194 | 'lrm': '\u200E',
|
195 | 'lsaquo': '\u2039',
|
196 | 'lsquo': '\u2018',
|
197 | 'lt': '<',
|
198 | 'macr': '\u00AF',
|
199 | 'mdash': '\u2014',
|
200 | 'micro': '\u00B5',
|
201 | 'middot': '\u00B7',
|
202 | 'minus': '\u2212',
|
203 | 'Mu': '\u039C',
|
204 | 'mu': '\u03BC',
|
205 | 'nabla': '\u2207',
|
206 | 'nbsp': '\u00A0',
|
207 | 'ndash': '\u2013',
|
208 | 'ne': '\u2260',
|
209 | 'ni': '\u220B',
|
210 | 'not': '\u00AC',
|
211 | 'notin': '\u2209',
|
212 | 'nsub': '\u2284',
|
213 | 'Ntilde': '\u00D1',
|
214 | 'ntilde': '\u00F1',
|
215 | 'Nu': '\u039D',
|
216 | 'nu': '\u03BD',
|
217 | 'Oacute': '\u00D3',
|
218 | 'oacute': '\u00F3',
|
219 | 'Ocirc': '\u00D4',
|
220 | 'ocirc': '\u00F4',
|
221 | 'OElig': '\u0152',
|
222 | 'oelig': '\u0153',
|
223 | 'Ograve': '\u00D2',
|
224 | 'ograve': '\u00F2',
|
225 | 'oline': '\u203E',
|
226 | 'Omega': '\u03A9',
|
227 | 'omega': '\u03C9',
|
228 | 'Omicron': '\u039F',
|
229 | 'omicron': '\u03BF',
|
230 | 'oplus': '\u2295',
|
231 | 'or': '\u2228',
|
232 | 'ordf': '\u00AA',
|
233 | 'ordm': '\u00BA',
|
234 | 'Oslash': '\u00D8',
|
235 | 'oslash': '\u00F8',
|
236 | 'Otilde': '\u00D5',
|
237 | 'otilde': '\u00F5',
|
238 | 'otimes': '\u2297',
|
239 | 'Ouml': '\u00D6',
|
240 | 'ouml': '\u00F6',
|
241 | 'para': '\u00B6',
|
242 | 'permil': '\u2030',
|
243 | 'perp': '\u22A5',
|
244 | 'Phi': '\u03A6',
|
245 | 'phi': '\u03C6',
|
246 | 'Pi': '\u03A0',
|
247 | 'pi': '\u03C0',
|
248 | 'piv': '\u03D6',
|
249 | 'plusmn': '\u00B1',
|
250 | 'pound': '\u00A3',
|
251 | 'prime': '\u2032',
|
252 | 'Prime': '\u2033',
|
253 | 'prod': '\u220F',
|
254 | 'prop': '\u221D',
|
255 | 'Psi': '\u03A8',
|
256 | 'psi': '\u03C8',
|
257 | 'quot': '\u0022',
|
258 | 'radic': '\u221A',
|
259 | 'rang': '\u27E9',
|
260 | 'raquo': '\u00BB',
|
261 | 'rarr': '\u2192',
|
262 | 'rArr': '\u21D2',
|
263 | 'rceil': '\u2309',
|
264 | 'rdquo': '\u201D',
|
265 | 'real': '\u211C',
|
266 | 'reg': '\u00AE',
|
267 | 'rfloor': '\u230B',
|
268 | 'Rho': '\u03A1',
|
269 | 'rho': '\u03C1',
|
270 | 'rlm': '\u200F',
|
271 | 'rsaquo': '\u203A',
|
272 | 'rsquo': '\u2019',
|
273 | 'sbquo': '\u201A',
|
274 | 'Scaron': '\u0160',
|
275 | 'scaron': '\u0161',
|
276 | 'sdot': '\u22C5',
|
277 | 'sect': '\u00A7',
|
278 | 'shy': '\u00AD',
|
279 | 'Sigma': '\u03A3',
|
280 | 'sigma': '\u03C3',
|
281 | 'sigmaf': '\u03C2',
|
282 | 'sim': '\u223C',
|
283 | 'spades': '\u2660',
|
284 | 'sub': '\u2282',
|
285 | 'sube': '\u2286',
|
286 | 'sum': '\u2211',
|
287 | 'sup': '\u2283',
|
288 | 'sup1': '\u00B9',
|
289 | 'sup2': '\u00B2',
|
290 | 'sup3': '\u00B3',
|
291 | 'supe': '\u2287',
|
292 | 'szlig': '\u00DF',
|
293 | 'Tau': '\u03A4',
|
294 | 'tau': '\u03C4',
|
295 | 'there4': '\u2234',
|
296 | 'Theta': '\u0398',
|
297 | 'theta': '\u03B8',
|
298 | 'thetasym': '\u03D1',
|
299 | 'thinsp': '\u2009',
|
300 | 'THORN': '\u00DE',
|
301 | 'thorn': '\u00FE',
|
302 | 'tilde': '\u02DC',
|
303 | 'times': '\u00D7',
|
304 | 'trade': '\u2122',
|
305 | 'Uacute': '\u00DA',
|
306 | 'uacute': '\u00FA',
|
307 | 'uarr': '\u2191',
|
308 | 'uArr': '\u21D1',
|
309 | 'Ucirc': '\u00DB',
|
310 | 'ucirc': '\u00FB',
|
311 | 'Ugrave': '\u00D9',
|
312 | 'ugrave': '\u00F9',
|
313 | 'uml': '\u00A8',
|
314 | 'upsih': '\u03D2',
|
315 | 'Upsilon': '\u03A5',
|
316 | 'upsilon': '\u03C5',
|
317 | 'Uuml': '\u00DC',
|
318 | 'uuml': '\u00FC',
|
319 | 'weierp': '\u2118',
|
320 | 'Xi': '\u039E',
|
321 | 'xi': '\u03BE',
|
322 | 'Yacute': '\u00DD',
|
323 | 'yacute': '\u00FD',
|
324 | 'yen': '\u00A5',
|
325 | 'yuml': '\u00FF',
|
326 | 'Yuml': '\u0178',
|
327 | 'Zeta': '\u0396',
|
328 | 'zeta': '\u03B6',
|
329 | 'zwj': '\u200D',
|
330 | 'zwnj': '\u200C',
|
331 | };
|
332 |
|
333 |
|
334 | const NGSP_UNICODE = '\uE500';
|
335 | NAMED_ENTITIES['ngsp'] = NGSP_UNICODE;
|
336 |
|
337 | |
338 |
|
339 |
|
340 |
|
341 |
|
342 |
|
343 |
|
344 | class HtmlTagDefinition {
|
345 | constructor({ closedByChildren, implicitNamespacePrefix, contentType = TagContentType.PARSABLE_DATA, closedByParent = false, isVoid = false, ignoreFirstLf = false, preventNamespaceInheritance = false } = {}) {
|
346 | this.closedByChildren = {};
|
347 | this.closedByParent = false;
|
348 | this.canSelfClose = false;
|
349 | if (closedByChildren && closedByChildren.length > 0) {
|
350 | closedByChildren.forEach(tagName => this.closedByChildren[tagName] = true);
|
351 | }
|
352 | this.isVoid = isVoid;
|
353 | this.closedByParent = closedByParent || isVoid;
|
354 | this.implicitNamespacePrefix = implicitNamespacePrefix || null;
|
355 | this.contentType = contentType;
|
356 | this.ignoreFirstLf = ignoreFirstLf;
|
357 | this.preventNamespaceInheritance = preventNamespaceInheritance;
|
358 | }
|
359 | isClosedByChild(name) {
|
360 | return this.isVoid || name.toLowerCase() in this.closedByChildren;
|
361 | }
|
362 | getContentType(prefix) {
|
363 | if (typeof this.contentType === 'object') {
|
364 | const overrideType = prefix == null ? undefined : this.contentType[prefix];
|
365 | return overrideType !== null && overrideType !== void 0 ? overrideType : this.contentType.default;
|
366 | }
|
367 | return this.contentType;
|
368 | }
|
369 | }
|
370 | let _DEFAULT_TAG_DEFINITION;
|
371 |
|
372 |
|
373 | let TAG_DEFINITIONS;
|
374 | function getHtmlTagDefinition(tagName) {
|
375 | var _a, _b;
|
376 | if (!TAG_DEFINITIONS) {
|
377 | _DEFAULT_TAG_DEFINITION = new HtmlTagDefinition();
|
378 | TAG_DEFINITIONS = {
|
379 | 'base': new HtmlTagDefinition({ isVoid: true }),
|
380 | 'meta': new HtmlTagDefinition({ isVoid: true }),
|
381 | 'area': new HtmlTagDefinition({ isVoid: true }),
|
382 | 'embed': new HtmlTagDefinition({ isVoid: true }),
|
383 | 'link': new HtmlTagDefinition({ isVoid: true }),
|
384 | 'img': new HtmlTagDefinition({ isVoid: true }),
|
385 | 'input': new HtmlTagDefinition({ isVoid: true }),
|
386 | 'param': new HtmlTagDefinition({ isVoid: true }),
|
387 | 'hr': new HtmlTagDefinition({ isVoid: true }),
|
388 | 'br': new HtmlTagDefinition({ isVoid: true }),
|
389 | 'source': new HtmlTagDefinition({ isVoid: true }),
|
390 | 'track': new HtmlTagDefinition({ isVoid: true }),
|
391 | 'wbr': new HtmlTagDefinition({ isVoid: true }),
|
392 | 'p': new HtmlTagDefinition({
|
393 | closedByChildren: [
|
394 | 'address', 'article', 'aside', 'blockquote', 'div', 'dl', 'fieldset',
|
395 | 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5',
|
396 | 'h6', 'header', 'hgroup', 'hr', 'main', 'nav', 'ol',
|
397 | 'p', 'pre', 'section', 'table', 'ul'
|
398 | ],
|
399 | closedByParent: true
|
400 | }),
|
401 | 'thead': new HtmlTagDefinition({ closedByChildren: ['tbody', 'tfoot'] }),
|
402 | 'tbody': new HtmlTagDefinition({ closedByChildren: ['tbody', 'tfoot'], closedByParent: true }),
|
403 | 'tfoot': new HtmlTagDefinition({ closedByChildren: ['tbody'], closedByParent: true }),
|
404 | 'tr': new HtmlTagDefinition({ closedByChildren: ['tr'], closedByParent: true }),
|
405 | 'td': new HtmlTagDefinition({ closedByChildren: ['td', 'th'], closedByParent: true }),
|
406 | 'th': new HtmlTagDefinition({ closedByChildren: ['td', 'th'], closedByParent: true }),
|
407 | 'col': new HtmlTagDefinition({ isVoid: true }),
|
408 | 'svg': new HtmlTagDefinition({ implicitNamespacePrefix: 'svg' }),
|
409 | 'foreignObject': new HtmlTagDefinition({
|
410 |
|
411 |
|
412 |
|
413 |
|
414 |
|
415 | implicitNamespacePrefix: 'svg',
|
416 |
|
417 |
|
418 | preventNamespaceInheritance: true,
|
419 | }),
|
420 | 'math': new HtmlTagDefinition({ implicitNamespacePrefix: 'math' }),
|
421 | 'li': new HtmlTagDefinition({ closedByChildren: ['li'], closedByParent: true }),
|
422 | 'dt': new HtmlTagDefinition({ closedByChildren: ['dt', 'dd'] }),
|
423 | 'dd': new HtmlTagDefinition({ closedByChildren: ['dt', 'dd'], closedByParent: true }),
|
424 | 'rb': new HtmlTagDefinition({ closedByChildren: ['rb', 'rt', 'rtc', 'rp'], closedByParent: true }),
|
425 | 'rt': new HtmlTagDefinition({ closedByChildren: ['rb', 'rt', 'rtc', 'rp'], closedByParent: true }),
|
426 | 'rtc': new HtmlTagDefinition({ closedByChildren: ['rb', 'rtc', 'rp'], closedByParent: true }),
|
427 | 'rp': new HtmlTagDefinition({ closedByChildren: ['rb', 'rt', 'rtc', 'rp'], closedByParent: true }),
|
428 | 'optgroup': new HtmlTagDefinition({ closedByChildren: ['optgroup'], closedByParent: true }),
|
429 | 'option': new HtmlTagDefinition({ closedByChildren: ['option', 'optgroup'], closedByParent: true }),
|
430 | 'pre': new HtmlTagDefinition({ ignoreFirstLf: true }),
|
431 | 'listing': new HtmlTagDefinition({ ignoreFirstLf: true }),
|
432 | 'style': new HtmlTagDefinition({ contentType: TagContentType.RAW_TEXT }),
|
433 | 'script': new HtmlTagDefinition({ contentType: TagContentType.RAW_TEXT }),
|
434 | 'title': new HtmlTagDefinition({
|
435 |
|
436 |
|
437 | contentType: { default: TagContentType.ESCAPABLE_RAW_TEXT, svg: TagContentType.PARSABLE_DATA }
|
438 | }),
|
439 | 'textarea': new HtmlTagDefinition({ contentType: TagContentType.ESCAPABLE_RAW_TEXT, ignoreFirstLf: true }),
|
440 | };
|
441 | }
|
442 |
|
443 |
|
444 | return (_b = (_a = TAG_DEFINITIONS[tagName]) !== null && _a !== void 0 ? _a : TAG_DEFINITIONS[tagName.toLowerCase()]) !== null && _b !== void 0 ? _b : _DEFAULT_TAG_DEFINITION;
|
445 | }
|
446 |
|
447 | |
448 |
|
449 |
|
450 |
|
451 |
|
452 |
|
453 |
|
454 | const _SELECTOR_REGEXP = new RegExp('(\\:not\\()|' +
|
455 | '(([\\.\\#]?)[-\\w]+)|' +
|
456 |
|
457 |
|
458 | '(?:\\[([-.\\w*]+)(?:=([\"\']?)([^\\]\"\']*)\\5)?\\])|' +
|
459 |
|
460 |
|
461 | '(\\))|' +
|
462 | '(\\s*,\\s*)',
|
463 | 'g');
|
464 | |
465 |
|
466 |
|
467 |
|
468 |
|
469 | class CssSelector {
|
470 | constructor() {
|
471 | this.element = null;
|
472 | this.classNames = [];
|
473 | |
474 |
|
475 |
|
476 |
|
477 |
|
478 |
|
479 |
|
480 |
|
481 |
|
482 |
|
483 |
|
484 | this.attrs = [];
|
485 | this.notSelectors = [];
|
486 | }
|
487 | static parse(selector) {
|
488 | const results = [];
|
489 | const _addResult = (res, cssSel) => {
|
490 | if (cssSel.notSelectors.length > 0 && !cssSel.element && cssSel.classNames.length == 0 &&
|
491 | cssSel.attrs.length == 0) {
|
492 | cssSel.element = '*';
|
493 | }
|
494 | res.push(cssSel);
|
495 | };
|
496 | let cssSelector = new CssSelector();
|
497 | let match;
|
498 | let current = cssSelector;
|
499 | let inNot = false;
|
500 | _SELECTOR_REGEXP.lastIndex = 0;
|
501 | while (match = _SELECTOR_REGEXP.exec(selector)) {
|
502 | if (match[1 ]) {
|
503 | if (inNot) {
|
504 | throw new Error('Nesting :not in a selector is not allowed');
|
505 | }
|
506 | inNot = true;
|
507 | current = new CssSelector();
|
508 | cssSelector.notSelectors.push(current);
|
509 | }
|
510 | const tag = match[2 ];
|
511 | if (tag) {
|
512 | const prefix = match[3 ];
|
513 | if (prefix === '#') {
|
514 |
|
515 | current.addAttribute('id', tag.substr(1));
|
516 | }
|
517 | else if (prefix === '.') {
|
518 |
|
519 | current.addClassName(tag.substr(1));
|
520 | }
|
521 | else {
|
522 |
|
523 | current.setElement(tag);
|
524 | }
|
525 | }
|
526 | const attribute = match[4 ];
|
527 | if (attribute) {
|
528 | current.addAttribute(attribute, match[6 ]);
|
529 | }
|
530 | if (match[7 ]) {
|
531 | inNot = false;
|
532 | current = cssSelector;
|
533 | }
|
534 | if (match[8 ]) {
|
535 | if (inNot) {
|
536 | throw new Error('Multiple selectors in :not are not supported');
|
537 | }
|
538 | _addResult(results, cssSelector);
|
539 | cssSelector = current = new CssSelector();
|
540 | }
|
541 | }
|
542 | _addResult(results, cssSelector);
|
543 | return results;
|
544 | }
|
545 | isElementSelector() {
|
546 | return this.hasElementSelector() && this.classNames.length == 0 && this.attrs.length == 0 &&
|
547 | this.notSelectors.length === 0;
|
548 | }
|
549 | hasElementSelector() {
|
550 | return !!this.element;
|
551 | }
|
552 | setElement(element = null) {
|
553 | this.element = element;
|
554 | }
|
555 |
|
556 | getMatchingElementTemplate() {
|
557 | const tagName = this.element || 'div';
|
558 | const classAttr = this.classNames.length > 0 ? ` class="${this.classNames.join(' ')}"` : '';
|
559 | let attrs = '';
|
560 | for (let i = 0; i < this.attrs.length; i += 2) {
|
561 | const attrName = this.attrs[i];
|
562 | const attrValue = this.attrs[i + 1] !== '' ? `="${this.attrs[i + 1]}"` : '';
|
563 | attrs += ` ${attrName}${attrValue}`;
|
564 | }
|
565 | return getHtmlTagDefinition(tagName).isVoid ? `<${tagName}${classAttr}${attrs}/>` :
|
566 | `<${tagName}${classAttr}${attrs}></${tagName}>`;
|
567 | }
|
568 | getAttrs() {
|
569 | const result = [];
|
570 | if (this.classNames.length > 0) {
|
571 | result.push('class', this.classNames.join(' '));
|
572 | }
|
573 | return result.concat(this.attrs);
|
574 | }
|
575 | addAttribute(name, value = '') {
|
576 | this.attrs.push(name, value && value.toLowerCase() || '');
|
577 | }
|
578 | addClassName(name) {
|
579 | this.classNames.push(name.toLowerCase());
|
580 | }
|
581 | toString() {
|
582 | let res = this.element || '';
|
583 | if (this.classNames) {
|
584 | this.classNames.forEach(klass => res += `.${klass}`);
|
585 | }
|
586 | if (this.attrs) {
|
587 | for (let i = 0; i < this.attrs.length; i += 2) {
|
588 | const name = this.attrs[i];
|
589 | const value = this.attrs[i + 1];
|
590 | res += `[${name}${value ? '=' + value : ''}]`;
|
591 | }
|
592 | }
|
593 | this.notSelectors.forEach(notSelector => res += `:not(${notSelector})`);
|
594 | return res;
|
595 | }
|
596 | }
|
597 | |
598 |
|
599 |
|
600 |
|
601 | class SelectorMatcher {
|
602 | constructor() {
|
603 | this._elementMap = new Map();
|
604 | this._elementPartialMap = new Map();
|
605 | this._classMap = new Map();
|
606 | this._classPartialMap = new Map();
|
607 | this._attrValueMap = new Map();
|
608 | this._attrValuePartialMap = new Map();
|
609 | this._listContexts = [];
|
610 | }
|
611 | static createNotMatcher(notSelectors) {
|
612 | const notMatcher = new SelectorMatcher();
|
613 | notMatcher.addSelectables(notSelectors, null);
|
614 | return notMatcher;
|
615 | }
|
616 | addSelectables(cssSelectors, callbackCtxt) {
|
617 | let listContext = null;
|
618 | if (cssSelectors.length > 1) {
|
619 | listContext = new SelectorListContext(cssSelectors);
|
620 | this._listContexts.push(listContext);
|
621 | }
|
622 | for (let i = 0; i < cssSelectors.length; i++) {
|
623 | this._addSelectable(cssSelectors[i], callbackCtxt, listContext);
|
624 | }
|
625 | }
|
626 | |
627 |
|
628 |
|
629 |
|
630 |
|
631 | _addSelectable(cssSelector, callbackCtxt, listContext) {
|
632 | let matcher = this;
|
633 | const element = cssSelector.element;
|
634 | const classNames = cssSelector.classNames;
|
635 | const attrs = cssSelector.attrs;
|
636 | const selectable = new SelectorContext(cssSelector, callbackCtxt, listContext);
|
637 | if (element) {
|
638 | const isTerminal = attrs.length === 0 && classNames.length === 0;
|
639 | if (isTerminal) {
|
640 | this._addTerminal(matcher._elementMap, element, selectable);
|
641 | }
|
642 | else {
|
643 | matcher = this._addPartial(matcher._elementPartialMap, element);
|
644 | }
|
645 | }
|
646 | if (classNames) {
|
647 | for (let i = 0; i < classNames.length; i++) {
|
648 | const isTerminal = attrs.length === 0 && i === classNames.length - 1;
|
649 | const className = classNames[i];
|
650 | if (isTerminal) {
|
651 | this._addTerminal(matcher._classMap, className, selectable);
|
652 | }
|
653 | else {
|
654 | matcher = this._addPartial(matcher._classPartialMap, className);
|
655 | }
|
656 | }
|
657 | }
|
658 | if (attrs) {
|
659 | for (let i = 0; i < attrs.length; i += 2) {
|
660 | const isTerminal = i === attrs.length - 2;
|
661 | const name = attrs[i];
|
662 | const value = attrs[i + 1];
|
663 | if (isTerminal) {
|
664 | const terminalMap = matcher._attrValueMap;
|
665 | let terminalValuesMap = terminalMap.get(name);
|
666 | if (!terminalValuesMap) {
|
667 | terminalValuesMap = new Map();
|
668 | terminalMap.set(name, terminalValuesMap);
|
669 | }
|
670 | this._addTerminal(terminalValuesMap, value, selectable);
|
671 | }
|
672 | else {
|
673 | const partialMap = matcher._attrValuePartialMap;
|
674 | let partialValuesMap = partialMap.get(name);
|
675 | if (!partialValuesMap) {
|
676 | partialValuesMap = new Map();
|
677 | partialMap.set(name, partialValuesMap);
|
678 | }
|
679 | matcher = this._addPartial(partialValuesMap, value);
|
680 | }
|
681 | }
|
682 | }
|
683 | }
|
684 | _addTerminal(map, name, selectable) {
|
685 | let terminalList = map.get(name);
|
686 | if (!terminalList) {
|
687 | terminalList = [];
|
688 | map.set(name, terminalList);
|
689 | }
|
690 | terminalList.push(selectable);
|
691 | }
|
692 | _addPartial(map, name) {
|
693 | let matcher = map.get(name);
|
694 | if (!matcher) {
|
695 | matcher = new SelectorMatcher();
|
696 | map.set(name, matcher);
|
697 | }
|
698 | return matcher;
|
699 | }
|
700 | |
701 |
|
702 |
|
703 |
|
704 |
|
705 |
|
706 |
|
707 | match(cssSelector, matchedCallback) {
|
708 | let result = false;
|
709 | const element = cssSelector.element;
|
710 | const classNames = cssSelector.classNames;
|
711 | const attrs = cssSelector.attrs;
|
712 | for (let i = 0; i < this._listContexts.length; i++) {
|
713 | this._listContexts[i].alreadyMatched = false;
|
714 | }
|
715 | result = this._matchTerminal(this._elementMap, element, cssSelector, matchedCallback) || result;
|
716 | result = this._matchPartial(this._elementPartialMap, element, cssSelector, matchedCallback) ||
|
717 | result;
|
718 | if (classNames) {
|
719 | for (let i = 0; i < classNames.length; i++) {
|
720 | const className = classNames[i];
|
721 | result =
|
722 | this._matchTerminal(this._classMap, className, cssSelector, matchedCallback) || result;
|
723 | result =
|
724 | this._matchPartial(this._classPartialMap, className, cssSelector, matchedCallback) ||
|
725 | result;
|
726 | }
|
727 | }
|
728 | if (attrs) {
|
729 | for (let i = 0; i < attrs.length; i += 2) {
|
730 | const name = attrs[i];
|
731 | const value = attrs[i + 1];
|
732 | const terminalValuesMap = this._attrValueMap.get(name);
|
733 | if (value) {
|
734 | result =
|
735 | this._matchTerminal(terminalValuesMap, '', cssSelector, matchedCallback) || result;
|
736 | }
|
737 | result =
|
738 | this._matchTerminal(terminalValuesMap, value, cssSelector, matchedCallback) || result;
|
739 | const partialValuesMap = this._attrValuePartialMap.get(name);
|
740 | if (value) {
|
741 | result = this._matchPartial(partialValuesMap, '', cssSelector, matchedCallback) || result;
|
742 | }
|
743 | result =
|
744 | this._matchPartial(partialValuesMap, value, cssSelector, matchedCallback) || result;
|
745 | }
|
746 | }
|
747 | return result;
|
748 | }
|
749 |
|
750 | _matchTerminal(map, name, cssSelector, matchedCallback) {
|
751 | if (!map || typeof name !== 'string') {
|
752 | return false;
|
753 | }
|
754 | let selectables = map.get(name) || [];
|
755 | const starSelectables = map.get('*');
|
756 | if (starSelectables) {
|
757 | selectables = selectables.concat(starSelectables);
|
758 | }
|
759 | if (selectables.length === 0) {
|
760 | return false;
|
761 | }
|
762 | let selectable;
|
763 | let result = false;
|
764 | for (let i = 0; i < selectables.length; i++) {
|
765 | selectable = selectables[i];
|
766 | result = selectable.finalize(cssSelector, matchedCallback) || result;
|
767 | }
|
768 | return result;
|
769 | }
|
770 |
|
771 | _matchPartial(map, name, cssSelector, matchedCallback) {
|
772 | if (!map || typeof name !== 'string') {
|
773 | return false;
|
774 | }
|
775 | const nestedSelector = map.get(name);
|
776 | if (!nestedSelector) {
|
777 | return false;
|
778 | }
|
779 |
|
780 |
|
781 |
|
782 | return nestedSelector.match(cssSelector, matchedCallback);
|
783 | }
|
784 | }
|
785 | class SelectorListContext {
|
786 | constructor(selectors) {
|
787 | this.selectors = selectors;
|
788 | this.alreadyMatched = false;
|
789 | }
|
790 | }
|
791 |
|
792 | class SelectorContext {
|
793 | constructor(selector, cbContext, listContext) {
|
794 | this.selector = selector;
|
795 | this.cbContext = cbContext;
|
796 | this.listContext = listContext;
|
797 | this.notSelectors = selector.notSelectors;
|
798 | }
|
799 | finalize(cssSelector, callback) {
|
800 | let result = true;
|
801 | if (this.notSelectors.length > 0 && (!this.listContext || !this.listContext.alreadyMatched)) {
|
802 | const notMatcher = SelectorMatcher.createNotMatcher(this.notSelectors);
|
803 | result = !notMatcher.match(cssSelector, null);
|
804 | }
|
805 | if (result && callback && (!this.listContext || !this.listContext.alreadyMatched)) {
|
806 | if (this.listContext) {
|
807 | this.listContext.alreadyMatched = true;
|
808 | }
|
809 | callback(this.selector, this.cbContext);
|
810 | }
|
811 | return result;
|
812 | }
|
813 | }
|
814 |
|
815 | |
816 |
|
817 |
|
818 |
|
819 |
|
820 |
|
821 |
|
822 | const createInject = makeMetadataFactory('Inject', (token) => ({ token }));
|
823 | const createInjectionToken = makeMetadataFactory('InjectionToken', (desc) => ({ _desc: desc, ɵprov: undefined }));
|
824 | const createAttribute = makeMetadataFactory('Attribute', (attributeName) => ({ attributeName }));
|
825 |
|
826 |
|
827 |
|
828 | const emitDistinctChangesOnlyDefaultValue = false;
|
829 | const createContentChildren = makeMetadataFactory('ContentChildren', (selector, data = {}) => (Object.assign({ selector, first: false, isViewQuery: false, descendants: false, emitDistinctChangesOnly: emitDistinctChangesOnlyDefaultValue }, data)));
|
830 | const createContentChild = makeMetadataFactory('ContentChild', (selector, data = {}) => (Object.assign({ selector, first: true, isViewQuery: false, descendants: true }, data)));
|
831 | const createViewChildren = makeMetadataFactory('ViewChildren', (selector, data = {}) => (Object.assign({ selector, first: false, isViewQuery: true, descendants: true, emitDistinctChangesOnly: emitDistinctChangesOnlyDefaultValue }, data)));
|
832 | const createViewChild = makeMetadataFactory('ViewChild', (selector, data) => (Object.assign({ selector, first: true, isViewQuery: true, descendants: true }, data)));
|
833 | const createDirective = makeMetadataFactory('Directive', (dir = {}) => dir);
|
834 | var ViewEncapsulation;
|
835 | (function (ViewEncapsulation) {
|
836 | ViewEncapsulation[ViewEncapsulation["Emulated"] = 0] = "Emulated";
|
837 |
|
838 | ViewEncapsulation[ViewEncapsulation["None"] = 2] = "None";
|
839 | ViewEncapsulation[ViewEncapsulation["ShadowDom"] = 3] = "ShadowDom";
|
840 | })(ViewEncapsulation || (ViewEncapsulation = {}));
|
841 | var ChangeDetectionStrategy;
|
842 | (function (ChangeDetectionStrategy) {
|
843 | ChangeDetectionStrategy[ChangeDetectionStrategy["OnPush"] = 0] = "OnPush";
|
844 | ChangeDetectionStrategy[ChangeDetectionStrategy["Default"] = 1] = "Default";
|
845 | })(ChangeDetectionStrategy || (ChangeDetectionStrategy = {}));
|
846 | const createComponent = makeMetadataFactory('Component', (c = {}) => (Object.assign({ changeDetection: ChangeDetectionStrategy.Default }, c)));
|
847 | const createPipe = makeMetadataFactory('Pipe', (p) => (Object.assign({ pure: true }, p)));
|
848 | const createInput = makeMetadataFactory('Input', (bindingPropertyName) => ({ bindingPropertyName }));
|
849 | const createOutput = makeMetadataFactory('Output', (bindingPropertyName) => ({ bindingPropertyName }));
|
850 | const createHostBinding = makeMetadataFactory('HostBinding', (hostPropertyName) => ({ hostPropertyName }));
|
851 | const createHostListener = makeMetadataFactory('HostListener', (eventName, args) => ({ eventName, args }));
|
852 | const createNgModule = makeMetadataFactory('NgModule', (ngModule) => ngModule);
|
853 | const createInjectable = makeMetadataFactory('Injectable', (injectable = {}) => injectable);
|
854 | const CUSTOM_ELEMENTS_SCHEMA = {
|
855 | name: 'custom-elements'
|
856 | };
|
857 | const NO_ERRORS_SCHEMA = {
|
858 | name: 'no-errors-schema'
|
859 | };
|
860 | const createOptional = makeMetadataFactory('Optional');
|
861 | const createSelf = makeMetadataFactory('Self');
|
862 | const createSkipSelf = makeMetadataFactory('SkipSelf');
|
863 | const createHost = makeMetadataFactory('Host');
|
864 | const Type = Function;
|
865 | var SecurityContext;
|
866 | (function (SecurityContext) {
|
867 | SecurityContext[SecurityContext["NONE"] = 0] = "NONE";
|
868 | SecurityContext[SecurityContext["HTML"] = 1] = "HTML";
|
869 | SecurityContext[SecurityContext["STYLE"] = 2] = "STYLE";
|
870 | SecurityContext[SecurityContext["SCRIPT"] = 3] = "SCRIPT";
|
871 | SecurityContext[SecurityContext["URL"] = 4] = "URL";
|
872 | SecurityContext[SecurityContext["RESOURCE_URL"] = 5] = "RESOURCE_URL";
|
873 | })(SecurityContext || (SecurityContext = {}));
|
874 | var MissingTranslationStrategy;
|
875 | (function (MissingTranslationStrategy) {
|
876 | MissingTranslationStrategy[MissingTranslationStrategy["Error"] = 0] = "Error";
|
877 | MissingTranslationStrategy[MissingTranslationStrategy["Warning"] = 1] = "Warning";
|
878 | MissingTranslationStrategy[MissingTranslationStrategy["Ignore"] = 2] = "Ignore";
|
879 | })(MissingTranslationStrategy || (MissingTranslationStrategy = {}));
|
880 | function makeMetadataFactory(name, props) {
|
881 |
|
882 |
|
883 |
|
884 |
|
885 | function factory(...args) {
|
886 | const values = props ? props(...args) : {};
|
887 | return Object.assign({ ngMetadataName: name }, values);
|
888 | }
|
889 | factory.isTypeOf = (obj) => obj && obj.ngMetadataName === name;
|
890 | factory.ngMetadataName = name;
|
891 | return factory;
|
892 | }
|
893 | function parserSelectorToSimpleSelector(selector) {
|
894 | const classes = selector.classNames && selector.classNames.length ?
|
895 | [8 , ...selector.classNames] :
|
896 | [];
|
897 | const elementName = selector.element && selector.element !== '*' ? selector.element : '';
|
898 | return [elementName, ...selector.attrs, ...classes];
|
899 | }
|
900 | function parserSelectorToNegativeSelector(selector) {
|
901 | const classes = selector.classNames && selector.classNames.length ?
|
902 | [8 , ...selector.classNames] :
|
903 | [];
|
904 | if (selector.element) {
|
905 | return [
|
906 | 1 | 4 , selector.element, ...selector.attrs, ...classes
|
907 | ];
|
908 | }
|
909 | else if (selector.attrs.length) {
|
910 | return [1 | 2 , ...selector.attrs, ...classes];
|
911 | }
|
912 | else {
|
913 | return selector.classNames && selector.classNames.length ?
|
914 | [1 | 8 , ...selector.classNames] :
|
915 | [];
|
916 | }
|
917 | }
|
918 | function parserSelectorToR3Selector(selector) {
|
919 | const positive = parserSelectorToSimpleSelector(selector);
|
920 | const negative = selector.notSelectors && selector.notSelectors.length ?
|
921 | selector.notSelectors.map(notSelector => parserSelectorToNegativeSelector(notSelector)) :
|
922 | [];
|
923 | return positive.concat(...negative);
|
924 | }
|
925 | function parseSelectorToR3Selector(selector) {
|
926 | return selector ? CssSelector.parse(selector).map(parserSelectorToR3Selector) : [];
|
927 | }
|
928 |
|
929 | |
930 |
|
931 |
|
932 |
|
933 |
|
934 |
|
935 |
|
936 |
|
937 | var TypeModifier;
|
938 | (function (TypeModifier) {
|
939 | TypeModifier[TypeModifier["Const"] = 0] = "Const";
|
940 | })(TypeModifier || (TypeModifier = {}));
|
941 | class Type$1 {
|
942 | constructor(modifiers = []) {
|
943 | this.modifiers = modifiers;
|
944 | }
|
945 | hasModifier(modifier) {
|
946 | return this.modifiers.indexOf(modifier) !== -1;
|
947 | }
|
948 | }
|
949 | var BuiltinTypeName;
|
950 | (function (BuiltinTypeName) {
|
951 | BuiltinTypeName[BuiltinTypeName["Dynamic"] = 0] = "Dynamic";
|
952 | BuiltinTypeName[BuiltinTypeName["Bool"] = 1] = "Bool";
|
953 | BuiltinTypeName[BuiltinTypeName["String"] = 2] = "String";
|
954 | BuiltinTypeName[BuiltinTypeName["Int"] = 3] = "Int";
|
955 | BuiltinTypeName[BuiltinTypeName["Number"] = 4] = "Number";
|
956 | BuiltinTypeName[BuiltinTypeName["Function"] = 5] = "Function";
|
957 | BuiltinTypeName[BuiltinTypeName["Inferred"] = 6] = "Inferred";
|
958 | BuiltinTypeName[BuiltinTypeName["None"] = 7] = "None";
|
959 | })(BuiltinTypeName || (BuiltinTypeName = {}));
|
960 | class BuiltinType extends Type$1 {
|
961 | constructor(name, modifiers) {
|
962 | super(modifiers);
|
963 | this.name = name;
|
964 | }
|
965 | visitType(visitor, context) {
|
966 | return visitor.visitBuiltinType(this, context);
|
967 | }
|
968 | }
|
969 | class ExpressionType extends Type$1 {
|
970 | constructor(value, modifiers, typeParams = null) {
|
971 | super(modifiers);
|
972 | this.value = value;
|
973 | this.typeParams = typeParams;
|
974 | }
|
975 | visitType(visitor, context) {
|
976 | return visitor.visitExpressionType(this, context);
|
977 | }
|
978 | }
|
979 | const DYNAMIC_TYPE = new BuiltinType(BuiltinTypeName.Dynamic);
|
980 | const INFERRED_TYPE = new BuiltinType(BuiltinTypeName.Inferred);
|
981 | const BOOL_TYPE = new BuiltinType(BuiltinTypeName.Bool);
|
982 | const INT_TYPE = new BuiltinType(BuiltinTypeName.Int);
|
983 | const NUMBER_TYPE = new BuiltinType(BuiltinTypeName.Number);
|
984 | const STRING_TYPE = new BuiltinType(BuiltinTypeName.String);
|
985 | const FUNCTION_TYPE = new BuiltinType(BuiltinTypeName.Function);
|
986 | const NONE_TYPE = new BuiltinType(BuiltinTypeName.None);
|
987 |
|
988 | var UnaryOperator;
|
989 | (function (UnaryOperator) {
|
990 | UnaryOperator[UnaryOperator["Minus"] = 0] = "Minus";
|
991 | UnaryOperator[UnaryOperator["Plus"] = 1] = "Plus";
|
992 | })(UnaryOperator || (UnaryOperator = {}));
|
993 | var BinaryOperator;
|
994 | (function (BinaryOperator) {
|
995 | BinaryOperator[BinaryOperator["Equals"] = 0] = "Equals";
|
996 | BinaryOperator[BinaryOperator["NotEquals"] = 1] = "NotEquals";
|
997 | BinaryOperator[BinaryOperator["Identical"] = 2] = "Identical";
|
998 | BinaryOperator[BinaryOperator["NotIdentical"] = 3] = "NotIdentical";
|
999 | BinaryOperator[BinaryOperator["Minus"] = 4] = "Minus";
|
1000 | BinaryOperator[BinaryOperator["Plus"] = 5] = "Plus";
|
1001 | BinaryOperator[BinaryOperator["Divide"] = 6] = "Divide";
|
1002 | BinaryOperator[BinaryOperator["Multiply"] = 7] = "Multiply";
|
1003 | BinaryOperator[BinaryOperator["Modulo"] = 8] = "Modulo";
|
1004 | BinaryOperator[BinaryOperator["And"] = 9] = "And";
|
1005 | BinaryOperator[BinaryOperator["Or"] = 10] = "Or";
|
1006 | BinaryOperator[BinaryOperator["BitwiseAnd"] = 11] = "BitwiseAnd";
|
1007 | BinaryOperator[BinaryOperator["Lower"] = 12] = "Lower";
|
1008 | BinaryOperator[BinaryOperator["LowerEquals"] = 13] = "LowerEquals";
|
1009 | BinaryOperator[BinaryOperator["Bigger"] = 14] = "Bigger";
|
1010 | BinaryOperator[BinaryOperator["BiggerEquals"] = 15] = "BiggerEquals";
|
1011 | })(BinaryOperator || (BinaryOperator = {}));
|
1012 | function nullSafeIsEquivalent(base, other) {
|
1013 | if (base == null || other == null) {
|
1014 | return base == other;
|
1015 | }
|
1016 | return base.isEquivalent(other);
|
1017 | }
|
1018 | function areAllEquivalentPredicate(base, other, equivalentPredicate) {
|
1019 | const len = base.length;
|
1020 | if (len !== other.length) {
|
1021 | return false;
|
1022 | }
|
1023 | for (let i = 0; i < len; i++) {
|
1024 | if (!equivalentPredicate(base[i], other[i])) {
|
1025 | return false;
|
1026 | }
|
1027 | }
|
1028 | return true;
|
1029 | }
|
1030 | function areAllEquivalent(base, other) {
|
1031 | return areAllEquivalentPredicate(base, other, (baseElement, otherElement) => baseElement.isEquivalent(otherElement));
|
1032 | }
|
1033 | class Expression {
|
1034 | constructor(type, sourceSpan) {
|
1035 | this.type = type || null;
|
1036 | this.sourceSpan = sourceSpan || null;
|
1037 | }
|
1038 | prop(name, sourceSpan) {
|
1039 | return new ReadPropExpr(this, name, null, sourceSpan);
|
1040 | }
|
1041 | key(index, type, sourceSpan) {
|
1042 | return new ReadKeyExpr(this, index, type, sourceSpan);
|
1043 | }
|
1044 | callMethod(name, params, sourceSpan) {
|
1045 | return new InvokeMethodExpr(this, name, params, null, sourceSpan);
|
1046 | }
|
1047 | callFn(params, sourceSpan, pure) {
|
1048 | return new InvokeFunctionExpr(this, params, null, sourceSpan, pure);
|
1049 | }
|
1050 | instantiate(params, type, sourceSpan) {
|
1051 | return new InstantiateExpr(this, params, type, sourceSpan);
|
1052 | }
|
1053 | conditional(trueCase, falseCase = null, sourceSpan) {
|
1054 | return new ConditionalExpr(this, trueCase, falseCase, null, sourceSpan);
|
1055 | }
|
1056 | equals(rhs, sourceSpan) {
|
1057 | return new BinaryOperatorExpr(BinaryOperator.Equals, this, rhs, null, sourceSpan);
|
1058 | }
|
1059 | notEquals(rhs, sourceSpan) {
|
1060 | return new BinaryOperatorExpr(BinaryOperator.NotEquals, this, rhs, null, sourceSpan);
|
1061 | }
|
1062 | identical(rhs, sourceSpan) {
|
1063 | return new BinaryOperatorExpr(BinaryOperator.Identical, this, rhs, null, sourceSpan);
|
1064 | }
|
1065 | notIdentical(rhs, sourceSpan) {
|
1066 | return new BinaryOperatorExpr(BinaryOperator.NotIdentical, this, rhs, null, sourceSpan);
|
1067 | }
|
1068 | minus(rhs, sourceSpan) {
|
1069 | return new BinaryOperatorExpr(BinaryOperator.Minus, this, rhs, null, sourceSpan);
|
1070 | }
|
1071 | plus(rhs, sourceSpan) {
|
1072 | return new BinaryOperatorExpr(BinaryOperator.Plus, this, rhs, null, sourceSpan);
|
1073 | }
|
1074 | divide(rhs, sourceSpan) {
|
1075 | return new BinaryOperatorExpr(BinaryOperator.Divide, this, rhs, null, sourceSpan);
|
1076 | }
|
1077 | multiply(rhs, sourceSpan) {
|
1078 | return new BinaryOperatorExpr(BinaryOperator.Multiply, this, rhs, null, sourceSpan);
|
1079 | }
|
1080 | modulo(rhs, sourceSpan) {
|
1081 | return new BinaryOperatorExpr(BinaryOperator.Modulo, this, rhs, null, sourceSpan);
|
1082 | }
|
1083 | and(rhs, sourceSpan) {
|
1084 | return new BinaryOperatorExpr(BinaryOperator.And, this, rhs, null, sourceSpan);
|
1085 | }
|
1086 | bitwiseAnd(rhs, sourceSpan, parens = true) {
|
1087 | return new BinaryOperatorExpr(BinaryOperator.BitwiseAnd, this, rhs, null, sourceSpan, parens);
|
1088 | }
|
1089 | or(rhs, sourceSpan) {
|
1090 | return new BinaryOperatorExpr(BinaryOperator.Or, this, rhs, null, sourceSpan);
|
1091 | }
|
1092 | lower(rhs, sourceSpan) {
|
1093 | return new BinaryOperatorExpr(BinaryOperator.Lower, this, rhs, null, sourceSpan);
|
1094 | }
|
1095 | lowerEquals(rhs, sourceSpan) {
|
1096 | return new BinaryOperatorExpr(BinaryOperator.LowerEquals, this, rhs, null, sourceSpan);
|
1097 | }
|
1098 | bigger(rhs, sourceSpan) {
|
1099 | return new BinaryOperatorExpr(BinaryOperator.Bigger, this, rhs, null, sourceSpan);
|
1100 | }
|
1101 | biggerEquals(rhs, sourceSpan) {
|
1102 | return new BinaryOperatorExpr(BinaryOperator.BiggerEquals, this, rhs, null, sourceSpan);
|
1103 | }
|
1104 | isBlank(sourceSpan) {
|
1105 |
|
1106 |
|
1107 | return this.equals(TYPED_NULL_EXPR, sourceSpan);
|
1108 | }
|
1109 | cast(type, sourceSpan) {
|
1110 | return new CastExpr(this, type, sourceSpan);
|
1111 | }
|
1112 | toStmt() {
|
1113 | return new ExpressionStatement(this, null);
|
1114 | }
|
1115 | }
|
1116 | var BuiltinVar;
|
1117 | (function (BuiltinVar) {
|
1118 | BuiltinVar[BuiltinVar["This"] = 0] = "This";
|
1119 | BuiltinVar[BuiltinVar["Super"] = 1] = "Super";
|
1120 | BuiltinVar[BuiltinVar["CatchError"] = 2] = "CatchError";
|
1121 | BuiltinVar[BuiltinVar["CatchStack"] = 3] = "CatchStack";
|
1122 | })(BuiltinVar || (BuiltinVar = {}));
|
1123 | class ReadVarExpr extends Expression {
|
1124 | constructor(name, type, sourceSpan) {
|
1125 | super(type, sourceSpan);
|
1126 | if (typeof name === 'string') {
|
1127 | this.name = name;
|
1128 | this.builtin = null;
|
1129 | }
|
1130 | else {
|
1131 | this.name = null;
|
1132 | this.builtin = name;
|
1133 | }
|
1134 | }
|
1135 | isEquivalent(e) {
|
1136 | return e instanceof ReadVarExpr && this.name === e.name && this.builtin === e.builtin;
|
1137 | }
|
1138 | isConstant() {
|
1139 | return false;
|
1140 | }
|
1141 | visitExpression(visitor, context) {
|
1142 | return visitor.visitReadVarExpr(this, context);
|
1143 | }
|
1144 | set(value) {
|
1145 | if (!this.name) {
|
1146 | throw new Error(`Built in variable ${this.builtin} can not be assigned to.`);
|
1147 | }
|
1148 | return new WriteVarExpr(this.name, value, null, this.sourceSpan);
|
1149 | }
|
1150 | }
|
1151 | class TypeofExpr extends Expression {
|
1152 | constructor(expr, type, sourceSpan) {
|
1153 | super(type, sourceSpan);
|
1154 | this.expr = expr;
|
1155 | }
|
1156 | visitExpression(visitor, context) {
|
1157 | return visitor.visitTypeofExpr(this, context);
|
1158 | }
|
1159 | isEquivalent(e) {
|
1160 | return e instanceof TypeofExpr && e.expr.isEquivalent(this.expr);
|
1161 | }
|
1162 | isConstant() {
|
1163 | return this.expr.isConstant();
|
1164 | }
|
1165 | }
|
1166 | class WrappedNodeExpr extends Expression {
|
1167 | constructor(node, type, sourceSpan) {
|
1168 | super(type, sourceSpan);
|
1169 | this.node = node;
|
1170 | }
|
1171 | isEquivalent(e) {
|
1172 | return e instanceof WrappedNodeExpr && this.node === e.node;
|
1173 | }
|
1174 | isConstant() {
|
1175 | return false;
|
1176 | }
|
1177 | visitExpression(visitor, context) {
|
1178 | return visitor.visitWrappedNodeExpr(this, context);
|
1179 | }
|
1180 | }
|
1181 | class WriteVarExpr extends Expression {
|
1182 | constructor(name, value, type, sourceSpan) {
|
1183 | super(type || value.type, sourceSpan);
|
1184 | this.name = name;
|
1185 | this.value = value;
|
1186 | }
|
1187 | isEquivalent(e) {
|
1188 | return e instanceof WriteVarExpr && this.name === e.name && this.value.isEquivalent(e.value);
|
1189 | }
|
1190 | isConstant() {
|
1191 | return false;
|
1192 | }
|
1193 | visitExpression(visitor, context) {
|
1194 | return visitor.visitWriteVarExpr(this, context);
|
1195 | }
|
1196 | toDeclStmt(type, modifiers) {
|
1197 | return new DeclareVarStmt(this.name, this.value, type, modifiers, this.sourceSpan);
|
1198 | }
|
1199 | toConstDecl() {
|
1200 | return this.toDeclStmt(INFERRED_TYPE, [StmtModifier.Final]);
|
1201 | }
|
1202 | }
|
1203 | class WriteKeyExpr extends Expression {
|
1204 | constructor(receiver, index, value, type, sourceSpan) {
|
1205 | super(type || value.type, sourceSpan);
|
1206 | this.receiver = receiver;
|
1207 | this.index = index;
|
1208 | this.value = value;
|
1209 | }
|
1210 | isEquivalent(e) {
|
1211 | return e instanceof WriteKeyExpr && this.receiver.isEquivalent(e.receiver) &&
|
1212 | this.index.isEquivalent(e.index) && this.value.isEquivalent(e.value);
|
1213 | }
|
1214 | isConstant() {
|
1215 | return false;
|
1216 | }
|
1217 | visitExpression(visitor, context) {
|
1218 | return visitor.visitWriteKeyExpr(this, context);
|
1219 | }
|
1220 | }
|
1221 | class WritePropExpr extends Expression {
|
1222 | constructor(receiver, name, value, type, sourceSpan) {
|
1223 | super(type || value.type, sourceSpan);
|
1224 | this.receiver = receiver;
|
1225 | this.name = name;
|
1226 | this.value = value;
|
1227 | }
|
1228 | isEquivalent(e) {
|
1229 | return e instanceof WritePropExpr && this.receiver.isEquivalent(e.receiver) &&
|
1230 | this.name === e.name && this.value.isEquivalent(e.value);
|
1231 | }
|
1232 | isConstant() {
|
1233 | return false;
|
1234 | }
|
1235 | visitExpression(visitor, context) {
|
1236 | return visitor.visitWritePropExpr(this, context);
|
1237 | }
|
1238 | }
|
1239 | var BuiltinMethod;
|
1240 | (function (BuiltinMethod) {
|
1241 | BuiltinMethod[BuiltinMethod["ConcatArray"] = 0] = "ConcatArray";
|
1242 | BuiltinMethod[BuiltinMethod["SubscribeObservable"] = 1] = "SubscribeObservable";
|
1243 | BuiltinMethod[BuiltinMethod["Bind"] = 2] = "Bind";
|
1244 | })(BuiltinMethod || (BuiltinMethod = {}));
|
1245 | class InvokeMethodExpr extends Expression {
|
1246 | constructor(receiver, method, args, type, sourceSpan) {
|
1247 | super(type, sourceSpan);
|
1248 | this.receiver = receiver;
|
1249 | this.args = args;
|
1250 | if (typeof method === 'string') {
|
1251 | this.name = method;
|
1252 | this.builtin = null;
|
1253 | }
|
1254 | else {
|
1255 | this.name = null;
|
1256 | this.builtin = method;
|
1257 | }
|
1258 | }
|
1259 | isEquivalent(e) {
|
1260 | return e instanceof InvokeMethodExpr && this.receiver.isEquivalent(e.receiver) &&
|
1261 | this.name === e.name && this.builtin === e.builtin && areAllEquivalent(this.args, e.args);
|
1262 | }
|
1263 | isConstant() {
|
1264 | return false;
|
1265 | }
|
1266 | visitExpression(visitor, context) {
|
1267 | return visitor.visitInvokeMethodExpr(this, context);
|
1268 | }
|
1269 | }
|
1270 | class InvokeFunctionExpr extends Expression {
|
1271 | constructor(fn, args, type, sourceSpan, pure = false) {
|
1272 | super(type, sourceSpan);
|
1273 | this.fn = fn;
|
1274 | this.args = args;
|
1275 | this.pure = pure;
|
1276 | }
|
1277 | isEquivalent(e) {
|
1278 | return e instanceof InvokeFunctionExpr && this.fn.isEquivalent(e.fn) &&
|
1279 | areAllEquivalent(this.args, e.args) && this.pure === e.pure;
|
1280 | }
|
1281 | isConstant() {
|
1282 | return false;
|
1283 | }
|
1284 | visitExpression(visitor, context) {
|
1285 | return visitor.visitInvokeFunctionExpr(this, context);
|
1286 | }
|
1287 | }
|
1288 | class TaggedTemplateExpr extends Expression {
|
1289 | constructor(tag, template, type, sourceSpan) {
|
1290 | super(type, sourceSpan);
|
1291 | this.tag = tag;
|
1292 | this.template = template;
|
1293 | }
|
1294 | isEquivalent(e) {
|
1295 | return e instanceof TaggedTemplateExpr && this.tag.isEquivalent(e.tag) &&
|
1296 | areAllEquivalentPredicate(this.template.elements, e.template.elements, (a, b) => a.text === b.text) &&
|
1297 | areAllEquivalent(this.template.expressions, e.template.expressions);
|
1298 | }
|
1299 | isConstant() {
|
1300 | return false;
|
1301 | }
|
1302 | visitExpression(visitor, context) {
|
1303 | return visitor.visitTaggedTemplateExpr(this, context);
|
1304 | }
|
1305 | }
|
1306 | class InstantiateExpr extends Expression {
|
1307 | constructor(classExpr, args, type, sourceSpan) {
|
1308 | super(type, sourceSpan);
|
1309 | this.classExpr = classExpr;
|
1310 | this.args = args;
|
1311 | }
|
1312 | isEquivalent(e) {
|
1313 | return e instanceof InstantiateExpr && this.classExpr.isEquivalent(e.classExpr) &&
|
1314 | areAllEquivalent(this.args, e.args);
|
1315 | }
|
1316 | isConstant() {
|
1317 | return false;
|
1318 | }
|
1319 | visitExpression(visitor, context) {
|
1320 | return visitor.visitInstantiateExpr(this, context);
|
1321 | }
|
1322 | }
|
1323 | class LiteralExpr extends Expression {
|
1324 | constructor(value, type, sourceSpan) {
|
1325 | super(type, sourceSpan);
|
1326 | this.value = value;
|
1327 | }
|
1328 | isEquivalent(e) {
|
1329 | return e instanceof LiteralExpr && this.value === e.value;
|
1330 | }
|
1331 | isConstant() {
|
1332 | return true;
|
1333 | }
|
1334 | visitExpression(visitor, context) {
|
1335 | return visitor.visitLiteralExpr(this, context);
|
1336 | }
|
1337 | }
|
1338 | class TemplateLiteral {
|
1339 | constructor(elements, expressions) {
|
1340 | this.elements = elements;
|
1341 | this.expressions = expressions;
|
1342 | }
|
1343 | }
|
1344 | class TemplateLiteralElement {
|
1345 | constructor(text, sourceSpan, rawText) {
|
1346 | var _a;
|
1347 | this.text = text;
|
1348 | this.sourceSpan = sourceSpan;
|
1349 |
|
1350 |
|
1351 |
|
1352 |
|
1353 |
|
1354 |
|
1355 | 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));
|
1356 | }
|
1357 | }
|
1358 | class MessagePiece {
|
1359 | constructor(text, sourceSpan) {
|
1360 | this.text = text;
|
1361 | this.sourceSpan = sourceSpan;
|
1362 | }
|
1363 | }
|
1364 | class LiteralPiece extends MessagePiece {
|
1365 | }
|
1366 | class PlaceholderPiece extends MessagePiece {
|
1367 | }
|
1368 | class LocalizedString extends Expression {
|
1369 | constructor(metaBlock, messageParts, placeHolderNames, expressions, sourceSpan) {
|
1370 | super(STRING_TYPE, sourceSpan);
|
1371 | this.metaBlock = metaBlock;
|
1372 | this.messageParts = messageParts;
|
1373 | this.placeHolderNames = placeHolderNames;
|
1374 | this.expressions = expressions;
|
1375 | }
|
1376 | isEquivalent(e) {
|
1377 |
|
1378 | return false;
|
1379 | }
|
1380 | isConstant() {
|
1381 | return false;
|
1382 | }
|
1383 | visitExpression(visitor, context) {
|
1384 | return visitor.visitLocalizedString(this, context);
|
1385 | }
|
1386 | |
1387 |
|
1388 |
|
1389 |
|
1390 |
|
1391 |
|
1392 |
|
1393 |
|
1394 | serializeI18nHead() {
|
1395 | const MEANING_SEPARATOR = '|';
|
1396 | const ID_SEPARATOR = '@@';
|
1397 | const LEGACY_ID_INDICATOR = '␟';
|
1398 | let metaBlock = this.metaBlock.description || '';
|
1399 | if (this.metaBlock.meaning) {
|
1400 | metaBlock = `${this.metaBlock.meaning}${MEANING_SEPARATOR}${metaBlock}`;
|
1401 | }
|
1402 | if (this.metaBlock.customId) {
|
1403 | metaBlock = `${metaBlock}${ID_SEPARATOR}${this.metaBlock.customId}`;
|
1404 | }
|
1405 | if (this.metaBlock.legacyIds) {
|
1406 | this.metaBlock.legacyIds.forEach(legacyId => {
|
1407 | metaBlock = `${metaBlock}${LEGACY_ID_INDICATOR}${legacyId}`;
|
1408 | });
|
1409 | }
|
1410 | return createCookedRawString(metaBlock, this.messageParts[0].text, this.getMessagePartSourceSpan(0));
|
1411 | }
|
1412 | getMessagePartSourceSpan(i) {
|
1413 | var _a, _b;
|
1414 | return (_b = (_a = this.messageParts[i]) === null || _a === void 0 ? void 0 : _a.sourceSpan) !== null && _b !== void 0 ? _b : this.sourceSpan;
|
1415 | }
|
1416 | getPlaceholderSourceSpan(i) {
|
1417 | var _a, _b, _c, _d;
|
1418 | 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;
|
1419 | }
|
1420 | |
1421 |
|
1422 |
|
1423 |
|
1424 |
|
1425 |
|
1426 |
|
1427 | serializeI18nTemplatePart(partIndex) {
|
1428 | const placeholderName = this.placeHolderNames[partIndex - 1].text;
|
1429 | const messagePart = this.messageParts[partIndex];
|
1430 | return createCookedRawString(placeholderName, messagePart.text, this.getMessagePartSourceSpan(partIndex));
|
1431 | }
|
1432 | }
|
1433 | const escapeSlashes = (str) => str.replace(/\\/g, '\\\\');
|
1434 | const escapeStartingColon = (str) => str.replace(/^:/, '\\:');
|
1435 | const escapeColons = (str) => str.replace(/:/g, '\\:');
|
1436 | const escapeForTemplateLiteral = (str) => str.replace(/`/g, '\\`').replace(/\${/g, '$\\{');
|
1437 | |
1438 |
|
1439 |
|
1440 |
|
1441 |
|
1442 |
|
1443 |
|
1444 |
|
1445 |
|
1446 |
|
1447 |
|
1448 |
|
1449 |
|
1450 |
|
1451 | function createCookedRawString(metaBlock, messagePart, range) {
|
1452 | if (metaBlock === '') {
|
1453 | return {
|
1454 | cooked: messagePart,
|
1455 | raw: escapeForTemplateLiteral(escapeStartingColon(escapeSlashes(messagePart))),
|
1456 | range,
|
1457 | };
|
1458 | }
|
1459 | else {
|
1460 | return {
|
1461 | cooked: `:${metaBlock}:${messagePart}`,
|
1462 | raw: escapeForTemplateLiteral(`:${escapeColons(escapeSlashes(metaBlock))}:${escapeSlashes(messagePart)}`),
|
1463 | range,
|
1464 | };
|
1465 | }
|
1466 | }
|
1467 | class ExternalExpr extends Expression {
|
1468 | constructor(value, type, typeParams = null, sourceSpan) {
|
1469 | super(type, sourceSpan);
|
1470 | this.value = value;
|
1471 | this.typeParams = typeParams;
|
1472 | }
|
1473 | isEquivalent(e) {
|
1474 | return e instanceof ExternalExpr && this.value.name === e.value.name &&
|
1475 | this.value.moduleName === e.value.moduleName && this.value.runtime === e.value.runtime;
|
1476 | }
|
1477 | isConstant() {
|
1478 | return false;
|
1479 | }
|
1480 | visitExpression(visitor, context) {
|
1481 | return visitor.visitExternalExpr(this, context);
|
1482 | }
|
1483 | }
|
1484 | class ConditionalExpr extends Expression {
|
1485 | constructor(condition, trueCase, falseCase = null, type, sourceSpan) {
|
1486 | super(type || trueCase.type, sourceSpan);
|
1487 | this.condition = condition;
|
1488 | this.falseCase = falseCase;
|
1489 | this.trueCase = trueCase;
|
1490 | }
|
1491 | isEquivalent(e) {
|
1492 | return e instanceof ConditionalExpr && this.condition.isEquivalent(e.condition) &&
|
1493 | this.trueCase.isEquivalent(e.trueCase) && nullSafeIsEquivalent(this.falseCase, e.falseCase);
|
1494 | }
|
1495 | isConstant() {
|
1496 | return false;
|
1497 | }
|
1498 | visitExpression(visitor, context) {
|
1499 | return visitor.visitConditionalExpr(this, context);
|
1500 | }
|
1501 | }
|
1502 | class NotExpr extends Expression {
|
1503 | constructor(condition, sourceSpan) {
|
1504 | super(BOOL_TYPE, sourceSpan);
|
1505 | this.condition = condition;
|
1506 | }
|
1507 | isEquivalent(e) {
|
1508 | return e instanceof NotExpr && this.condition.isEquivalent(e.condition);
|
1509 | }
|
1510 | isConstant() {
|
1511 | return false;
|
1512 | }
|
1513 | visitExpression(visitor, context) {
|
1514 | return visitor.visitNotExpr(this, context);
|
1515 | }
|
1516 | }
|
1517 | class AssertNotNull extends Expression {
|
1518 | constructor(condition, sourceSpan) {
|
1519 | super(condition.type, sourceSpan);
|
1520 | this.condition = condition;
|
1521 | }
|
1522 | isEquivalent(e) {
|
1523 | return e instanceof AssertNotNull && this.condition.isEquivalent(e.condition);
|
1524 | }
|
1525 | isConstant() {
|
1526 | return false;
|
1527 | }
|
1528 | visitExpression(visitor, context) {
|
1529 | return visitor.visitAssertNotNullExpr(this, context);
|
1530 | }
|
1531 | }
|
1532 | class CastExpr extends Expression {
|
1533 | constructor(value, type, sourceSpan) {
|
1534 | super(type, sourceSpan);
|
1535 | this.value = value;
|
1536 | }
|
1537 | isEquivalent(e) {
|
1538 | return e instanceof CastExpr && this.value.isEquivalent(e.value);
|
1539 | }
|
1540 | isConstant() {
|
1541 | return false;
|
1542 | }
|
1543 | visitExpression(visitor, context) {
|
1544 | return visitor.visitCastExpr(this, context);
|
1545 | }
|
1546 | }
|
1547 | class FnParam {
|
1548 | constructor(name, type = null) {
|
1549 | this.name = name;
|
1550 | this.type = type;
|
1551 | }
|
1552 | isEquivalent(param) {
|
1553 | return this.name === param.name;
|
1554 | }
|
1555 | }
|
1556 | class FunctionExpr extends Expression {
|
1557 | constructor(params, statements, type, sourceSpan, name) {
|
1558 | super(type, sourceSpan);
|
1559 | this.params = params;
|
1560 | this.statements = statements;
|
1561 | this.name = name;
|
1562 | }
|
1563 | isEquivalent(e) {
|
1564 | return e instanceof FunctionExpr && areAllEquivalent(this.params, e.params) &&
|
1565 | areAllEquivalent(this.statements, e.statements);
|
1566 | }
|
1567 | isConstant() {
|
1568 | return false;
|
1569 | }
|
1570 | visitExpression(visitor, context) {
|
1571 | return visitor.visitFunctionExpr(this, context);
|
1572 | }
|
1573 | toDeclStmt(name, modifiers) {
|
1574 | return new DeclareFunctionStmt(name, this.params, this.statements, this.type, modifiers, this.sourceSpan);
|
1575 | }
|
1576 | }
|
1577 | class UnaryOperatorExpr extends Expression {
|
1578 | constructor(operator, expr, type, sourceSpan, parens = true) {
|
1579 | super(type || NUMBER_TYPE, sourceSpan);
|
1580 | this.operator = operator;
|
1581 | this.expr = expr;
|
1582 | this.parens = parens;
|
1583 | }
|
1584 | isEquivalent(e) {
|
1585 | return e instanceof UnaryOperatorExpr && this.operator === e.operator &&
|
1586 | this.expr.isEquivalent(e.expr);
|
1587 | }
|
1588 | isConstant() {
|
1589 | return false;
|
1590 | }
|
1591 | visitExpression(visitor, context) {
|
1592 | return visitor.visitUnaryOperatorExpr(this, context);
|
1593 | }
|
1594 | }
|
1595 | class BinaryOperatorExpr extends Expression {
|
1596 | constructor(operator, lhs, rhs, type, sourceSpan, parens = true) {
|
1597 | super(type || lhs.type, sourceSpan);
|
1598 | this.operator = operator;
|
1599 | this.rhs = rhs;
|
1600 | this.parens = parens;
|
1601 | this.lhs = lhs;
|
1602 | }
|
1603 | isEquivalent(e) {
|
1604 | return e instanceof BinaryOperatorExpr && this.operator === e.operator &&
|
1605 | this.lhs.isEquivalent(e.lhs) && this.rhs.isEquivalent(e.rhs);
|
1606 | }
|
1607 | isConstant() {
|
1608 | return false;
|
1609 | }
|
1610 | visitExpression(visitor, context) {
|
1611 | return visitor.visitBinaryOperatorExpr(this, context);
|
1612 | }
|
1613 | }
|
1614 | class ReadPropExpr extends Expression {
|
1615 | constructor(receiver, name, type, sourceSpan) {
|
1616 | super(type, sourceSpan);
|
1617 | this.receiver = receiver;
|
1618 | this.name = name;
|
1619 | }
|
1620 | isEquivalent(e) {
|
1621 | return e instanceof ReadPropExpr && this.receiver.isEquivalent(e.receiver) &&
|
1622 | this.name === e.name;
|
1623 | }
|
1624 | isConstant() {
|
1625 | return false;
|
1626 | }
|
1627 | visitExpression(visitor, context) {
|
1628 | return visitor.visitReadPropExpr(this, context);
|
1629 | }
|
1630 | set(value) {
|
1631 | return new WritePropExpr(this.receiver, this.name, value, null, this.sourceSpan);
|
1632 | }
|
1633 | }
|
1634 | class ReadKeyExpr extends Expression {
|
1635 | constructor(receiver, index, type, sourceSpan) {
|
1636 | super(type, sourceSpan);
|
1637 | this.receiver = receiver;
|
1638 | this.index = index;
|
1639 | }
|
1640 | isEquivalent(e) {
|
1641 | return e instanceof ReadKeyExpr && this.receiver.isEquivalent(e.receiver) &&
|
1642 | this.index.isEquivalent(e.index);
|
1643 | }
|
1644 | isConstant() {
|
1645 | return false;
|
1646 | }
|
1647 | visitExpression(visitor, context) {
|
1648 | return visitor.visitReadKeyExpr(this, context);
|
1649 | }
|
1650 | set(value) {
|
1651 | return new WriteKeyExpr(this.receiver, this.index, value, null, this.sourceSpan);
|
1652 | }
|
1653 | }
|
1654 | class LiteralArrayExpr extends Expression {
|
1655 | constructor(entries, type, sourceSpan) {
|
1656 | super(type, sourceSpan);
|
1657 | this.entries = entries;
|
1658 | }
|
1659 | isConstant() {
|
1660 | return this.entries.every(e => e.isConstant());
|
1661 | }
|
1662 | isEquivalent(e) {
|
1663 | return e instanceof LiteralArrayExpr && areAllEquivalent(this.entries, e.entries);
|
1664 | }
|
1665 | visitExpression(visitor, context) {
|
1666 | return visitor.visitLiteralArrayExpr(this, context);
|
1667 | }
|
1668 | }
|
1669 | class LiteralMapEntry {
|
1670 | constructor(key, value, quoted) {
|
1671 | this.key = key;
|
1672 | this.value = value;
|
1673 | this.quoted = quoted;
|
1674 | }
|
1675 | isEquivalent(e) {
|
1676 | return this.key === e.key && this.value.isEquivalent(e.value);
|
1677 | }
|
1678 | }
|
1679 | class LiteralMapExpr extends Expression {
|
1680 | constructor(entries, type, sourceSpan) {
|
1681 | super(type, sourceSpan);
|
1682 | this.entries = entries;
|
1683 | this.valueType = null;
|
1684 | if (type) {
|
1685 | this.valueType = type.valueType;
|
1686 | }
|
1687 | }
|
1688 | isEquivalent(e) {
|
1689 | return e instanceof LiteralMapExpr && areAllEquivalent(this.entries, e.entries);
|
1690 | }
|
1691 | isConstant() {
|
1692 | return this.entries.every(e => e.value.isConstant());
|
1693 | }
|
1694 | visitExpression(visitor, context) {
|
1695 | return visitor.visitLiteralMapExpr(this, context);
|
1696 | }
|
1697 | }
|
1698 | const THIS_EXPR = new ReadVarExpr(BuiltinVar.This, null, null);
|
1699 | const SUPER_EXPR = new ReadVarExpr(BuiltinVar.Super, null, null);
|
1700 | const CATCH_ERROR_VAR = new ReadVarExpr(BuiltinVar.CatchError, null, null);
|
1701 | const CATCH_STACK_VAR = new ReadVarExpr(BuiltinVar.CatchStack, null, null);
|
1702 | const NULL_EXPR = new LiteralExpr(null, null, null);
|
1703 | const TYPED_NULL_EXPR = new LiteralExpr(null, INFERRED_TYPE, null);
|
1704 |
|
1705 | var StmtModifier;
|
1706 | (function (StmtModifier) {
|
1707 | StmtModifier[StmtModifier["Final"] = 0] = "Final";
|
1708 | StmtModifier[StmtModifier["Private"] = 1] = "Private";
|
1709 | StmtModifier[StmtModifier["Exported"] = 2] = "Exported";
|
1710 | StmtModifier[StmtModifier["Static"] = 3] = "Static";
|
1711 | })(StmtModifier || (StmtModifier = {}));
|
1712 | class LeadingComment {
|
1713 | constructor(text, multiline, trailingNewline) {
|
1714 | this.text = text;
|
1715 | this.multiline = multiline;
|
1716 | this.trailingNewline = trailingNewline;
|
1717 | }
|
1718 | toString() {
|
1719 | return this.multiline ? ` ${this.text} ` : this.text;
|
1720 | }
|
1721 | }
|
1722 | class JSDocComment extends LeadingComment {
|
1723 | constructor(tags) {
|
1724 | super('', true, true);
|
1725 | this.tags = tags;
|
1726 | }
|
1727 | toString() {
|
1728 | return serializeTags(this.tags);
|
1729 | }
|
1730 | }
|
1731 | class Statement {
|
1732 | constructor(modifiers = [], sourceSpan = null, leadingComments) {
|
1733 | this.modifiers = modifiers;
|
1734 | this.sourceSpan = sourceSpan;
|
1735 | this.leadingComments = leadingComments;
|
1736 | }
|
1737 | hasModifier(modifier) {
|
1738 | return this.modifiers.indexOf(modifier) !== -1;
|
1739 | }
|
1740 | addLeadingComment(leadingComment) {
|
1741 | var _a;
|
1742 | this.leadingComments = (_a = this.leadingComments) !== null && _a !== void 0 ? _a : [];
|
1743 | this.leadingComments.push(leadingComment);
|
1744 | }
|
1745 | }
|
1746 | class DeclareVarStmt extends Statement {
|
1747 | constructor(name, value, type, modifiers, sourceSpan, leadingComments) {
|
1748 | super(modifiers, sourceSpan, leadingComments);
|
1749 | this.name = name;
|
1750 | this.value = value;
|
1751 | this.type = type || (value && value.type) || null;
|
1752 | }
|
1753 | isEquivalent(stmt) {
|
1754 | return stmt instanceof DeclareVarStmt && this.name === stmt.name &&
|
1755 | (this.value ? !!stmt.value && this.value.isEquivalent(stmt.value) : !stmt.value);
|
1756 | }
|
1757 | visitStatement(visitor, context) {
|
1758 | return visitor.visitDeclareVarStmt(this, context);
|
1759 | }
|
1760 | }
|
1761 | class DeclareFunctionStmt extends Statement {
|
1762 | constructor(name, params, statements, type, modifiers, sourceSpan, leadingComments) {
|
1763 | super(modifiers, sourceSpan, leadingComments);
|
1764 | this.name = name;
|
1765 | this.params = params;
|
1766 | this.statements = statements;
|
1767 | this.type = type || null;
|
1768 | }
|
1769 | isEquivalent(stmt) {
|
1770 | return stmt instanceof DeclareFunctionStmt && areAllEquivalent(this.params, stmt.params) &&
|
1771 | areAllEquivalent(this.statements, stmt.statements);
|
1772 | }
|
1773 | visitStatement(visitor, context) {
|
1774 | return visitor.visitDeclareFunctionStmt(this, context);
|
1775 | }
|
1776 | }
|
1777 | class ExpressionStatement extends Statement {
|
1778 | constructor(expr, sourceSpan, leadingComments) {
|
1779 | super([], sourceSpan, leadingComments);
|
1780 | this.expr = expr;
|
1781 | }
|
1782 | isEquivalent(stmt) {
|
1783 | return stmt instanceof ExpressionStatement && this.expr.isEquivalent(stmt.expr);
|
1784 | }
|
1785 | visitStatement(visitor, context) {
|
1786 | return visitor.visitExpressionStmt(this, context);
|
1787 | }
|
1788 | }
|
1789 | class ReturnStatement extends Statement {
|
1790 | constructor(value, sourceSpan = null, leadingComments) {
|
1791 | super([], sourceSpan, leadingComments);
|
1792 | this.value = value;
|
1793 | }
|
1794 | isEquivalent(stmt) {
|
1795 | return stmt instanceof ReturnStatement && this.value.isEquivalent(stmt.value);
|
1796 | }
|
1797 | visitStatement(visitor, context) {
|
1798 | return visitor.visitReturnStmt(this, context);
|
1799 | }
|
1800 | }
|
1801 | class IfStmt extends Statement {
|
1802 | constructor(condition, trueCase, falseCase = [], sourceSpan, leadingComments) {
|
1803 | super([], sourceSpan, leadingComments);
|
1804 | this.condition = condition;
|
1805 | this.trueCase = trueCase;
|
1806 | this.falseCase = falseCase;
|
1807 | }
|
1808 | isEquivalent(stmt) {
|
1809 | return stmt instanceof IfStmt && this.condition.isEquivalent(stmt.condition) &&
|
1810 | areAllEquivalent(this.trueCase, stmt.trueCase) &&
|
1811 | areAllEquivalent(this.falseCase, stmt.falseCase);
|
1812 | }
|
1813 | visitStatement(visitor, context) {
|
1814 | return visitor.visitIfStmt(this, context);
|
1815 | }
|
1816 | }
|
1817 | function jsDocComment(tags = []) {
|
1818 | return new JSDocComment(tags);
|
1819 | }
|
1820 | function variable(name, type, sourceSpan) {
|
1821 | return new ReadVarExpr(name, type, sourceSpan);
|
1822 | }
|
1823 | function importExpr(id, typeParams = null, sourceSpan) {
|
1824 | return new ExternalExpr(id, null, typeParams, sourceSpan);
|
1825 | }
|
1826 | function expressionType(expr, typeModifiers, typeParams) {
|
1827 | return new ExpressionType(expr, typeModifiers, typeParams);
|
1828 | }
|
1829 | function typeofExpr(expr) {
|
1830 | return new TypeofExpr(expr);
|
1831 | }
|
1832 | function literalArr(values, type, sourceSpan) {
|
1833 | return new LiteralArrayExpr(values, type, sourceSpan);
|
1834 | }
|
1835 | function literalMap(values, type = null) {
|
1836 | return new LiteralMapExpr(values.map(e => new LiteralMapEntry(e.key, e.value, e.quoted)), type, null);
|
1837 | }
|
1838 | function not(expr, sourceSpan) {
|
1839 | return new NotExpr(expr, sourceSpan);
|
1840 | }
|
1841 | function assertNotNull(expr, sourceSpan) {
|
1842 | return new AssertNotNull(expr, sourceSpan);
|
1843 | }
|
1844 | function fn(params, body, type, sourceSpan, name) {
|
1845 | return new FunctionExpr(params, body, type, sourceSpan, name);
|
1846 | }
|
1847 | function ifStmt(condition, thenClause, elseClause, sourceSpan, leadingComments) {
|
1848 | return new IfStmt(condition, thenClause, elseClause, sourceSpan, leadingComments);
|
1849 | }
|
1850 | function taggedTemplate(tag, template, type, sourceSpan) {
|
1851 | return new TaggedTemplateExpr(tag, template, type, sourceSpan);
|
1852 | }
|
1853 | function literal(value, type, sourceSpan) {
|
1854 | return new LiteralExpr(value, type, sourceSpan);
|
1855 | }
|
1856 | function localizedString(metaBlock, messageParts, placeholderNames, expressions, sourceSpan) {
|
1857 | return new LocalizedString(metaBlock, messageParts, placeholderNames, expressions, sourceSpan);
|
1858 | }
|
1859 | function isNull(exp) {
|
1860 | return exp instanceof LiteralExpr && exp.value === null;
|
1861 | }
|
1862 | |
1863 |
|
1864 |
|
1865 |
|
1866 | function tagToString(tag) {
|
1867 | let out = '';
|
1868 | if (tag.tagName) {
|
1869 | out += ` @${tag.tagName}`;
|
1870 | }
|
1871 | if (tag.text) {
|
1872 | if (tag.text.match(/\/\*|\*\//)) {
|
1873 | throw new Error('JSDoc text cannot contain "/*" and "*/"');
|
1874 | }
|
1875 | out += ' ' + tag.text.replace(/@/g, '\\@');
|
1876 | }
|
1877 | return out;
|
1878 | }
|
1879 | function serializeTags(tags) {
|
1880 | if (tags.length === 0)
|
1881 | return '';
|
1882 | if (tags.length === 1 && tags[0].tagName && !tags[0].text) {
|
1883 |
|
1884 | return `*${tagToString(tags[0])} `;
|
1885 | }
|
1886 | let out = '*\n';
|
1887 | for (const tag of tags) {
|
1888 | out += ' *';
|
1889 |
|
1890 | out += tagToString(tag).replace(/\n/g, '\n * ');
|
1891 | out += '\n';
|
1892 | }
|
1893 | out += ' ';
|
1894 | return out;
|
1895 | }
|
1896 |
|
1897 | |
1898 |
|
1899 |
|
1900 |
|
1901 |
|
1902 |
|
1903 |
|
1904 | const DASH_CASE_REGEXP = /-+([a-z0-9])/g;
|
1905 | function dashCaseToCamelCase(input) {
|
1906 | return input.replace(DASH_CASE_REGEXP, (...m) => m[1].toUpperCase());
|
1907 | }
|
1908 | function splitAtColon(input, defaultValues) {
|
1909 | return _splitAt(input, ':', defaultValues);
|
1910 | }
|
1911 | function splitAtPeriod(input, defaultValues) {
|
1912 | return _splitAt(input, '.', defaultValues);
|
1913 | }
|
1914 | function _splitAt(input, character, defaultValues) {
|
1915 | const characterIndex = input.indexOf(character);
|
1916 | if (characterIndex == -1)
|
1917 | return defaultValues;
|
1918 | return [input.slice(0, characterIndex).trim(), input.slice(characterIndex + 1).trim()];
|
1919 | }
|
1920 | function visitValue(value, visitor, context) {
|
1921 | if (Array.isArray(value)) {
|
1922 | return visitor.visitArray(value, context);
|
1923 | }
|
1924 | if (isStrictStringMap(value)) {
|
1925 | return visitor.visitStringMap(value, context);
|
1926 | }
|
1927 | if (value == null || typeof value == 'string' || typeof value == 'number' ||
|
1928 | typeof value == 'boolean') {
|
1929 | return visitor.visitPrimitive(value, context);
|
1930 | }
|
1931 | return visitor.visitOther(value, context);
|
1932 | }
|
1933 | function isDefined(val) {
|
1934 | return val !== null && val !== undefined;
|
1935 | }
|
1936 | function noUndefined(val) {
|
1937 | return val === undefined ? null : val;
|
1938 | }
|
1939 | class ValueTransformer {
|
1940 | visitArray(arr, context) {
|
1941 | return arr.map(value => visitValue(value, this, context));
|
1942 | }
|
1943 | visitStringMap(map, context) {
|
1944 | const result = {};
|
1945 | Object.keys(map).forEach(key => {
|
1946 | result[key] = visitValue(map[key], this, context);
|
1947 | });
|
1948 | return result;
|
1949 | }
|
1950 | visitPrimitive(value, context) {
|
1951 | return value;
|
1952 | }
|
1953 | visitOther(value, context) {
|
1954 | return value;
|
1955 | }
|
1956 | }
|
1957 | const SyncAsync = {
|
1958 | assertSync: (value) => {
|
1959 | if (isPromise(value)) {
|
1960 | throw new Error(`Illegal state: value cannot be a promise`);
|
1961 | }
|
1962 | return value;
|
1963 | },
|
1964 | then: (value, cb) => {
|
1965 | return isPromise(value) ? value.then(cb) : cb(value);
|
1966 | },
|
1967 | all: (syncAsyncValues) => {
|
1968 | return syncAsyncValues.some(isPromise) ? Promise.all(syncAsyncValues) : syncAsyncValues;
|
1969 | }
|
1970 | };
|
1971 | function error(msg) {
|
1972 | throw new Error(`Internal Error: ${msg}`);
|
1973 | }
|
1974 | function syntaxError(msg, parseErrors) {
|
1975 | const error = Error(msg);
|
1976 | error[ERROR_SYNTAX_ERROR] = true;
|
1977 | if (parseErrors)
|
1978 | error[ERROR_PARSE_ERRORS] = parseErrors;
|
1979 | return error;
|
1980 | }
|
1981 | const ERROR_SYNTAX_ERROR = 'ngSyntaxError';
|
1982 | const ERROR_PARSE_ERRORS = 'ngParseErrors';
|
1983 | const STRING_MAP_PROTO = Object.getPrototypeOf({});
|
1984 | function isStrictStringMap(obj) {
|
1985 | return typeof obj === 'object' && obj !== null && Object.getPrototypeOf(obj) === STRING_MAP_PROTO;
|
1986 | }
|
1987 | function utf8Encode(str) {
|
1988 | let encoded = [];
|
1989 | for (let index = 0; index < str.length; index++) {
|
1990 | let codePoint = str.charCodeAt(index);
|
1991 |
|
1992 |
|
1993 | if (codePoint >= 0xd800 && codePoint <= 0xdbff && str.length > (index + 1)) {
|
1994 | const low = str.charCodeAt(index + 1);
|
1995 | if (low >= 0xdc00 && low <= 0xdfff) {
|
1996 | index++;
|
1997 | codePoint = ((codePoint - 0xd800) << 10) + low - 0xdc00 + 0x10000;
|
1998 | }
|
1999 | }
|
2000 | if (codePoint <= 0x7f) {
|
2001 | encoded.push(codePoint);
|
2002 | }
|
2003 | else if (codePoint <= 0x7ff) {
|
2004 | encoded.push(((codePoint >> 6) & 0x1F) | 0xc0, (codePoint & 0x3f) | 0x80);
|
2005 | }
|
2006 | else if (codePoint <= 0xffff) {
|
2007 | encoded.push((codePoint >> 12) | 0xe0, ((codePoint >> 6) & 0x3f) | 0x80, (codePoint & 0x3f) | 0x80);
|
2008 | }
|
2009 | else if (codePoint <= 0x1fffff) {
|
2010 | encoded.push(((codePoint >> 18) & 0x07) | 0xf0, ((codePoint >> 12) & 0x3f) | 0x80, ((codePoint >> 6) & 0x3f) | 0x80, (codePoint & 0x3f) | 0x80);
|
2011 | }
|
2012 | }
|
2013 | return encoded;
|
2014 | }
|
2015 | function stringify(token) {
|
2016 | if (typeof token === 'string') {
|
2017 | return token;
|
2018 | }
|
2019 | if (Array.isArray(token)) {
|
2020 | return '[' + token.map(stringify).join(', ') + ']';
|
2021 | }
|
2022 | if (token == null) {
|
2023 | return '' + token;
|
2024 | }
|
2025 | if (token.overriddenName) {
|
2026 | return `${token.overriddenName}`;
|
2027 | }
|
2028 | if (token.name) {
|
2029 | return `${token.name}`;
|
2030 | }
|
2031 | if (!token.toString) {
|
2032 | return 'object';
|
2033 | }
|
2034 |
|
2035 |
|
2036 | const res = token.toString();
|
2037 | if (res == null) {
|
2038 | return '' + res;
|
2039 | }
|
2040 | const newLineIndex = res.indexOf('\n');
|
2041 | return newLineIndex === -1 ? res : res.substring(0, newLineIndex);
|
2042 | }
|
2043 | |
2044 |
|
2045 |
|
2046 | function resolveForwardRef(type) {
|
2047 | if (typeof type === 'function' && type.hasOwnProperty('__forward_ref__')) {
|
2048 | return type();
|
2049 | }
|
2050 | else {
|
2051 | return type;
|
2052 | }
|
2053 | }
|
2054 | |
2055 |
|
2056 |
|
2057 | function isPromise(obj) {
|
2058 |
|
2059 |
|
2060 | return !!obj && typeof obj.then === 'function';
|
2061 | }
|
2062 | class Version {
|
2063 | constructor(full) {
|
2064 | this.full = full;
|
2065 | const splits = full.split('.');
|
2066 | this.major = splits[0];
|
2067 | this.minor = splits[1];
|
2068 | this.patch = splits.slice(2).join('.');
|
2069 | }
|
2070 | }
|
2071 | const __window = typeof window !== 'undefined' && window;
|
2072 | const __self = typeof self !== 'undefined' && typeof WorkerGlobalScope !== 'undefined' &&
|
2073 | self instanceof WorkerGlobalScope && self;
|
2074 | const __global = typeof global !== 'undefined' && global;
|
2075 |
|
2076 |
|
2077 | const _global = __global || __window || __self;
|
2078 | function newArray(size, value) {
|
2079 | const list = [];
|
2080 | for (let i = 0; i < size; i++) {
|
2081 | list.push(value);
|
2082 | }
|
2083 | return list;
|
2084 | }
|
2085 | |
2086 |
|
2087 |
|
2088 |
|
2089 |
|
2090 |
|
2091 |
|
2092 |
|
2093 | function partitionArray(arr, conditionFn) {
|
2094 | const truthy = [];
|
2095 | const falsy = [];
|
2096 | for (const item of arr) {
|
2097 | (conditionFn(item) ? truthy : falsy).push(item);
|
2098 | }
|
2099 | return [truthy, falsy];
|
2100 | }
|
2101 |
|
2102 | |
2103 |
|
2104 |
|
2105 |
|
2106 |
|
2107 |
|
2108 |
|
2109 | const CONSTANT_PREFIX = '_c';
|
2110 | |
2111 |
|
2112 |
|
2113 |
|
2114 |
|
2115 |
|
2116 |
|
2117 |
|
2118 | const UNKNOWN_VALUE_KEY = variable('<unknown>');
|
2119 | |
2120 |
|
2121 |
|
2122 |
|
2123 |
|
2124 |
|
2125 | const KEY_CONTEXT = {};
|
2126 | |
2127 |
|
2128 |
|
2129 |
|
2130 |
|
2131 | const POOL_INCLUSION_LENGTH_THRESHOLD_FOR_STRINGS = 50;
|
2132 | |
2133 |
|
2134 |
|
2135 |
|
2136 |
|
2137 |
|
2138 |
|
2139 |
|
2140 | class FixupExpression extends Expression {
|
2141 | constructor(resolved) {
|
2142 | super(resolved.type);
|
2143 | this.resolved = resolved;
|
2144 | this.original = resolved;
|
2145 | }
|
2146 | visitExpression(visitor, context) {
|
2147 | if (context === KEY_CONTEXT) {
|
2148 |
|
2149 |
|
2150 | return this.original.visitExpression(visitor, context);
|
2151 | }
|
2152 | else {
|
2153 | return this.resolved.visitExpression(visitor, context);
|
2154 | }
|
2155 | }
|
2156 | isEquivalent(e) {
|
2157 | return e instanceof FixupExpression && this.resolved.isEquivalent(e.resolved);
|
2158 | }
|
2159 | isConstant() {
|
2160 | return true;
|
2161 | }
|
2162 | fixup(expression) {
|
2163 | this.resolved = expression;
|
2164 | this.shared = true;
|
2165 | }
|
2166 | }
|
2167 | |
2168 |
|
2169 |
|
2170 |
|
2171 |
|
2172 | class ConstantPool {
|
2173 | constructor(isClosureCompilerEnabled = false) {
|
2174 | this.isClosureCompilerEnabled = isClosureCompilerEnabled;
|
2175 | this.statements = [];
|
2176 | this.literals = new Map();
|
2177 | this.literalFactories = new Map();
|
2178 | this.injectorDefinitions = new Map();
|
2179 | this.directiveDefinitions = new Map();
|
2180 | this.componentDefinitions = new Map();
|
2181 | this.pipeDefinitions = new Map();
|
2182 | this.nextNameIndex = 0;
|
2183 | }
|
2184 | getConstLiteral(literal, forceShared) {
|
2185 | if ((literal instanceof LiteralExpr && !isLongStringLiteral(literal)) ||
|
2186 | literal instanceof FixupExpression) {
|
2187 |
|
2188 |
|
2189 | return literal;
|
2190 | }
|
2191 | const key = this.keyOf(literal);
|
2192 | let fixup = this.literals.get(key);
|
2193 | let newValue = false;
|
2194 | if (!fixup) {
|
2195 | fixup = new FixupExpression(literal);
|
2196 | this.literals.set(key, fixup);
|
2197 | newValue = true;
|
2198 | }
|
2199 | if ((!newValue && !fixup.shared) || (newValue && forceShared)) {
|
2200 |
|
2201 | const name = this.freshName();
|
2202 | let definition;
|
2203 | let usage;
|
2204 | if (this.isClosureCompilerEnabled && isLongStringLiteral(literal)) {
|
2205 |
|
2206 |
|
2207 |
|
2208 |
|
2209 |
|
2210 |
|
2211 |
|
2212 |
|
2213 |
|
2214 |
|
2215 |
|
2216 |
|
2217 |
|
2218 | definition = variable(name).set(new FunctionExpr([],
|
2219 | [
|
2220 |
|
2221 | new ReturnStatement(literal),
|
2222 | ]));
|
2223 | usage = variable(name).callFn([]);
|
2224 | }
|
2225 | else {
|
2226 |
|
2227 |
|
2228 | definition = variable(name).set(literal);
|
2229 | usage = variable(name);
|
2230 | }
|
2231 | this.statements.push(definition.toDeclStmt(INFERRED_TYPE, [StmtModifier.Final]));
|
2232 | fixup.fixup(usage);
|
2233 | }
|
2234 | return fixup;
|
2235 | }
|
2236 | getDefinition(type, kind, ctx, forceShared = false) {
|
2237 | const definitions = this.definitionsOf(kind);
|
2238 | let fixup = definitions.get(type);
|
2239 | let newValue = false;
|
2240 | if (!fixup) {
|
2241 | const property = this.propertyNameOf(kind);
|
2242 | fixup = new FixupExpression(ctx.importExpr(type).prop(property));
|
2243 | definitions.set(type, fixup);
|
2244 | newValue = true;
|
2245 | }
|
2246 | if ((!newValue && !fixup.shared) || (newValue && forceShared)) {
|
2247 | const name = this.freshName();
|
2248 | this.statements.push(variable(name).set(fixup.resolved).toDeclStmt(INFERRED_TYPE, [StmtModifier.Final]));
|
2249 | fixup.fixup(variable(name));
|
2250 | }
|
2251 | return fixup;
|
2252 | }
|
2253 | getLiteralFactory(literal) {
|
2254 |
|
2255 | if (literal instanceof LiteralArrayExpr) {
|
2256 | const argumentsForKey = literal.entries.map(e => e.isConstant() ? e : UNKNOWN_VALUE_KEY);
|
2257 | const key = this.keyOf(literalArr(argumentsForKey));
|
2258 | return this._getLiteralFactory(key, literal.entries, entries => literalArr(entries));
|
2259 | }
|
2260 | else {
|
2261 | const expressionForKey = literalMap(literal.entries.map(e => ({
|
2262 | key: e.key,
|
2263 | value: e.value.isConstant() ? e.value : UNKNOWN_VALUE_KEY,
|
2264 | quoted: e.quoted
|
2265 | })));
|
2266 | const key = this.keyOf(expressionForKey);
|
2267 | return this._getLiteralFactory(key, literal.entries.map(e => e.value), entries => literalMap(entries.map((value, index) => ({
|
2268 | key: literal.entries[index].key,
|
2269 | value,
|
2270 | quoted: literal.entries[index].quoted
|
2271 | }))));
|
2272 | }
|
2273 | }
|
2274 | _getLiteralFactory(key, values, resultMap) {
|
2275 | let literalFactory = this.literalFactories.get(key);
|
2276 | const literalFactoryArguments = values.filter((e => !e.isConstant()));
|
2277 | if (!literalFactory) {
|
2278 | const resultExpressions = values.map((e, index) => e.isConstant() ? this.getConstLiteral(e, true) : variable(`a${index}`));
|
2279 | const parameters = resultExpressions.filter(isVariable).map(e => new FnParam(e.name, DYNAMIC_TYPE));
|
2280 | const pureFunctionDeclaration = fn(parameters, [new ReturnStatement(resultMap(resultExpressions))], INFERRED_TYPE);
|
2281 | const name = this.freshName();
|
2282 | this.statements.push(variable(name).set(pureFunctionDeclaration).toDeclStmt(INFERRED_TYPE, [
|
2283 | StmtModifier.Final
|
2284 | ]));
|
2285 | literalFactory = variable(name);
|
2286 | this.literalFactories.set(key, literalFactory);
|
2287 | }
|
2288 | return { literalFactory, literalFactoryArguments };
|
2289 | }
|
2290 | |
2291 |
|
2292 |
|
2293 |
|
2294 |
|
2295 |
|
2296 |
|
2297 | uniqueName(prefix) {
|
2298 | return `${prefix}${this.nextNameIndex++}`;
|
2299 | }
|
2300 | definitionsOf(kind) {
|
2301 | switch (kind) {
|
2302 | case 2 :
|
2303 | return this.componentDefinitions;
|
2304 | case 1 :
|
2305 | return this.directiveDefinitions;
|
2306 | case 0 :
|
2307 | return this.injectorDefinitions;
|
2308 | case 3 :
|
2309 | return this.pipeDefinitions;
|
2310 | }
|
2311 | error(`Unknown definition kind ${kind}`);
|
2312 | return this.componentDefinitions;
|
2313 | }
|
2314 | propertyNameOf(kind) {
|
2315 | switch (kind) {
|
2316 | case 2 :
|
2317 | return 'ɵcmp';
|
2318 | case 1 :
|
2319 | return 'ɵdir';
|
2320 | case 0 :
|
2321 | return 'ɵinj';
|
2322 | case 3 :
|
2323 | return 'ɵpipe';
|
2324 | }
|
2325 | error(`Unknown definition kind ${kind}`);
|
2326 | return '<unknown>';
|
2327 | }
|
2328 | freshName() {
|
2329 | return this.uniqueName(CONSTANT_PREFIX);
|
2330 | }
|
2331 | keyOf(expression) {
|
2332 | return expression.visitExpression(new KeyVisitor(), KEY_CONTEXT);
|
2333 | }
|
2334 | }
|
2335 | |
2336 |
|
2337 |
|
2338 |
|
2339 |
|
2340 |
|
2341 | class KeyVisitor {
|
2342 | constructor() {
|
2343 | this.visitWrappedNodeExpr = invalid;
|
2344 | this.visitWriteVarExpr = invalid;
|
2345 | this.visitWriteKeyExpr = invalid;
|
2346 | this.visitWritePropExpr = invalid;
|
2347 | this.visitInvokeMethodExpr = invalid;
|
2348 | this.visitInvokeFunctionExpr = invalid;
|
2349 | this.visitTaggedTemplateExpr = invalid;
|
2350 | this.visitInstantiateExpr = invalid;
|
2351 | this.visitConditionalExpr = invalid;
|
2352 | this.visitNotExpr = invalid;
|
2353 | this.visitAssertNotNullExpr = invalid;
|
2354 | this.visitCastExpr = invalid;
|
2355 | this.visitFunctionExpr = invalid;
|
2356 | this.visitUnaryOperatorExpr = invalid;
|
2357 | this.visitBinaryOperatorExpr = invalid;
|
2358 | this.visitReadPropExpr = invalid;
|
2359 | this.visitReadKeyExpr = invalid;
|
2360 | this.visitCommaExpr = invalid;
|
2361 | this.visitLocalizedString = invalid;
|
2362 | }
|
2363 | visitLiteralExpr(ast) {
|
2364 | return `${typeof ast.value === 'string' ? '"' + ast.value + '"' : ast.value}`;
|
2365 | }
|
2366 | visitLiteralArrayExpr(ast, context) {
|
2367 | return `[${ast.entries.map(entry => entry.visitExpression(this, context)).join(',')}]`;
|
2368 | }
|
2369 | visitLiteralMapExpr(ast, context) {
|
2370 | const mapKey = (entry) => {
|
2371 | const quote = entry.quoted ? '"' : '';
|
2372 | return `${quote}${entry.key}${quote}`;
|
2373 | };
|
2374 | const mapEntry = (entry) => `${mapKey(entry)}:${entry.value.visitExpression(this, context)}`;
|
2375 | return `{${ast.entries.map(mapEntry).join(',')}`;
|
2376 | }
|
2377 | visitExternalExpr(ast) {
|
2378 | return ast.value.moduleName ? `EX:${ast.value.moduleName}:${ast.value.name}` :
|
2379 | `EX:${ast.value.runtime.name}`;
|
2380 | }
|
2381 | visitReadVarExpr(node) {
|
2382 | return `VAR:${node.name}`;
|
2383 | }
|
2384 | visitTypeofExpr(node, context) {
|
2385 | return `TYPEOF:${node.expr.visitExpression(this, context)}`;
|
2386 | }
|
2387 | }
|
2388 | function invalid(arg) {
|
2389 | throw new Error(`Invalid state: Visitor ${this.constructor.name} doesn't handle ${arg.constructor.name}`);
|
2390 | }
|
2391 | function isVariable(e) {
|
2392 | return e instanceof ReadVarExpr;
|
2393 | }
|
2394 | function isLongStringLiteral(expr) {
|
2395 | return expr instanceof LiteralExpr && typeof expr.value === 'string' &&
|
2396 | expr.value.length >= POOL_INCLUSION_LENGTH_THRESHOLD_FOR_STRINGS;
|
2397 | }
|
2398 |
|
2399 | |
2400 |
|
2401 |
|
2402 |
|
2403 |
|
2404 |
|
2405 |
|
2406 | const CORE = '@angular/core';
|
2407 | class Identifiers {
|
2408 | }
|
2409 | Identifiers.ANALYZE_FOR_ENTRY_COMPONENTS = {
|
2410 | name: 'ANALYZE_FOR_ENTRY_COMPONENTS',
|
2411 | moduleName: CORE,
|
2412 | };
|
2413 | Identifiers.ElementRef = { name: 'ElementRef', moduleName: CORE };
|
2414 | Identifiers.NgModuleRef = { name: 'NgModuleRef', moduleName: CORE };
|
2415 | Identifiers.ViewContainerRef = { name: 'ViewContainerRef', moduleName: CORE };
|
2416 | Identifiers.ChangeDetectorRef = {
|
2417 | name: 'ChangeDetectorRef',
|
2418 | moduleName: CORE,
|
2419 | };
|
2420 | Identifiers.QueryList = { name: 'QueryList', moduleName: CORE };
|
2421 | Identifiers.TemplateRef = { name: 'TemplateRef', moduleName: CORE };
|
2422 | Identifiers.Renderer2 = { name: 'Renderer2', moduleName: CORE };
|
2423 | Identifiers.CodegenComponentFactoryResolver = {
|
2424 | name: 'ɵCodegenComponentFactoryResolver',
|
2425 | moduleName: CORE,
|
2426 | };
|
2427 | Identifiers.ComponentFactoryResolver = {
|
2428 | name: 'ComponentFactoryResolver',
|
2429 | moduleName: CORE,
|
2430 | };
|
2431 | Identifiers.ComponentFactory = { name: 'ComponentFactory', moduleName: CORE };
|
2432 | Identifiers.ComponentRef = { name: 'ComponentRef', moduleName: CORE };
|
2433 | Identifiers.NgModuleFactory = { name: 'NgModuleFactory', moduleName: CORE };
|
2434 | Identifiers.createModuleFactory = {
|
2435 | name: 'ɵcmf',
|
2436 | moduleName: CORE,
|
2437 | };
|
2438 | Identifiers.moduleDef = {
|
2439 | name: 'ɵmod',
|
2440 | moduleName: CORE,
|
2441 | };
|
2442 | Identifiers.moduleProviderDef = {
|
2443 | name: 'ɵmpd',
|
2444 | moduleName: CORE,
|
2445 | };
|
2446 | Identifiers.RegisterModuleFactoryFn = {
|
2447 | name: 'ɵregisterModuleFactory',
|
2448 | moduleName: CORE,
|
2449 | };
|
2450 | Identifiers.inject = { name: 'ɵɵinject', moduleName: CORE };
|
2451 | Identifiers.directiveInject = { name: 'ɵɵdirectiveInject', moduleName: CORE };
|
2452 | Identifiers.INJECTOR = { name: 'INJECTOR', moduleName: CORE };
|
2453 | Identifiers.Injector = { name: 'Injector', moduleName: CORE };
|
2454 | Identifiers.ɵɵdefineInjectable = { name: 'ɵɵdefineInjectable', moduleName: CORE };
|
2455 | Identifiers.InjectableDef = { name: 'ɵɵInjectableDef', moduleName: CORE };
|
2456 | Identifiers.ViewEncapsulation = {
|
2457 | name: 'ViewEncapsulation',
|
2458 | moduleName: CORE,
|
2459 | };
|
2460 | Identifiers.ChangeDetectionStrategy = {
|
2461 | name: 'ChangeDetectionStrategy',
|
2462 | moduleName: CORE,
|
2463 | };
|
2464 | Identifiers.SecurityContext = {
|
2465 | name: 'SecurityContext',
|
2466 | moduleName: CORE,
|
2467 | };
|
2468 | Identifiers.LOCALE_ID = { name: 'LOCALE_ID', moduleName: CORE };
|
2469 | Identifiers.TRANSLATIONS_FORMAT = {
|
2470 | name: 'TRANSLATIONS_FORMAT',
|
2471 | moduleName: CORE,
|
2472 | };
|
2473 | Identifiers.inlineInterpolate = {
|
2474 | name: 'ɵinlineInterpolate',
|
2475 | moduleName: CORE,
|
2476 | };
|
2477 | Identifiers.interpolate = { name: 'ɵinterpolate', moduleName: CORE };
|
2478 | Identifiers.EMPTY_ARRAY = { name: 'ɵEMPTY_ARRAY', moduleName: CORE };
|
2479 | Identifiers.EMPTY_MAP = { name: 'ɵEMPTY_MAP', moduleName: CORE };
|
2480 | Identifiers.Renderer = { name: 'Renderer', moduleName: CORE };
|
2481 | Identifiers.viewDef = { name: 'ɵvid', moduleName: CORE };
|
2482 | Identifiers.elementDef = { name: 'ɵeld', moduleName: CORE };
|
2483 | Identifiers.anchorDef = { name: 'ɵand', moduleName: CORE };
|
2484 | Identifiers.textDef = { name: 'ɵted', moduleName: CORE };
|
2485 | Identifiers.directiveDef = { name: 'ɵdid', moduleName: CORE };
|
2486 | Identifiers.providerDef = { name: 'ɵprd', moduleName: CORE };
|
2487 | Identifiers.queryDef = { name: 'ɵqud', moduleName: CORE };
|
2488 | Identifiers.pureArrayDef = { name: 'ɵpad', moduleName: CORE };
|
2489 | Identifiers.pureObjectDef = { name: 'ɵpod', moduleName: CORE };
|
2490 | Identifiers.purePipeDef = { name: 'ɵppd', moduleName: CORE };
|
2491 | Identifiers.pipeDef = { name: 'ɵpid', moduleName: CORE };
|
2492 | Identifiers.nodeValue = { name: 'ɵnov', moduleName: CORE };
|
2493 | Identifiers.ngContentDef = { name: 'ɵncd', moduleName: CORE };
|
2494 | Identifiers.unwrapValue = { name: 'ɵunv', moduleName: CORE };
|
2495 | Identifiers.createRendererType2 = { name: 'ɵcrt', moduleName: CORE };
|
2496 |
|
2497 | Identifiers.RendererType2 = {
|
2498 | name: 'RendererType2',
|
2499 | moduleName: CORE,
|
2500 | };
|
2501 |
|
2502 | Identifiers.ViewDefinition = {
|
2503 | name: 'ɵViewDefinition',
|
2504 | moduleName: CORE,
|
2505 | };
|
2506 | Identifiers.createComponentFactory = { name: 'ɵccf', moduleName: CORE };
|
2507 | Identifiers.setClassMetadata = { name: 'ɵsetClassMetadata', moduleName: CORE };
|
2508 | function createTokenForReference(reference) {
|
2509 | return { identifier: { reference: reference } };
|
2510 | }
|
2511 | function createTokenForExternalReference(reflector, reference) {
|
2512 | return createTokenForReference(reflector.resolveExternalReference(reference));
|
2513 | }
|
2514 |
|
2515 | |
2516 |
|
2517 |
|
2518 |
|
2519 |
|
2520 |
|
2521 |
|
2522 | |
2523 |
|
2524 |
|
2525 |
|
2526 |
|
2527 | class StaticSymbol {
|
2528 | constructor(filePath, name, members) {
|
2529 | this.filePath = filePath;
|
2530 | this.name = name;
|
2531 | this.members = members;
|
2532 | }
|
2533 | assertNoMembers() {
|
2534 | if (this.members.length) {
|
2535 | throw new Error(`Illegal state: symbol without members expected, but got ${JSON.stringify(this)}.`);
|
2536 | }
|
2537 | }
|
2538 | }
|
2539 | |
2540 |
|
2541 |
|
2542 |
|
2543 | class StaticSymbolCache {
|
2544 | constructor() {
|
2545 | this.cache = new Map();
|
2546 | }
|
2547 | get(declarationFile, name, members) {
|
2548 | members = members || [];
|
2549 | const memberSuffix = members.length ? `.${members.join('.')}` : '';
|
2550 | const key = `"${declarationFile}".${name}${memberSuffix}`;
|
2551 | let result = this.cache.get(key);
|
2552 | if (!result) {
|
2553 | result = new StaticSymbol(declarationFile, name, members);
|
2554 | this.cache.set(key, result);
|
2555 | }
|
2556 | return result;
|
2557 | }
|
2558 | }
|
2559 |
|
2560 | |
2561 |
|
2562 |
|
2563 |
|
2564 |
|
2565 |
|
2566 |
|
2567 |
|
2568 |
|
2569 |
|
2570 |
|
2571 | const HOST_REG_EXP = /^(?:(?:\[([^\]]+)\])|(?:\(([^\)]+)\)))|(\@[-\w]+)$/;
|
2572 | function sanitizeIdentifier(name) {
|
2573 | return name.replace(/\W/g, '_');
|
2574 | }
|
2575 | let _anonymousTypeIndex = 0;
|
2576 | function identifierName(compileIdentifier) {
|
2577 | if (!compileIdentifier || !compileIdentifier.reference) {
|
2578 | return null;
|
2579 | }
|
2580 | const ref = compileIdentifier.reference;
|
2581 | if (ref instanceof StaticSymbol) {
|
2582 | return ref.name;
|
2583 | }
|
2584 | if (ref['__anonymousType']) {
|
2585 | return ref['__anonymousType'];
|
2586 | }
|
2587 | let identifier = stringify(ref);
|
2588 | if (identifier.indexOf('(') >= 0) {
|
2589 |
|
2590 | identifier = `anonymous_${_anonymousTypeIndex++}`;
|
2591 | ref['__anonymousType'] = identifier;
|
2592 | }
|
2593 | else {
|
2594 | identifier = sanitizeIdentifier(identifier);
|
2595 | }
|
2596 | return identifier;
|
2597 | }
|
2598 | function viewClassName(compType, embeddedTemplateIndex) {
|
2599 | return `View_${identifierName({ reference: compType })}_${embeddedTemplateIndex}`;
|
2600 | }
|
2601 | function rendererTypeName(compType) {
|
2602 | return `RenderType_${identifierName({ reference: compType })}`;
|
2603 | }
|
2604 | function hostViewClassName(compType) {
|
2605 | return `HostView_${identifierName({ reference: compType })}`;
|
2606 | }
|
2607 | function componentFactoryName(compType) {
|
2608 | return `${identifierName({ reference: compType })}NgFactory`;
|
2609 | }
|
2610 | var CompileSummaryKind;
|
2611 | (function (CompileSummaryKind) {
|
2612 | CompileSummaryKind[CompileSummaryKind["Pipe"] = 0] = "Pipe";
|
2613 | CompileSummaryKind[CompileSummaryKind["Directive"] = 1] = "Directive";
|
2614 | CompileSummaryKind[CompileSummaryKind["NgModule"] = 2] = "NgModule";
|
2615 | CompileSummaryKind[CompileSummaryKind["Injectable"] = 3] = "Injectable";
|
2616 | })(CompileSummaryKind || (CompileSummaryKind = {}));
|
2617 | function tokenName(token) {
|
2618 | return token.value != null ? sanitizeIdentifier(token.value) : identifierName(token.identifier);
|
2619 | }
|
2620 | function tokenReference(token) {
|
2621 | if (token.identifier != null) {
|
2622 | return token.identifier.reference;
|
2623 | }
|
2624 | else {
|
2625 | return token.value;
|
2626 | }
|
2627 | }
|
2628 | |
2629 |
|
2630 |
|
2631 | class CompileStylesheetMetadata {
|
2632 | constructor({ moduleUrl, styles, styleUrls } = {}) {
|
2633 | this.moduleUrl = moduleUrl || null;
|
2634 | this.styles = _normalizeArray(styles);
|
2635 | this.styleUrls = _normalizeArray(styleUrls);
|
2636 | }
|
2637 | }
|
2638 | |
2639 |
|
2640 |
|
2641 | class CompileTemplateMetadata {
|
2642 | constructor({ encapsulation, template, templateUrl, htmlAst, styles, styleUrls, externalStylesheets, animations, ngContentSelectors, interpolation, isInline, preserveWhitespaces }) {
|
2643 | this.encapsulation = encapsulation;
|
2644 | this.template = template;
|
2645 | this.templateUrl = templateUrl;
|
2646 | this.htmlAst = htmlAst;
|
2647 | this.styles = _normalizeArray(styles);
|
2648 | this.styleUrls = _normalizeArray(styleUrls);
|
2649 | this.externalStylesheets = _normalizeArray(externalStylesheets);
|
2650 | this.animations = animations ? flatten(animations) : [];
|
2651 | this.ngContentSelectors = ngContentSelectors || [];
|
2652 | if (interpolation && interpolation.length != 2) {
|
2653 | throw new Error(`'interpolation' should have a start and an end symbol.`);
|
2654 | }
|
2655 | this.interpolation = interpolation;
|
2656 | this.isInline = isInline;
|
2657 | this.preserveWhitespaces = preserveWhitespaces;
|
2658 | }
|
2659 | toSummary() {
|
2660 | return {
|
2661 | ngContentSelectors: this.ngContentSelectors,
|
2662 | encapsulation: this.encapsulation,
|
2663 | styles: this.styles,
|
2664 | animations: this.animations
|
2665 | };
|
2666 | }
|
2667 | }
|
2668 | |
2669 |
|
2670 |
|
2671 | class CompileDirectiveMetadata {
|
2672 | constructor({ isHost, type, isComponent, selector, exportAs, changeDetection, inputs, outputs, hostListeners, hostProperties, hostAttributes, providers, viewProviders, queries, guards, viewQueries, entryComponents, template, componentViewType, rendererType, componentFactory }) {
|
2673 | this.isHost = !!isHost;
|
2674 | this.type = type;
|
2675 | this.isComponent = isComponent;
|
2676 | this.selector = selector;
|
2677 | this.exportAs = exportAs;
|
2678 | this.changeDetection = changeDetection;
|
2679 | this.inputs = inputs;
|
2680 | this.outputs = outputs;
|
2681 | this.hostListeners = hostListeners;
|
2682 | this.hostProperties = hostProperties;
|
2683 | this.hostAttributes = hostAttributes;
|
2684 | this.providers = _normalizeArray(providers);
|
2685 | this.viewProviders = _normalizeArray(viewProviders);
|
2686 | this.queries = _normalizeArray(queries);
|
2687 | this.guards = guards;
|
2688 | this.viewQueries = _normalizeArray(viewQueries);
|
2689 | this.entryComponents = _normalizeArray(entryComponents);
|
2690 | this.template = template;
|
2691 | this.componentViewType = componentViewType;
|
2692 | this.rendererType = rendererType;
|
2693 | this.componentFactory = componentFactory;
|
2694 | }
|
2695 | static create({ isHost, type, isComponent, selector, exportAs, changeDetection, inputs, outputs, host, providers, viewProviders, queries, guards, viewQueries, entryComponents, template, componentViewType, rendererType, componentFactory }) {
|
2696 | const hostListeners = {};
|
2697 | const hostProperties = {};
|
2698 | const hostAttributes = {};
|
2699 | if (host != null) {
|
2700 | Object.keys(host).forEach(key => {
|
2701 | const value = host[key];
|
2702 | const matches = key.match(HOST_REG_EXP);
|
2703 | if (matches === null) {
|
2704 | hostAttributes[key] = value;
|
2705 | }
|
2706 | else if (matches[1] != null) {
|
2707 | hostProperties[matches[1]] = value;
|
2708 | }
|
2709 | else if (matches[2] != null) {
|
2710 | hostListeners[matches[2]] = value;
|
2711 | }
|
2712 | });
|
2713 | }
|
2714 | const inputsMap = {};
|
2715 | if (inputs != null) {
|
2716 | inputs.forEach((bindConfig) => {
|
2717 |
|
2718 |
|
2719 | const parts = splitAtColon(bindConfig, [bindConfig, bindConfig]);
|
2720 | inputsMap[parts[0]] = parts[1];
|
2721 | });
|
2722 | }
|
2723 | const outputsMap = {};
|
2724 | if (outputs != null) {
|
2725 | outputs.forEach((bindConfig) => {
|
2726 |
|
2727 |
|
2728 | const parts = splitAtColon(bindConfig, [bindConfig, bindConfig]);
|
2729 | outputsMap[parts[0]] = parts[1];
|
2730 | });
|
2731 | }
|
2732 | return new CompileDirectiveMetadata({
|
2733 | isHost,
|
2734 | type,
|
2735 | isComponent: !!isComponent,
|
2736 | selector,
|
2737 | exportAs,
|
2738 | changeDetection,
|
2739 | inputs: inputsMap,
|
2740 | outputs: outputsMap,
|
2741 | hostListeners,
|
2742 | hostProperties,
|
2743 | hostAttributes,
|
2744 | providers,
|
2745 | viewProviders,
|
2746 | queries,
|
2747 | guards,
|
2748 | viewQueries,
|
2749 | entryComponents,
|
2750 | template,
|
2751 | componentViewType,
|
2752 | rendererType,
|
2753 | componentFactory,
|
2754 | });
|
2755 | }
|
2756 | toSummary() {
|
2757 | return {
|
2758 | summaryKind: CompileSummaryKind.Directive,
|
2759 | type: this.type,
|
2760 | isComponent: this.isComponent,
|
2761 | selector: this.selector,
|
2762 | exportAs: this.exportAs,
|
2763 | inputs: this.inputs,
|
2764 | outputs: this.outputs,
|
2765 | hostListeners: this.hostListeners,
|
2766 | hostProperties: this.hostProperties,
|
2767 | hostAttributes: this.hostAttributes,
|
2768 | providers: this.providers,
|
2769 | viewProviders: this.viewProviders,
|
2770 | queries: this.queries,
|
2771 | guards: this.guards,
|
2772 | viewQueries: this.viewQueries,
|
2773 | entryComponents: this.entryComponents,
|
2774 | changeDetection: this.changeDetection,
|
2775 | template: this.template && this.template.toSummary(),
|
2776 | componentViewType: this.componentViewType,
|
2777 | rendererType: this.rendererType,
|
2778 | componentFactory: this.componentFactory
|
2779 | };
|
2780 | }
|
2781 | }
|
2782 | class CompilePipeMetadata {
|
2783 | constructor({ type, name, pure }) {
|
2784 | this.type = type;
|
2785 | this.name = name;
|
2786 | this.pure = !!pure;
|
2787 | }
|
2788 | toSummary() {
|
2789 | return {
|
2790 | summaryKind: CompileSummaryKind.Pipe,
|
2791 | type: this.type,
|
2792 | name: this.name,
|
2793 | pure: this.pure
|
2794 | };
|
2795 | }
|
2796 | }
|
2797 | |
2798 |
|
2799 |
|
2800 | class CompileNgModuleMetadata {
|
2801 | constructor({ type, providers, declaredDirectives, exportedDirectives, declaredPipes, exportedPipes, entryComponents, bootstrapComponents, importedModules, exportedModules, schemas, transitiveModule, id }) {
|
2802 | this.type = type || null;
|
2803 | this.declaredDirectives = _normalizeArray(declaredDirectives);
|
2804 | this.exportedDirectives = _normalizeArray(exportedDirectives);
|
2805 | this.declaredPipes = _normalizeArray(declaredPipes);
|
2806 | this.exportedPipes = _normalizeArray(exportedPipes);
|
2807 | this.providers = _normalizeArray(providers);
|
2808 | this.entryComponents = _normalizeArray(entryComponents);
|
2809 | this.bootstrapComponents = _normalizeArray(bootstrapComponents);
|
2810 | this.importedModules = _normalizeArray(importedModules);
|
2811 | this.exportedModules = _normalizeArray(exportedModules);
|
2812 | this.schemas = _normalizeArray(schemas);
|
2813 | this.id = id || null;
|
2814 | this.transitiveModule = transitiveModule || null;
|
2815 | }
|
2816 | toSummary() {
|
2817 | const module = this.transitiveModule;
|
2818 | return {
|
2819 | summaryKind: CompileSummaryKind.NgModule,
|
2820 | type: this.type,
|
2821 | entryComponents: module.entryComponents,
|
2822 | providers: module.providers,
|
2823 | modules: module.modules,
|
2824 | exportedDirectives: module.exportedDirectives,
|
2825 | exportedPipes: module.exportedPipes
|
2826 | };
|
2827 | }
|
2828 | }
|
2829 | class TransitiveCompileNgModuleMetadata {
|
2830 | constructor() {
|
2831 | this.directivesSet = new Set();
|
2832 | this.directives = [];
|
2833 | this.exportedDirectivesSet = new Set();
|
2834 | this.exportedDirectives = [];
|
2835 | this.pipesSet = new Set();
|
2836 | this.pipes = [];
|
2837 | this.exportedPipesSet = new Set();
|
2838 | this.exportedPipes = [];
|
2839 | this.modulesSet = new Set();
|
2840 | this.modules = [];
|
2841 | this.entryComponentsSet = new Set();
|
2842 | this.entryComponents = [];
|
2843 | this.providers = [];
|
2844 | }
|
2845 | addProvider(provider, module) {
|
2846 | this.providers.push({ provider: provider, module: module });
|
2847 | }
|
2848 | addDirective(id) {
|
2849 | if (!this.directivesSet.has(id.reference)) {
|
2850 | this.directivesSet.add(id.reference);
|
2851 | this.directives.push(id);
|
2852 | }
|
2853 | }
|
2854 | addExportedDirective(id) {
|
2855 | if (!this.exportedDirectivesSet.has(id.reference)) {
|
2856 | this.exportedDirectivesSet.add(id.reference);
|
2857 | this.exportedDirectives.push(id);
|
2858 | }
|
2859 | }
|
2860 | addPipe(id) {
|
2861 | if (!this.pipesSet.has(id.reference)) {
|
2862 | this.pipesSet.add(id.reference);
|
2863 | this.pipes.push(id);
|
2864 | }
|
2865 | }
|
2866 | addExportedPipe(id) {
|
2867 | if (!this.exportedPipesSet.has(id.reference)) {
|
2868 | this.exportedPipesSet.add(id.reference);
|
2869 | this.exportedPipes.push(id);
|
2870 | }
|
2871 | }
|
2872 | addModule(id) {
|
2873 | if (!this.modulesSet.has(id.reference)) {
|
2874 | this.modulesSet.add(id.reference);
|
2875 | this.modules.push(id);
|
2876 | }
|
2877 | }
|
2878 | addEntryComponent(ec) {
|
2879 | if (!this.entryComponentsSet.has(ec.componentType)) {
|
2880 | this.entryComponentsSet.add(ec.componentType);
|
2881 | this.entryComponents.push(ec);
|
2882 | }
|
2883 | }
|
2884 | }
|
2885 | function _normalizeArray(obj) {
|
2886 | return obj || [];
|
2887 | }
|
2888 | class ProviderMeta {
|
2889 | constructor(token, { useClass, useValue, useExisting, useFactory, deps, multi }) {
|
2890 | this.token = token;
|
2891 | this.useClass = useClass || null;
|
2892 | this.useValue = useValue;
|
2893 | this.useExisting = useExisting;
|
2894 | this.useFactory = useFactory || null;
|
2895 | this.dependencies = deps || null;
|
2896 | this.multi = !!multi;
|
2897 | }
|
2898 | }
|
2899 | function flatten(list) {
|
2900 | return list.reduce((flat, item) => {
|
2901 | const flatItem = Array.isArray(item) ? flatten(item) : item;
|
2902 | return flat.concat(flatItem);
|
2903 | }, []);
|
2904 | }
|
2905 | function jitSourceUrl(url) {
|
2906 |
|
2907 |
|
2908 | return url.replace(/(\w+:\/\/[\w:-]+)?(\/+)?/, 'ng:///');
|
2909 | }
|
2910 | function templateSourceUrl(ngModuleType, compMeta, templateMeta) {
|
2911 | let url;
|
2912 | if (templateMeta.isInline) {
|
2913 | if (compMeta.type.reference instanceof StaticSymbol) {
|
2914 |
|
2915 |
|
2916 | url = `${compMeta.type.reference.filePath}.${compMeta.type.reference.name}.html`;
|
2917 | }
|
2918 | else {
|
2919 | url = `${identifierName(ngModuleType)}/${identifierName(compMeta.type)}.html`;
|
2920 | }
|
2921 | }
|
2922 | else {
|
2923 | url = templateMeta.templateUrl;
|
2924 | }
|
2925 | return compMeta.type.reference instanceof StaticSymbol ? url : jitSourceUrl(url);
|
2926 | }
|
2927 |
|
2928 | |
2929 |
|
2930 |
|
2931 |
|
2932 |
|
2933 |
|
2934 |
|
2935 | const CORE$1 = '@angular/core';
|
2936 | class Identifiers$1 {
|
2937 | }
|
2938 |
|
2939 | Identifiers$1.NEW_METHOD = 'factory';
|
2940 | Identifiers$1.TRANSFORM_METHOD = 'transform';
|
2941 | Identifiers$1.PATCH_DEPS = 'patchedDeps';
|
2942 | Identifiers$1.core = { name: null, moduleName: CORE$1 };
|
2943 |
|
2944 | Identifiers$1.namespaceHTML = { name: 'ɵɵnamespaceHTML', moduleName: CORE$1 };
|
2945 | Identifiers$1.namespaceMathML = { name: 'ɵɵnamespaceMathML', moduleName: CORE$1 };
|
2946 | Identifiers$1.namespaceSVG = { name: 'ɵɵnamespaceSVG', moduleName: CORE$1 };
|
2947 | Identifiers$1.element = { name: 'ɵɵelement', moduleName: CORE$1 };
|
2948 | Identifiers$1.elementStart = { name: 'ɵɵelementStart', moduleName: CORE$1 };
|
2949 | Identifiers$1.elementEnd = { name: 'ɵɵelementEnd', moduleName: CORE$1 };
|
2950 | Identifiers$1.advance = { name: 'ɵɵadvance', moduleName: CORE$1 };
|
2951 | Identifiers$1.syntheticHostProperty = { name: 'ɵɵsyntheticHostProperty', moduleName: CORE$1 };
|
2952 | Identifiers$1.syntheticHostListener = { name: 'ɵɵsyntheticHostListener', moduleName: CORE$1 };
|
2953 | Identifiers$1.attribute = { name: 'ɵɵattribute', moduleName: CORE$1 };
|
2954 | Identifiers$1.attributeInterpolate1 = { name: 'ɵɵattributeInterpolate1', moduleName: CORE$1 };
|
2955 | Identifiers$1.attributeInterpolate2 = { name: 'ɵɵattributeInterpolate2', moduleName: CORE$1 };
|
2956 | Identifiers$1.attributeInterpolate3 = { name: 'ɵɵattributeInterpolate3', moduleName: CORE$1 };
|
2957 | Identifiers$1.attributeInterpolate4 = { name: 'ɵɵattributeInterpolate4', moduleName: CORE$1 };
|
2958 | Identifiers$1.attributeInterpolate5 = { name: 'ɵɵattributeInterpolate5', moduleName: CORE$1 };
|
2959 | Identifiers$1.attributeInterpolate6 = { name: 'ɵɵattributeInterpolate6', moduleName: CORE$1 };
|
2960 | Identifiers$1.attributeInterpolate7 = { name: 'ɵɵattributeInterpolate7', moduleName: CORE$1 };
|
2961 | Identifiers$1.attributeInterpolate8 = { name: 'ɵɵattributeInterpolate8', moduleName: CORE$1 };
|
2962 | Identifiers$1.attributeInterpolateV = { name: 'ɵɵattributeInterpolateV', moduleName: CORE$1 };
|
2963 | Identifiers$1.classProp = { name: 'ɵɵclassProp', moduleName: CORE$1 };
|
2964 | Identifiers$1.elementContainerStart = { name: 'ɵɵelementContainerStart', moduleName: CORE$1 };
|
2965 | Identifiers$1.elementContainerEnd = { name: 'ɵɵelementContainerEnd', moduleName: CORE$1 };
|
2966 | Identifiers$1.elementContainer = { name: 'ɵɵelementContainer', moduleName: CORE$1 };
|
2967 | Identifiers$1.styleMap = { name: 'ɵɵstyleMap', moduleName: CORE$1 };
|
2968 | Identifiers$1.styleMapInterpolate1 = { name: 'ɵɵstyleMapInterpolate1', moduleName: CORE$1 };
|
2969 | Identifiers$1.styleMapInterpolate2 = { name: 'ɵɵstyleMapInterpolate2', moduleName: CORE$1 };
|
2970 | Identifiers$1.styleMapInterpolate3 = { name: 'ɵɵstyleMapInterpolate3', moduleName: CORE$1 };
|
2971 | Identifiers$1.styleMapInterpolate4 = { name: 'ɵɵstyleMapInterpolate4', moduleName: CORE$1 };
|
2972 | Identifiers$1.styleMapInterpolate5 = { name: 'ɵɵstyleMapInterpolate5', moduleName: CORE$1 };
|
2973 | Identifiers$1.styleMapInterpolate6 = { name: 'ɵɵstyleMapInterpolate6', moduleName: CORE$1 };
|
2974 | Identifiers$1.styleMapInterpolate7 = { name: 'ɵɵstyleMapInterpolate7', moduleName: CORE$1 };
|
2975 | Identifiers$1.styleMapInterpolate8 = { name: 'ɵɵstyleMapInterpolate8', moduleName: CORE$1 };
|
2976 | Identifiers$1.styleMapInterpolateV = { name: 'ɵɵstyleMapInterpolateV', moduleName: CORE$1 };
|
2977 | Identifiers$1.classMap = { name: 'ɵɵclassMap', moduleName: CORE$1 };
|
2978 | Identifiers$1.classMapInterpolate1 = { name: 'ɵɵclassMapInterpolate1', moduleName: CORE$1 };
|
2979 | Identifiers$1.classMapInterpolate2 = { name: 'ɵɵclassMapInterpolate2', moduleName: CORE$1 };
|
2980 | Identifiers$1.classMapInterpolate3 = { name: 'ɵɵclassMapInterpolate3', moduleName: CORE$1 };
|
2981 | Identifiers$1.classMapInterpolate4 = { name: 'ɵɵclassMapInterpolate4', moduleName: CORE$1 };
|
2982 | Identifiers$1.classMapInterpolate5 = { name: 'ɵɵclassMapInterpolate5', moduleName: CORE$1 };
|
2983 | Identifiers$1.classMapInterpolate6 = { name: 'ɵɵclassMapInterpolate6', moduleName: CORE$1 };
|
2984 | Identifiers$1.classMapInterpolate7 = { name: 'ɵɵclassMapInterpolate7', moduleName: CORE$1 };
|
2985 | Identifiers$1.classMapInterpolate8 = { name: 'ɵɵclassMapInterpolate8', moduleName: CORE$1 };
|
2986 | Identifiers$1.classMapInterpolateV = { name: 'ɵɵclassMapInterpolateV', moduleName: CORE$1 };
|
2987 | Identifiers$1.styleProp = { name: 'ɵɵstyleProp', moduleName: CORE$1 };
|
2988 | Identifiers$1.stylePropInterpolate1 = { name: 'ɵɵstylePropInterpolate1', moduleName: CORE$1 };
|
2989 | Identifiers$1.stylePropInterpolate2 = { name: 'ɵɵstylePropInterpolate2', moduleName: CORE$1 };
|
2990 | Identifiers$1.stylePropInterpolate3 = { name: 'ɵɵstylePropInterpolate3', moduleName: CORE$1 };
|
2991 | Identifiers$1.stylePropInterpolate4 = { name: 'ɵɵstylePropInterpolate4', moduleName: CORE$1 };
|
2992 | Identifiers$1.stylePropInterpolate5 = { name: 'ɵɵstylePropInterpolate5', moduleName: CORE$1 };
|
2993 | Identifiers$1.stylePropInterpolate6 = { name: 'ɵɵstylePropInterpolate6', moduleName: CORE$1 };
|
2994 | Identifiers$1.stylePropInterpolate7 = { name: 'ɵɵstylePropInterpolate7', moduleName: CORE$1 };
|
2995 | Identifiers$1.stylePropInterpolate8 = { name: 'ɵɵstylePropInterpolate8', moduleName: CORE$1 };
|
2996 | Identifiers$1.stylePropInterpolateV = { name: 'ɵɵstylePropInterpolateV', moduleName: CORE$1 };
|
2997 | Identifiers$1.nextContext = { name: 'ɵɵnextContext', moduleName: CORE$1 };
|
2998 | Identifiers$1.templateCreate = { name: 'ɵɵtemplate', moduleName: CORE$1 };
|
2999 | Identifiers$1.text = { name: 'ɵɵtext', moduleName: CORE$1 };
|
3000 | Identifiers$1.enableBindings = { name: 'ɵɵenableBindings', moduleName: CORE$1 };
|
3001 | Identifiers$1.disableBindings = { name: 'ɵɵdisableBindings', moduleName: CORE$1 };
|
3002 | Identifiers$1.getCurrentView = { name: 'ɵɵgetCurrentView', moduleName: CORE$1 };
|
3003 | Identifiers$1.textInterpolate = { name: 'ɵɵtextInterpolate', moduleName: CORE$1 };
|
3004 | Identifiers$1.textInterpolate1 = { name: 'ɵɵtextInterpolate1', moduleName: CORE$1 };
|
3005 | Identifiers$1.textInterpolate2 = { name: 'ɵɵtextInterpolate2', moduleName: CORE$1 };
|
3006 | Identifiers$1.textInterpolate3 = { name: 'ɵɵtextInterpolate3', moduleName: CORE$1 };
|
3007 | Identifiers$1.textInterpolate4 = { name: 'ɵɵtextInterpolate4', moduleName: CORE$1 };
|
3008 | Identifiers$1.textInterpolate5 = { name: 'ɵɵtextInterpolate5', moduleName: CORE$1 };
|
3009 | Identifiers$1.textInterpolate6 = { name: 'ɵɵtextInterpolate6', moduleName: CORE$1 };
|
3010 | Identifiers$1.textInterpolate7 = { name: 'ɵɵtextInterpolate7', moduleName: CORE$1 };
|
3011 | Identifiers$1.textInterpolate8 = { name: 'ɵɵtextInterpolate8', moduleName: CORE$1 };
|
3012 | Identifiers$1.textInterpolateV = { name: 'ɵɵtextInterpolateV', moduleName: CORE$1 };
|
3013 | Identifiers$1.restoreView = { name: 'ɵɵrestoreView', moduleName: CORE$1 };
|
3014 | Identifiers$1.pureFunction0 = { name: 'ɵɵpureFunction0', moduleName: CORE$1 };
|
3015 | Identifiers$1.pureFunction1 = { name: 'ɵɵpureFunction1', moduleName: CORE$1 };
|
3016 | Identifiers$1.pureFunction2 = { name: 'ɵɵpureFunction2', moduleName: CORE$1 };
|
3017 | Identifiers$1.pureFunction3 = { name: 'ɵɵpureFunction3', moduleName: CORE$1 };
|
3018 | Identifiers$1.pureFunction4 = { name: 'ɵɵpureFunction4', moduleName: CORE$1 };
|
3019 | Identifiers$1.pureFunction5 = { name: 'ɵɵpureFunction5', moduleName: CORE$1 };
|
3020 | Identifiers$1.pureFunction6 = { name: 'ɵɵpureFunction6', moduleName: CORE$1 };
|
3021 | Identifiers$1.pureFunction7 = { name: 'ɵɵpureFunction7', moduleName: CORE$1 };
|
3022 | Identifiers$1.pureFunction8 = { name: 'ɵɵpureFunction8', moduleName: CORE$1 };
|
3023 | Identifiers$1.pureFunctionV = { name: 'ɵɵpureFunctionV', moduleName: CORE$1 };
|
3024 | Identifiers$1.pipeBind1 = { name: 'ɵɵpipeBind1', moduleName: CORE$1 };
|
3025 | Identifiers$1.pipeBind2 = { name: 'ɵɵpipeBind2', moduleName: CORE$1 };
|
3026 | Identifiers$1.pipeBind3 = { name: 'ɵɵpipeBind3', moduleName: CORE$1 };
|
3027 | Identifiers$1.pipeBind4 = { name: 'ɵɵpipeBind4', moduleName: CORE$1 };
|
3028 | Identifiers$1.pipeBindV = { name: 'ɵɵpipeBindV', moduleName: CORE$1 };
|
3029 | Identifiers$1.hostProperty = { name: 'ɵɵhostProperty', moduleName: CORE$1 };
|
3030 | Identifiers$1.property = { name: 'ɵɵproperty', moduleName: CORE$1 };
|
3031 | Identifiers$1.propertyInterpolate = { name: 'ɵɵpropertyInterpolate', moduleName: CORE$1 };
|
3032 | Identifiers$1.propertyInterpolate1 = { name: 'ɵɵpropertyInterpolate1', moduleName: CORE$1 };
|
3033 | Identifiers$1.propertyInterpolate2 = { name: 'ɵɵpropertyInterpolate2', moduleName: CORE$1 };
|
3034 | Identifiers$1.propertyInterpolate3 = { name: 'ɵɵpropertyInterpolate3', moduleName: CORE$1 };
|
3035 | Identifiers$1.propertyInterpolate4 = { name: 'ɵɵpropertyInterpolate4', moduleName: CORE$1 };
|
3036 | Identifiers$1.propertyInterpolate5 = { name: 'ɵɵpropertyInterpolate5', moduleName: CORE$1 };
|
3037 | Identifiers$1.propertyInterpolate6 = { name: 'ɵɵpropertyInterpolate6', moduleName: CORE$1 };
|
3038 | Identifiers$1.propertyInterpolate7 = { name: 'ɵɵpropertyInterpolate7', moduleName: CORE$1 };
|
3039 | Identifiers$1.propertyInterpolate8 = { name: 'ɵɵpropertyInterpolate8', moduleName: CORE$1 };
|
3040 | Identifiers$1.propertyInterpolateV = { name: 'ɵɵpropertyInterpolateV', moduleName: CORE$1 };
|
3041 | Identifiers$1.i18n = { name: 'ɵɵi18n', moduleName: CORE$1 };
|
3042 | Identifiers$1.i18nAttributes = { name: 'ɵɵi18nAttributes', moduleName: CORE$1 };
|
3043 | Identifiers$1.i18nExp = { name: 'ɵɵi18nExp', moduleName: CORE$1 };
|
3044 | Identifiers$1.i18nStart = { name: 'ɵɵi18nStart', moduleName: CORE$1 };
|
3045 | Identifiers$1.i18nEnd = { name: 'ɵɵi18nEnd', moduleName: CORE$1 };
|
3046 | Identifiers$1.i18nApply = { name: 'ɵɵi18nApply', moduleName: CORE$1 };
|
3047 | Identifiers$1.i18nPostprocess = { name: 'ɵɵi18nPostprocess', moduleName: CORE$1 };
|
3048 | Identifiers$1.pipe = { name: 'ɵɵpipe', moduleName: CORE$1 };
|
3049 | Identifiers$1.projection = { name: 'ɵɵprojection', moduleName: CORE$1 };
|
3050 | Identifiers$1.projectionDef = { name: 'ɵɵprojectionDef', moduleName: CORE$1 };
|
3051 | Identifiers$1.reference = { name: 'ɵɵreference', moduleName: CORE$1 };
|
3052 | Identifiers$1.inject = { name: 'ɵɵinject', moduleName: CORE$1 };
|
3053 | Identifiers$1.injectAttribute = { name: 'ɵɵinjectAttribute', moduleName: CORE$1 };
|
3054 | Identifiers$1.injectPipeChangeDetectorRef = { name: 'ɵɵinjectPipeChangeDetectorRef', moduleName: CORE$1 };
|
3055 | Identifiers$1.directiveInject = { name: 'ɵɵdirectiveInject', moduleName: CORE$1 };
|
3056 | Identifiers$1.invalidFactory = { name: 'ɵɵinvalidFactory', moduleName: CORE$1 };
|
3057 | Identifiers$1.invalidFactoryDep = { name: 'ɵɵinvalidFactoryDep', moduleName: CORE$1 };
|
3058 | Identifiers$1.templateRefExtractor = { name: 'ɵɵtemplateRefExtractor', moduleName: CORE$1 };
|
3059 | Identifiers$1.forwardRef = { name: 'forwardRef', moduleName: CORE$1 };
|
3060 | Identifiers$1.resolveForwardRef = { name: 'resolveForwardRef', moduleName: CORE$1 };
|
3061 | Identifiers$1.resolveWindow = { name: 'ɵɵresolveWindow', moduleName: CORE$1 };
|
3062 | Identifiers$1.resolveDocument = { name: 'ɵɵresolveDocument', moduleName: CORE$1 };
|
3063 | Identifiers$1.resolveBody = { name: 'ɵɵresolveBody', moduleName: CORE$1 };
|
3064 | Identifiers$1.defineComponent = { name: 'ɵɵdefineComponent', moduleName: CORE$1 };
|
3065 | Identifiers$1.declareComponent = { name: 'ɵɵngDeclareComponent', moduleName: CORE$1 };
|
3066 | Identifiers$1.setComponentScope = { name: 'ɵɵsetComponentScope', moduleName: CORE$1 };
|
3067 | Identifiers$1.ChangeDetectionStrategy = {
|
3068 | name: 'ChangeDetectionStrategy',
|
3069 | moduleName: CORE$1,
|
3070 | };
|
3071 | Identifiers$1.ViewEncapsulation = {
|
3072 | name: 'ViewEncapsulation',
|
3073 | moduleName: CORE$1,
|
3074 | };
|
3075 | Identifiers$1.ComponentDefWithMeta = {
|
3076 | name: 'ɵɵComponentDefWithMeta',
|
3077 | moduleName: CORE$1,
|
3078 | };
|
3079 | Identifiers$1.FactoryDef = {
|
3080 | name: 'ɵɵFactoryDef',
|
3081 | moduleName: CORE$1,
|
3082 | };
|
3083 | Identifiers$1.defineDirective = { name: 'ɵɵdefineDirective', moduleName: CORE$1 };
|
3084 | Identifiers$1.declareDirective = { name: 'ɵɵngDeclareDirective', moduleName: CORE$1 };
|
3085 | Identifiers$1.DirectiveDefWithMeta = {
|
3086 | name: 'ɵɵDirectiveDefWithMeta',
|
3087 | moduleName: CORE$1,
|
3088 | };
|
3089 | Identifiers$1.InjectorDef = {
|
3090 | name: 'ɵɵInjectorDef',
|
3091 | moduleName: CORE$1,
|
3092 | };
|
3093 | Identifiers$1.defineInjector = {
|
3094 | name: 'ɵɵdefineInjector',
|
3095 | moduleName: CORE$1,
|
3096 | };
|
3097 | Identifiers$1.NgModuleDefWithMeta = {
|
3098 | name: 'ɵɵNgModuleDefWithMeta',
|
3099 | moduleName: CORE$1,
|
3100 | };
|
3101 | Identifiers$1.ModuleWithProviders = {
|
3102 | name: 'ModuleWithProviders',
|
3103 | moduleName: CORE$1,
|
3104 | };
|
3105 | Identifiers$1.defineNgModule = { name: 'ɵɵdefineNgModule', moduleName: CORE$1 };
|
3106 | Identifiers$1.setNgModuleScope = { name: 'ɵɵsetNgModuleScope', moduleName: CORE$1 };
|
3107 | Identifiers$1.PipeDefWithMeta = { name: 'ɵɵPipeDefWithMeta', moduleName: CORE$1 };
|
3108 | Identifiers$1.definePipe = { name: 'ɵɵdefinePipe', moduleName: CORE$1 };
|
3109 | Identifiers$1.queryRefresh = { name: 'ɵɵqueryRefresh', moduleName: CORE$1 };
|
3110 | Identifiers$1.viewQuery = { name: 'ɵɵviewQuery', moduleName: CORE$1 };
|
3111 | Identifiers$1.loadQuery = { name: 'ɵɵloadQuery', moduleName: CORE$1 };
|
3112 | Identifiers$1.contentQuery = { name: 'ɵɵcontentQuery', moduleName: CORE$1 };
|
3113 | Identifiers$1.NgOnChangesFeature = { name: 'ɵɵNgOnChangesFeature', moduleName: CORE$1 };
|
3114 | Identifiers$1.InheritDefinitionFeature = { name: 'ɵɵInheritDefinitionFeature', moduleName: CORE$1 };
|
3115 | Identifiers$1.CopyDefinitionFeature = { name: 'ɵɵCopyDefinitionFeature', moduleName: CORE$1 };
|
3116 | Identifiers$1.ProvidersFeature = { name: 'ɵɵProvidersFeature', moduleName: CORE$1 };
|
3117 | Identifiers$1.listener = { name: 'ɵɵlistener', moduleName: CORE$1 };
|
3118 | Identifiers$1.getFactoryOf = {
|
3119 | name: 'ɵɵgetFactoryOf',
|
3120 | moduleName: CORE$1,
|
3121 | };
|
3122 | Identifiers$1.getInheritedFactory = {
|
3123 | name: 'ɵɵgetInheritedFactory',
|
3124 | moduleName: CORE$1,
|
3125 | };
|
3126 |
|
3127 | Identifiers$1.sanitizeHtml = { name: 'ɵɵsanitizeHtml', moduleName: CORE$1 };
|
3128 | Identifiers$1.sanitizeStyle = { name: 'ɵɵsanitizeStyle', moduleName: CORE$1 };
|
3129 | Identifiers$1.sanitizeResourceUrl = { name: 'ɵɵsanitizeResourceUrl', moduleName: CORE$1 };
|
3130 | Identifiers$1.sanitizeScript = { name: 'ɵɵsanitizeScript', moduleName: CORE$1 };
|
3131 | Identifiers$1.sanitizeUrl = { name: 'ɵɵsanitizeUrl', moduleName: CORE$1 };
|
3132 | Identifiers$1.sanitizeUrlOrResourceUrl = { name: 'ɵɵsanitizeUrlOrResourceUrl', moduleName: CORE$1 };
|
3133 | Identifiers$1.trustConstantHtml = { name: 'ɵɵtrustConstantHtml', moduleName: CORE$1 };
|
3134 | Identifiers$1.trustConstantResourceUrl = { name: 'ɵɵtrustConstantResourceUrl', moduleName: CORE$1 };
|
3135 |
|
3136 | |
3137 |
|
3138 |
|
3139 |
|
3140 |
|
3141 |
|
3142 |
|
3143 |
|
3144 | const VERSION = 3;
|
3145 | const JS_B64_PREFIX = '# sourceMappingURL=data:application/json;base64,';
|
3146 | class SourceMapGenerator {
|
3147 | constructor(file = null) {
|
3148 | this.file = file;
|
3149 | this.sourcesContent = new Map();
|
3150 | this.lines = [];
|
3151 | this.lastCol0 = 0;
|
3152 | this.hasMappings = false;
|
3153 | }
|
3154 |
|
3155 | addSource(url, content = null) {
|
3156 | if (!this.sourcesContent.has(url)) {
|
3157 | this.sourcesContent.set(url, content);
|
3158 | }
|
3159 | return this;
|
3160 | }
|
3161 | addLine() {
|
3162 | this.lines.push([]);
|
3163 | this.lastCol0 = 0;
|
3164 | return this;
|
3165 | }
|
3166 | addMapping(col0, sourceUrl, sourceLine0, sourceCol0) {
|
3167 | if (!this.currentLine) {
|
3168 | throw new Error(`A line must be added before mappings can be added`);
|
3169 | }
|
3170 | if (sourceUrl != null && !this.sourcesContent.has(sourceUrl)) {
|
3171 | throw new Error(`Unknown source file "${sourceUrl}"`);
|
3172 | }
|
3173 | if (col0 == null) {
|
3174 | throw new Error(`The column in the generated code must be provided`);
|
3175 | }
|
3176 | if (col0 < this.lastCol0) {
|
3177 | throw new Error(`Mapping should be added in output order`);
|
3178 | }
|
3179 | if (sourceUrl && (sourceLine0 == null || sourceCol0 == null)) {
|
3180 | throw new Error(`The source location must be provided when a source url is provided`);
|
3181 | }
|
3182 | this.hasMappings = true;
|
3183 | this.lastCol0 = col0;
|
3184 | this.currentLine.push({ col0, sourceUrl, sourceLine0, sourceCol0 });
|
3185 | return this;
|
3186 | }
|
3187 | |
3188 |
|
3189 |
|
3190 |
|
3191 | get currentLine() {
|
3192 | return this.lines.slice(-1)[0];
|
3193 | }
|
3194 | toJSON() {
|
3195 | if (!this.hasMappings) {
|
3196 | return null;
|
3197 | }
|
3198 | const sourcesIndex = new Map();
|
3199 | const sources = [];
|
3200 | const sourcesContent = [];
|
3201 | Array.from(this.sourcesContent.keys()).forEach((url, i) => {
|
3202 | sourcesIndex.set(url, i);
|
3203 | sources.push(url);
|
3204 | sourcesContent.push(this.sourcesContent.get(url) || null);
|
3205 | });
|
3206 | let mappings = '';
|
3207 | let lastCol0 = 0;
|
3208 | let lastSourceIndex = 0;
|
3209 | let lastSourceLine0 = 0;
|
3210 | let lastSourceCol0 = 0;
|
3211 | this.lines.forEach(segments => {
|
3212 | lastCol0 = 0;
|
3213 | mappings += segments
|
3214 | .map(segment => {
|
3215 |
|
3216 | let segAsStr = toBase64VLQ(segment.col0 - lastCol0);
|
3217 | lastCol0 = segment.col0;
|
3218 | if (segment.sourceUrl != null) {
|
3219 |
|
3220 | segAsStr +=
|
3221 | toBase64VLQ(sourcesIndex.get(segment.sourceUrl) - lastSourceIndex);
|
3222 | lastSourceIndex = sourcesIndex.get(segment.sourceUrl);
|
3223 |
|
3224 | segAsStr += toBase64VLQ(segment.sourceLine0 - lastSourceLine0);
|
3225 | lastSourceLine0 = segment.sourceLine0;
|
3226 |
|
3227 | segAsStr += toBase64VLQ(segment.sourceCol0 - lastSourceCol0);
|
3228 | lastSourceCol0 = segment.sourceCol0;
|
3229 | }
|
3230 | return segAsStr;
|
3231 | })
|
3232 | .join(',');
|
3233 | mappings += ';';
|
3234 | });
|
3235 | mappings = mappings.slice(0, -1);
|
3236 | return {
|
3237 | 'file': this.file || '',
|
3238 | 'version': VERSION,
|
3239 | 'sourceRoot': '',
|
3240 | 'sources': sources,
|
3241 | 'sourcesContent': sourcesContent,
|
3242 | 'mappings': mappings,
|
3243 | };
|
3244 | }
|
3245 | toJsComment() {
|
3246 | return this.hasMappings ? '//' + JS_B64_PREFIX + toBase64String(JSON.stringify(this, null, 0)) :
|
3247 | '';
|
3248 | }
|
3249 | }
|
3250 | function toBase64String(value) {
|
3251 | let b64 = '';
|
3252 | const encoded = utf8Encode(value);
|
3253 | for (let i = 0; i < encoded.length;) {
|
3254 | const i1 = encoded[i++];
|
3255 | const i2 = i < encoded.length ? encoded[i++] : null;
|
3256 | const i3 = i < encoded.length ? encoded[i++] : null;
|
3257 | b64 += toBase64Digit(i1 >> 2);
|
3258 | b64 += toBase64Digit(((i1 & 3) << 4) | (i2 === null ? 0 : i2 >> 4));
|
3259 | b64 += i2 === null ? '=' : toBase64Digit(((i2 & 15) << 2) | (i3 === null ? 0 : i3 >> 6));
|
3260 | b64 += i2 === null || i3 === null ? '=' : toBase64Digit(i3 & 63);
|
3261 | }
|
3262 | return b64;
|
3263 | }
|
3264 | function toBase64VLQ(value) {
|
3265 | value = value < 0 ? ((-value) << 1) + 1 : value << 1;
|
3266 | let out = '';
|
3267 | do {
|
3268 | let digit = value & 31;
|
3269 | value = value >> 5;
|
3270 | if (value > 0) {
|
3271 | digit = digit | 32;
|
3272 | }
|
3273 | out += toBase64Digit(digit);
|
3274 | } while (value > 0);
|
3275 | return out;
|
3276 | }
|
3277 | const B64_DIGITS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
3278 | function toBase64Digit(value) {
|
3279 | if (value < 0 || value >= 64) {
|
3280 | throw new Error(`Can only encode value in the range [0, 63]`);
|
3281 | }
|
3282 | return B64_DIGITS[value];
|
3283 | }
|
3284 |
|
3285 | |
3286 |
|
3287 |
|
3288 |
|
3289 |
|
3290 |
|
3291 |
|
3292 | const _SINGLE_QUOTE_ESCAPE_STRING_RE = /'|\\|\n|\r|\$/g;
|
3293 | const _LEGAL_IDENTIFIER_RE = /^[$A-Z_][0-9A-Z_$]*$/i;
|
3294 | const _INDENT_WITH = ' ';
|
3295 | const CATCH_ERROR_VAR$1 = variable('error', null, null);
|
3296 | const CATCH_STACK_VAR$1 = variable('stack', null, null);
|
3297 | class _EmittedLine {
|
3298 | constructor(indent) {
|
3299 | this.indent = indent;
|
3300 | this.partsLength = 0;
|
3301 | this.parts = [];
|
3302 | this.srcSpans = [];
|
3303 | }
|
3304 | }
|
3305 | class EmitterVisitorContext {
|
3306 | constructor(_indent) {
|
3307 | this._indent = _indent;
|
3308 | this._classes = [];
|
3309 | this._preambleLineCount = 0;
|
3310 | this._lines = [new _EmittedLine(_indent)];
|
3311 | }
|
3312 | static createRoot() {
|
3313 | return new EmitterVisitorContext(0);
|
3314 | }
|
3315 | |
3316 |
|
3317 |
|
3318 |
|
3319 | get _currentLine() {
|
3320 | return this._lines[this._lines.length - 1];
|
3321 | }
|
3322 | println(from, lastPart = '') {
|
3323 | this.print(from || null, lastPart, true);
|
3324 | }
|
3325 | lineIsEmpty() {
|
3326 | return this._currentLine.parts.length === 0;
|
3327 | }
|
3328 | lineLength() {
|
3329 | return this._currentLine.indent * _INDENT_WITH.length + this._currentLine.partsLength;
|
3330 | }
|
3331 | print(from, part, newLine = false) {
|
3332 | if (part.length > 0) {
|
3333 | this._currentLine.parts.push(part);
|
3334 | this._currentLine.partsLength += part.length;
|
3335 | this._currentLine.srcSpans.push(from && from.sourceSpan || null);
|
3336 | }
|
3337 | if (newLine) {
|
3338 | this._lines.push(new _EmittedLine(this._indent));
|
3339 | }
|
3340 | }
|
3341 | removeEmptyLastLine() {
|
3342 | if (this.lineIsEmpty()) {
|
3343 | this._lines.pop();
|
3344 | }
|
3345 | }
|
3346 | incIndent() {
|
3347 | this._indent++;
|
3348 | if (this.lineIsEmpty()) {
|
3349 | this._currentLine.indent = this._indent;
|
3350 | }
|
3351 | }
|
3352 | decIndent() {
|
3353 | this._indent--;
|
3354 | if (this.lineIsEmpty()) {
|
3355 | this._currentLine.indent = this._indent;
|
3356 | }
|
3357 | }
|
3358 | pushClass(clazz) {
|
3359 | this._classes.push(clazz);
|
3360 | }
|
3361 | popClass() {
|
3362 | return this._classes.pop();
|
3363 | }
|
3364 | get currentClass() {
|
3365 | return this._classes.length > 0 ? this._classes[this._classes.length - 1] : null;
|
3366 | }
|
3367 | toSource() {
|
3368 | return this.sourceLines
|
3369 | .map(l => l.parts.length > 0 ? _createIndent(l.indent) + l.parts.join('') : '')
|
3370 | .join('\n');
|
3371 | }
|
3372 | toSourceMapGenerator(genFilePath, startsAtLine = 0) {
|
3373 | const map = new SourceMapGenerator(genFilePath);
|
3374 | let firstOffsetMapped = false;
|
3375 | const mapFirstOffsetIfNeeded = () => {
|
3376 | if (!firstOffsetMapped) {
|
3377 |
|
3378 |
|
3379 |
|
3380 | map.addSource(genFilePath, ' ').addMapping(0, genFilePath, 0, 0);
|
3381 | firstOffsetMapped = true;
|
3382 | }
|
3383 | };
|
3384 | for (let i = 0; i < startsAtLine; i++) {
|
3385 | map.addLine();
|
3386 | mapFirstOffsetIfNeeded();
|
3387 | }
|
3388 | this.sourceLines.forEach((line, lineIdx) => {
|
3389 | map.addLine();
|
3390 | const spans = line.srcSpans;
|
3391 | const parts = line.parts;
|
3392 | let col0 = line.indent * _INDENT_WITH.length;
|
3393 | let spanIdx = 0;
|
3394 |
|
3395 | while (spanIdx < spans.length && !spans[spanIdx]) {
|
3396 | col0 += parts[spanIdx].length;
|
3397 | spanIdx++;
|
3398 | }
|
3399 | if (spanIdx < spans.length && lineIdx === 0 && col0 === 0) {
|
3400 | firstOffsetMapped = true;
|
3401 | }
|
3402 | else {
|
3403 | mapFirstOffsetIfNeeded();
|
3404 | }
|
3405 | while (spanIdx < spans.length) {
|
3406 | const span = spans[spanIdx];
|
3407 | const source = span.start.file;
|
3408 | const sourceLine = span.start.line;
|
3409 | const sourceCol = span.start.col;
|
3410 | map.addSource(source.url, source.content)
|
3411 | .addMapping(col0, source.url, sourceLine, sourceCol);
|
3412 | col0 += parts[spanIdx].length;
|
3413 | spanIdx++;
|
3414 |
|
3415 | while (spanIdx < spans.length && (span === spans[spanIdx] || !spans[spanIdx])) {
|
3416 | col0 += parts[spanIdx].length;
|
3417 | spanIdx++;
|
3418 | }
|
3419 | }
|
3420 | });
|
3421 | return map;
|
3422 | }
|
3423 | setPreambleLineCount(count) {
|
3424 | return this._preambleLineCount = count;
|
3425 | }
|
3426 | spanOf(line, column) {
|
3427 | const emittedLine = this._lines[line - this._preambleLineCount];
|
3428 | if (emittedLine) {
|
3429 | let columnsLeft = column - _createIndent(emittedLine.indent).length;
|
3430 | for (let partIndex = 0; partIndex < emittedLine.parts.length; partIndex++) {
|
3431 | const part = emittedLine.parts[partIndex];
|
3432 | if (part.length > columnsLeft) {
|
3433 | return emittedLine.srcSpans[partIndex];
|
3434 | }
|
3435 | columnsLeft -= part.length;
|
3436 | }
|
3437 | }
|
3438 | return null;
|
3439 | }
|
3440 | |
3441 |
|
3442 |
|
3443 |
|
3444 | get sourceLines() {
|
3445 | if (this._lines.length && this._lines[this._lines.length - 1].parts.length === 0) {
|
3446 | return this._lines.slice(0, -1);
|
3447 | }
|
3448 | return this._lines;
|
3449 | }
|
3450 | }
|
3451 | class AbstractEmitterVisitor {
|
3452 | constructor(_escapeDollarInStrings) {
|
3453 | this._escapeDollarInStrings = _escapeDollarInStrings;
|
3454 | }
|
3455 | printLeadingComments(stmt, ctx) {
|
3456 | if (stmt.leadingComments === undefined) {
|
3457 | return;
|
3458 | }
|
3459 | for (const comment of stmt.leadingComments) {
|
3460 | if (comment instanceof JSDocComment) {
|
3461 | ctx.print(stmt, `/*${comment.toString()}*/`, comment.trailingNewline);
|
3462 | }
|
3463 | else {
|
3464 | if (comment.multiline) {
|
3465 | ctx.print(stmt, `/* ${comment.text} */`, comment.trailingNewline);
|
3466 | }
|
3467 | else {
|
3468 | comment.text.split('\n').forEach((line) => {
|
3469 | ctx.println(stmt, `// ${line}`);
|
3470 | });
|
3471 | }
|
3472 | }
|
3473 | }
|
3474 | }
|
3475 | visitExpressionStmt(stmt, ctx) {
|
3476 | this.printLeadingComments(stmt, ctx);
|
3477 | stmt.expr.visitExpression(this, ctx);
|
3478 | ctx.println(stmt, ';');
|
3479 | return null;
|
3480 | }
|
3481 | visitReturnStmt(stmt, ctx) {
|
3482 | this.printLeadingComments(stmt, ctx);
|
3483 | ctx.print(stmt, `return `);
|
3484 | stmt.value.visitExpression(this, ctx);
|
3485 | ctx.println(stmt, ';');
|
3486 | return null;
|
3487 | }
|
3488 | visitIfStmt(stmt, ctx) {
|
3489 | this.printLeadingComments(stmt, ctx);
|
3490 | ctx.print(stmt, `if (`);
|
3491 | stmt.condition.visitExpression(this, ctx);
|
3492 | ctx.print(stmt, `) {`);
|
3493 | const hasElseCase = stmt.falseCase != null && stmt.falseCase.length > 0;
|
3494 | if (stmt.trueCase.length <= 1 && !hasElseCase) {
|
3495 | ctx.print(stmt, ` `);
|
3496 | this.visitAllStatements(stmt.trueCase, ctx);
|
3497 | ctx.removeEmptyLastLine();
|
3498 | ctx.print(stmt, ` `);
|
3499 | }
|
3500 | else {
|
3501 | ctx.println();
|
3502 | ctx.incIndent();
|
3503 | this.visitAllStatements(stmt.trueCase, ctx);
|
3504 | ctx.decIndent();
|
3505 | if (hasElseCase) {
|
3506 | ctx.println(stmt, `} else {`);
|
3507 | ctx.incIndent();
|
3508 | this.visitAllStatements(stmt.falseCase, ctx);
|
3509 | ctx.decIndent();
|
3510 | }
|
3511 | }
|
3512 | ctx.println(stmt, `}`);
|
3513 | return null;
|
3514 | }
|
3515 | visitThrowStmt(stmt, ctx) {
|
3516 | this.printLeadingComments(stmt, ctx);
|
3517 | ctx.print(stmt, `throw `);
|
3518 | stmt.error.visitExpression(this, ctx);
|
3519 | ctx.println(stmt, `;`);
|
3520 | return null;
|
3521 | }
|
3522 | visitWriteVarExpr(expr, ctx) {
|
3523 | const lineWasEmpty = ctx.lineIsEmpty();
|
3524 | if (!lineWasEmpty) {
|
3525 | ctx.print(expr, '(');
|
3526 | }
|
3527 | ctx.print(expr, `${expr.name} = `);
|
3528 | expr.value.visitExpression(this, ctx);
|
3529 | if (!lineWasEmpty) {
|
3530 | ctx.print(expr, ')');
|
3531 | }
|
3532 | return null;
|
3533 | }
|
3534 | visitWriteKeyExpr(expr, ctx) {
|
3535 | const lineWasEmpty = ctx.lineIsEmpty();
|
3536 | if (!lineWasEmpty) {
|
3537 | ctx.print(expr, '(');
|
3538 | }
|
3539 | expr.receiver.visitExpression(this, ctx);
|
3540 | ctx.print(expr, `[`);
|
3541 | expr.index.visitExpression(this, ctx);
|
3542 | ctx.print(expr, `] = `);
|
3543 | expr.value.visitExpression(this, ctx);
|
3544 | if (!lineWasEmpty) {
|
3545 | ctx.print(expr, ')');
|
3546 | }
|
3547 | return null;
|
3548 | }
|
3549 | visitWritePropExpr(expr, ctx) {
|
3550 | const lineWasEmpty = ctx.lineIsEmpty();
|
3551 | if (!lineWasEmpty) {
|
3552 | ctx.print(expr, '(');
|
3553 | }
|
3554 | expr.receiver.visitExpression(this, ctx);
|
3555 | ctx.print(expr, `.${expr.name} = `);
|
3556 | expr.value.visitExpression(this, ctx);
|
3557 | if (!lineWasEmpty) {
|
3558 | ctx.print(expr, ')');
|
3559 | }
|
3560 | return null;
|
3561 | }
|
3562 | visitInvokeMethodExpr(expr, ctx) {
|
3563 | expr.receiver.visitExpression(this, ctx);
|
3564 | let name = expr.name;
|
3565 | if (expr.builtin != null) {
|
3566 | name = this.getBuiltinMethodName(expr.builtin);
|
3567 | if (name == null) {
|
3568 |
|
3569 | return null;
|
3570 | }
|
3571 | }
|
3572 | ctx.print(expr, `.${name}(`);
|
3573 | this.visitAllExpressions(expr.args, ctx, `,`);
|
3574 | ctx.print(expr, `)`);
|
3575 | return null;
|
3576 | }
|
3577 | visitInvokeFunctionExpr(expr, ctx) {
|
3578 | expr.fn.visitExpression(this, ctx);
|
3579 | ctx.print(expr, `(`);
|
3580 | this.visitAllExpressions(expr.args, ctx, ',');
|
3581 | ctx.print(expr, `)`);
|
3582 | return null;
|
3583 | }
|
3584 | visitTaggedTemplateExpr(expr, ctx) {
|
3585 | expr.tag.visitExpression(this, ctx);
|
3586 | ctx.print(expr, '`' + expr.template.elements[0].rawText);
|
3587 | for (let i = 1; i < expr.template.elements.length; i++) {
|
3588 | ctx.print(expr, '${');
|
3589 | expr.template.expressions[i - 1].visitExpression(this, ctx);
|
3590 | ctx.print(expr, `}${expr.template.elements[i].rawText}`);
|
3591 | }
|
3592 | ctx.print(expr, '`');
|
3593 | return null;
|
3594 | }
|
3595 | visitWrappedNodeExpr(ast, ctx) {
|
3596 | throw new Error('Abstract emitter cannot visit WrappedNodeExpr.');
|
3597 | }
|
3598 | visitTypeofExpr(expr, ctx) {
|
3599 | ctx.print(expr, 'typeof ');
|
3600 | expr.expr.visitExpression(this, ctx);
|
3601 | }
|
3602 | visitReadVarExpr(ast, ctx) {
|
3603 | let varName = ast.name;
|
3604 | if (ast.builtin != null) {
|
3605 | switch (ast.builtin) {
|
3606 | case BuiltinVar.Super:
|
3607 | varName = 'super';
|
3608 | break;
|
3609 | case BuiltinVar.This:
|
3610 | varName = 'this';
|
3611 | break;
|
3612 | case BuiltinVar.CatchError:
|
3613 | varName = CATCH_ERROR_VAR$1.name;
|
3614 | break;
|
3615 | case BuiltinVar.CatchStack:
|
3616 | varName = CATCH_STACK_VAR$1.name;
|
3617 | break;
|
3618 | default:
|
3619 | throw new Error(`Unknown builtin variable ${ast.builtin}`);
|
3620 | }
|
3621 | }
|
3622 | ctx.print(ast, varName);
|
3623 | return null;
|
3624 | }
|
3625 | visitInstantiateExpr(ast, ctx) {
|
3626 | ctx.print(ast, `new `);
|
3627 | ast.classExpr.visitExpression(this, ctx);
|
3628 | ctx.print(ast, `(`);
|
3629 | this.visitAllExpressions(ast.args, ctx, ',');
|
3630 | ctx.print(ast, `)`);
|
3631 | return null;
|
3632 | }
|
3633 | visitLiteralExpr(ast, ctx) {
|
3634 | const value = ast.value;
|
3635 | if (typeof value === 'string') {
|
3636 | ctx.print(ast, escapeIdentifier(value, this._escapeDollarInStrings));
|
3637 | }
|
3638 | else {
|
3639 | ctx.print(ast, `${value}`);
|
3640 | }
|
3641 | return null;
|
3642 | }
|
3643 | visitLocalizedString(ast, ctx) {
|
3644 | const head = ast.serializeI18nHead();
|
3645 | ctx.print(ast, '$localize `' + head.raw);
|
3646 | for (let i = 1; i < ast.messageParts.length; i++) {
|
3647 | ctx.print(ast, '${');
|
3648 | ast.expressions[i - 1].visitExpression(this, ctx);
|
3649 | ctx.print(ast, `}${ast.serializeI18nTemplatePart(i).raw}`);
|
3650 | }
|
3651 | ctx.print(ast, '`');
|
3652 | return null;
|
3653 | }
|
3654 | visitConditionalExpr(ast, ctx) {
|
3655 | ctx.print(ast, `(`);
|
3656 | ast.condition.visitExpression(this, ctx);
|
3657 | ctx.print(ast, '? ');
|
3658 | ast.trueCase.visitExpression(this, ctx);
|
3659 | ctx.print(ast, ': ');
|
3660 | ast.falseCase.visitExpression(this, ctx);
|
3661 | ctx.print(ast, `)`);
|
3662 | return null;
|
3663 | }
|
3664 | visitNotExpr(ast, ctx) {
|
3665 | ctx.print(ast, '!');
|
3666 | ast.condition.visitExpression(this, ctx);
|
3667 | return null;
|
3668 | }
|
3669 | visitAssertNotNullExpr(ast, ctx) {
|
3670 | ast.condition.visitExpression(this, ctx);
|
3671 | return null;
|
3672 | }
|
3673 | visitUnaryOperatorExpr(ast, ctx) {
|
3674 | let opStr;
|
3675 | switch (ast.operator) {
|
3676 | case UnaryOperator.Plus:
|
3677 | opStr = '+';
|
3678 | break;
|
3679 | case UnaryOperator.Minus:
|
3680 | opStr = '-';
|
3681 | break;
|
3682 | default:
|
3683 | throw new Error(`Unknown operator ${ast.operator}`);
|
3684 | }
|
3685 | if (ast.parens)
|
3686 | ctx.print(ast, `(`);
|
3687 | ctx.print(ast, opStr);
|
3688 | ast.expr.visitExpression(this, ctx);
|
3689 | if (ast.parens)
|
3690 | ctx.print(ast, `)`);
|
3691 | return null;
|
3692 | }
|
3693 | visitBinaryOperatorExpr(ast, ctx) {
|
3694 | let opStr;
|
3695 | switch (ast.operator) {
|
3696 | case BinaryOperator.Equals:
|
3697 | opStr = '==';
|
3698 | break;
|
3699 | case BinaryOperator.Identical:
|
3700 | opStr = '===';
|
3701 | break;
|
3702 | case BinaryOperator.NotEquals:
|
3703 | opStr = '!=';
|
3704 | break;
|
3705 | case BinaryOperator.NotIdentical:
|
3706 | opStr = '!==';
|
3707 | break;
|
3708 | case BinaryOperator.And:
|
3709 | opStr = '&&';
|
3710 | break;
|
3711 | case BinaryOperator.BitwiseAnd:
|
3712 | opStr = '&';
|
3713 | break;
|
3714 | case BinaryOperator.Or:
|
3715 | opStr = '||';
|
3716 | break;
|
3717 | case BinaryOperator.Plus:
|
3718 | opStr = '+';
|
3719 | break;
|
3720 | case BinaryOperator.Minus:
|
3721 | opStr = '-';
|
3722 | break;
|
3723 | case BinaryOperator.Divide:
|
3724 | opStr = '/';
|
3725 | break;
|
3726 | case BinaryOperator.Multiply:
|
3727 | opStr = '*';
|
3728 | break;
|
3729 | case BinaryOperator.Modulo:
|
3730 | opStr = '%';
|
3731 | break;
|
3732 | case BinaryOperator.Lower:
|
3733 | opStr = '<';
|
3734 | break;
|
3735 | case BinaryOperator.LowerEquals:
|
3736 | opStr = '<=';
|
3737 | break;
|
3738 | case BinaryOperator.Bigger:
|
3739 | opStr = '>';
|
3740 | break;
|
3741 | case BinaryOperator.BiggerEquals:
|
3742 | opStr = '>=';
|
3743 | break;
|
3744 | default:
|
3745 | throw new Error(`Unknown operator ${ast.operator}`);
|
3746 | }
|
3747 | if (ast.parens)
|
3748 | ctx.print(ast, `(`);
|
3749 | ast.lhs.visitExpression(this, ctx);
|
3750 | ctx.print(ast, ` ${opStr} `);
|
3751 | ast.rhs.visitExpression(this, ctx);
|
3752 | if (ast.parens)
|
3753 | ctx.print(ast, `)`);
|
3754 | return null;
|
3755 | }
|
3756 | visitReadPropExpr(ast, ctx) {
|
3757 | ast.receiver.visitExpression(this, ctx);
|
3758 | ctx.print(ast, `.`);
|
3759 | ctx.print(ast, ast.name);
|
3760 | return null;
|
3761 | }
|
3762 | visitReadKeyExpr(ast, ctx) {
|
3763 | ast.receiver.visitExpression(this, ctx);
|
3764 | ctx.print(ast, `[`);
|
3765 | ast.index.visitExpression(this, ctx);
|
3766 | ctx.print(ast, `]`);
|
3767 | return null;
|
3768 | }
|
3769 | visitLiteralArrayExpr(ast, ctx) {
|
3770 | ctx.print(ast, `[`);
|
3771 | this.visitAllExpressions(ast.entries, ctx, ',');
|
3772 | ctx.print(ast, `]`);
|
3773 | return null;
|
3774 | }
|
3775 | visitLiteralMapExpr(ast, ctx) {
|
3776 | ctx.print(ast, `{`);
|
3777 | this.visitAllObjects(entry => {
|
3778 | ctx.print(ast, `${escapeIdentifier(entry.key, this._escapeDollarInStrings, entry.quoted)}:`);
|
3779 | entry.value.visitExpression(this, ctx);
|
3780 | }, ast.entries, ctx, ',');
|
3781 | ctx.print(ast, `}`);
|
3782 | return null;
|
3783 | }
|
3784 | visitCommaExpr(ast, ctx) {
|
3785 | ctx.print(ast, '(');
|
3786 | this.visitAllExpressions(ast.parts, ctx, ',');
|
3787 | ctx.print(ast, ')');
|
3788 | return null;
|
3789 | }
|
3790 | visitAllExpressions(expressions, ctx, separator) {
|
3791 | this.visitAllObjects(expr => expr.visitExpression(this, ctx), expressions, ctx, separator);
|
3792 | }
|
3793 | visitAllObjects(handler, expressions, ctx, separator) {
|
3794 | let incrementedIndent = false;
|
3795 | for (let i = 0; i < expressions.length; i++) {
|
3796 | if (i > 0) {
|
3797 | if (ctx.lineLength() > 80) {
|
3798 | ctx.print(null, separator, true);
|
3799 | if (!incrementedIndent) {
|
3800 |
|
3801 | ctx.incIndent();
|
3802 | ctx.incIndent();
|
3803 | incrementedIndent = true;
|
3804 | }
|
3805 | }
|
3806 | else {
|
3807 | ctx.print(null, separator, false);
|
3808 | }
|
3809 | }
|
3810 | handler(expressions[i]);
|
3811 | }
|
3812 | if (incrementedIndent) {
|
3813 |
|
3814 | ctx.decIndent();
|
3815 | ctx.decIndent();
|
3816 | }
|
3817 | }
|
3818 | visitAllStatements(statements, ctx) {
|
3819 | statements.forEach((stmt) => stmt.visitStatement(this, ctx));
|
3820 | }
|
3821 | }
|
3822 | function escapeIdentifier(input, escapeDollar, alwaysQuote = true) {
|
3823 | if (input == null) {
|
3824 | return null;
|
3825 | }
|
3826 | const body = input.replace(_SINGLE_QUOTE_ESCAPE_STRING_RE, (...match) => {
|
3827 | if (match[0] == '$') {
|
3828 | return escapeDollar ? '\\$' : '$';
|
3829 | }
|
3830 | else if (match[0] == '\n') {
|
3831 | return '\\n';
|
3832 | }
|
3833 | else if (match[0] == '\r') {
|
3834 | return '\\r';
|
3835 | }
|
3836 | else {
|
3837 | return `\\${match[0]}`;
|
3838 | }
|
3839 | });
|
3840 | const requiresQuotes = alwaysQuote || !_LEGAL_IDENTIFIER_RE.test(body);
|
3841 | return requiresQuotes ? `'${body}'` : body;
|
3842 | }
|
3843 | function _createIndent(count) {
|
3844 | let res = '';
|
3845 | for (let i = 0; i < count; i++) {
|
3846 | res += _INDENT_WITH;
|
3847 | }
|
3848 | return res;
|
3849 | }
|
3850 |
|
3851 | |
3852 |
|
3853 |
|
3854 |
|
3855 |
|
3856 |
|
3857 |
|
3858 | |
3859 |
|
3860 |
|
3861 | function mapToMapExpression(map) {
|
3862 | const result = Object.keys(map).map(key => ({
|
3863 | key,
|
3864 |
|
3865 |
|
3866 | value: map[key],
|
3867 | quoted: false,
|
3868 | }));
|
3869 | return literalMap(result);
|
3870 | }
|
3871 | function typeWithParameters(type, numParams) {
|
3872 | if (numParams === 0) {
|
3873 | return expressionType(type);
|
3874 | }
|
3875 | const params = [];
|
3876 | for (let i = 0; i < numParams; i++) {
|
3877 | params.push(DYNAMIC_TYPE);
|
3878 | }
|
3879 | return expressionType(type, undefined, params);
|
3880 | }
|
3881 | const ANIMATE_SYMBOL_PREFIX = '@';
|
3882 | function prepareSyntheticPropertyName(name) {
|
3883 | return `${ANIMATE_SYMBOL_PREFIX}${name}`;
|
3884 | }
|
3885 | function prepareSyntheticListenerName(name, phase) {
|
3886 | return `${ANIMATE_SYMBOL_PREFIX}${name}.${phase}`;
|
3887 | }
|
3888 | function getSafePropertyAccessString(accessor, name) {
|
3889 | const escapedName = escapeIdentifier(name, false, false);
|
3890 | return escapedName !== name ? `${accessor}[${escapedName}]` : `${accessor}.${name}`;
|
3891 | }
|
3892 | function prepareSyntheticListenerFunctionName(name, phase) {
|
3893 | return `animation_${name}_${phase}`;
|
3894 | }
|
3895 | function jitOnlyGuardedExpression(expr) {
|
3896 | return guardedExpression('ngJitMode', expr);
|
3897 | }
|
3898 | function guardedExpression(guard, expr) {
|
3899 | const guardExpr = new ExternalExpr({ name: guard, moduleName: null });
|
3900 | const guardNotDefined = new BinaryOperatorExpr(BinaryOperator.Identical, new TypeofExpr(guardExpr), literal('undefined'));
|
3901 | const guardUndefinedOrTrue = new BinaryOperatorExpr(BinaryOperator.Or, guardNotDefined, guardExpr, undefined,
|
3902 | undefined, true);
|
3903 | return new BinaryOperatorExpr(BinaryOperator.And, guardUndefinedOrTrue, expr);
|
3904 | }
|
3905 |
|
3906 | |
3907 |
|
3908 |
|
3909 |
|
3910 |
|
3911 |
|
3912 |
|
3913 | class Text {
|
3914 | constructor(value, sourceSpan) {
|
3915 | this.value = value;
|
3916 | this.sourceSpan = sourceSpan;
|
3917 | }
|
3918 | visit(visitor) {
|
3919 | return visitor.visitText(this);
|
3920 | }
|
3921 | }
|
3922 | class BoundText {
|
3923 | constructor(value, sourceSpan, i18n) {
|
3924 | this.value = value;
|
3925 | this.sourceSpan = sourceSpan;
|
3926 | this.i18n = i18n;
|
3927 | }
|
3928 | visit(visitor) {
|
3929 | return visitor.visitBoundText(this);
|
3930 | }
|
3931 | }
|
3932 | |
3933 |
|
3934 |
|
3935 |
|
3936 |
|
3937 |
|
3938 | class TextAttribute {
|
3939 | constructor(name, value, sourceSpan, keySpan, valueSpan, i18n) {
|
3940 | this.name = name;
|
3941 | this.value = value;
|
3942 | this.sourceSpan = sourceSpan;
|
3943 | this.keySpan = keySpan;
|
3944 | this.valueSpan = valueSpan;
|
3945 | this.i18n = i18n;
|
3946 | }
|
3947 | visit(visitor) {
|
3948 | return visitor.visitTextAttribute(this);
|
3949 | }
|
3950 | }
|
3951 | class BoundAttribute {
|
3952 | constructor(name, type, securityContext, value, unit, sourceSpan, keySpan, valueSpan, i18n) {
|
3953 | this.name = name;
|
3954 | this.type = type;
|
3955 | this.securityContext = securityContext;
|
3956 | this.value = value;
|
3957 | this.unit = unit;
|
3958 | this.sourceSpan = sourceSpan;
|
3959 | this.keySpan = keySpan;
|
3960 | this.valueSpan = valueSpan;
|
3961 | this.i18n = i18n;
|
3962 | }
|
3963 | static fromBoundElementProperty(prop, i18n) {
|
3964 | if (prop.keySpan === undefined) {
|
3965 | throw new Error(`Unexpected state: keySpan must be defined for bound attributes but was not for ${prop.name}: ${prop.sourceSpan}`);
|
3966 | }
|
3967 | return new BoundAttribute(prop.name, prop.type, prop.securityContext, prop.value, prop.unit, prop.sourceSpan, prop.keySpan, prop.valueSpan, i18n);
|
3968 | }
|
3969 | visit(visitor) {
|
3970 | return visitor.visitBoundAttribute(this);
|
3971 | }
|
3972 | }
|
3973 | class BoundEvent {
|
3974 | constructor(name, type, handler, target, phase, sourceSpan, handlerSpan, keySpan) {
|
3975 | this.name = name;
|
3976 | this.type = type;
|
3977 | this.handler = handler;
|
3978 | this.target = target;
|
3979 | this.phase = phase;
|
3980 | this.sourceSpan = sourceSpan;
|
3981 | this.handlerSpan = handlerSpan;
|
3982 | this.keySpan = keySpan;
|
3983 | }
|
3984 | static fromParsedEvent(event) {
|
3985 | const target = event.type === 0 ? event.targetOrPhase : null;
|
3986 | const phase = event.type === 1 ? event.targetOrPhase : null;
|
3987 | if (event.keySpan === undefined) {
|
3988 | throw new Error(`Unexpected state: keySpan must be defined for bound event but was not for ${event.name}: ${event.sourceSpan}`);
|
3989 | }
|
3990 | return new BoundEvent(event.name, event.type, event.handler, target, phase, event.sourceSpan, event.handlerSpan, event.keySpan);
|
3991 | }
|
3992 | visit(visitor) {
|
3993 | return visitor.visitBoundEvent(this);
|
3994 | }
|
3995 | }
|
3996 | class Element {
|
3997 | constructor(name, attributes, inputs, outputs, children, references, sourceSpan, startSourceSpan, endSourceSpan, i18n) {
|
3998 | this.name = name;
|
3999 | this.attributes = attributes;
|
4000 | this.inputs = inputs;
|
4001 | this.outputs = outputs;
|
4002 | this.children = children;
|
4003 | this.references = references;
|
4004 | this.sourceSpan = sourceSpan;
|
4005 | this.startSourceSpan = startSourceSpan;
|
4006 | this.endSourceSpan = endSourceSpan;
|
4007 | this.i18n = i18n;
|
4008 | }
|
4009 | visit(visitor) {
|
4010 | return visitor.visitElement(this);
|
4011 | }
|
4012 | }
|
4013 | class Template {
|
4014 | constructor(tagName, attributes, inputs, outputs, templateAttrs, children, references, variables, sourceSpan, startSourceSpan, endSourceSpan, i18n) {
|
4015 | this.tagName = tagName;
|
4016 | this.attributes = attributes;
|
4017 | this.inputs = inputs;
|
4018 | this.outputs = outputs;
|
4019 | this.templateAttrs = templateAttrs;
|
4020 | this.children = children;
|
4021 | this.references = references;
|
4022 | this.variables = variables;
|
4023 | this.sourceSpan = sourceSpan;
|
4024 | this.startSourceSpan = startSourceSpan;
|
4025 | this.endSourceSpan = endSourceSpan;
|
4026 | this.i18n = i18n;
|
4027 | }
|
4028 | visit(visitor) {
|
4029 | return visitor.visitTemplate(this);
|
4030 | }
|
4031 | }
|
4032 | class Content {
|
4033 | constructor(selector, attributes, sourceSpan, i18n) {
|
4034 | this.selector = selector;
|
4035 | this.attributes = attributes;
|
4036 | this.sourceSpan = sourceSpan;
|
4037 | this.i18n = i18n;
|
4038 | this.name = 'ng-content';
|
4039 | }
|
4040 | visit(visitor) {
|
4041 | return visitor.visitContent(this);
|
4042 | }
|
4043 | }
|
4044 | class Variable {
|
4045 | constructor(name, value, sourceSpan, keySpan, valueSpan) {
|
4046 | this.name = name;
|
4047 | this.value = value;
|
4048 | this.sourceSpan = sourceSpan;
|
4049 | this.keySpan = keySpan;
|
4050 | this.valueSpan = valueSpan;
|
4051 | }
|
4052 | visit(visitor) {
|
4053 | return visitor.visitVariable(this);
|
4054 | }
|
4055 | }
|
4056 | class Reference {
|
4057 | constructor(name, value, sourceSpan, keySpan, valueSpan) {
|
4058 | this.name = name;
|
4059 | this.value = value;
|
4060 | this.sourceSpan = sourceSpan;
|
4061 | this.keySpan = keySpan;
|
4062 | this.valueSpan = valueSpan;
|
4063 | }
|
4064 | visit(visitor) {
|
4065 | return visitor.visitReference(this);
|
4066 | }
|
4067 | }
|
4068 | class Icu {
|
4069 | constructor(vars, placeholders, sourceSpan, i18n) {
|
4070 | this.vars = vars;
|
4071 | this.placeholders = placeholders;
|
4072 | this.sourceSpan = sourceSpan;
|
4073 | this.i18n = i18n;
|
4074 | }
|
4075 | visit(visitor) {
|
4076 | return visitor.visitIcu(this);
|
4077 | }
|
4078 | }
|
4079 | function visitAll(visitor, nodes) {
|
4080 | const result = [];
|
4081 | if (visitor.visit) {
|
4082 | for (const node of nodes) {
|
4083 | const newNode = visitor.visit(node) || node.visit(visitor);
|
4084 | }
|
4085 | }
|
4086 | else {
|
4087 | for (const node of nodes) {
|
4088 | const newNode = node.visit(visitor);
|
4089 | if (newNode) {
|
4090 | result.push(newNode);
|
4091 | }
|
4092 | }
|
4093 | }
|
4094 | return result;
|
4095 | }
|
4096 |
|
4097 | |
4098 |
|
4099 |
|
4100 |
|
4101 |
|
4102 |
|
4103 |
|
4104 | class Message {
|
4105 | |
4106 |
|
4107 |
|
4108 |
|
4109 |
|
4110 |
|
4111 |
|
4112 |
|
4113 | constructor(nodes, placeholders, placeholderToMessage, meaning, description, customId) {
|
4114 | this.nodes = nodes;
|
4115 | this.placeholders = placeholders;
|
4116 | this.placeholderToMessage = placeholderToMessage;
|
4117 | this.meaning = meaning;
|
4118 | this.description = description;
|
4119 | this.customId = customId;
|
4120 | this.id = this.customId;
|
4121 |
|
4122 | this.legacyIds = [];
|
4123 | if (nodes.length) {
|
4124 | this.sources = [{
|
4125 | filePath: nodes[0].sourceSpan.start.file.url,
|
4126 | startLine: nodes[0].sourceSpan.start.line + 1,
|
4127 | startCol: nodes[0].sourceSpan.start.col + 1,
|
4128 | endLine: nodes[nodes.length - 1].sourceSpan.end.line + 1,
|
4129 | endCol: nodes[0].sourceSpan.start.col + 1
|
4130 | }];
|
4131 | }
|
4132 | else {
|
4133 | this.sources = [];
|
4134 | }
|
4135 | }
|
4136 | }
|
4137 | class Text$1 {
|
4138 | constructor(value, sourceSpan) {
|
4139 | this.value = value;
|
4140 | this.sourceSpan = sourceSpan;
|
4141 | }
|
4142 | visit(visitor, context) {
|
4143 | return visitor.visitText(this, context);
|
4144 | }
|
4145 | }
|
4146 |
|
4147 | class Container {
|
4148 | constructor(children, sourceSpan) {
|
4149 | this.children = children;
|
4150 | this.sourceSpan = sourceSpan;
|
4151 | }
|
4152 | visit(visitor, context) {
|
4153 | return visitor.visitContainer(this, context);
|
4154 | }
|
4155 | }
|
4156 | class Icu$1 {
|
4157 | constructor(expression, type, cases, sourceSpan) {
|
4158 | this.expression = expression;
|
4159 | this.type = type;
|
4160 | this.cases = cases;
|
4161 | this.sourceSpan = sourceSpan;
|
4162 | }
|
4163 | visit(visitor, context) {
|
4164 | return visitor.visitIcu(this, context);
|
4165 | }
|
4166 | }
|
4167 | class TagPlaceholder {
|
4168 | constructor(tag, attrs, startName, closeName, children, isVoid,
|
4169 | // TODO sourceSpan should cover all (we need a startSourceSpan and endSourceSpan)
|
4170 | sourceSpan, startSourceSpan, endSourceSpan) {
|
4171 | this.tag = tag;
|
4172 | this.attrs = attrs;
|
4173 | this.startName = startName;
|
4174 | this.closeName = closeName;
|
4175 | this.children = children;
|
4176 | this.isVoid = isVoid;
|
4177 | this.sourceSpan = sourceSpan;
|
4178 | this.startSourceSpan = startSourceSpan;
|
4179 | this.endSourceSpan = endSourceSpan;
|
4180 | }
|
4181 | visit(visitor, context) {
|
4182 | return visitor.visitTagPlaceholder(this, context);
|
4183 | }
|
4184 | }
|
4185 | class Placeholder {
|
4186 | constructor(value, name, sourceSpan) {
|
4187 | this.value = value;
|
4188 | this.name = name;
|
4189 | this.sourceSpan = sourceSpan;
|
4190 | }
|
4191 | visit(visitor, context) {
|
4192 | return visitor.visitPlaceholder(this, context);
|
4193 | }
|
4194 | }
|
4195 | class IcuPlaceholder {
|
4196 | constructor(value, name, sourceSpan) {
|
4197 | this.value = value;
|
4198 | this.name = name;
|
4199 | this.sourceSpan = sourceSpan;
|
4200 | }
|
4201 | visit(visitor, context) {
|
4202 | return visitor.visitIcuPlaceholder(this, context);
|
4203 | }
|
4204 | }
|
4205 |
|
4206 | |
4207 |
|
4208 |
|
4209 |
|
4210 |
|
4211 |
|
4212 |
|
4213 | |
4214 |
|
4215 |
|
4216 |
|
4217 |
|
4218 |
|
4219 |
|
4220 | class BigInteger {
|
4221 | |
4222 |
|
4223 |
|
4224 | constructor(digits) {
|
4225 | this.digits = digits;
|
4226 | }
|
4227 | static zero() {
|
4228 | return new BigInteger([0]);
|
4229 | }
|
4230 | static one() {
|
4231 | return new BigInteger([1]);
|
4232 | }
|
4233 | |
4234 |
|
4235 |
|
4236 | clone() {
|
4237 | return new BigInteger(this.digits.slice());
|
4238 | }
|
4239 | |
4240 |
|
4241 |
|
4242 |
|
4243 | add(other) {
|
4244 | const result = this.clone();
|
4245 | result.addToSelf(other);
|
4246 | return result;
|
4247 | }
|
4248 | |
4249 |
|
4250 |
|
4251 | addToSelf(other) {
|
4252 | const maxNrOfDigits = Math.max(this.digits.length, other.digits.length);
|
4253 | let carry = 0;
|
4254 | for (let i = 0; i < maxNrOfDigits; i++) {
|
4255 | let digitSum = carry;
|
4256 | if (i < this.digits.length) {
|
4257 | digitSum += this.digits[i];
|
4258 | }
|
4259 | if (i < other.digits.length) {
|
4260 | digitSum += other.digits[i];
|
4261 | }
|
4262 | if (digitSum >= 10) {
|
4263 | this.digits[i] = digitSum - 10;
|
4264 | carry = 1;
|
4265 | }
|
4266 | else {
|
4267 | this.digits[i] = digitSum;
|
4268 | carry = 0;
|
4269 | }
|
4270 | }
|
4271 |
|
4272 | if (carry > 0) {
|
4273 | this.digits[maxNrOfDigits] = 1;
|
4274 | }
|
4275 | }
|
4276 | |
4277 |
|
4278 |
|
4279 |
|
4280 | toString() {
|
4281 | let res = '';
|
4282 | for (let i = this.digits.length - 1; i >= 0; i--) {
|
4283 | res += this.digits[i];
|
4284 | }
|
4285 | return res;
|
4286 | }
|
4287 | }
|
4288 | |
4289 |
|
4290 |
|
4291 |
|
4292 | class BigIntForMultiplication {
|
4293 | constructor(value) {
|
4294 | this.powerOfTwos = [value];
|
4295 | }
|
4296 | |
4297 |
|
4298 |
|
4299 | getValue() {
|
4300 | return this.powerOfTwos[0];
|
4301 | }
|
4302 | |
4303 |
|
4304 |
|
4305 |
|
4306 |
|
4307 |
|
4308 |
|
4309 |
|
4310 |
|
4311 |
|
4312 |
|
4313 |
|
4314 |
|
4315 |
|
4316 |
|
4317 |
|
4318 |
|
4319 |
|
4320 |
|
4321 |
|
4322 |
|
4323 |
|
4324 |
|
4325 |
|
4326 |
|
4327 |
|
4328 | multiplyBy(num) {
|
4329 | const product = BigInteger.zero();
|
4330 | this.multiplyByAndAddTo(num, product);
|
4331 | return product;
|
4332 | }
|
4333 | |
4334 |
|
4335 |
|
4336 |
|
4337 | multiplyByAndAddTo(num, result) {
|
4338 | for (let exponent = 0; num !== 0; num = num >>> 1, exponent++) {
|
4339 | if (num & 1) {
|
4340 | const value = this.getMultipliedByPowerOfTwo(exponent);
|
4341 | result.addToSelf(value);
|
4342 | }
|
4343 | }
|
4344 | }
|
4345 | |
4346 |
|
4347 |
|
4348 | getMultipliedByPowerOfTwo(exponent) {
|
4349 |
|
4350 |
|
4351 |
|
4352 | for (let i = this.powerOfTwos.length; i <= exponent; i++) {
|
4353 | const previousPower = this.powerOfTwos[i - 1];
|
4354 | this.powerOfTwos[i] = previousPower.add(previousPower);
|
4355 | }
|
4356 | return this.powerOfTwos[exponent];
|
4357 | }
|
4358 | }
|
4359 | |
4360 |
|
4361 |
|
4362 |
|
4363 |
|
4364 |
|
4365 | class BigIntExponentiation {
|
4366 | constructor(base) {
|
4367 | this.base = base;
|
4368 | this.exponents = [new BigIntForMultiplication(BigInteger.one())];
|
4369 | }
|
4370 | |
4371 |
|
4372 |
|
4373 |
|
4374 | toThePowerOf(exponent) {
|
4375 |
|
4376 |
|
4377 |
|
4378 | for (let i = this.exponents.length; i <= exponent; i++) {
|
4379 | const value = this.exponents[i - 1].multiplyBy(this.base);
|
4380 | this.exponents[i] = new BigIntForMultiplication(value);
|
4381 | }
|
4382 | return this.exponents[exponent];
|
4383 | }
|
4384 | }
|
4385 |
|
4386 | |
4387 |
|
4388 |
|
4389 |
|
4390 |
|
4391 |
|
4392 |
|
4393 | |
4394 |
|
4395 |
|
4396 | function computeDigest(message) {
|
4397 | return sha1(serializeNodes(message.nodes).join('') + `[${message.meaning}]`);
|
4398 | }
|
4399 | |
4400 |
|
4401 |
|
4402 | function decimalDigest(message) {
|
4403 | return message.id || computeDecimalDigest(message);
|
4404 | }
|
4405 | |
4406 |
|
4407 |
|
4408 | function computeDecimalDigest(message) {
|
4409 | const visitor = new _SerializerIgnoreIcuExpVisitor();
|
4410 | const parts = message.nodes.map(a => a.visit(visitor, null));
|
4411 | return computeMsgId(parts.join(''), message.meaning);
|
4412 | }
|
4413 | |
4414 |
|
4415 |
|
4416 |
|
4417 |
|
4418 |
|
4419 |
|
4420 | class _SerializerVisitor {
|
4421 | visitText(text, context) {
|
4422 | return text.value;
|
4423 | }
|
4424 | visitContainer(container, context) {
|
4425 | return `[${container.children.map(child => child.visit(this)).join(', ')}]`;
|
4426 | }
|
4427 | visitIcu(icu, context) {
|
4428 | const strCases = Object.keys(icu.cases).map((k) => `${k} {${icu.cases[k].visit(this)}}`);
|
4429 | return `{${icu.expression}, ${icu.type}, ${strCases.join(', ')}}`;
|
4430 | }
|
4431 | visitTagPlaceholder(ph, context) {
|
4432 | return ph.isVoid ?
|
4433 | `<ph tag name="${ph.startName}"/>` :
|
4434 | `<ph tag name="${ph.startName}">${ph.children.map(child => child.visit(this)).join(', ')}</ph name="${ph.closeName}">`;
|
4435 | }
|
4436 | visitPlaceholder(ph, context) {
|
4437 | return ph.value ? `<ph name="${ph.name}">${ph.value}</ph>` : `<ph name="${ph.name}"/>`;
|
4438 | }
|
4439 | visitIcuPlaceholder(ph, context) {
|
4440 | return `<ph icu name="${ph.name}">${ph.value.visit(this)}</ph>`;
|
4441 | }
|
4442 | }
|
4443 | const serializerVisitor = new _SerializerVisitor();
|
4444 | function serializeNodes(nodes) {
|
4445 | return nodes.map(a => a.visit(serializerVisitor, null));
|
4446 | }
|
4447 | |
4448 |
|
4449 |
|
4450 |
|
4451 |
|
4452 |
|
4453 |
|
4454 | class _SerializerIgnoreIcuExpVisitor extends _SerializerVisitor {
|
4455 | visitIcu(icu, context) {
|
4456 | let strCases = Object.keys(icu.cases).map((k) => `${k} {${icu.cases[k].visit(this)}}`);
|
4457 |
|
4458 | return `{${icu.type}, ${strCases.join(', ')}}`;
|
4459 | }
|
4460 | }
|
4461 | |
4462 |
|
4463 |
|
4464 |
|
4465 |
|
4466 |
|
4467 |
|
4468 |
|
4469 | function sha1(str) {
|
4470 | const utf8 = utf8Encode(str);
|
4471 | const words32 = bytesToWords32(utf8, Endian.Big);
|
4472 | const len = utf8.length * 8;
|
4473 | const w = newArray(80);
|
4474 | let a = 0x67452301, b = 0xefcdab89, c = 0x98badcfe, d = 0x10325476, e = 0xc3d2e1f0;
|
4475 | words32[len >> 5] |= 0x80 << (24 - len % 32);
|
4476 | words32[((len + 64 >> 9) << 4) + 15] = len;
|
4477 | for (let i = 0; i < words32.length; i += 16) {
|
4478 | const h0 = a, h1 = b, h2 = c, h3 = d, h4 = e;
|
4479 | for (let j = 0; j < 80; j++) {
|
4480 | if (j < 16) {
|
4481 | w[j] = words32[i + j];
|
4482 | }
|
4483 | else {
|
4484 | w[j] = rol32(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1);
|
4485 | }
|
4486 | const fkVal = fk(j, b, c, d);
|
4487 | const f = fkVal[0];
|
4488 | const k = fkVal[1];
|
4489 | const temp = [rol32(a, 5), f, e, k, w[j]].reduce(add32);
|
4490 | e = d;
|
4491 | d = c;
|
4492 | c = rol32(b, 30);
|
4493 | b = a;
|
4494 | a = temp;
|
4495 | }
|
4496 | a = add32(a, h0);
|
4497 | b = add32(b, h1);
|
4498 | c = add32(c, h2);
|
4499 | d = add32(d, h3);
|
4500 | e = add32(e, h4);
|
4501 | }
|
4502 | return bytesToHexString(words32ToByteString([a, b, c, d, e]));
|
4503 | }
|
4504 | function fk(index, b, c, d) {
|
4505 | if (index < 20) {
|
4506 | return [(b & c) | (~b & d), 0x5a827999];
|
4507 | }
|
4508 | if (index < 40) {
|
4509 | return [b ^ c ^ d, 0x6ed9eba1];
|
4510 | }
|
4511 | if (index < 60) {
|
4512 | return [(b & c) | (b & d) | (c & d), 0x8f1bbcdc];
|
4513 | }
|
4514 | return [b ^ c ^ d, 0xca62c1d6];
|
4515 | }
|
4516 | |
4517 |
|
4518 |
|
4519 |
|
4520 |
|
4521 |
|
4522 |
|
4523 |
|
4524 | function fingerprint(str) {
|
4525 | const utf8 = utf8Encode(str);
|
4526 | let hi = hash32(utf8, 0);
|
4527 | let lo = hash32(utf8, 102072);
|
4528 | if (hi == 0 && (lo == 0 || lo == 1)) {
|
4529 | hi = hi ^ 0x130f9bef;
|
4530 | lo = lo ^ -0x6b5f56d8;
|
4531 | }
|
4532 | return [hi, lo];
|
4533 | }
|
4534 | function computeMsgId(msg, meaning = '') {
|
4535 | let msgFingerprint = fingerprint(msg);
|
4536 | if (meaning) {
|
4537 | const meaningFingerprint = fingerprint(meaning);
|
4538 | msgFingerprint = add64(rol64(msgFingerprint, 1), meaningFingerprint);
|
4539 | }
|
4540 | const hi = msgFingerprint[0];
|
4541 | const lo = msgFingerprint[1];
|
4542 | return wordsToDecimalString(hi & 0x7fffffff, lo);
|
4543 | }
|
4544 | function hash32(bytes, c) {
|
4545 | let a = 0x9e3779b9, b = 0x9e3779b9;
|
4546 | let i;
|
4547 | const len = bytes.length;
|
4548 | for (i = 0; i + 12 <= len; i += 12) {
|
4549 | a = add32(a, wordAt(bytes, i, Endian.Little));
|
4550 | b = add32(b, wordAt(bytes, i + 4, Endian.Little));
|
4551 | c = add32(c, wordAt(bytes, i + 8, Endian.Little));
|
4552 | const res = mix(a, b, c);
|
4553 | a = res[0], b = res[1], c = res[2];
|
4554 | }
|
4555 | a = add32(a, wordAt(bytes, i, Endian.Little));
|
4556 | b = add32(b, wordAt(bytes, i + 4, Endian.Little));
|
4557 |
|
4558 | c = add32(c, len);
|
4559 | c = add32(c, wordAt(bytes, i + 8, Endian.Little) << 8);
|
4560 | return mix(a, b, c)[2];
|
4561 | }
|
4562 |
|
4563 | function mix(a, b, c) {
|
4564 | a = sub32(a, b);
|
4565 | a = sub32(a, c);
|
4566 | a ^= c >>> 13;
|
4567 | b = sub32(b, c);
|
4568 | b = sub32(b, a);
|
4569 | b ^= a << 8;
|
4570 | c = sub32(c, a);
|
4571 | c = sub32(c, b);
|
4572 | c ^= b >>> 13;
|
4573 | a = sub32(a, b);
|
4574 | a = sub32(a, c);
|
4575 | a ^= c >>> 12;
|
4576 | b = sub32(b, c);
|
4577 | b = sub32(b, a);
|
4578 | b ^= a << 16;
|
4579 | c = sub32(c, a);
|
4580 | c = sub32(c, b);
|
4581 | c ^= b >>> 5;
|
4582 | a = sub32(a, b);
|
4583 | a = sub32(a, c);
|
4584 | a ^= c >>> 3;
|
4585 | b = sub32(b, c);
|
4586 | b = sub32(b, a);
|
4587 | b ^= a << 10;
|
4588 | c = sub32(c, a);
|
4589 | c = sub32(c, b);
|
4590 | c ^= b >>> 15;
|
4591 | return [a, b, c];
|
4592 | }
|
4593 |
|
4594 |
|
4595 | var Endian;
|
4596 | (function (Endian) {
|
4597 | Endian[Endian["Little"] = 0] = "Little";
|
4598 | Endian[Endian["Big"] = 1] = "Big";
|
4599 | })(Endian || (Endian = {}));
|
4600 | function add32(a, b) {
|
4601 | return add32to64(a, b)[1];
|
4602 | }
|
4603 | function add32to64(a, b) {
|
4604 | const low = (a & 0xffff) + (b & 0xffff);
|
4605 | const high = (a >>> 16) + (b >>> 16) + (low >>> 16);
|
4606 | return [high >>> 16, (high << 16) | (low & 0xffff)];
|
4607 | }
|
4608 | function add64(a, b) {
|
4609 | const ah = a[0], al = a[1];
|
4610 | const bh = b[0], bl = b[1];
|
4611 | const result = add32to64(al, bl);
|
4612 | const carry = result[0];
|
4613 | const l = result[1];
|
4614 | const h = add32(add32(ah, bh), carry);
|
4615 | return [h, l];
|
4616 | }
|
4617 | function sub32(a, b) {
|
4618 | const low = (a & 0xffff) - (b & 0xffff);
|
4619 | const high = (a >> 16) - (b >> 16) + (low >> 16);
|
4620 | return (high << 16) | (low & 0xffff);
|
4621 | }
|
4622 |
|
4623 | function rol32(a, count) {
|
4624 | return (a << count) | (a >>> (32 - count));
|
4625 | }
|
4626 |
|
4627 | function rol64(num, count) {
|
4628 | const hi = num[0], lo = num[1];
|
4629 | const h = (hi << count) | (lo >>> (32 - count));
|
4630 | const l = (lo << count) | (hi >>> (32 - count));
|
4631 | return [h, l];
|
4632 | }
|
4633 | function bytesToWords32(bytes, endian) {
|
4634 | const size = (bytes.length + 3) >>> 2;
|
4635 | const words32 = [];
|
4636 | for (let i = 0; i < size; i++) {
|
4637 | words32[i] = wordAt(bytes, i * 4, endian);
|
4638 | }
|
4639 | return words32;
|
4640 | }
|
4641 | function byteAt(bytes, index) {
|
4642 | return index >= bytes.length ? 0 : bytes[index];
|
4643 | }
|
4644 | function wordAt(bytes, index, endian) {
|
4645 | let word = 0;
|
4646 | if (endian === Endian.Big) {
|
4647 | for (let i = 0; i < 4; i++) {
|
4648 | word += byteAt(bytes, index + i) << (24 - 8 * i);
|
4649 | }
|
4650 | }
|
4651 | else {
|
4652 | for (let i = 0; i < 4; i++) {
|
4653 | word += byteAt(bytes, index + i) << 8 * i;
|
4654 | }
|
4655 | }
|
4656 | return word;
|
4657 | }
|
4658 | function words32ToByteString(words32) {
|
4659 | return words32.reduce((bytes, word) => bytes.concat(word32ToByteString(word)), []);
|
4660 | }
|
4661 | function word32ToByteString(word) {
|
4662 | let bytes = [];
|
4663 | for (let i = 0; i < 4; i++) {
|
4664 | bytes.push((word >>> 8 * (3 - i)) & 0xff);
|
4665 | }
|
4666 | return bytes;
|
4667 | }
|
4668 | function bytesToHexString(bytes) {
|
4669 | let hex = '';
|
4670 | for (let i = 0; i < bytes.length; i++) {
|
4671 | const b = byteAt(bytes, i);
|
4672 | hex += (b >>> 4).toString(16) + (b & 0x0f).toString(16);
|
4673 | }
|
4674 | return hex.toLowerCase();
|
4675 | }
|
4676 | |
4677 |
|
4678 |
|
4679 |
|
4680 |
|
4681 |
|
4682 |
|
4683 |
|
4684 | const base256 = new BigIntExponentiation(256);
|
4685 | |
4686 |
|
4687 |
|
4688 |
|
4689 |
|
4690 |
|
4691 | function wordsToDecimalString(hi, lo) {
|
4692 |
|
4693 |
|
4694 |
|
4695 | const decimal = base256.toThePowerOf(0).multiplyBy(lo);
|
4696 |
|
4697 |
|
4698 | base256.toThePowerOf(4).multiplyByAndAddTo(hi, decimal);
|
4699 | return decimal.toString();
|
4700 | }
|
4701 |
|
4702 | |
4703 |
|
4704 |
|
4705 |
|
4706 |
|
4707 |
|
4708 |
|
4709 |
|
4710 | function toPublicName(internalName) {
|
4711 | return internalName.toUpperCase().replace(/[^A-Z0-9_]/g, '_');
|
4712 | }
|
4713 |
|
4714 | |
4715 |
|
4716 |
|
4717 |
|
4718 |
|
4719 |
|
4720 |
|
4721 |
|
4722 | const CLOSURE_TRANSLATION_VAR_PREFIX = 'MSG_';
|
4723 | |
4724 |
|
4725 |
|
4726 |
|
4727 |
|
4728 | const TRANSLATION_VAR_PREFIX = 'i18n_';
|
4729 |
|
4730 | const I18N_ATTR = 'i18n';
|
4731 | const I18N_ATTR_PREFIX = 'i18n-';
|
4732 |
|
4733 | const I18N_ICU_VAR_PREFIX = 'VAR_';
|
4734 |
|
4735 | const I18N_ICU_MAPPING_PREFIX = 'I18N_EXP_';
|
4736 |
|
4737 | const I18N_PLACEHOLDER_SYMBOL = '�';
|
4738 | function isI18nAttribute(name) {
|
4739 | return name === I18N_ATTR || name.startsWith(I18N_ATTR_PREFIX);
|
4740 | }
|
4741 | function isI18nRootNode(meta) {
|
4742 | return meta instanceof Message;
|
4743 | }
|
4744 | function isSingleI18nIcu(meta) {
|
4745 | return isI18nRootNode(meta) && meta.nodes.length === 1 && meta.nodes[0] instanceof Icu$1;
|
4746 | }
|
4747 | function hasI18nMeta(node) {
|
4748 | return !!node.i18n;
|
4749 | }
|
4750 | function hasI18nAttrs(element) {
|
4751 | return element.attrs.some((attr) => isI18nAttribute(attr.name));
|
4752 | }
|
4753 | function icuFromI18nMessage(message) {
|
4754 | return message.nodes[0];
|
4755 | }
|
4756 | function wrapI18nPlaceholder(content, contextId = 0) {
|
4757 | const blockId = contextId > 0 ? `:${contextId}` : '';
|
4758 | return `${I18N_PLACEHOLDER_SYMBOL}${content}${blockId}${I18N_PLACEHOLDER_SYMBOL}`;
|
4759 | }
|
4760 | function assembleI18nBoundString(strings, bindingStartIndex = 0, contextId = 0) {
|
4761 | if (!strings.length)
|
4762 | return '';
|
4763 | let acc = '';
|
4764 | const lastIdx = strings.length - 1;
|
4765 | for (let i = 0; i < lastIdx; i++) {
|
4766 | acc += `${strings[i]}${wrapI18nPlaceholder(bindingStartIndex + i, contextId)}`;
|
4767 | }
|
4768 | acc += strings[lastIdx];
|
4769 | return acc;
|
4770 | }
|
4771 | function getSeqNumberGenerator(startsAt = 0) {
|
4772 | let current = startsAt;
|
4773 | return () => current++;
|
4774 | }
|
4775 | function placeholdersToParams(placeholders) {
|
4776 | const params = {};
|
4777 | placeholders.forEach((values, key) => {
|
4778 | params[key] = literal(values.length > 1 ? `[${values.join('|')}]` : values[0]);
|
4779 | });
|
4780 | return params;
|
4781 | }
|
4782 | function updatePlaceholderMap(map, name, ...values) {
|
4783 | const current = map.get(name) || [];
|
4784 | current.push(...values);
|
4785 | map.set(name, current);
|
4786 | }
|
4787 | function assembleBoundTextPlaceholders(meta, bindingStartIndex = 0, contextId = 0) {
|
4788 | const startIdx = bindingStartIndex;
|
4789 | const placeholders = new Map();
|
4790 | const node = meta instanceof Message ? meta.nodes.find(node => node instanceof Container) : meta;
|
4791 | if (node) {
|
4792 | node
|
4793 | .children
|
4794 | .filter((child) => child instanceof Placeholder)
|
4795 | .forEach((child, idx) => {
|
4796 | const content = wrapI18nPlaceholder(startIdx + idx, contextId);
|
4797 | updatePlaceholderMap(placeholders, child.name, content);
|
4798 | });
|
4799 | }
|
4800 | return placeholders;
|
4801 | }
|
4802 | |
4803 |
|
4804 |
|
4805 |
|
4806 |
|
4807 |
|
4808 |
|
4809 |
|
4810 |
|
4811 |
|
4812 | function i18nFormatPlaceholderNames(params = {}, useCamelCase) {
|
4813 | const _params = {};
|
4814 | if (params && Object.keys(params).length) {
|
4815 | Object.keys(params).forEach(key => _params[formatI18nPlaceholderName(key, useCamelCase)] = params[key]);
|
4816 | }
|
4817 | return _params;
|
4818 | }
|
4819 | |
4820 |
|
4821 |
|
4822 |
|
4823 |
|
4824 |
|
4825 |
|
4826 |
|
4827 | function formatI18nPlaceholderName(name, useCamelCase = true) {
|
4828 | const publicName = toPublicName(name);
|
4829 | if (!useCamelCase) {
|
4830 | return publicName;
|
4831 | }
|
4832 | const chunks = publicName.split('_');
|
4833 | if (chunks.length === 1) {
|
4834 |
|
4835 | return name.toLowerCase();
|
4836 | }
|
4837 | let postfix;
|
4838 |
|
4839 | if (/^\d+$/.test(chunks[chunks.length - 1])) {
|
4840 | postfix = chunks.pop();
|
4841 | }
|
4842 | let raw = chunks.shift().toLowerCase();
|
4843 | if (chunks.length) {
|
4844 | raw += chunks.map(c => c.charAt(0).toUpperCase() + c.slice(1).toLowerCase()).join('');
|
4845 | }
|
4846 | return postfix ? `${raw}_${postfix}` : raw;
|
4847 | }
|
4848 | |
4849 |
|
4850 |
|
4851 |
|
4852 |
|
4853 |
|
4854 | function getTranslationConstPrefix(extra) {
|
4855 | return `${CLOSURE_TRANSLATION_VAR_PREFIX}${extra}`.toUpperCase();
|
4856 | }
|
4857 | |
4858 |
|
4859 |
|
4860 |
|
4861 | function declareI18nVariable(variable) {
|
4862 | return new DeclareVarStmt(variable.name, undefined, INFERRED_TYPE, undefined, variable.sourceSpan);
|
4863 | }
|
4864 |
|
4865 | |
4866 |
|
4867 |
|
4868 |
|
4869 |
|
4870 |
|
4871 |
|
4872 | |
4873 |
|
4874 |
|
4875 |
|
4876 |
|
4877 |
|
4878 |
|
4879 |
|
4880 | const UNSAFE_OBJECT_KEY_NAME_REGEXP = /[-.]/;
|
4881 |
|
4882 | const TEMPORARY_NAME = '_t';
|
4883 |
|
4884 | const CONTEXT_NAME = 'ctx';
|
4885 |
|
4886 | const RENDER_FLAGS = 'rf';
|
4887 |
|
4888 | const REFERENCE_PREFIX = '_r';
|
4889 |
|
4890 | const IMPLICIT_REFERENCE = '$implicit';
|
4891 |
|
4892 | const NON_BINDABLE_ATTR = 'ngNonBindable';
|
4893 | |
4894 |
|
4895 |
|
4896 |
|
4897 |
|
4898 | function temporaryAllocator(statements, name) {
|
4899 | let temp = null;
|
4900 | return () => {
|
4901 | if (!temp) {
|
4902 | statements.push(new DeclareVarStmt(TEMPORARY_NAME, undefined, DYNAMIC_TYPE));
|
4903 | temp = variable(name);
|
4904 | }
|
4905 | return temp;
|
4906 | };
|
4907 | }
|
4908 | function unsupported(feature) {
|
4909 | if (this) {
|
4910 | throw new Error(`Builder ${this.constructor.name} doesn't support ${feature} yet`);
|
4911 | }
|
4912 | throw new Error(`Feature ${feature} is not supported yet`);
|
4913 | }
|
4914 | function invalid$1(arg) {
|
4915 | throw new Error(`Invalid state: Visitor ${this.constructor.name} doesn't handle ${arg.constructor.name}`);
|
4916 | }
|
4917 | function asLiteral(value) {
|
4918 | if (Array.isArray(value)) {
|
4919 | return literalArr(value.map(asLiteral));
|
4920 | }
|
4921 | return literal(value, INFERRED_TYPE);
|
4922 | }
|
4923 | function conditionallyCreateMapObjectLiteral(keys, keepDeclared) {
|
4924 | if (Object.getOwnPropertyNames(keys).length > 0) {
|
4925 | return mapToExpression(keys, keepDeclared);
|
4926 | }
|
4927 | return null;
|
4928 | }
|
4929 | function mapToExpression(map, keepDeclared) {
|
4930 | return literalMap(Object.getOwnPropertyNames(map).map(key => {
|
4931 |
|
4932 |
|
4933 | const value = map[key];
|
4934 | let declaredName;
|
4935 | let publicName;
|
4936 | let minifiedName;
|
4937 | let needsDeclaredName;
|
4938 | if (Array.isArray(value)) {
|
4939 | [publicName, declaredName] = value;
|
4940 | minifiedName = key;
|
4941 | needsDeclaredName = publicName !== declaredName;
|
4942 | }
|
4943 | else {
|
4944 | [declaredName, publicName] = splitAtColon(key, [key, value]);
|
4945 | minifiedName = declaredName;
|
4946 |
|
4947 |
|
4948 |
|
4949 | needsDeclaredName = publicName !== declaredName && key.includes(':');
|
4950 | }
|
4951 | return {
|
4952 | key: minifiedName,
|
4953 |
|
4954 | quoted: UNSAFE_OBJECT_KEY_NAME_REGEXP.test(minifiedName),
|
4955 | value: (keepDeclared && needsDeclaredName) ?
|
4956 | literalArr([asLiteral(publicName), asLiteral(declaredName)]) :
|
4957 | asLiteral(publicName)
|
4958 | };
|
4959 | }));
|
4960 | }
|
4961 | |
4962 |
|
4963 |
|
4964 | function trimTrailingNulls(parameters) {
|
4965 | while (isNull(parameters[parameters.length - 1])) {
|
4966 | parameters.pop();
|
4967 | }
|
4968 | return parameters;
|
4969 | }
|
4970 | function getQueryPredicate(query, constantPool) {
|
4971 | if (Array.isArray(query.predicate)) {
|
4972 | let predicate = [];
|
4973 | query.predicate.forEach((selector) => {
|
4974 |
|
4975 |
|
4976 |
|
4977 | const selectors = selector.split(',').map(token => literal(token.trim()));
|
4978 | predicate.push(...selectors);
|
4979 | });
|
4980 | return constantPool.getConstLiteral(literalArr(predicate), true);
|
4981 | }
|
4982 | else {
|
4983 | return query.predicate;
|
4984 | }
|
4985 | }
|
4986 | |
4987 |
|
4988 |
|
4989 |
|
4990 |
|
4991 | class DefinitionMap {
|
4992 | constructor() {
|
4993 | this.values = [];
|
4994 | }
|
4995 | set(key, value) {
|
4996 | if (value) {
|
4997 | this.values.push({ key: key, value, quoted: false });
|
4998 | }
|
4999 | }
|
5000 | toLiteralMap() {
|
5001 | return literalMap(this.values);
|
5002 | }
|
5003 | }
|
5004 | |
5005 |
|
5006 |
|
5007 |
|
5008 |
|
5009 |
|
5010 |
|
5011 |
|
5012 |
|
5013 | function getAttrsForDirectiveMatching(elOrTpl) {
|
5014 | const attributesMap = {};
|
5015 | if (elOrTpl instanceof Template && elOrTpl.tagName !== 'ng-template') {
|
5016 | elOrTpl.templateAttrs.forEach(a => attributesMap[a.name] = '');
|
5017 | }
|
5018 | else {
|
5019 | elOrTpl.attributes.forEach(a => {
|
5020 | if (!isI18nAttribute(a.name)) {
|
5021 | attributesMap[a.name] = a.value;
|
5022 | }
|
5023 | });
|
5024 | elOrTpl.inputs.forEach(i => {
|
5025 | attributesMap[i.name] = '';
|
5026 | });
|
5027 | elOrTpl.outputs.forEach(o => {
|
5028 | attributesMap[o.name] = '';
|
5029 | });
|
5030 | }
|
5031 | return attributesMap;
|
5032 | }
|
5033 |
|
5034 | function chainedInstruction(reference, calls, span) {
|
5035 | let expression = importExpr(reference, null, span);
|
5036 | if (calls.length > 0) {
|
5037 | for (let i = 0; i < calls.length; i++) {
|
5038 | expression = expression.callFn(calls[i], span);
|
5039 | }
|
5040 | }
|
5041 | else {
|
5042 |
|
5043 | expression = expression.callFn([], span);
|
5044 | }
|
5045 | return expression;
|
5046 | }
|
5047 | |
5048 |
|
5049 |
|
5050 |
|
5051 |
|
5052 | function getInterpolationArgsLength(interpolation) {
|
5053 | const { expressions, strings } = interpolation;
|
5054 | if (expressions.length === 1 && strings.length === 2 && strings[0] === '' && strings[1] === '') {
|
5055 |
|
5056 |
|
5057 |
|
5058 | return 1;
|
5059 | }
|
5060 | else {
|
5061 | return expressions.length + strings.length;
|
5062 | }
|
5063 | }
|
5064 |
|
5065 | |
5066 |
|
5067 |
|
5068 |
|
5069 |
|
5070 |
|
5071 |
|
5072 | var R3FactoryDelegateType;
|
5073 | (function (R3FactoryDelegateType) {
|
5074 | R3FactoryDelegateType[R3FactoryDelegateType["Class"] = 0] = "Class";
|
5075 | R3FactoryDelegateType[R3FactoryDelegateType["Function"] = 1] = "Function";
|
5076 | R3FactoryDelegateType[R3FactoryDelegateType["Factory"] = 2] = "Factory";
|
5077 | })(R3FactoryDelegateType || (R3FactoryDelegateType = {}));
|
5078 | var R3FactoryTarget;
|
5079 | (function (R3FactoryTarget) {
|
5080 | R3FactoryTarget[R3FactoryTarget["Directive"] = 0] = "Directive";
|
5081 | R3FactoryTarget[R3FactoryTarget["Component"] = 1] = "Component";
|
5082 | R3FactoryTarget[R3FactoryTarget["Injectable"] = 2] = "Injectable";
|
5083 | R3FactoryTarget[R3FactoryTarget["Pipe"] = 3] = "Pipe";
|
5084 | R3FactoryTarget[R3FactoryTarget["NgModule"] = 4] = "NgModule";
|
5085 | })(R3FactoryTarget || (R3FactoryTarget = {}));
|
5086 | |
5087 |
|
5088 |
|
5089 |
|
5090 |
|
5091 |
|
5092 |
|
5093 |
|
5094 | var R3ResolvedDependencyType;
|
5095 | (function (R3ResolvedDependencyType) {
|
5096 | |
5097 |
|
5098 |
|
5099 | R3ResolvedDependencyType[R3ResolvedDependencyType["Token"] = 0] = "Token";
|
5100 | |
5101 |
|
5102 |
|
5103 |
|
5104 |
|
5105 | R3ResolvedDependencyType[R3ResolvedDependencyType["Attribute"] = 1] = "Attribute";
|
5106 | |
5107 |
|
5108 |
|
5109 | R3ResolvedDependencyType[R3ResolvedDependencyType["ChangeDetectorRef"] = 2] = "ChangeDetectorRef";
|
5110 | |
5111 |
|
5112 |
|
5113 | R3ResolvedDependencyType[R3ResolvedDependencyType["Invalid"] = 3] = "Invalid";
|
5114 | })(R3ResolvedDependencyType || (R3ResolvedDependencyType = {}));
|
5115 | |
5116 |
|
5117 |
|
5118 | function compileFactoryFunction(meta) {
|
5119 | const t = variable('t');
|
5120 | const statements = [];
|
5121 | let ctorDepsType = NONE_TYPE;
|
5122 |
|
5123 |
|
5124 |
|
5125 |
|
5126 |
|
5127 | const typeForCtor = !isDelegatedMetadata(meta) ?
|
5128 | new BinaryOperatorExpr(BinaryOperator.Or, t, meta.internalType) :
|
5129 | t;
|
5130 | let ctorExpr = null;
|
5131 | if (meta.deps !== null) {
|
5132 |
|
5133 | if (meta.deps !== 'invalid') {
|
5134 | ctorExpr = new InstantiateExpr(typeForCtor, injectDependencies(meta.deps, meta.injectFn, meta.target === R3FactoryTarget.Pipe));
|
5135 | ctorDepsType = createCtorDepsType(meta.deps);
|
5136 | }
|
5137 | }
|
5138 | else {
|
5139 | const baseFactory = variable(`ɵ${meta.name}_BaseFactory`);
|
5140 | const getInheritedFactory = importExpr(Identifiers$1.getInheritedFactory);
|
5141 | const baseFactoryStmt = baseFactory
|
5142 | .set(getInheritedFactory.callFn([meta.internalType], undefined, true))
|
5143 | .toDeclStmt(INFERRED_TYPE, [StmtModifier.Exported, StmtModifier.Final]);
|
5144 | statements.push(baseFactoryStmt);
|
5145 |
|
5146 | ctorExpr = baseFactory.callFn([typeForCtor]);
|
5147 | }
|
5148 | const ctorExprFinal = ctorExpr;
|
5149 | const body = [];
|
5150 | let retExpr = null;
|
5151 | function makeConditionalFactory(nonCtorExpr) {
|
5152 | const r = variable('r');
|
5153 | body.push(r.set(NULL_EXPR).toDeclStmt());
|
5154 | let ctorStmt = null;
|
5155 | if (ctorExprFinal !== null) {
|
5156 | ctorStmt = r.set(ctorExprFinal).toStmt();
|
5157 | }
|
5158 | else {
|
5159 | ctorStmt = importExpr(Identifiers$1.invalidFactory).callFn([]).toStmt();
|
5160 | }
|
5161 | body.push(ifStmt(t, [ctorStmt], [r.set(nonCtorExpr).toStmt()]));
|
5162 | return r;
|
5163 | }
|
5164 | if (isDelegatedMetadata(meta) && meta.delegateType === R3FactoryDelegateType.Factory) {
|
5165 | const delegateFactory = variable(`ɵ${meta.name}_BaseFactory`);
|
5166 | const getFactoryOf = importExpr(Identifiers$1.getFactoryOf);
|
5167 | if (meta.delegate.isEquivalent(meta.internalType)) {
|
5168 | throw new Error(`Illegal state: compiling factory that delegates to itself`);
|
5169 | }
|
5170 | const delegateFactoryStmt = delegateFactory.set(getFactoryOf.callFn([meta.delegate])).toDeclStmt(INFERRED_TYPE, [
|
5171 | StmtModifier.Exported, StmtModifier.Final
|
5172 | ]);
|
5173 | statements.push(delegateFactoryStmt);
|
5174 | retExpr = makeConditionalFactory(delegateFactory.callFn([]));
|
5175 | }
|
5176 | else if (isDelegatedMetadata(meta)) {
|
5177 |
|
5178 |
|
5179 | const delegateArgs = injectDependencies(meta.delegateDeps, meta.injectFn, meta.target === R3FactoryTarget.Pipe);
|
5180 |
|
5181 | const factoryExpr = new (meta.delegateType === R3FactoryDelegateType.Class ?
|
5182 | InstantiateExpr :
|
5183 | InvokeFunctionExpr)(meta.delegate, delegateArgs);
|
5184 | retExpr = makeConditionalFactory(factoryExpr);
|
5185 | }
|
5186 | else if (isExpressionFactoryMetadata(meta)) {
|
5187 |
|
5188 | retExpr = makeConditionalFactory(meta.expression);
|
5189 | }
|
5190 | else {
|
5191 | retExpr = ctorExpr;
|
5192 | }
|
5193 | if (retExpr !== null) {
|
5194 | body.push(new ReturnStatement(retExpr));
|
5195 | }
|
5196 | else {
|
5197 | body.push(importExpr(Identifiers$1.invalidFactory).callFn([]).toStmt());
|
5198 | }
|
5199 | return {
|
5200 | factory: fn([new FnParam('t', DYNAMIC_TYPE)], body, INFERRED_TYPE, undefined, `${meta.name}_Factory`),
|
5201 | statements,
|
5202 | type: expressionType(importExpr(Identifiers$1.FactoryDef, [typeWithParameters(meta.type.type, meta.typeArgumentCount), ctorDepsType]))
|
5203 | };
|
5204 | }
|
5205 | function injectDependencies(deps, injectFn, isPipe) {
|
5206 | return deps.map((dep, index) => compileInjectDependency(dep, injectFn, isPipe, index));
|
5207 | }
|
5208 | function compileInjectDependency(dep, injectFn, isPipe, index) {
|
5209 |
|
5210 | switch (dep.resolved) {
|
5211 | case R3ResolvedDependencyType.Token:
|
5212 | case R3ResolvedDependencyType.ChangeDetectorRef:
|
5213 |
|
5214 | const flags = 0 | (dep.self ? 2 : 0) |
|
5215 | (dep.skipSelf ? 4 : 0) | (dep.host ? 1 : 0) |
|
5216 | (dep.optional ? 8 : 0);
|
5217 |
|
5218 |
|
5219 |
|
5220 | let flagsParam = (flags !== 0 || dep.optional) ? literal(flags) : null;
|
5221 |
|
5222 | if (isPipe && dep.resolved === R3ResolvedDependencyType.ChangeDetectorRef) {
|
5223 | return importExpr(Identifiers$1.injectPipeChangeDetectorRef).callFn(flagsParam ? [flagsParam] : []);
|
5224 | }
|
5225 |
|
5226 | const injectArgs = [dep.token];
|
5227 | if (flagsParam) {
|
5228 | injectArgs.push(flagsParam);
|
5229 | }
|
5230 | return importExpr(injectFn).callFn(injectArgs);
|
5231 | case R3ResolvedDependencyType.Attribute:
|
5232 |
|
5233 | return importExpr(Identifiers$1.injectAttribute).callFn([dep.token]);
|
5234 | case R3ResolvedDependencyType.Invalid:
|
5235 | return importExpr(Identifiers$1.invalidFactoryDep).callFn([literal(index)]);
|
5236 | default:
|
5237 | return unsupported(`Unknown R3ResolvedDependencyType: ${R3ResolvedDependencyType[dep.resolved]}`);
|
5238 | }
|
5239 | }
|
5240 | function createCtorDepsType(deps) {
|
5241 | let hasTypes = false;
|
5242 | const attributeTypes = deps.map(dep => {
|
5243 | const type = createCtorDepType(dep);
|
5244 | if (type !== null) {
|
5245 | hasTypes = true;
|
5246 | return type;
|
5247 | }
|
5248 | else {
|
5249 | return literal(null);
|
5250 | }
|
5251 | });
|
5252 | if (hasTypes) {
|
5253 | return expressionType(literalArr(attributeTypes));
|
5254 | }
|
5255 | else {
|
5256 | return NONE_TYPE;
|
5257 | }
|
5258 | }
|
5259 | function createCtorDepType(dep) {
|
5260 | const entries = [];
|
5261 | if (dep.resolved === R3ResolvedDependencyType.Attribute) {
|
5262 | if (dep.attribute !== null) {
|
5263 | entries.push({ key: 'attribute', value: dep.attribute, quoted: false });
|
5264 | }
|
5265 | }
|
5266 | if (dep.optional) {
|
5267 | entries.push({ key: 'optional', value: literal(true), quoted: false });
|
5268 | }
|
5269 | if (dep.host) {
|
5270 | entries.push({ key: 'host', value: literal(true), quoted: false });
|
5271 | }
|
5272 | if (dep.self) {
|
5273 | entries.push({ key: 'self', value: literal(true), quoted: false });
|
5274 | }
|
5275 | if (dep.skipSelf) {
|
5276 | entries.push({ key: 'skipSelf', value: literal(true), quoted: false });
|
5277 | }
|
5278 | return entries.length > 0 ? literalMap(entries) : null;
|
5279 | }
|
5280 | function isDelegatedMetadata(meta) {
|
5281 | return meta.delegateType !== undefined;
|
5282 | }
|
5283 | function isExpressionFactoryMetadata(meta) {
|
5284 | return meta.expression !== undefined;
|
5285 | }
|
5286 |
|
5287 | |
5288 |
|
5289 |
|
5290 |
|
5291 |
|
5292 |
|
5293 |
|
5294 | function compileInjectable(meta) {
|
5295 | let result = null;
|
5296 | const factoryMeta = {
|
5297 | name: meta.name,
|
5298 | type: meta.type,
|
5299 | internalType: meta.internalType,
|
5300 | typeArgumentCount: meta.typeArgumentCount,
|
5301 | deps: [],
|
5302 | injectFn: Identifiers.inject,
|
5303 | target: R3FactoryTarget.Injectable,
|
5304 | };
|
5305 | if (meta.useClass !== undefined) {
|
5306 |
|
5307 |
|
5308 |
|
5309 |
|
5310 |
|
5311 |
|
5312 | const useClassOnSelf = meta.useClass.isEquivalent(meta.internalType);
|
5313 | let deps = undefined;
|
5314 | if (meta.userDeps !== undefined) {
|
5315 | deps = meta.userDeps;
|
5316 | }
|
5317 | if (deps !== undefined) {
|
5318 |
|
5319 | result = compileFactoryFunction(Object.assign(Object.assign({}, factoryMeta), { delegate: meta.useClass, delegateDeps: deps, delegateType: R3FactoryDelegateType.Class }));
|
5320 | }
|
5321 | else if (useClassOnSelf) {
|
5322 | result = compileFactoryFunction(factoryMeta);
|
5323 | }
|
5324 | else {
|
5325 | result = delegateToFactory(meta.type.value, meta.useClass);
|
5326 | }
|
5327 | }
|
5328 | else if (meta.useFactory !== undefined) {
|
5329 | if (meta.userDeps !== undefined) {
|
5330 | result = compileFactoryFunction(Object.assign(Object.assign({}, factoryMeta), { delegate: meta.useFactory, delegateDeps: meta.userDeps || [], delegateType: R3FactoryDelegateType.Function }));
|
5331 | }
|
5332 | else {
|
5333 | result = {
|
5334 | statements: [],
|
5335 | factory: fn([], [new ReturnStatement(meta.useFactory.callFn([]))])
|
5336 | };
|
5337 | }
|
5338 | }
|
5339 | else if (meta.useValue !== undefined) {
|
5340 |
|
5341 |
|
5342 |
|
5343 | result = compileFactoryFunction(Object.assign(Object.assign({}, factoryMeta), { expression: meta.useValue }));
|
5344 | }
|
5345 | else if (meta.useExisting !== undefined) {
|
5346 |
|
5347 | result = compileFactoryFunction(Object.assign(Object.assign({}, factoryMeta), { expression: importExpr(Identifiers.inject).callFn([meta.useExisting]) }));
|
5348 | }
|
5349 | else {
|
5350 | result = delegateToFactory(meta.type.value, meta.internalType);
|
5351 | }
|
5352 | const token = meta.internalType;
|
5353 | const injectableProps = { token, factory: result.factory };
|
5354 |
|
5355 | if (meta.providedIn.value !== null) {
|
5356 | injectableProps.providedIn = meta.providedIn;
|
5357 | }
|
5358 | const expression = importExpr(Identifiers.ɵɵdefineInjectable).callFn([mapToMapExpression(injectableProps)]);
|
5359 | const type = new ExpressionType(importExpr(Identifiers.InjectableDef, [typeWithParameters(meta.type.type, meta.typeArgumentCount)]));
|
5360 | return {
|
5361 | expression,
|
5362 | type,
|
5363 | statements: result.statements,
|
5364 | };
|
5365 | }
|
5366 | function delegateToFactory(type, internalType) {
|
5367 | return {
|
5368 | statements: [],
|
5369 |
|
5370 |
|
5371 |
|
5372 | factory: type.node === internalType.node ?
|
5373 | internalType.prop('ɵfac') :
|
5374 | fn([new FnParam('t', DYNAMIC_TYPE)], [new ReturnStatement(internalType.callMethod('ɵfac', [variable('t')]))])
|
5375 | };
|
5376 | }
|
5377 |
|
5378 | |
5379 |
|
5380 |
|
5381 |
|
5382 |
|
5383 |
|
5384 |
|
5385 | function assertArrayOfStrings(identifier, value) {
|
5386 | if (value == null) {
|
5387 | return;
|
5388 | }
|
5389 | if (!Array.isArray(value)) {
|
5390 | throw new Error(`Expected '${identifier}' to be an array of strings.`);
|
5391 | }
|
5392 | for (let i = 0; i < value.length; i += 1) {
|
5393 | if (typeof value[i] !== 'string') {
|
5394 | throw new Error(`Expected '${identifier}' to be an array of strings.`);
|
5395 | }
|
5396 | }
|
5397 | }
|
5398 | const UNUSABLE_INTERPOLATION_REGEXPS = [
|
5399 | /^\s*$/,
|
5400 | /[<>]/,
|
5401 | /^[{}]$/,
|
5402 | /&(#|[a-z])/i,
|
5403 | /^\/\//,
|
5404 | ];
|
5405 | function assertInterpolationSymbols(identifier, value) {
|
5406 | if (value != null && !(Array.isArray(value) && value.length == 2)) {
|
5407 | throw new Error(`Expected '${identifier}' to be an array, [start, end].`);
|
5408 | }
|
5409 | else if (value != null) {
|
5410 | const start = value[0];
|
5411 | const end = value[1];
|
5412 | // Check for unusable interpolation symbols
|
5413 | UNUSABLE_INTERPOLATION_REGEXPS.forEach(regexp => {
|
5414 | if (regexp.test(start) || regexp.test(end)) {
|
5415 | throw new Error(`['${start}', '${end}'] contains unusable interpolation symbol.`);
|
5416 | }
|
5417 | });
|
5418 | }
|
5419 | }
|
5420 |
|
5421 | /**
|
5422 | * @license
|
5423 | * Copyright Google LLC All Rights Reserved.
|
5424 | *
|
5425 | * Use of this source code is governed by an MIT-style license that can be
|
5426 | * found in the LICENSE file at https://angular.io/license
|
5427 | */
|
5428 | class InterpolationConfig {
|
5429 | constructor(start, end) {
|
5430 | this.start = start;
|
5431 | this.end = end;
|
5432 | }
|
5433 | static fromArray(markers) {
|
5434 | if (!markers) {
|
5435 | return DEFAULT_INTERPOLATION_CONFIG;
|
5436 | }
|
5437 | assertInterpolationSymbols('interpolation', markers);
|
5438 | return new InterpolationConfig(markers[0], markers[1]);
|
5439 | }
|
5440 | }
|
5441 | const DEFAULT_INTERPOLATION_CONFIG = new InterpolationConfig('{{', '}}');
|
5442 |
|
5443 | /**
|
5444 | * @license
|
5445 | * Copyright Google LLC All Rights Reserved.
|
5446 | *
|
5447 | * Use of this source code is governed by an MIT-style license that can be
|
5448 | * found in the LICENSE file at https://angular.io/license
|
5449 | */
|
5450 | /**
|
5451 | * In TypeScript, tagged template functions expect a "template object", which is an array of
|
5452 | * "cooked" strings plus a `raw` property that contains an array of "raw" strings. This is
|
5453 | * typically constructed with a function called `__makeTemplateObject(cooked, raw)`, but it may not
|
5454 | * be available in all environments.
|
5455 | *
|
5456 | * This is a JavaScript polyfill that uses __makeTemplateObject when it's available, but otherwise
|
5457 | * creates an inline helper with the same functionality.
|
5458 | *
|
5459 | * In the inline function, if `Object.defineProperty` is available we use that to attach the `raw`
|
5460 | * array.
|
5461 | */
|
5462 | const makeTemplateObjectPolyfill = '(this&&this.__makeTemplateObject||function(e,t){return Object.defineProperty?Object.defineProperty(e,"raw",{value:t}):e.raw=t,e})';
|
5463 | class AbstractJsEmitterVisitor extends AbstractEmitterVisitor {
|
5464 | constructor() {
|
5465 | super(false);
|
5466 | }
|
5467 | visitDeclareClassStmt(stmt, ctx) {
|
5468 | ctx.pushClass(stmt);
|
5469 | this._visitClassConstructor(stmt, ctx);
|
5470 | if (stmt.parent != null) {
|
5471 | ctx.print(stmt, `${stmt.name}.prototype = Object.create(`);
|
5472 | stmt.parent.visitExpression(this, ctx);
|
5473 | ctx.println(stmt, `.prototype);`);
|
5474 | }
|
5475 | stmt.getters.forEach((getter) => this._visitClassGetter(stmt, getter, ctx));
|
5476 | stmt.methods.forEach((method) => this._visitClassMethod(stmt, method, ctx));
|
5477 | ctx.popClass();
|
5478 | return null;
|
5479 | }
|
5480 | _visitClassConstructor(stmt, ctx) {
|
5481 | ctx.print(stmt, `function ${stmt.name}(`);
|
5482 | if (stmt.constructorMethod != null) {
|
5483 | this._visitParams(stmt.constructorMethod.params, ctx);
|
5484 | }
|
5485 | ctx.println(stmt, `) {`);
|
5486 | ctx.incIndent();
|
5487 | if (stmt.constructorMethod != null) {
|
5488 | if (stmt.constructorMethod.body.length > 0) {
|
5489 | ctx.println(stmt, `var self = this;`);
|
5490 | this.visitAllStatements(stmt.constructorMethod.body, ctx);
|
5491 | }
|
5492 | }
|
5493 | ctx.decIndent();
|
5494 | ctx.println(stmt, `}`);
|
5495 | }
|
5496 | _visitClassGetter(stmt, getter, ctx) {
|
5497 | ctx.println(stmt, `Object.defineProperty(${stmt.name}.prototype, '${getter.name}', { get: function() {`);
|
5498 | ctx.incIndent();
|
5499 | if (getter.body.length > 0) {
|
5500 | ctx.println(stmt, `var self = this;`);
|
5501 | this.visitAllStatements(getter.body, ctx);
|
5502 | }
|
5503 | ctx.decIndent();
|
5504 | ctx.println(stmt, `}});`);
|
5505 | }
|
5506 | _visitClassMethod(stmt, method, ctx) {
|
5507 | ctx.print(stmt, `${stmt.name}.prototype.${method.name} = function(`);
|
5508 | this._visitParams(method.params, ctx);
|
5509 | ctx.println(stmt, `) {`);
|
5510 | ctx.incIndent();
|
5511 | if (method.body.length > 0) {
|
5512 | ctx.println(stmt, `var self = this;`);
|
5513 | this.visitAllStatements(method.body, ctx);
|
5514 | }
|
5515 | ctx.decIndent();
|
5516 | ctx.println(stmt, `};`);
|
5517 | }
|
5518 | visitWrappedNodeExpr(ast, ctx) {
|
5519 | throw new Error('Cannot emit a WrappedNodeExpr in Javascript.');
|
5520 | }
|
5521 | visitReadVarExpr(ast, ctx) {
|
5522 | if (ast.builtin === BuiltinVar.This) {
|
5523 | ctx.print(ast, 'self');
|
5524 | }
|
5525 | else if (ast.builtin === BuiltinVar.Super) {
|
5526 | throw new Error(`'super' needs to be handled at a parent ast node, not at the variable level!`);
|
5527 | }
|
5528 | else {
|
5529 | super.visitReadVarExpr(ast, ctx);
|
5530 | }
|
5531 | return null;
|
5532 | }
|
5533 | visitDeclareVarStmt(stmt, ctx) {
|
5534 | ctx.print(stmt, `var ${stmt.name}`);
|
5535 | if (stmt.value) {
|
5536 | ctx.print(stmt, ' = ');
|
5537 | stmt.value.visitExpression(this, ctx);
|
5538 | }
|
5539 | ctx.println(stmt, `;`);
|
5540 | return null;
|
5541 | }
|
5542 | visitCastExpr(ast, ctx) {
|
5543 | ast.value.visitExpression(this, ctx);
|
5544 | return null;
|
5545 | }
|
5546 | visitInvokeFunctionExpr(expr, ctx) {
|
5547 | const fnExpr = expr.fn;
|
5548 | if (fnExpr instanceof ReadVarExpr && fnExpr.builtin === BuiltinVar.Super) {
|
5549 | ctx.currentClass.parent.visitExpression(this, ctx);
|
5550 | ctx.print(expr, `.call(this`);
|
5551 | if (expr.args.length > 0) {
|
5552 | ctx.print(expr, `, `);
|
5553 | this.visitAllExpressions(expr.args, ctx, ',');
|
5554 | }
|
5555 | ctx.print(expr, `)`);
|
5556 | }
|
5557 | else {
|
5558 | super.visitInvokeFunctionExpr(expr, ctx);
|
5559 | }
|
5560 | return null;
|
5561 | }
|
5562 | visitTaggedTemplateExpr(ast, ctx) {
|
5563 | // The following convoluted piece of code is effectively the downlevelled equivalent of
|
5564 | // ```
|
5565 | // tag`...`
|
5566 | // ```
|
5567 | // which is effectively like:
|
5568 | // ```
|
5569 | // tag(__makeTemplateObject(cooked, raw), expression1, expression2, ...);
|
5570 | // ```
|
5571 | const elements = ast.template.elements;
|
5572 | ast.tag.visitExpression(this, ctx);
|
5573 | ctx.print(ast, `(${makeTemplateObjectPolyfill}(`);
|
5574 | ctx.print(ast, `[${elements.map(part => escapeIdentifier(part.text, false)).join(', ')}], `);
|
5575 | ctx.print(ast, `[${elements.map(part => escapeIdentifier(part.rawText, false)).join(', ')}])`);
|
5576 | ast.template.expressions.forEach(expression => {
|
5577 | ctx.print(ast, ', ');
|
5578 | expression.visitExpression(this, ctx);
|
5579 | });
|
5580 | ctx.print(ast, ')');
|
5581 | return null;
|
5582 | }
|
5583 | visitFunctionExpr(ast, ctx) {
|
5584 | ctx.print(ast, `function${ast.name ? ' ' + ast.name : ''}(`);
|
5585 | this._visitParams(ast.params, ctx);
|
5586 | ctx.println(ast, `) {`);
|
5587 | ctx.incIndent();
|
5588 | this.visitAllStatements(ast.statements, ctx);
|
5589 | ctx.decIndent();
|
5590 | ctx.print(ast, `}`);
|
5591 | return null;
|
5592 | }
|
5593 | visitDeclareFunctionStmt(stmt, ctx) {
|
5594 | ctx.print(stmt, `function ${stmt.name}(`);
|
5595 | this._visitParams(stmt.params, ctx);
|
5596 | ctx.println(stmt, `) {`);
|
5597 | ctx.incIndent();
|
5598 | this.visitAllStatements(stmt.statements, ctx);
|
5599 | ctx.decIndent();
|
5600 | ctx.println(stmt, `}`);
|
5601 | return null;
|
5602 | }
|
5603 | visitTryCatchStmt(stmt, ctx) {
|
5604 | ctx.println(stmt, `try {`);
|
5605 | ctx.incIndent();
|
5606 | this.visitAllStatements(stmt.bodyStmts, ctx);
|
5607 | ctx.decIndent();
|
5608 | ctx.println(stmt, `} catch (${CATCH_ERROR_VAR$1.name}) {`);
|
5609 | ctx.incIndent();
|
5610 | const catchStmts = [CATCH_STACK_VAR$1.set(CATCH_ERROR_VAR$1.prop('stack')).toDeclStmt(null, [
|
5611 | StmtModifier.Final
|
5612 | ])].concat(stmt.catchStmts);
|
5613 | this.visitAllStatements(catchStmts, ctx);
|
5614 | ctx.decIndent();
|
5615 | ctx.println(stmt, `}`);
|
5616 | return null;
|
5617 | }
|
5618 | visitLocalizedString(ast, ctx) {
|
5619 | // The following convoluted piece of code is effectively the downlevelled equivalent of
|
5620 | // ```
|
5621 | // $localize `...`
|
5622 | // ```
|
5623 | // which is effectively like:
|
5624 | // ```
|
5625 | // $localize(__makeTemplateObject(cooked, raw), expression1, expression2, ...);
|
5626 | // ```
|
5627 | ctx.print(ast, `$localize(${makeTemplateObjectPolyfill}(`);
|
5628 | const parts = [ast.serializeI18nHead()];
|
5629 | for (let i = 1; i < ast.messageParts.length; i++) {
|
5630 | parts.push(ast.serializeI18nTemplatePart(i));
|
5631 | }
|
5632 | ctx.print(ast, `[${parts.map(part => escapeIdentifier(part.cooked, false)).join(', ')}], `);
|
5633 | ctx.print(ast, `[${parts.map(part => escapeIdentifier(part.raw, false)).join(', ')}])`);
|
5634 | ast.expressions.forEach(expression => {
|
5635 | ctx.print(ast, ', ');
|
5636 | expression.visitExpression(this, ctx);
|
5637 | });
|
5638 | ctx.print(ast, ')');
|
5639 | return null;
|
5640 | }
|
5641 | _visitParams(params, ctx) {
|
5642 | this.visitAllObjects(param => ctx.print(null, param.name), params, ctx, ',');
|
5643 | }
|
5644 | getBuiltinMethodName(method) {
|
5645 | let name;
|
5646 | switch (method) {
|
5647 | case BuiltinMethod.ConcatArray:
|
5648 | name = 'concat';
|
5649 | break;
|
5650 | case BuiltinMethod.SubscribeObservable:
|
5651 | name = 'subscribe';
|
5652 | break;
|
5653 | case BuiltinMethod.Bind:
|
5654 | name = 'bind';
|
5655 | break;
|
5656 | default:
|
5657 | throw new Error(`Unknown builtin method: ${method}`);
|
5658 | }
|
5659 | return name;
|
5660 | }
|
5661 | }
|
5662 |
|
5663 | /**
|
5664 | * @license
|
5665 | * Copyright Google LLC All Rights Reserved.
|
5666 | *
|
5667 | * Use of this source code is governed by an MIT-style license that can be
|
5668 | * found in the LICENSE file at https://angular.io/license
|
5669 | */
|
5670 | /**
|
5671 | * The Trusted Types policy, or null if Trusted Types are not
|
5672 | * enabled/supported, or undefined if the policy has not been created yet.
|
5673 | */
|
5674 | let policy;
|
5675 | /**
|
5676 | * Returns the Trusted Types policy, or null if Trusted Types are not
|
5677 | * enabled/supported. The first call to this function will create the policy.
|
5678 | */
|
5679 | function getPolicy() {
|
5680 | if (policy === undefined) {
|
5681 | policy = null;
|
5682 | if (_global.trustedTypes) {
|
5683 | try {
|
5684 | policy =
|
5685 | _global.trustedTypes.createPolicy('angular#unsafe-jit', {
|
5686 | createScript: (s) => s,
|
5687 | });
|
5688 | }
|
5689 | catch (_a) {
|
5690 | // trustedTypes.createPolicy throws if called with a name that is
|
5691 | // already registered, even in report-only mode. Until the API changes,
|
5692 | // catch the error not to break the applications functionally. In such
|
5693 | // cases, the code will fall back to using strings.
|
5694 | }
|
5695 | }
|
5696 | }
|
5697 | return policy;
|
5698 | }
|
5699 | /**
|
5700 | * Unsafely promote a string to a TrustedScript, falling back to strings when
|
5701 | * Trusted Types are not available.
|
5702 | * @security In particular, it must be assured that the provided string will
|
5703 | * never cause an XSS vulnerability if used in a context that will be
|
5704 | * interpreted and executed as a script by a browser, e.g. when calling eval.
|
5705 | */
|
5706 | function trustedScriptFromString(script) {
|
5707 | var _a;
|
5708 | return ((_a = getPolicy()) === null || _a === void 0 ? void 0 : _a.createScript(script)) || script;
|
5709 | }
|
5710 | /**
|
5711 | * Unsafely call the Function constructor with the given string arguments. It
|
5712 | * is only available in development mode, and should be stripped out of
|
5713 | * production code.
|
5714 | * @security This is a security-sensitive function; any use of this function
|
5715 | * must go through security review. In particular, it must be assured that it
|
5716 | * is only called from the JIT compiler, as use in other code can lead to XSS
|
5717 | * vulnerabilities.
|
5718 | */
|
5719 | function newTrustedFunctionForJIT(...args) {
|
5720 | if (!_global.trustedTypes) {
|
5721 | // In environments that don't support Trusted Types, fall back to the most
|
5722 | // straightforward implementation:
|
5723 | return new Function(...args);
|
5724 | }
|
5725 | // Chrome currently does not support passing TrustedScript to the Function
|
5726 | // constructor. The following implements the workaround proposed on the page
|
5727 | // below, where the Chromium bug is also referenced:
|
5728 | // https://github.com/w3c/webappsec-trusted-types/wiki/Trusted-Types-for-function-constructor
|
5729 | const fnArgs = args.slice(0, -1).join(',');
|
5730 | const fnBody = args.pop().toString();
|
5731 | const body = `(function anonymous(${fnArgs}
|
5732 | ) { ${fnBody}
|
5733 | })`;
|
5734 | // Using eval directly confuses the compiler and prevents this module from
|
5735 | // being stripped out of JS binaries even if not used. The global['eval']
|
5736 | // indirection fixes that.
|
5737 | const fn = _global['eval'](trustedScriptFromString(body));
|
5738 | // To completely mimic the behavior of calling "new Function", two more
|
5739 | // things need to happen:
|
5740 | // 1. Stringifying the resulting function should return its source code
|
5741 | fn.toString = () => body;
|
5742 | // 2. When calling the resulting function, `this` should refer to `global`
|
5743 | return fn.bind(_global);
|
5744 | // When Trusted Types support in Function constructors is widely available,
|
5745 | // the implementation of this function can be simplified to:
|
5746 | // return new Function(...args.map(a => trustedScriptFromString(a)));
|
5747 | }
|
5748 |
|
5749 | /**
|
5750 | * @license
|
5751 | * Copyright Google LLC All Rights Reserved.
|
5752 | *
|
5753 | * Use of this source code is governed by an MIT-style license that can be
|
5754 | * found in the LICENSE file at https://angular.io/license
|
5755 | */
|
5756 | /**
|
5757 | * A helper class to manage the evaluation of JIT generated code.
|
5758 | */
|
5759 | class JitEvaluator {
|
5760 | /**
|
5761 | *
|
5762 | * @param sourceUrl The URL of the generated code.
|
5763 | * @param statements An array of Angular statement AST nodes to be evaluated.
|
5764 | * @param reflector A helper used when converting the statements to executable code.
|
5765 | * @param createSourceMaps If true then create a source-map for the generated code and include it
|
5766 | * inline as a source-map comment.
|
5767 | * @returns A map of all the variables in the generated code.
|
5768 | */
|
5769 | evaluateStatements(sourceUrl, statements, reflector, createSourceMaps) {
|
5770 | const converter = new JitEmitterVisitor(reflector);
|
5771 | const ctx = EmitterVisitorContext.createRoot();
|
5772 | // Ensure generated code is in strict mode
|
5773 | if (statements.length > 0 && !isUseStrictStatement(statements[0])) {
|
5774 | statements = [
|
5775 | literal('use strict').toStmt(),
|
5776 | ...statements,
|
5777 | ];
|
5778 | }
|
5779 | converter.visitAllStatements(statements, ctx);
|
5780 | converter.createReturnStmt(ctx);
|
5781 | return this.evaluateCode(sourceUrl, ctx, converter.getArgs(), createSourceMaps);
|
5782 | }
|
5783 | /**
|
5784 | * Evaluate a piece of JIT generated code.
|
5785 | * @param sourceUrl The URL of this generated code.
|
5786 | * @param ctx A context object that contains an AST of the code to be evaluated.
|
5787 | * @param vars A map containing the names and values of variables that the evaluated code might
|
5788 | * reference.
|
5789 | * @param createSourceMap If true then create a source-map for the generated code and include it
|
5790 | * inline as a source-map comment.
|
5791 | * @returns The result of evaluating the code.
|
5792 | */
|
5793 | evaluateCode(sourceUrl, ctx, vars, createSourceMap) {
|
5794 | let fnBody = `"use strict";${ctx.toSource()}\n//# sourceURL=${sourceUrl}`;
|
5795 | const fnArgNames = [];
|
5796 | const fnArgValues = [];
|
5797 | for (const argName in vars) {
|
5798 | fnArgValues.push(vars[argName]);
|
5799 | fnArgNames.push(argName);
|
5800 | }
|
5801 | if (createSourceMap) {
|
5802 | // using `new Function(...)` generates a header, 1 line of no arguments, 2 lines otherwise
|
5803 | // E.g. ```
|
5804 | // function anonymous(a,b,c
|
5805 | // /**/) { ... }```
|
5806 | // We don't want to hard code this fact, so we auto detect it via an empty function first.
|
5807 | const emptyFn = newTrustedFunctionForJIT(...fnArgNames.concat('return null;')).toString();
|
5808 | const headerLines = emptyFn.slice(0, emptyFn.indexOf('return null;')).split('\n').length - 1;
|
5809 | fnBody += `\n${ctx.toSourceMapGenerator(sourceUrl, headerLines).toJsComment()}`;
|
5810 | }
|
5811 | const fn = newTrustedFunctionForJIT(...fnArgNames.concat(fnBody));
|
5812 | return this.executeFunction(fn, fnArgValues);
|
5813 | }
|
5814 | /**
|
5815 | * Execute a JIT generated function by calling it.
|
5816 | *
|
5817 | * This method can be overridden in tests to capture the functions that are generated
|
5818 | * by this `JitEvaluator` class.
|
5819 | *
|
5820 | * @param fn A function to execute.
|
5821 | * @param args The arguments to pass to the function being executed.
|
5822 | * @returns The return value of the executed function.
|
5823 | */
|
5824 | executeFunction(fn, args) {
|
5825 | return fn(...args);
|
5826 | }
|
5827 | }
|
5828 | /**
|
5829 | * An Angular AST visitor that converts AST nodes into executable JavaScript code.
|
5830 | */
|
5831 | class JitEmitterVisitor extends AbstractJsEmitterVisitor {
|
5832 | constructor(reflector) {
|
5833 | super();
|
5834 | this.reflector = reflector;
|
5835 | this._evalArgNames = [];
|
5836 | this._evalArgValues = [];
|
5837 | this._evalExportedVars = [];
|
5838 | }
|
5839 | createReturnStmt(ctx) {
|
5840 | const stmt = new ReturnStatement(new LiteralMapExpr(this._evalExportedVars.map(resultVar => new LiteralMapEntry(resultVar, variable(resultVar), false))));
|
5841 | stmt.visitStatement(this, ctx);
|
5842 | }
|
5843 | getArgs() {
|
5844 | const result = {};
|
5845 | for (let i = 0; i < this._evalArgNames.length; i++) {
|
5846 | result[this._evalArgNames[i]] = this._evalArgValues[i];
|
5847 | }
|
5848 | return result;
|
5849 | }
|
5850 | visitExternalExpr(ast, ctx) {
|
5851 | this._emitReferenceToExternal(ast, this.reflector.resolveExternalReference(ast.value), ctx);
|
5852 | return null;
|
5853 | }
|
5854 | visitWrappedNodeExpr(ast, ctx) {
|
5855 | this._emitReferenceToExternal(ast, ast.node, ctx);
|
5856 | return null;
|
5857 | }
|
5858 | visitDeclareVarStmt(stmt, ctx) {
|
5859 | if (stmt.hasModifier(StmtModifier.Exported)) {
|
5860 | this._evalExportedVars.push(stmt.name);
|
5861 | }
|
5862 | return super.visitDeclareVarStmt(stmt, ctx);
|
5863 | }
|
5864 | visitDeclareFunctionStmt(stmt, ctx) {
|
5865 | if (stmt.hasModifier(StmtModifier.Exported)) {
|
5866 | this._evalExportedVars.push(stmt.name);
|
5867 | }
|
5868 | return super.visitDeclareFunctionStmt(stmt, ctx);
|
5869 | }
|
5870 | visitDeclareClassStmt(stmt, ctx) {
|
5871 | if (stmt.hasModifier(StmtModifier.Exported)) {
|
5872 | this._evalExportedVars.push(stmt.name);
|
5873 | }
|
5874 | return super.visitDeclareClassStmt(stmt, ctx);
|
5875 | }
|
5876 | _emitReferenceToExternal(ast, value, ctx) {
|
5877 | let id = this._evalArgValues.indexOf(value);
|
5878 | if (id === -1) {
|
5879 | id = this._evalArgValues.length;
|
5880 | this._evalArgValues.push(value);
|
5881 | const name = identifierName({ reference: value }) || 'val';
|
5882 | this._evalArgNames.push(`jit_${name}_${id}`);
|
5883 | }
|
5884 | ctx.print(ast, this._evalArgNames[id]);
|
5885 | }
|
5886 | }
|
5887 | function isUseStrictStatement(statement) {
|
5888 | return statement.isEquivalent(literal('use strict').toStmt());
|
5889 | }
|
5890 |
|
5891 | /**
|
5892 | * @license
|
5893 | * Copyright Google LLC All Rights Reserved.
|
5894 | *
|
5895 | * Use of this source code is governed by an MIT-style license that can be
|
5896 | * found in the LICENSE file at https://angular.io/license
|
5897 | */
|
5898 | const $EOF = 0;
|
5899 | const $BSPACE = 8;
|
5900 | const $TAB = 9;
|
5901 | const $LF = 10;
|
5902 | const $VTAB = 11;
|
5903 | const $FF = 12;
|
5904 | const $CR = 13;
|
5905 | const $SPACE = 32;
|
5906 | const $BANG = 33;
|
5907 | const $DQ = 34;
|
5908 | const $HASH = 35;
|
5909 | const $$ = 36;
|
5910 | const $PERCENT = 37;
|
5911 | const $AMPERSAND = 38;
|
5912 | const $SQ = 39;
|
5913 | const $LPAREN = 40;
|
5914 | const $RPAREN = 41;
|
5915 | const $STAR = 42;
|
5916 | const $PLUS = 43;
|
5917 | const $COMMA = 44;
|
5918 | const $MINUS = 45;
|
5919 | const $PERIOD = 46;
|
5920 | const $SLASH = 47;
|
5921 | const $COLON = 58;
|
5922 | const $SEMICOLON = 59;
|
5923 | const $LT = 60;
|
5924 | const $EQ = 61;
|
5925 | const $GT = 62;
|
5926 | const $QUESTION = 63;
|
5927 | const $0 = 48;
|
5928 | const $7 = 55;
|
5929 | const $9 = 57;
|
5930 | const $A = 65;
|
5931 | const $E = 69;
|
5932 | const $F = 70;
|
5933 | const $X = 88;
|
5934 | const $Z = 90;
|
5935 | const $LBRACKET = 91;
|
5936 | const $BACKSLASH = 92;
|
5937 | const $RBRACKET = 93;
|
5938 | const $CARET = 94;
|
5939 | const $_ = 95;
|
5940 | const $a = 97;
|
5941 | const $b = 98;
|
5942 | const $e = 101;
|
5943 | const $f = 102;
|
5944 | const $n = 110;
|
5945 | const $r = 114;
|
5946 | const $t = 116;
|
5947 | const $u = 117;
|
5948 | const $v = 118;
|
5949 | const $x = 120;
|
5950 | const $z = 122;
|
5951 | const $LBRACE = 123;
|
5952 | const $BAR = 124;
|
5953 | const $RBRACE = 125;
|
5954 | const $NBSP = 160;
|
5955 | const $BT = 96;
|
5956 | function isWhitespace(code) {
|
5957 | return (code >= $TAB && code <= $SPACE) || (code == $NBSP);
|
5958 | }
|
5959 | function isDigit(code) {
|
5960 | return $0 <= code && code <= $9;
|
5961 | }
|
5962 | function isAsciiLetter(code) {
|
5963 | return code >= $a && code <= $z || code >= $A && code <= $Z;
|
5964 | }
|
5965 | function isAsciiHexDigit(code) {
|
5966 | return code >= $a && code <= $f || code >= $A && code <= $F || isDigit(code);
|
5967 | }
|
5968 | function isNewLine(code) {
|
5969 | return code === $LF || code === $CR;
|
5970 | }
|
5971 | function isOctalDigit(code) {
|
5972 | return $0 <= code && code <= $7;
|
5973 | }
|
5974 |
|
5975 | /**
|
5976 | * @license
|
5977 | * Copyright Google LLC All Rights Reserved.
|
5978 | *
|
5979 | * Use of this source code is governed by an MIT-style license that can be
|
5980 | * found in the LICENSE file at https://angular.io/license
|
5981 | */
|
5982 | class ParseLocation {
|
5983 | constructor(file, offset, line, col) {
|
5984 | this.file = file;
|
5985 | this.offset = offset;
|
5986 | this.line = line;
|
5987 | this.col = col;
|
5988 | }
|
5989 | toString() {
|
5990 | return this.offset != null ? `${this.file.url}@${this.line}:${this.col}` : this.file.url;
|
5991 | }
|
5992 | moveBy(delta) {
|
5993 | const source = this.file.content;
|
5994 | const len = source.length;
|
5995 | let offset = this.offset;
|
5996 | let line = this.line;
|
5997 | let col = this.col;
|
5998 | while (offset > 0 && delta < 0) {
|
5999 | offset--;
|
6000 | delta++;
|
6001 | const ch = source.charCodeAt(offset);
|
6002 | if (ch == $LF) {
|
6003 | line--;
|
6004 | const priorLine = source.substr(0, offset - 1).lastIndexOf(String.fromCharCode($LF));
|
6005 | col = priorLine > 0 ? offset - priorLine : offset;
|
6006 | }
|
6007 | else {
|
6008 | col--;
|
6009 | }
|
6010 | }
|
6011 | while (offset < len && delta > 0) {
|
6012 | const ch = source.charCodeAt(offset);
|
6013 | offset++;
|
6014 | delta--;
|
6015 | if (ch == $LF) {
|
6016 | line++;
|
6017 | col = 0;
|
6018 | }
|
6019 | else {
|
6020 | col++;
|
6021 | }
|
6022 | }
|
6023 | return new ParseLocation(this.file, offset, line, col);
|
6024 | }
|
6025 | // Return the source around the location
|
6026 | // Up to `maxChars` or `maxLines` on each side of the location
|
6027 | getContext(maxChars, maxLines) {
|
6028 | const content = this.file.content;
|
6029 | let startOffset = this.offset;
|
6030 | if (startOffset != null) {
|
6031 | if (startOffset > content.length - 1) {
|
6032 | startOffset = content.length - 1;
|
6033 | }
|
6034 | let endOffset = startOffset;
|
6035 | let ctxChars = 0;
|
6036 | let ctxLines = 0;
|
6037 | while (ctxChars < maxChars && startOffset > 0) {
|
6038 | startOffset--;
|
6039 | ctxChars++;
|
6040 | if (content[startOffset] == '\n') {
|
6041 | if (++ctxLines == maxLines) {
|
6042 | break;
|
6043 | }
|
6044 | }
|
6045 | }
|
6046 | ctxChars = 0;
|
6047 | ctxLines = 0;
|
6048 | while (ctxChars < maxChars && endOffset < content.length - 1) {
|
6049 | endOffset++;
|
6050 | ctxChars++;
|
6051 | if (content[endOffset] == '\n') {
|
6052 | if (++ctxLines == maxLines) {
|
6053 | break;
|
6054 | }
|
6055 | }
|
6056 | }
|
6057 | return {
|
6058 | before: content.substring(startOffset, this.offset),
|
6059 | after: content.substring(this.offset, endOffset + 1),
|
6060 | };
|
6061 | }
|
6062 | return null;
|
6063 | }
|
6064 | }
|
6065 | class ParseSourceFile {
|
6066 | constructor(content, url) {
|
6067 | this.content = content;
|
6068 | this.url = url;
|
6069 | }
|
6070 | }
|
6071 | class ParseSourceSpan {
|
6072 | /**
|
6073 | * Create an object that holds information about spans of tokens/nodes captured during
|
6074 | * lexing/parsing of text.
|
6075 | *
|
6076 | * @param start
|
6077 | * The location of the start of the span (having skipped leading trivia).
|
6078 | * Skipping leading trivia makes source-spans more "user friendly", since things like HTML
|
6079 | * elements will appear to begin at the start of the opening tag, rather than at the start of any
|
6080 | * leading trivia, which could include newlines.
|
6081 | *
|
6082 | * @param end
|
6083 | * The location of the end of the span.
|
6084 | *
|
6085 | * @param fullStart
|
6086 | * The start of the token without skipping the leading trivia.
|
6087 | * This is used by tooling that splits tokens further, such as extracting Angular interpolations
|
6088 | * from text tokens. Such tooling creates new source-spans relative to the original token's
|
6089 | * source-span. If leading trivia characters have been skipped then the new source-spans may be
|
6090 | * incorrectly offset.
|
6091 | *
|
6092 | * @param details
|
6093 | * Additional information (such as identifier names) that should be associated with the span.
|
6094 | */
|
6095 | constructor(start, end, fullStart = start, details = null) {
|
6096 | this.start = start;
|
6097 | this.end = end;
|
6098 | this.fullStart = fullStart;
|
6099 | this.details = details;
|
6100 | }
|
6101 | toString() {
|
6102 | return this.start.file.content.substring(this.start.offset, this.end.offset);
|
6103 | }
|
6104 | }
|
6105 | var ParseErrorLevel;
|
6106 | (function (ParseErrorLevel) {
|
6107 | ParseErrorLevel[ParseErrorLevel["WARNING"] = 0] = "WARNING";
|
6108 | ParseErrorLevel[ParseErrorLevel["ERROR"] = 1] = "ERROR";
|
6109 | })(ParseErrorLevel || (ParseErrorLevel = {}));
|
6110 | class ParseError {
|
6111 | constructor(span, msg, level = ParseErrorLevel.ERROR) {
|
6112 | this.span = span;
|
6113 | this.msg = msg;
|
6114 | this.level = level;
|
6115 | }
|
6116 | contextualMessage() {
|
6117 | const ctx = this.span.start.getContext(100, 3);
|
6118 | return ctx ? `${this.msg} ("${ctx.before}[${ParseErrorLevel[this.level]} ->]${ctx.after}")` :
|
6119 | this.msg;
|
6120 | }
|
6121 | toString() {
|
6122 | const details = this.span.details ? `, ${this.span.details}` : '';
|
6123 | return `${this.contextualMessage()}: ${this.span.start}${details}`;
|
6124 | }
|
6125 | }
|
6126 | /**
|
6127 | * Generates Source Span object for a given R3 Type for JIT mode.
|
6128 | *
|
6129 | * @param kind Component or Directive.
|
6130 | * @param typeName name of the Component or Directive.
|
6131 | * @param sourceUrl reference to Component or Directive source.
|
6132 | * @returns instance of ParseSourceSpan that represent a given Component or Directive.
|
6133 | */
|
6134 | function r3JitTypeSourceSpan(kind, typeName, sourceUrl) {
|
6135 | const sourceFileName = `in ${kind} ${typeName} in ${sourceUrl}`;
|
6136 | const sourceFile = new ParseSourceFile('', sourceFileName);
|
6137 | return new ParseSourceSpan(new ParseLocation(sourceFile, -1, -1, -1), new ParseLocation(sourceFile, -1, -1, -1));
|
6138 | }
|
6139 |
|
6140 | /**
|
6141 | * @license
|
6142 | * Copyright Google LLC All Rights Reserved.
|
6143 | *
|
6144 | * Use of this source code is governed by an MIT-style license that can be
|
6145 | * found in the LICENSE file at https://angular.io/license
|
6146 | */
|
6147 | /**
|
6148 | * Implementation of `CompileReflector` which resolves references to @angular/core
|
6149 | * symbols at runtime, according to a consumer-provided mapping.
|
6150 | *
|
6151 | * Only supports `resolveExternalReference`, all other methods throw.
|
6152 | */
|
6153 | class R3JitReflector {
|
6154 | constructor(context) {
|
6155 | this.context = context;
|
6156 | }
|
6157 | resolveExternalReference(ref) {
|
6158 | // This reflector only handles @angular/core imports.
|
6159 | if (ref.moduleName !== '@angular/core') {
|
6160 | throw new Error(`Cannot resolve external reference to ${ref.moduleName}, only references to @angular/core are supported.`);
|
6161 | }
|
6162 | if (!this.context.hasOwnProperty(ref.name)) {
|
6163 | throw new Error(`No value provided for @angular/core symbol '${ref.name}'.`);
|
6164 | }
|
6165 | return this.context[ref.name];
|
6166 | }
|
6167 | parameters(typeOrFunc) {
|
6168 | throw new Error('Not implemented.');
|
6169 | }
|
6170 | annotations(typeOrFunc) {
|
6171 | throw new Error('Not implemented.');
|
6172 | }
|
6173 | shallowAnnotations(typeOrFunc) {
|
6174 | throw new Error('Not implemented.');
|
6175 | }
|
6176 | tryAnnotations(typeOrFunc) {
|
6177 | throw new Error('Not implemented.');
|
6178 | }
|
6179 | propMetadata(typeOrFunc) {
|
6180 | throw new Error('Not implemented.');
|
6181 | }
|
6182 | hasLifecycleHook(type, lcProperty) {
|
6183 | throw new Error('Not implemented.');
|
6184 | }
|
6185 | guards(typeOrFunc) {
|
6186 | throw new Error('Not implemented.');
|
6187 | }
|
6188 | componentModuleUrl(type, cmpMetadata) {
|
6189 | throw new Error('Not implemented.');
|
6190 | }
|
6191 | }
|
6192 |
|
6193 | /**
|
6194 | * @license
|
6195 | * Copyright Google LLC All Rights Reserved.
|
6196 | *
|
6197 | * Use of this source code is governed by an MIT-style license that can be
|
6198 | * found in the LICENSE file at https://angular.io/license
|
6199 | */
|
6200 | function mapLiteral(obj, quoted = false) {
|
6201 | return literalMap(Object.keys(obj).map(key => ({
|
6202 | key,
|
6203 | quoted,
|
6204 | value: obj[key],
|
6205 | })));
|
6206 | }
|
6207 |
|
6208 | /**
|
6209 | * @license
|
6210 | * Copyright Google LLC All Rights Reserved.
|
6211 | *
|
6212 | * Use of this source code is governed by an MIT-style license that can be
|
6213 | * found in the LICENSE file at https://angular.io/license
|
6214 | */
|
6215 | /**
|
6216 | * Construct an `R3NgModuleDef` for the given `R3NgModuleMetadata`.
|
6217 | */
|
6218 | function compileNgModule(meta) {
|
6219 | const { internalType, type: moduleType, bootstrap, declarations, imports, exports, schemas, containsForwardDecls, emitInline, id } = meta;
|
6220 | const additionalStatements = [];
|
6221 | const definitionMap = { type: internalType };
|
6222 | // Only generate the keys in the metadata if the arrays have values.
|
6223 | if (bootstrap.length) {
|
6224 | definitionMap.bootstrap = refsToArray(bootstrap, containsForwardDecls);
|
6225 | }
|
6226 | // If requested to emit scope information inline, pass the declarations, imports and exports to
|
6227 | // the `ɵɵdefineNgModule` call. The JIT compilation uses this.
|
6228 | if (emitInline) {
|
6229 | if (declarations.length) {
|
6230 | definitionMap.declarations = refsToArray(declarations, containsForwardDecls);
|
6231 | }
|
6232 | if (imports.length) {
|
6233 | definitionMap.imports = refsToArray(imports, containsForwardDecls);
|
6234 | }
|
6235 | if (exports.length) {
|
6236 | definitionMap.exports = refsToArray(exports, containsForwardDecls);
|
6237 | }
|
6238 | }
|
6239 | // If not emitting inline, the scope information is not passed into `ɵɵdefineNgModule` as it would
|
6240 | // prevent tree-shaking of the declarations, imports and exports references.
|
6241 | else {
|
6242 | const setNgModuleScopeCall = generateSetNgModuleScopeCall(meta);
|
6243 | if (setNgModuleScopeCall !== null) {
|
6244 | additionalStatements.push(setNgModuleScopeCall);
|
6245 | }
|
6246 | }
|
6247 | if (schemas && schemas.length) {
|
6248 | definitionMap.schemas = literalArr(schemas.map(ref => ref.value));
|
6249 | }
|
6250 | if (id) {
|
6251 | definitionMap.id = id;
|
6252 | }
|
6253 | const expression = importExpr(Identifiers$1.defineNgModule).callFn([mapToMapExpression(definitionMap)]);
|
6254 | const type = new ExpressionType(importExpr(Identifiers$1.NgModuleDefWithMeta, [
|
6255 | new ExpressionType(moduleType.type), tupleTypeOf(declarations), tupleTypeOf(imports),
|
6256 | tupleTypeOf(exports)
|
6257 | ]));
|
6258 | return { expression, type, additionalStatements };
|
6259 | }
|
6260 | /**
|
6261 | * Generates a function call to `ɵɵsetNgModuleScope` with all necessary information so that the
|
6262 | * transitive module scope can be computed during runtime in JIT mode. This call is marked pure
|
6263 | * such that the references to declarations, imports and exports may be elided causing these
|
6264 | * symbols to become tree-shakeable.
|
6265 | */
|
6266 | function generateSetNgModuleScopeCall(meta) {
|
6267 | const { adjacentType: moduleType, declarations, imports, exports, containsForwardDecls } = meta;
|
6268 | const scopeMap = {};
|
6269 | if (declarations.length) {
|
6270 | scopeMap.declarations = refsToArray(declarations, containsForwardDecls);
|
6271 | }
|
6272 | if (imports.length) {
|
6273 | scopeMap.imports = refsToArray(imports, containsForwardDecls);
|
6274 | }
|
6275 | if (exports.length) {
|
6276 | scopeMap.exports = refsToArray(exports, containsForwardDecls);
|
6277 | }
|
6278 | if (Object.keys(scopeMap).length === 0) {
|
6279 | return null;
|
6280 | }
|
6281 | // setNgModuleScope(...)
|
6282 | const fnCall = new InvokeFunctionExpr(
|
6283 | /* fn */ importExpr(Identifiers$1.setNgModuleScope),
|
6284 | /* args */ [moduleType, mapToMapExpression(scopeMap)]);
|
6285 | // (ngJitMode guard) && setNgModuleScope(...)
|
6286 | const guardedCall = jitOnlyGuardedExpression(fnCall);
|
6287 | // function() { (ngJitMode guard) && setNgModuleScope(...); }
|
6288 | const iife = new FunctionExpr(
|
6289 | /* params */ [],
|
6290 | /* statements */ [guardedCall.toStmt()]);
|
6291 | // (function() { (ngJitMode guard) && setNgModuleScope(...); })()
|
6292 | const iifeCall = new InvokeFunctionExpr(
|
6293 | /* fn */ iife,
|
6294 | /* args */ []);
|
6295 | return iifeCall.toStmt();
|
6296 | }
|
6297 | function compileInjector(meta) {
|
6298 | const result = compileFactoryFunction({
|
6299 | name: meta.name,
|
6300 | type: meta.type,
|
6301 | internalType: meta.internalType,
|
6302 | typeArgumentCount: 0,
|
6303 | deps: meta.deps,
|
6304 | injectFn: Identifiers$1.inject,
|
6305 | target: R3FactoryTarget.NgModule,
|
6306 | });
|
6307 | const definitionMap = {
|
6308 | factory: result.factory,
|
6309 | };
|
6310 | if (meta.providers !== null) {
|
6311 | definitionMap.providers = meta.providers;
|
6312 | }
|
6313 | if (meta.imports.length > 0) {
|
6314 | definitionMap.imports = literalArr(meta.imports);
|
6315 | }
|
6316 | const expression = importExpr(Identifiers$1.defineInjector).callFn([mapToMapExpression(definitionMap)]);
|
6317 | const type = new ExpressionType(importExpr(Identifiers$1.InjectorDef, [new ExpressionType(meta.type.type)]));
|
6318 | return { expression, type, statements: result.statements };
|
6319 | }
|
6320 | function tupleTypeOf(exp) {
|
6321 | const types = exp.map(ref => typeofExpr(ref.type));
|
6322 | return exp.length > 0 ? expressionType(literalArr(types)) : NONE_TYPE;
|
6323 | }
|
6324 | function refsToArray(refs, shouldForwardDeclare) {
|
6325 | const values = literalArr(refs.map(ref => ref.value));
|
6326 | return shouldForwardDeclare ? fn([], [new ReturnStatement(values)]) : values;
|
6327 | }
|
6328 |
|
6329 | /**
|
6330 | * @license
|
6331 | * Copyright Google LLC All Rights Reserved.
|
6332 | *
|
6333 | * Use of this source code is governed by an MIT-style license that can be
|
6334 | * found in the LICENSE file at https://angular.io/license
|
6335 | */
|
6336 | function compilePipeFromMetadata(metadata) {
|
6337 | const definitionMapValues = [];
|
6338 | // e.g. `name: 'myPipe'`
|
6339 | definitionMapValues.push({ key: 'name', value: literal(metadata.pipeName), quoted: false });
|
6340 | // e.g. `type: MyPipe`
|
6341 | definitionMapValues.push({ key: 'type', value: metadata.type.value, quoted: false });
|
6342 | // e.g. `pure: true`
|
6343 | definitionMapValues.push({ key: 'pure', value: literal(metadata.pure), quoted: false });
|
6344 | const expression = importExpr(Identifiers$1.definePipe).callFn([literalMap(definitionMapValues)]);
|
6345 | const type = new ExpressionType(importExpr(Identifiers$1.PipeDefWithMeta, [
|
6346 | typeWithParameters(metadata.type.type, metadata.typeArgumentCount),
|
6347 | new ExpressionType(new LiteralExpr(metadata.pipeName)),
|
6348 | ]));
|
6349 | return { expression, type };
|
6350 | }
|
6351 |
|
6352 | /**
|
6353 | * @license
|
6354 | * Copyright Google LLC All Rights Reserved.
|
6355 | *
|
6356 | * Use of this source code is governed by an MIT-style license that can be
|
6357 | * found in the LICENSE file at https://angular.io/license
|
6358 | */
|
6359 | class ParserError {
|
6360 | constructor(message, input, errLocation, ctxLocation) {
|
6361 | this.input = input;
|
6362 | this.errLocation = errLocation;
|
6363 | this.ctxLocation = ctxLocation;
|
6364 | this.message = `Parser Error: ${message} ${errLocation} [${input}] in ${ctxLocation}`;
|
6365 | }
|
6366 | }
|
6367 | class ParseSpan {
|
6368 | constructor(start, end) {
|
6369 | this.start = start;
|
6370 | this.end = end;
|
6371 | }
|
6372 | toAbsolute(absoluteOffset) {
|
6373 | return new AbsoluteSourceSpan(absoluteOffset + this.start, absoluteOffset + this.end);
|
6374 | }
|
6375 | }
|
6376 | class AST {
|
6377 | constructor(span,
|
6378 | /**
|
6379 | * Absolute location of the expression AST in a source code file.
|
6380 | */
|
6381 | sourceSpan) {
|
6382 | this.span = span;
|
6383 | this.sourceSpan = sourceSpan;
|
6384 | }
|
6385 | visit(visitor, context = null) {
|
6386 | return null;
|
6387 | }
|
6388 | toString() {
|
6389 | return 'AST';
|
6390 | }
|
6391 | }
|
6392 | class ASTWithName extends AST {
|
6393 | constructor(span, sourceSpan, nameSpan) {
|
6394 | super(span, sourceSpan);
|
6395 | this.nameSpan = nameSpan;
|
6396 | }
|
6397 | }
|
6398 | /**
|
6399 | * Represents a quoted expression of the form:
|
6400 | *
|
6401 | * quote = prefix `:` uninterpretedExpression
|
6402 | * prefix = identifier
|
6403 | * uninterpretedExpression = arbitrary string
|
6404 | *
|
6405 | * A quoted expression is meant to be pre-processed by an AST transformer that
|
6406 | * converts it into another AST that no longer contains quoted expressions.
|
6407 | * It is meant to allow third-party developers to extend Angular template
|
6408 | * expression language. The `uninterpretedExpression` part of the quote is
|
6409 | * therefore not interpreted by the Angular's own expression parser.
|
6410 | */
|
6411 | class Quote extends AST {
|
6412 | constructor(span, sourceSpan, prefix, uninterpretedExpression, location) {
|
6413 | super(span, sourceSpan);
|
6414 | this.prefix = prefix;
|
6415 | this.uninterpretedExpression = uninterpretedExpression;
|
6416 | this.location = location;
|
6417 | }
|
6418 | visit(visitor, context = null) {
|
6419 | return visitor.visitQuote(this, context);
|
6420 | }
|
6421 | toString() {
|
6422 | return 'Quote';
|
6423 | }
|
6424 | }
|
6425 | class EmptyExpr extends AST {
|
6426 | visit(visitor, context = null) {
|
6427 | // do nothing
|
6428 | }
|
6429 | }
|
6430 | class ImplicitReceiver extends AST {
|
6431 | visit(visitor, context = null) {
|
6432 | return visitor.visitImplicitReceiver(this, context);
|
6433 | }
|
6434 | }
|
6435 | /**
|
6436 | * Receiver when something is accessed through `this` (e.g. `this.foo`). Note that this class
|
6437 | * inherits from `ImplicitReceiver`, because accessing something through `this` is treated the
|
6438 | * same as accessing it implicitly inside of an Angular template (e.g. `[attr.title]="this.title"`
|
6439 | * is the same as `[attr.title]="title"`.). Inheriting allows for the `this` accesses to be treated
|
6440 | * the same as implicit ones, except for a couple of exceptions like `$event` and `$any`.
|
6441 | * TODO: we should find a way for this class not to extend from `ImplicitReceiver` in the future.
|
6442 | */
|
6443 | class ThisReceiver extends ImplicitReceiver {
|
6444 | visit(visitor, context = null) {
|
6445 | var _a;
|
6446 | return (_a = visitor.visitThisReceiver) === null || _a === void 0 ? void 0 : _a.call(visitor, this, context);
|
6447 | }
|
6448 | }
|
6449 | /**
|
6450 | * Multiple expressions separated by a semicolon.
|
6451 | */
|
6452 | class Chain extends AST {
|
6453 | constructor(span, sourceSpan, expressions) {
|
6454 | super(span, sourceSpan);
|
6455 | this.expressions = expressions;
|
6456 | }
|
6457 | visit(visitor, context = null) {
|
6458 | return visitor.visitChain(this, context);
|
6459 | }
|
6460 | }
|
6461 | class Conditional extends AST {
|
6462 | constructor(span, sourceSpan, condition, trueExp, falseExp) {
|
6463 | super(span, sourceSpan);
|
6464 | this.condition = condition;
|
6465 | this.trueExp = trueExp;
|
6466 | this.falseExp = falseExp;
|
6467 | }
|
6468 | visit(visitor, context = null) {
|
6469 | return visitor.visitConditional(this, context);
|
6470 | }
|
6471 | }
|
6472 | class PropertyRead extends ASTWithName {
|
6473 | constructor(span, sourceSpan, nameSpan, receiver, name) {
|
6474 | super(span, sourceSpan, nameSpan);
|
6475 | this.receiver = receiver;
|
6476 | this.name = name;
|
6477 | }
|
6478 | visit(visitor, context = null) {
|
6479 | return visitor.visitPropertyRead(this, context);
|
6480 | }
|
6481 | }
|
6482 | class PropertyWrite extends ASTWithName {
|
6483 | constructor(span, sourceSpan, nameSpan, receiver, name, value) {
|
6484 | super(span, sourceSpan, nameSpan);
|
6485 | this.receiver = receiver;
|
6486 | this.name = name;
|
6487 | this.value = value;
|
6488 | }
|
6489 | visit(visitor, context = null) {
|
6490 | return visitor.visitPropertyWrite(this, context);
|
6491 | }
|
6492 | }
|
6493 | class SafePropertyRead extends ASTWithName {
|
6494 | constructor(span, sourceSpan, nameSpan, receiver, name) {
|
6495 | super(span, sourceSpan, nameSpan);
|
6496 | this.receiver = receiver;
|
6497 | this.name = name;
|
6498 | }
|
6499 | visit(visitor, context = null) {
|
6500 | return visitor.visitSafePropertyRead(this, context);
|
6501 | }
|
6502 | }
|
6503 | class KeyedRead extends AST {
|
6504 | constructor(span, sourceSpan, obj, key) {
|
6505 | super(span, sourceSpan);
|
6506 | this.obj = obj;
|
6507 | this.key = key;
|
6508 | }
|
6509 | visit(visitor, context = null) {
|
6510 | return visitor.visitKeyedRead(this, context);
|
6511 | }
|
6512 | }
|
6513 | class KeyedWrite extends AST {
|
6514 | constructor(span, sourceSpan, obj, key, value) {
|
6515 | super(span, sourceSpan);
|
6516 | this.obj = obj;
|
6517 | this.key = key;
|
6518 | this.value = value;
|
6519 | }
|
6520 | visit(visitor, context = null) {
|
6521 | return visitor.visitKeyedWrite(this, context);
|
6522 | }
|
6523 | }
|
6524 | class BindingPipe extends ASTWithName {
|
6525 | constructor(span, sourceSpan, exp, name, args, nameSpan) {
|
6526 | super(span, sourceSpan, nameSpan);
|
6527 | this.exp = exp;
|
6528 | this.name = name;
|
6529 | this.args = args;
|
6530 | }
|
6531 | visit(visitor, context = null) {
|
6532 | return visitor.visitPipe(this, context);
|
6533 | }
|
6534 | }
|
6535 | class LiteralPrimitive extends AST {
|
6536 | constructor(span, sourceSpan, value) {
|
6537 | super(span, sourceSpan);
|
6538 | this.value = value;
|
6539 | }
|
6540 | visit(visitor, context = null) {
|
6541 | return visitor.visitLiteralPrimitive(this, context);
|
6542 | }
|
6543 | }
|
6544 | class LiteralArray extends AST {
|
6545 | constructor(span, sourceSpan, expressions) {
|
6546 | super(span, sourceSpan);
|
6547 | this.expressions = expressions;
|
6548 | }
|
6549 | visit(visitor, context = null) {
|
6550 | return visitor.visitLiteralArray(this, context);
|
6551 | }
|
6552 | }
|
6553 | class LiteralMap extends AST {
|
6554 | constructor(span, sourceSpan, keys, values) {
|
6555 | super(span, sourceSpan);
|
6556 | this.keys = keys;
|
6557 | this.values = values;
|
6558 | }
|
6559 | visit(visitor, context = null) {
|
6560 | return visitor.visitLiteralMap(this, context);
|
6561 | }
|
6562 | }
|
6563 | class Interpolation extends AST {
|
6564 | constructor(span, sourceSpan, strings, expressions) {
|
6565 | super(span, sourceSpan);
|
6566 | this.strings = strings;
|
6567 | this.expressions = expressions;
|
6568 | }
|
6569 | visit(visitor, context = null) {
|
6570 | return visitor.visitInterpolation(this, context);
|
6571 | }
|
6572 | }
|
6573 | class Binary extends AST {
|
6574 | constructor(span, sourceSpan, operation, left, right) {
|
6575 | super(span, sourceSpan);
|
6576 | this.operation = operation;
|
6577 | this.left = left;
|
6578 | this.right = right;
|
6579 | }
|
6580 | visit(visitor, context = null) {
|
6581 | return visitor.visitBinary(this, context);
|
6582 | }
|
6583 | }
|
6584 | /**
|
6585 | * For backwards compatibility reasons, `Unary` inherits from `Binary` and mimics the binary AST
|
6586 | * node that was originally used. This inheritance relation can be deleted in some future major,
|
6587 | * after consumers have been given a chance to fully support Unary.
|
6588 | */
|
6589 | class Unary extends Binary {
|
6590 | /**
|
6591 | * During the deprecation period this constructor is private, to avoid consumers from creating
|
6592 | * a `Unary` with the fallback properties for `Binary`.
|
6593 | */
|
6594 | constructor(span, sourceSpan, operator, expr, binaryOp, binaryLeft, binaryRight) {
|
6595 | super(span, sourceSpan, binaryOp, binaryLeft, binaryRight);
|
6596 | this.operator = operator;
|
6597 | this.expr = expr;
|
6598 | }
|
6599 | /**
|
6600 | * Creates a unary minus expression "-x", represented as `Binary` using "0 - x".
|
6601 | */
|
6602 | static createMinus(span, sourceSpan, expr) {
|
6603 | return new Unary(span, sourceSpan, '-', expr, '-', new LiteralPrimitive(span, sourceSpan, 0), expr);
|
6604 | }
|
6605 | /**
|
6606 | * Creates a unary plus expression "+x", represented as `Binary` using "x - 0".
|
6607 | */
|
6608 | static createPlus(span, sourceSpan, expr) {
|
6609 | return new Unary(span, sourceSpan, '+', expr, '-', expr, new LiteralPrimitive(span, sourceSpan, 0));
|
6610 | }
|
6611 | visit(visitor, context = null) {
|
6612 | if (visitor.visitUnary !== undefined) {
|
6613 | return visitor.visitUnary(this, context);
|
6614 | }
|
6615 | return visitor.visitBinary(this, context);
|
6616 | }
|
6617 | }
|
6618 | class PrefixNot extends AST {
|
6619 | constructor(span, sourceSpan, expression) {
|
6620 | super(span, sourceSpan);
|
6621 | this.expression = expression;
|
6622 | }
|
6623 | visit(visitor, context = null) {
|
6624 | return visitor.visitPrefixNot(this, context);
|
6625 | }
|
6626 | }
|
6627 | class NonNullAssert extends AST {
|
6628 | constructor(span, sourceSpan, expression) {
|
6629 | super(span, sourceSpan);
|
6630 | this.expression = expression;
|
6631 | }
|
6632 | visit(visitor, context = null) {
|
6633 | return visitor.visitNonNullAssert(this, context);
|
6634 | }
|
6635 | }
|
6636 | class MethodCall extends ASTWithName {
|
6637 | constructor(span, sourceSpan, nameSpan, receiver, name, args) {
|
6638 | super(span, sourceSpan, nameSpan);
|
6639 | this.receiver = receiver;
|
6640 | this.name = name;
|
6641 | this.args = args;
|
6642 | }
|
6643 | visit(visitor, context = null) {
|
6644 | return visitor.visitMethodCall(this, context);
|
6645 | }
|
6646 | }
|
6647 | class SafeMethodCall extends ASTWithName {
|
6648 | constructor(span, sourceSpan, nameSpan, receiver, name, args) {
|
6649 | super(span, sourceSpan, nameSpan);
|
6650 | this.receiver = receiver;
|
6651 | this.name = name;
|
6652 | this.args = args;
|
6653 | }
|
6654 | visit(visitor, context = null) {
|
6655 | return visitor.visitSafeMethodCall(this, context);
|
6656 | }
|
6657 | }
|
6658 | class FunctionCall extends AST {
|
6659 | constructor(span, sourceSpan, target, args) {
|
6660 | super(span, sourceSpan);
|
6661 | this.target = target;
|
6662 | this.args = args;
|
6663 | }
|
6664 | visit(visitor, context = null) {
|
6665 | return visitor.visitFunctionCall(this, context);
|
6666 | }
|
6667 | }
|
6668 | /**
|
6669 | * Records the absolute position of a text span in a source file, where `start` and `end` are the
|
6670 | * starting and ending byte offsets, respectively, of the text span in a source file.
|
6671 | */
|
6672 | class AbsoluteSourceSpan {
|
6673 | constructor(start, end) {
|
6674 | this.start = start;
|
6675 | this.end = end;
|
6676 | }
|
6677 | }
|
6678 | class ASTWithSource extends AST {
|
6679 | constructor(ast, source, location, absoluteOffset, errors) {
|
6680 | super(new ParseSpan(0, source === null ? 0 : source.length), new AbsoluteSourceSpan(absoluteOffset, source === null ? absoluteOffset : absoluteOffset + source.length));
|
6681 | this.ast = ast;
|
6682 | this.source = source;
|
6683 | this.location = location;
|
6684 | this.errors = errors;
|
6685 | }
|
6686 | visit(visitor, context = null) {
|
6687 | if (visitor.visitASTWithSource) {
|
6688 | return visitor.visitASTWithSource(this, context);
|
6689 | }
|
6690 | return this.ast.visit(visitor, context);
|
6691 | }
|
6692 | toString() {
|
6693 | return `${this.source} in ${this.location}`;
|
6694 | }
|
6695 | }
|
6696 | class VariableBinding {
|
6697 | /**
|
6698 | * @param sourceSpan entire span of the binding.
|
6699 | * @param key name of the LHS along with its span.
|
6700 | * @param value optional value for the RHS along with its span.
|
6701 | */
|
6702 | constructor(sourceSpan, key, value) {
|
6703 | this.sourceSpan = sourceSpan;
|
6704 | this.key = key;
|
6705 | this.value = value;
|
6706 | }
|
6707 | }
|
6708 | class ExpressionBinding {
|
6709 | /**
|
6710 | * @param sourceSpan entire span of the binding.
|
6711 | * @param key binding name, like ngForOf, ngForTrackBy, ngIf, along with its
|
6712 | * span. Note that the length of the span may not be the same as
|
6713 | * `key.source.length`. For example,
|
6714 | * 1. key.source = ngFor, key.span is for "ngFor"
|
6715 | * 2. key.source = ngForOf, key.span is for "of"
|
6716 | * 3. key.source = ngForTrackBy, key.span is for "trackBy"
|
6717 | * @param value optional expression for the RHS.
|
6718 | */
|
6719 | constructor(sourceSpan, key, value) {
|
6720 | this.sourceSpan = sourceSpan;
|
6721 | this.key = key;
|
6722 | this.value = value;
|
6723 | }
|
6724 | }
|
6725 | class RecursiveAstVisitor {
|
6726 | visit(ast, context) {
|
6727 | // The default implementation just visits every node.
|
6728 | // Classes that extend RecursiveAstVisitor should override this function
|
6729 | // to selectively visit the specified node.
|
6730 | ast.visit(this, context);
|
6731 | }
|
6732 | visitUnary(ast, context) {
|
6733 | this.visit(ast.expr, context);
|
6734 | }
|
6735 | visitBinary(ast, context) {
|
6736 | this.visit(ast.left, context);
|
6737 | this.visit(ast.right, context);
|
6738 | }
|
6739 | visitChain(ast, context) {
|
6740 | this.visitAll(ast.expressions, context);
|
6741 | }
|
6742 | visitConditional(ast, context) {
|
6743 | this.visit(ast.condition, context);
|
6744 | this.visit(ast.trueExp, context);
|
6745 | this.visit(ast.falseExp, context);
|
6746 | }
|
6747 | visitPipe(ast, context) {
|
6748 | this.visit(ast.exp, context);
|
6749 | this.visitAll(ast.args, context);
|
6750 | }
|
6751 | visitFunctionCall(ast, context) {
|
6752 | if (ast.target) {
|
6753 | this.visit(ast.target, context);
|
6754 | }
|
6755 | this.visitAll(ast.args, context);
|
6756 | }
|
6757 | visitImplicitReceiver(ast, context) { }
|
6758 | visitThisReceiver(ast, context) { }
|
6759 | visitInterpolation(ast, context) {
|
6760 | this.visitAll(ast.expressions, context);
|
6761 | }
|
6762 | visitKeyedRead(ast, context) {
|
6763 | this.visit(ast.obj, context);
|
6764 | this.visit(ast.key, context);
|
6765 | }
|
6766 | visitKeyedWrite(ast, context) {
|
6767 | this.visit(ast.obj, context);
|
6768 | this.visit(ast.key, context);
|
6769 | this.visit(ast.value, context);
|
6770 | }
|
6771 | visitLiteralArray(ast, context) {
|
6772 | this.visitAll(ast.expressions, context);
|
6773 | }
|
6774 | visitLiteralMap(ast, context) {
|
6775 | this.visitAll(ast.values, context);
|
6776 | }
|
6777 | visitLiteralPrimitive(ast, context) { }
|
6778 | visitMethodCall(ast, context) {
|
6779 | this.visit(ast.receiver, context);
|
6780 | this.visitAll(ast.args, context);
|
6781 | }
|
6782 | visitPrefixNot(ast, context) {
|
6783 | this.visit(ast.expression, context);
|
6784 | }
|
6785 | visitNonNullAssert(ast, context) {
|
6786 | this.visit(ast.expression, context);
|
6787 | }
|
6788 | visitPropertyRead(ast, context) {
|
6789 | this.visit(ast.receiver, context);
|
6790 | }
|
6791 | visitPropertyWrite(ast, context) {
|
6792 | this.visit(ast.receiver, context);
|
6793 | this.visit(ast.value, context);
|
6794 | }
|
6795 | visitSafePropertyRead(ast, context) {
|
6796 | this.visit(ast.receiver, context);
|
6797 | }
|
6798 | visitSafeMethodCall(ast, context) {
|
6799 | this.visit(ast.receiver, context);
|
6800 | this.visitAll(ast.args, context);
|
6801 | }
|
6802 | visitQuote(ast, context) { }
|
6803 | // This is not part of the AstVisitor interface, just a helper method
|
6804 | visitAll(asts, context) {
|
6805 | for (const ast of asts) {
|
6806 | this.visit(ast, context);
|
6807 | }
|
6808 | }
|
6809 | }
|
6810 | class AstTransformer {
|
6811 | visitImplicitReceiver(ast, context) {
|
6812 | return ast;
|
6813 | }
|
6814 | visitThisReceiver(ast, context) {
|
6815 | return ast;
|
6816 | }
|
6817 | visitInterpolation(ast, context) {
|
6818 | return new Interpolation(ast.span, ast.sourceSpan, ast.strings, this.visitAll(ast.expressions));
|
6819 | }
|
6820 | visitLiteralPrimitive(ast, context) {
|
6821 | return new LiteralPrimitive(ast.span, ast.sourceSpan, ast.value);
|
6822 | }
|
6823 | visitPropertyRead(ast, context) {
|
6824 | return new PropertyRead(ast.span, ast.sourceSpan, ast.nameSpan, ast.receiver.visit(this), ast.name);
|
6825 | }
|
6826 | visitPropertyWrite(ast, context) {
|
6827 | return new PropertyWrite(ast.span, ast.sourceSpan, ast.nameSpan, ast.receiver.visit(this), ast.name, ast.value.visit(this));
|
6828 | }
|
6829 | visitSafePropertyRead(ast, context) {
|
6830 | return new SafePropertyRead(ast.span, ast.sourceSpan, ast.nameSpan, ast.receiver.visit(this), ast.name);
|
6831 | }
|
6832 | visitMethodCall(ast, context) {
|
6833 | return new MethodCall(ast.span, ast.sourceSpan, ast.nameSpan, ast.receiver.visit(this), ast.name, this.visitAll(ast.args));
|
6834 | }
|
6835 | visitSafeMethodCall(ast, context) {
|
6836 | return new SafeMethodCall(ast.span, ast.sourceSpan, ast.nameSpan, ast.receiver.visit(this), ast.name, this.visitAll(ast.args));
|
6837 | }
|
6838 | visitFunctionCall(ast, context) {
|
6839 | return new FunctionCall(ast.span, ast.sourceSpan, ast.target.visit(this), this.visitAll(ast.args));
|
6840 | }
|
6841 | visitLiteralArray(ast, context) {
|
6842 | return new LiteralArray(ast.span, ast.sourceSpan, this.visitAll(ast.expressions));
|
6843 | }
|
6844 | visitLiteralMap(ast, context) {
|
6845 | return new LiteralMap(ast.span, ast.sourceSpan, ast.keys, this.visitAll(ast.values));
|
6846 | }
|
6847 | visitUnary(ast, context) {
|
6848 | switch (ast.operator) {
|
6849 | case '+':
|
6850 | return Unary.createPlus(ast.span, ast.sourceSpan, ast.expr.visit(this));
|
6851 | case '-':
|
6852 | return Unary.createMinus(ast.span, ast.sourceSpan, ast.expr.visit(this));
|
6853 | default:
|
6854 | throw new Error(`Unknown unary operator ${ast.operator}`);
|
6855 | }
|
6856 | }
|
6857 | visitBinary(ast, context) {
|
6858 | return new Binary(ast.span, ast.sourceSpan, ast.operation, ast.left.visit(this), ast.right.visit(this));
|
6859 | }
|
6860 | visitPrefixNot(ast, context) {
|
6861 | return new PrefixNot(ast.span, ast.sourceSpan, ast.expression.visit(this));
|
6862 | }
|
6863 | visitNonNullAssert(ast, context) {
|
6864 | return new NonNullAssert(ast.span, ast.sourceSpan, ast.expression.visit(this));
|
6865 | }
|
6866 | visitConditional(ast, context) {
|
6867 | return new Conditional(ast.span, ast.sourceSpan, ast.condition.visit(this), ast.trueExp.visit(this), ast.falseExp.visit(this));
|
6868 | }
|
6869 | visitPipe(ast, context) {
|
6870 | return new BindingPipe(ast.span, ast.sourceSpan, ast.exp.visit(this), ast.name, this.visitAll(ast.args), ast.nameSpan);
|
6871 | }
|
6872 | visitKeyedRead(ast, context) {
|
6873 | return new KeyedRead(ast.span, ast.sourceSpan, ast.obj.visit(this), ast.key.visit(this));
|
6874 | }
|
6875 | visitKeyedWrite(ast, context) {
|
6876 | return new KeyedWrite(ast.span, ast.sourceSpan, ast.obj.visit(this), ast.key.visit(this), ast.value.visit(this));
|
6877 | }
|
6878 | visitAll(asts) {
|
6879 | const res = [];
|
6880 | for (let i = 0; i < asts.length; ++i) {
|
6881 | res[i] = asts[i].visit(this);
|
6882 | }
|
6883 | return res;
|
6884 | }
|
6885 | visitChain(ast, context) {
|
6886 | return new Chain(ast.span, ast.sourceSpan, this.visitAll(ast.expressions));
|
6887 | }
|
6888 | visitQuote(ast, context) {
|
6889 | return new Quote(ast.span, ast.sourceSpan, ast.prefix, ast.uninterpretedExpression, ast.location);
|
6890 | }
|
6891 | }
|
6892 | // A transformer that only creates new nodes if the transformer makes a change or
|
6893 | // a change is made a child node.
|
6894 | class AstMemoryEfficientTransformer {
|
6895 | visitImplicitReceiver(ast, context) {
|
6896 | return ast;
|
6897 | }
|
6898 | visitThisReceiver(ast, context) {
|
6899 | return ast;
|
6900 | }
|
6901 | visitInterpolation(ast, context) {
|
6902 | const expressions = this.visitAll(ast.expressions);
|
6903 | if (expressions !== ast.expressions)
|
6904 | return new Interpolation(ast.span, ast.sourceSpan, ast.strings, expressions);
|
6905 | return ast;
|
6906 | }
|
6907 | visitLiteralPrimitive(ast, context) {
|
6908 | return ast;
|
6909 | }
|
6910 | visitPropertyRead(ast, context) {
|
6911 | const receiver = ast.receiver.visit(this);
|
6912 | if (receiver !== ast.receiver) {
|
6913 | return new PropertyRead(ast.span, ast.sourceSpan, ast.nameSpan, receiver, ast.name);
|
6914 | }
|
6915 | return ast;
|
6916 | }
|
6917 | visitPropertyWrite(ast, context) {
|
6918 | const receiver = ast.receiver.visit(this);
|
6919 | const value = ast.value.visit(this);
|
6920 | if (receiver !== ast.receiver || value !== ast.value) {
|
6921 | return new PropertyWrite(ast.span, ast.sourceSpan, ast.nameSpan, receiver, ast.name, value);
|
6922 | }
|
6923 | return ast;
|
6924 | }
|
6925 | visitSafePropertyRead(ast, context) {
|
6926 | const receiver = ast.receiver.visit(this);
|
6927 | if (receiver !== ast.receiver) {
|
6928 | return new SafePropertyRead(ast.span, ast.sourceSpan, ast.nameSpan, receiver, ast.name);
|
6929 | }
|
6930 | return ast;
|
6931 | }
|
6932 | visitMethodCall(ast, context) {
|
6933 | const receiver = ast.receiver.visit(this);
|
6934 | const args = this.visitAll(ast.args);
|
6935 | if (receiver !== ast.receiver || args !== ast.args) {
|
6936 | return new MethodCall(ast.span, ast.sourceSpan, ast.nameSpan, receiver, ast.name, args);
|
6937 | }
|
6938 | return ast;
|
6939 | }
|
6940 | visitSafeMethodCall(ast, context) {
|
6941 | const receiver = ast.receiver.visit(this);
|
6942 | const args = this.visitAll(ast.args);
|
6943 | if (receiver !== ast.receiver || args !== ast.args) {
|
6944 | return new SafeMethodCall(ast.span, ast.sourceSpan, ast.nameSpan, receiver, ast.name, args);
|
6945 | }
|
6946 | return ast;
|
6947 | }
|
6948 | visitFunctionCall(ast, context) {
|
6949 | const target = ast.target && ast.target.visit(this);
|
6950 | const args = this.visitAll(ast.args);
|
6951 | if (target !== ast.target || args !== ast.args) {
|
6952 | return new FunctionCall(ast.span, ast.sourceSpan, target, args);
|
6953 | }
|
6954 | return ast;
|
6955 | }
|
6956 | visitLiteralArray(ast, context) {
|
6957 | const expressions = this.visitAll(ast.expressions);
|
6958 | if (expressions !== ast.expressions) {
|
6959 | return new LiteralArray(ast.span, ast.sourceSpan, expressions);
|
6960 | }
|
6961 | return ast;
|
6962 | }
|
6963 | visitLiteralMap(ast, context) {
|
6964 | const values = this.visitAll(ast.values);
|
6965 | if (values !== ast.values) {
|
6966 | return new LiteralMap(ast.span, ast.sourceSpan, ast.keys, values);
|
6967 | }
|
6968 | return ast;
|
6969 | }
|
6970 | visitUnary(ast, context) {
|
6971 | const expr = ast.expr.visit(this);
|
6972 | if (expr !== ast.expr) {
|
6973 | switch (ast.operator) {
|
6974 | case '+':
|
6975 | return Unary.createPlus(ast.span, ast.sourceSpan, expr);
|
6976 | case '-':
|
6977 | return Unary.createMinus(ast.span, ast.sourceSpan, expr);
|
6978 | default:
|
6979 | throw new Error(`Unknown unary operator ${ast.operator}`);
|
6980 | }
|
6981 | }
|
6982 | return ast;
|
6983 | }
|
6984 | visitBinary(ast, context) {
|
6985 | const left = ast.left.visit(this);
|
6986 | const right = ast.right.visit(this);
|
6987 | if (left !== ast.left || right !== ast.right) {
|
6988 | return new Binary(ast.span, ast.sourceSpan, ast.operation, left, right);
|
6989 | }
|
6990 | return ast;
|
6991 | }
|
6992 | visitPrefixNot(ast, context) {
|
6993 | const expression = ast.expression.visit(this);
|
6994 | if (expression !== ast.expression) {
|
6995 | return new PrefixNot(ast.span, ast.sourceSpan, expression);
|
6996 | }
|
6997 | return ast;
|
6998 | }
|
6999 | visitNonNullAssert(ast, context) {
|
7000 | const expression = ast.expression.visit(this);
|
7001 | if (expression !== ast.expression) {
|
7002 | return new NonNullAssert(ast.span, ast.sourceSpan, expression);
|
7003 | }
|
7004 | return ast;
|
7005 | }
|
7006 | visitConditional(ast, context) {
|
7007 | const condition = ast.condition.visit(this);
|
7008 | const trueExp = ast.trueExp.visit(this);
|
7009 | const falseExp = ast.falseExp.visit(this);
|
7010 | if (condition !== ast.condition || trueExp !== ast.trueExp || falseExp !== ast.falseExp) {
|
7011 | return new Conditional(ast.span, ast.sourceSpan, condition, trueExp, falseExp);
|
7012 | }
|
7013 | return ast;
|
7014 | }
|
7015 | visitPipe(ast, context) {
|
7016 | const exp = ast.exp.visit(this);
|
7017 | const args = this.visitAll(ast.args);
|
7018 | if (exp !== ast.exp || args !== ast.args) {
|
7019 | return new BindingPipe(ast.span, ast.sourceSpan, exp, ast.name, args, ast.nameSpan);
|
7020 | }
|
7021 | return ast;
|
7022 | }
|
7023 | visitKeyedRead(ast, context) {
|
7024 | const obj = ast.obj.visit(this);
|
7025 | const key = ast.key.visit(this);
|
7026 | if (obj !== ast.obj || key !== ast.key) {
|
7027 | return new KeyedRead(ast.span, ast.sourceSpan, obj, key);
|
7028 | }
|
7029 | return ast;
|
7030 | }
|
7031 | visitKeyedWrite(ast, context) {
|
7032 | const obj = ast.obj.visit(this);
|
7033 | const key = ast.key.visit(this);
|
7034 | const value = ast.value.visit(this);
|
7035 | if (obj !== ast.obj || key !== ast.key || value !== ast.value) {
|
7036 | return new KeyedWrite(ast.span, ast.sourceSpan, obj, key, value);
|
7037 | }
|
7038 | return ast;
|
7039 | }
|
7040 | visitAll(asts) {
|
7041 | const res = [];
|
7042 | let modified = false;
|
7043 | for (let i = 0; i < asts.length; ++i) {
|
7044 | const original = asts[i];
|
7045 | const value = original.visit(this);
|
7046 | res[i] = value;
|
7047 | modified = modified || value !== original;
|
7048 | }
|
7049 | return modified ? res : asts;
|
7050 | }
|
7051 | visitChain(ast, context) {
|
7052 | const expressions = this.visitAll(ast.expressions);
|
7053 | if (expressions !== ast.expressions) {
|
7054 | return new Chain(ast.span, ast.sourceSpan, expressions);
|
7055 | }
|
7056 | return ast;
|
7057 | }
|
7058 | visitQuote(ast, context) {
|
7059 | return ast;
|
7060 | }
|
7061 | }
|
7062 | // Bindings
|
7063 | class ParsedProperty {
|
7064 | constructor(name, expression, type,
|
7065 | // TODO(FW-2095): `keySpan` should really be required but allows `undefined` so VE does
|
7066 | // not need to be updated. Make `keySpan` required when VE is removed.
|
7067 | sourceSpan, keySpan, valueSpan) {
|
7068 | this.name = name;
|
7069 | this.expression = expression;
|
7070 | this.type = type;
|
7071 | this.sourceSpan = sourceSpan;
|
7072 | this.keySpan = keySpan;
|
7073 | this.valueSpan = valueSpan;
|
7074 | this.isLiteral = this.type === ParsedPropertyType.LITERAL_ATTR;
|
7075 | this.isAnimation = this.type === ParsedPropertyType.ANIMATION;
|
7076 | }
|
7077 | }
|
7078 | var ParsedPropertyType;
|
7079 | (function (ParsedPropertyType) {
|
7080 | ParsedPropertyType[ParsedPropertyType["DEFAULT"] = 0] = "DEFAULT";
|
7081 | ParsedPropertyType[ParsedPropertyType["LITERAL_ATTR"] = 1] = "LITERAL_ATTR";
|
7082 | ParsedPropertyType[ParsedPropertyType["ANIMATION"] = 2] = "ANIMATION";
|
7083 | })(ParsedPropertyType || (ParsedPropertyType = {}));
|
7084 | class ParsedEvent {
|
7085 | // Regular events have a target
|
7086 | // Animation events have a phase
|
7087 | constructor(name, targetOrPhase, type, handler, sourceSpan,
|
7088 | // TODO(FW-2095): keySpan should be required but was made optional to avoid changing VE
|
7089 | handlerSpan, keySpan) {
|
7090 | this.name = name;
|
7091 | this.targetOrPhase = targetOrPhase;
|
7092 | this.type = type;
|
7093 | this.handler = handler;
|
7094 | this.sourceSpan = sourceSpan;
|
7095 | this.handlerSpan = handlerSpan;
|
7096 | this.keySpan = keySpan;
|
7097 | }
|
7098 | }
|
7099 | /**
|
7100 | * ParsedVariable represents a variable declaration in a microsyntax expression.
|
7101 | */
|
7102 | class ParsedVariable {
|
7103 | constructor(name, value, sourceSpan, keySpan, valueSpan) {
|
7104 | this.name = name;
|
7105 | this.value = value;
|
7106 | this.sourceSpan = sourceSpan;
|
7107 | this.keySpan = keySpan;
|
7108 | this.valueSpan = valueSpan;
|
7109 | }
|
7110 | }
|
7111 | class BoundElementProperty {
|
7112 | constructor(name, type, securityContext, value, unit, sourceSpan, keySpan, valueSpan) {
|
7113 | this.name = name;
|
7114 | this.type = type;
|
7115 | this.securityContext = securityContext;
|
7116 | this.value = value;
|
7117 | this.unit = unit;
|
7118 | this.sourceSpan = sourceSpan;
|
7119 | this.keySpan = keySpan;
|
7120 | this.valueSpan = valueSpan;
|
7121 | }
|
7122 | }
|
7123 |
|
7124 | /**
|
7125 | * @license
|
7126 | * Copyright Google LLC All Rights Reserved.
|
7127 | *
|
7128 | * Use of this source code is governed by an MIT-style license that can be
|
7129 | * found in the LICENSE file at https://angular.io/license
|
7130 | */
|
7131 | class EventHandlerVars {
|
7132 | }
|
7133 | EventHandlerVars.event = variable('$event');
|
7134 | class ConvertActionBindingResult {
|
7135 | constructor(
|
7136 | /**
|
7137 | * Render2 compatible statements,
|
7138 | */
|
7139 | stmts,
|
7140 | /**
|
7141 | * Variable name used with render2 compatible statements.
|
7142 | */
|
7143 | allowDefault) {
|
7144 | this.stmts = stmts;
|
7145 | this.allowDefault = allowDefault;
|
7146 | /**
|
7147 | * This is bit of a hack. It converts statements which render2 expects to statements which are
|
7148 | * expected by render3.
|
7149 | *
|
7150 | * Example: `<div click="doSomething($event)">` will generate:
|
7151 | *
|
7152 | * Render3:
|
7153 | * ```
|
7154 | * const pd_b:any = ((<any>ctx.doSomething($event)) !== false);
|
7155 | * return pd_b;
|
7156 | * ```
|
7157 | *
|
7158 | * but render2 expects:
|
7159 | * ```
|
7160 | * return ctx.doSomething($event);
|
7161 | * ```
|
7162 | */
|
7163 | // TODO(misko): remove this hack once we no longer support ViewEngine.
|
7164 | this.render3Stmts = stmts.map((statement) => {
|
7165 | if (statement instanceof DeclareVarStmt && statement.name == allowDefault.name &&
|
7166 | statement.value instanceof BinaryOperatorExpr) {
|
7167 | const lhs = statement.value.lhs;
|
7168 | return new ReturnStatement(lhs.value);
|
7169 | }
|
7170 | return statement;
|
7171 | });
|
7172 | }
|
7173 | }
|
7174 | /**
|
7175 | * Converts the given expression AST into an executable output AST, assuming the expression is
|
7176 | * used in an action binding (e.g. an event handler).
|
7177 | */
|
7178 | function convertActionBinding(localResolver, implicitReceiver, action, bindingId, interpolationFunction, baseSourceSpan, implicitReceiverAccesses, globals) {
|
7179 | if (!localResolver) {
|
7180 | localResolver = new DefaultLocalResolver(globals);
|
7181 | }
|
7182 | const actionWithoutBuiltins = convertPropertyBindingBuiltins({
|
7183 | createLiteralArrayConverter: (argCount) => {
|
7184 | // Note: no caching for literal arrays in actions.
|
7185 | return (args) => literalArr(args);
|
7186 | },
|
7187 | createLiteralMapConverter: (keys) => {
|
7188 | // Note: no caching for literal maps in actions.
|
7189 | return (values) => {
|
7190 | const entries = keys.map((k, i) => ({
|
7191 | key: k.key,
|
7192 | value: values[i],
|
7193 | quoted: k.quoted,
|
7194 | }));
|
7195 | return literalMap(entries);
|
7196 | };
|
7197 | },
|
7198 | createPipeConverter: (name) => {
|
7199 | throw new Error(`Illegal State: Actions are not allowed to contain pipes. Pipe: ${name}`);
|
7200 | }
|
7201 | }, action);
|
7202 | const visitor = new _AstToIrVisitor(localResolver, implicitReceiver, bindingId, interpolationFunction, baseSourceSpan, implicitReceiverAccesses);
|
7203 | const actionStmts = [];
|
7204 | flattenStatements(actionWithoutBuiltins.visit(visitor, _Mode.Statement), actionStmts);
|
7205 | prependTemporaryDecls(visitor.temporaryCount, bindingId, actionStmts);
|
7206 | if (visitor.usesImplicitReceiver) {
|
7207 | localResolver.notifyImplicitReceiverUse();
|
7208 | }
|
7209 | const lastIndex = actionStmts.length - 1;
|
7210 | let preventDefaultVar = null;
|
7211 | if (lastIndex >= 0) {
|
7212 | const lastStatement = actionStmts[lastIndex];
|
7213 | const returnExpr = convertStmtIntoExpression(lastStatement);
|
7214 | if (returnExpr) {
|
7215 | // Note: We need to cast the result of the method call to dynamic,
|
7216 | // as it might be a void method!
|
7217 | preventDefaultVar = createPreventDefaultVar(bindingId);
|
7218 | actionStmts[lastIndex] =
|
7219 | preventDefaultVar.set(returnExpr.cast(DYNAMIC_TYPE).notIdentical(literal(false)))
|
7220 | .toDeclStmt(null, [StmtModifier.Final]);
|
7221 | }
|
7222 | }
|
7223 | return new ConvertActionBindingResult(actionStmts, preventDefaultVar);
|
7224 | }
|
7225 | function convertPropertyBindingBuiltins(converterFactory, ast) {
|
7226 | return convertBuiltins(converterFactory, ast);
|
7227 | }
|
7228 | class ConvertPropertyBindingResult {
|
7229 | constructor(stmts, currValExpr) {
|
7230 | this.stmts = stmts;
|
7231 | this.currValExpr = currValExpr;
|
7232 | }
|
7233 | }
|
7234 | var BindingForm;
|
7235 | (function (BindingForm) {
|
7236 | // The general form of binding expression, supports all expressions.
|
7237 | BindingForm[BindingForm["General"] = 0] = "General";
|
7238 | // Try to generate a simple binding (no temporaries or statements)
|
7239 | // otherwise generate a general binding
|
7240 | BindingForm[BindingForm["TrySimple"] = 1] = "TrySimple";
|
7241 | // Inlines assignment of temporaries into the generated expression. The result may still
|
7242 | // have statements attached for declarations of temporary variables.
|
7243 | // This is the only relevant form for Ivy, the other forms are only used in ViewEngine.
|
7244 | BindingForm[BindingForm["Expression"] = 2] = "Expression";
|
7245 | })(BindingForm || (BindingForm = {}));
|
7246 | /**
|
7247 | * Converts the given expression AST into an executable output AST, assuming the expression
|
7248 | * is used in property binding. The expression has to be preprocessed via
|
7249 | * `convertPropertyBindingBuiltins`.
|
7250 | */
|
7251 | function convertPropertyBinding(localResolver, implicitReceiver, expressionWithoutBuiltins, bindingId, form, interpolationFunction) {
|
7252 | if (!localResolver) {
|
7253 | localResolver = new DefaultLocalResolver();
|
7254 | }
|
7255 | const visitor = new _AstToIrVisitor(localResolver, implicitReceiver, bindingId, interpolationFunction);
|
7256 | const outputExpr = expressionWithoutBuiltins.visit(visitor, _Mode.Expression);
|
7257 | const stmts = getStatementsFromVisitor(visitor, bindingId);
|
7258 | if (visitor.usesImplicitReceiver) {
|
7259 | localResolver.notifyImplicitReceiverUse();
|
7260 | }
|
7261 | if (visitor.temporaryCount === 0 && form == BindingForm.TrySimple) {
|
7262 | return new ConvertPropertyBindingResult([], outputExpr);
|
7263 | }
|
7264 | else if (form === BindingForm.Expression) {
|
7265 | return new ConvertPropertyBindingResult(stmts, outputExpr);
|
7266 | }
|
7267 | const currValExpr = createCurrValueExpr(bindingId);
|
7268 | stmts.push(currValExpr.set(outputExpr).toDeclStmt(DYNAMIC_TYPE, [StmtModifier.Final]));
|
7269 | return new ConvertPropertyBindingResult(stmts, currValExpr);
|
7270 | }
|
7271 | /**
|
7272 | * Given some expression, such as a binding or interpolation expression, and a context expression to
|
7273 | * look values up on, visit each facet of the given expression resolving values from the context
|
7274 | * expression such that a list of arguments can be derived from the found values that can be used as
|
7275 | * arguments to an external update instruction.
|
7276 | *
|
7277 | * @param localResolver The resolver to use to look up expressions by name appropriately
|
7278 | * @param contextVariableExpression The expression representing the context variable used to create
|
7279 | * the final argument expressions
|
7280 | * @param expressionWithArgumentsToExtract The expression to visit to figure out what values need to
|
7281 | * be resolved and what arguments list to build.
|
7282 | * @param bindingId A name prefix used to create temporary variable names if they're needed for the
|
7283 | * arguments generated
|
7284 | * @returns An array of expressions that can be passed as arguments to instruction expressions like
|
7285 | * `o.importExpr(R3.propertyInterpolate).callFn(result)`
|
7286 | */
|
7287 | function convertUpdateArguments(localResolver, contextVariableExpression, expressionWithArgumentsToExtract, bindingId) {
|
7288 | const visitor = new _AstToIrVisitor(localResolver, contextVariableExpression, bindingId, undefined);
|
7289 | const outputExpr = expressionWithArgumentsToExtract.visit(visitor, _Mode.Expression);
|
7290 | if (visitor.usesImplicitReceiver) {
|
7291 | localResolver.notifyImplicitReceiverUse();
|
7292 | }
|
7293 | const stmts = getStatementsFromVisitor(visitor, bindingId);
|
7294 | // Removing the first argument, because it was a length for ViewEngine, not Ivy.
|
7295 | let args = outputExpr.args.slice(1);
|
7296 | if (expressionWithArgumentsToExtract instanceof Interpolation) {
|
7297 | // If we're dealing with an interpolation of 1 value with an empty prefix and suffix, reduce the
|
7298 | // args returned to just the value, because we're going to pass it to a special instruction.
|
7299 | const strings = expressionWithArgumentsToExtract.strings;
|
7300 | if (args.length === 3 && strings[0] === '' && strings[1] === '') {
|
7301 | // Single argument interpolate instructions.
|
7302 | args = [args[1]];
|
7303 | }
|
7304 | else if (args.length >= 19) {
|
7305 | // 19 or more arguments must be passed to the `interpolateV`-style instructions, which accept
|
7306 | // an array of arguments
|
7307 | args = [literalArr(args)];
|
7308 | }
|
7309 | }
|
7310 | return { stmts, args };
|
7311 | }
|
7312 | function getStatementsFromVisitor(visitor, bindingId) {
|
7313 | const stmts = [];
|
7314 | for (let i = 0; i < visitor.temporaryCount; i++) {
|
7315 | stmts.push(temporaryDeclaration(bindingId, i));
|
7316 | }
|
7317 | return stmts;
|
7318 | }
|
7319 | function convertBuiltins(converterFactory, ast) {
|
7320 | const visitor = new _BuiltinAstConverter(converterFactory);
|
7321 | return ast.visit(visitor);
|
7322 | }
|
7323 | function temporaryName(bindingId, temporaryNumber) {
|
7324 | return `tmp_${bindingId}_${temporaryNumber}`;
|
7325 | }
|
7326 | function temporaryDeclaration(bindingId, temporaryNumber) {
|
7327 | return new DeclareVarStmt(temporaryName(bindingId, temporaryNumber), NULL_EXPR);
|
7328 | }
|
7329 | function prependTemporaryDecls(temporaryCount, bindingId, statements) {
|
7330 | for (let i = temporaryCount - 1; i >= 0; i--) {
|
7331 | statements.unshift(temporaryDeclaration(bindingId, i));
|
7332 | }
|
7333 | }
|
7334 | var _Mode;
|
7335 | (function (_Mode) {
|
7336 | _Mode[_Mode["Statement"] = 0] = "Statement";
|
7337 | _Mode[_Mode["Expression"] = 1] = "Expression";
|
7338 | })(_Mode || (_Mode = {}));
|
7339 | function ensureStatementMode(mode, ast) {
|
7340 | if (mode !== _Mode.Statement) {
|
7341 | throw new Error(`Expected a statement, but saw ${ast}`);
|
7342 | }
|
7343 | }
|
7344 | function ensureExpressionMode(mode, ast) {
|
7345 | if (mode !== _Mode.Expression) {
|
7346 | throw new Error(`Expected an expression, but saw ${ast}`);
|
7347 | }
|
7348 | }
|
7349 | function convertToStatementIfNeeded(mode, expr) {
|
7350 | if (mode === _Mode.Statement) {
|
7351 | return expr.toStmt();
|
7352 | }
|
7353 | else {
|
7354 | return expr;
|
7355 | }
|
7356 | }
|
7357 | class _BuiltinAstConverter extends AstTransformer {
|
7358 | constructor(_converterFactory) {
|
7359 | super();
|
7360 | this._converterFactory = _converterFactory;
|
7361 | }
|
7362 | visitPipe(ast, context) {
|
7363 | const args = [ast.exp, ...ast.args].map(ast => ast.visit(this, context));
|
7364 | return new BuiltinFunctionCall(ast.span, ast.sourceSpan, args, this._converterFactory.createPipeConverter(ast.name, args.length));
|
7365 | }
|
7366 | visitLiteralArray(ast, context) {
|
7367 | const args = ast.expressions.map(ast => ast.visit(this, context));
|
7368 | return new BuiltinFunctionCall(ast.span, ast.sourceSpan, args, this._converterFactory.createLiteralArrayConverter(ast.expressions.length));
|
7369 | }
|
7370 | visitLiteralMap(ast, context) {
|
7371 | const args = ast.values.map(ast => ast.visit(this, context));
|
7372 | return new BuiltinFunctionCall(ast.span, ast.sourceSpan, args, this._converterFactory.createLiteralMapConverter(ast.keys));
|
7373 | }
|
7374 | }
|
7375 | class _AstToIrVisitor {
|
7376 | constructor(_localResolver, _implicitReceiver, bindingId, interpolationFunction, baseSourceSpan, implicitReceiverAccesses) {
|
7377 | this._localResolver = _localResolver;
|
7378 | this._implicitReceiver = _implicitReceiver;
|
7379 | this.bindingId = bindingId;
|
7380 | this.interpolationFunction = interpolationFunction;
|
7381 | this.baseSourceSpan = baseSourceSpan;
|
7382 | this.implicitReceiverAccesses = implicitReceiverAccesses;
|
7383 | this._nodeMap = new Map();
|
7384 | this._resultMap = new Map();
|
7385 | this._currentTemporary = 0;
|
7386 | this.temporaryCount = 0;
|
7387 | this.usesImplicitReceiver = false;
|
7388 | }
|
7389 | visitUnary(ast, mode) {
|
7390 | let op;
|
7391 | switch (ast.operator) {
|
7392 | case '+':
|
7393 | op = UnaryOperator.Plus;
|
7394 | break;
|
7395 | case '-':
|
7396 | op = UnaryOperator.Minus;
|
7397 | break;
|
7398 | default:
|
7399 | throw new Error(`Unsupported operator ${ast.operator}`);
|
7400 | }
|
7401 | return convertToStatementIfNeeded(mode, new UnaryOperatorExpr(op, this._visit(ast.expr, _Mode.Expression), undefined, this.convertSourceSpan(ast.span)));
|
7402 | }
|
7403 | visitBinary(ast, mode) {
|
7404 | let op;
|
7405 | switch (ast.operation) {
|
7406 | case '+':
|
7407 | op = BinaryOperator.Plus;
|
7408 | break;
|
7409 | case '-':
|
7410 | op = BinaryOperator.Minus;
|
7411 | break;
|
7412 | case '*':
|
7413 | op = BinaryOperator.Multiply;
|
7414 | break;
|
7415 | case '/':
|
7416 | op = BinaryOperator.Divide;
|
7417 | break;
|
7418 | case '%':
|
7419 | op = BinaryOperator.Modulo;
|
7420 | break;
|
7421 | case '&&':
|
7422 | op = BinaryOperator.And;
|
7423 | break;
|
7424 | case '||':
|
7425 | op = BinaryOperator.Or;
|
7426 | break;
|
7427 | case '==':
|
7428 | op = BinaryOperator.Equals;
|
7429 | break;
|
7430 | case '!=':
|
7431 | op = BinaryOperator.NotEquals;
|
7432 | break;
|
7433 | case '===':
|
7434 | op = BinaryOperator.Identical;
|
7435 | break;
|
7436 | case '!==':
|
7437 | op = BinaryOperator.NotIdentical;
|
7438 | break;
|
7439 | case '<':
|
7440 | op = BinaryOperator.Lower;
|
7441 | break;
|
7442 | case '>':
|
7443 | op = BinaryOperator.Bigger;
|
7444 | break;
|
7445 | case '<=':
|
7446 | op = BinaryOperator.LowerEquals;
|
7447 | break;
|
7448 | case '>=':
|
7449 | op = BinaryOperator.BiggerEquals;
|
7450 | break;
|
7451 | default:
|
7452 | throw new Error(`Unsupported operation ${ast.operation}`);
|
7453 | }
|
7454 | return convertToStatementIfNeeded(mode, new BinaryOperatorExpr(op, this._visit(ast.left, _Mode.Expression), this._visit(ast.right, _Mode.Expression), undefined, this.convertSourceSpan(ast.span)));
|
7455 | }
|
7456 | visitChain(ast, mode) {
|
7457 | ensureStatementMode(mode, ast);
|
7458 | return this.visitAll(ast.expressions, mode);
|
7459 | }
|
7460 | visitConditional(ast, mode) {
|
7461 | const value = this._visit(ast.condition, _Mode.Expression);
|
7462 | return convertToStatementIfNeeded(mode, value.conditional(this._visit(ast.trueExp, _Mode.Expression), this._visit(ast.falseExp, _Mode.Expression), this.convertSourceSpan(ast.span)));
|
7463 | }
|
7464 | visitPipe(ast, mode) {
|
7465 | throw new Error(`Illegal state: Pipes should have been converted into functions. Pipe: ${ast.name}`);
|
7466 | }
|
7467 | visitFunctionCall(ast, mode) {
|
7468 | const convertedArgs = this.visitAll(ast.args, _Mode.Expression);
|
7469 | let fnResult;
|
7470 | if (ast instanceof BuiltinFunctionCall) {
|
7471 | fnResult = ast.converter(convertedArgs);
|
7472 | }
|
7473 | else {
|
7474 | fnResult = this._visit(ast.target, _Mode.Expression)
|
7475 | .callFn(convertedArgs, this.convertSourceSpan(ast.span));
|
7476 | }
|
7477 | return convertToStatementIfNeeded(mode, fnResult);
|
7478 | }
|
7479 | visitImplicitReceiver(ast, mode) {
|
7480 | ensureExpressionMode(mode, ast);
|
7481 | this.usesImplicitReceiver = true;
|
7482 | return this._implicitReceiver;
|
7483 | }
|
7484 | visitThisReceiver(ast, mode) {
|
7485 | return this.visitImplicitReceiver(ast, mode);
|
7486 | }
|
7487 | visitInterpolation(ast, mode) {
|
7488 | ensureExpressionMode(mode, ast);
|
7489 | const args = [literal(ast.expressions.length)];
|
7490 | for (let i = 0; i < ast.strings.length - 1; i++) {
|
7491 | args.push(literal(ast.strings[i]));
|
7492 | args.push(this._visit(ast.expressions[i], _Mode.Expression));
|
7493 | }
|
7494 | args.push(literal(ast.strings[ast.strings.length - 1]));
|
7495 | if (this.interpolationFunction) {
|
7496 | return this.interpolationFunction(args);
|
7497 | }
|
7498 | return ast.expressions.length <= 9 ?
|
7499 | importExpr(Identifiers.inlineInterpolate).callFn(args) :
|
7500 | importExpr(Identifiers.interpolate).callFn([
|
7501 | args[0], literalArr(args.slice(1), undefined, this.convertSourceSpan(ast.span))
|
7502 | ]);
|
7503 | }
|
7504 | visitKeyedRead(ast, mode) {
|
7505 | const leftMostSafe = this.leftMostSafeNode(ast);
|
7506 | if (leftMostSafe) {
|
7507 | return this.convertSafeAccess(ast, leftMostSafe, mode);
|
7508 | }
|
7509 | else {
|
7510 | return convertToStatementIfNeeded(mode, this._visit(ast.obj, _Mode.Expression).key(this._visit(ast.key, _Mode.Expression)));
|
7511 | }
|
7512 | }
|
7513 | visitKeyedWrite(ast, mode) {
|
7514 | const obj = this._visit(ast.obj, _Mode.Expression);
|
7515 | const key = this._visit(ast.key, _Mode.Expression);
|
7516 | const value = this._visit(ast.value, _Mode.Expression);
|
7517 | return convertToStatementIfNeeded(mode, obj.key(key).set(value));
|
7518 | }
|
7519 | visitLiteralArray(ast, mode) {
|
7520 | throw new Error(`Illegal State: literal arrays should have been converted into functions`);
|
7521 | }
|
7522 | visitLiteralMap(ast, mode) {
|
7523 | throw new Error(`Illegal State: literal maps should have been converted into functions`);
|
7524 | }
|
7525 | visitLiteralPrimitive(ast, mode) {
|
7526 | // For literal values of null, undefined, true, or false allow type interference
|
7527 | // to infer the type.
|
7528 | const type = ast.value === null || ast.value === undefined || ast.value === true || ast.value === true ?
|
7529 | INFERRED_TYPE :
|
7530 | undefined;
|
7531 | return convertToStatementIfNeeded(mode, literal(ast.value, type, this.convertSourceSpan(ast.span)));
|
7532 | }
|
7533 | _getLocal(name, receiver) {
|
7534 | var _a;
|
7535 | if (((_a = this._localResolver.globals) === null || _a === void 0 ? void 0 : _a.has(name)) && receiver instanceof ThisReceiver) {
|
7536 | return null;
|
7537 | }
|
7538 | return this._localResolver.getLocal(name);
|
7539 | }
|
7540 | visitMethodCall(ast, mode) {
|
7541 | if (ast.receiver instanceof ImplicitReceiver &&
|
7542 | !(ast.receiver instanceof ThisReceiver) && ast.name === '$any') {
|
7543 | const args = this.visitAll(ast.args, _Mode.Expression);
|
7544 | if (args.length != 1) {
|
7545 | throw new Error(`Invalid call to $any, expected 1 argument but received ${args.length || 'none'}`);
|
7546 | }
|
7547 | return args[0].cast(DYNAMIC_TYPE, this.convertSourceSpan(ast.span));
|
7548 | }
|
7549 | const leftMostSafe = this.leftMostSafeNode(ast);
|
7550 | if (leftMostSafe) {
|
7551 | return this.convertSafeAccess(ast, leftMostSafe, mode);
|
7552 | }
|
7553 | else {
|
7554 | const args = this.visitAll(ast.args, _Mode.Expression);
|
7555 | const prevUsesImplicitReceiver = this.usesImplicitReceiver;
|
7556 | let result = null;
|
7557 | const receiver = this._visit(ast.receiver, _Mode.Expression);
|
7558 | if (receiver === this._implicitReceiver) {
|
7559 | const varExpr = this._getLocal(ast.name, ast.receiver);
|
7560 | if (varExpr) {
|
7561 | // Restore the previous "usesImplicitReceiver" state since the implicit
|
7562 | // receiver has been replaced with a resolved local expression.
|
7563 | this.usesImplicitReceiver = prevUsesImplicitReceiver;
|
7564 | result = varExpr.callFn(args);
|
7565 | this.addImplicitReceiverAccess(ast.name);
|
7566 | }
|
7567 | }
|
7568 | if (result == null) {
|
7569 | result = receiver.callMethod(ast.name, args, this.convertSourceSpan(ast.span));
|
7570 | }
|
7571 | return convertToStatementIfNeeded(mode, result);
|
7572 | }
|
7573 | }
|
7574 | visitPrefixNot(ast, mode) {
|
7575 | return convertToStatementIfNeeded(mode, not(this._visit(ast.expression, _Mode.Expression)));
|
7576 | }
|
7577 | visitNonNullAssert(ast, mode) {
|
7578 | return convertToStatementIfNeeded(mode, assertNotNull(this._visit(ast.expression, _Mode.Expression)));
|
7579 | }
|
7580 | visitPropertyRead(ast, mode) {
|
7581 | const leftMostSafe = this.leftMostSafeNode(ast);
|
7582 | if (leftMostSafe) {
|
7583 | return this.convertSafeAccess(ast, leftMostSafe, mode);
|
7584 | }
|
7585 | else {
|
7586 | let result = null;
|
7587 | const prevUsesImplicitReceiver = this.usesImplicitReceiver;
|
7588 | const receiver = this._visit(ast.receiver, _Mode.Expression);
|
7589 | if (receiver === this._implicitReceiver) {
|
7590 | result = this._getLocal(ast.name, ast.receiver);
|
7591 | if (result) {
|
7592 | // Restore the previous "usesImplicitReceiver" state since the implicit
|
7593 | // receiver has been replaced with a resolved local expression.
|
7594 | this.usesImplicitReceiver = prevUsesImplicitReceiver;
|
7595 | this.addImplicitReceiverAccess(ast.name);
|
7596 | }
|
7597 | }
|
7598 | if (result == null) {
|
7599 | result = receiver.prop(ast.name);
|
7600 | }
|
7601 | return convertToStatementIfNeeded(mode, result);
|
7602 | }
|
7603 | }
|
7604 | visitPropertyWrite(ast, mode) {
|
7605 | const receiver = this._visit(ast.receiver, _Mode.Expression);
|
7606 | const prevUsesImplicitReceiver = this.usesImplicitReceiver;
|
7607 | let varExpr = null;
|
7608 | if (receiver === this._implicitReceiver) {
|
7609 | const localExpr = this._getLocal(ast.name, ast.receiver);
|
7610 | if (localExpr) {
|
7611 | if (localExpr instanceof ReadPropExpr) {
|
7612 | // If the local variable is a property read expression, it's a reference
|
7613 | // to a 'context.property' value and will be used as the target of the
|
7614 | // write expression.
|
7615 | varExpr = localExpr;
|
7616 | // Restore the previous "usesImplicitReceiver" state since the implicit
|
7617 | // receiver has been replaced with a resolved local expression.
|
7618 | this.usesImplicitReceiver = prevUsesImplicitReceiver;
|
7619 | this.addImplicitReceiverAccess(ast.name);
|
7620 | }
|
7621 | else {
|
7622 | // Otherwise it's an error.
|
7623 | const receiver = ast.name;
|
7624 | const value = (ast.value instanceof PropertyRead) ? ast.value.name : undefined;
|
7625 | throw new Error(`Cannot assign value "${value}" to template variable "${receiver}". Template variables are read-only.`);
|
7626 | }
|
7627 | }
|
7628 | }
|
7629 | // If no local expression could be produced, use the original receiver's
|
7630 | // property as the target.
|
7631 | if (varExpr === null) {
|
7632 | varExpr = receiver.prop(ast.name);
|
7633 | }
|
7634 | return convertToStatementIfNeeded(mode, varExpr.set(this._visit(ast.value, _Mode.Expression)));
|
7635 | }
|
7636 | visitSafePropertyRead(ast, mode) {
|
7637 | return this.convertSafeAccess(ast, this.leftMostSafeNode(ast), mode);
|
7638 | }
|
7639 | visitSafeMethodCall(ast, mode) {
|
7640 | return this.convertSafeAccess(ast, this.leftMostSafeNode(ast), mode);
|
7641 | }
|
7642 | visitAll(asts, mode) {
|
7643 | return asts.map(ast => this._visit(ast, mode));
|
7644 | }
|
7645 | visitQuote(ast, mode) {
|
7646 | throw new Error(`Quotes are not supported for evaluation!
|
7647 | Statement: ${ast.uninterpretedExpression} located at ${ast.location}`);
|
7648 | }
|
7649 | _visit(ast, mode) {
|
7650 | const result = this._resultMap.get(ast);
|
7651 | if (result)
|
7652 | return result;
|
7653 | return (this._nodeMap.get(ast) || ast).visit(this, mode);
|
7654 | }
|
7655 | convertSafeAccess(ast, leftMostSafe, mode) {
|
7656 | // If the expression contains a safe access node on the left it needs to be converted to
|
7657 | // an expression that guards the access to the member by checking the receiver for blank. As
|
7658 | // execution proceeds from left to right, the left most part of the expression must be guarded
|
7659 | // first but, because member access is left associative, the right side of the expression is at
|
7660 | // the top of the AST. The desired result requires lifting a copy of the left part of the
|
7661 | // expression up to test it for blank before generating the unguarded version.
|
7662 | // Consider, for example the following expression: a?.b.c?.d.e
|
7663 | // This results in the ast:
|
7664 | // .
|
7665 | // / \
|
7666 | // ?. e
|
7667 | // / \
|
7668 | // . d
|
7669 | // / \
|
7670 | // ?. c
|
7671 | // / \
|
7672 | // a b
|
7673 | // The following tree should be generated:
|
7674 | //
|
7675 | // /---- ? ----\
|
7676 | // / | \
|
7677 | // a /--- ? ---\ null
|
7678 | // / | \
|
7679 | // . . null
|
7680 | // / \ / \
|
7681 | // . c . e
|
7682 | // / \ / \
|
7683 | // a b . d
|
7684 | // / \
|
7685 | // . c
|
7686 | // / \
|
7687 | // a b
|
7688 | //
|
7689 | // Notice that the first guard condition is the left hand of the left most safe access node
|
7690 | // which comes in as leftMostSafe to this routine.
|
7691 | let guardedExpression = this._visit(leftMostSafe.receiver, _Mode.Expression);
|
7692 | let temporary = undefined;
|
7693 | if (this.needsTemporary(leftMostSafe.receiver)) {
|
7694 | // If the expression has method calls or pipes then we need to save the result into a
|
7695 | // temporary variable to avoid calling stateful or impure code more than once.
|
7696 | temporary = this.allocateTemporary();
|
7697 | // Preserve the result in the temporary variable
|
7698 | guardedExpression = temporary.set(guardedExpression);
|
7699 | // Ensure all further references to the guarded expression refer to the temporary instead.
|
7700 | this._resultMap.set(leftMostSafe.receiver, temporary);
|
7701 | }
|
7702 | const condition = guardedExpression.isBlank();
|
7703 | // Convert the ast to an unguarded access to the receiver's member. The map will substitute
|
7704 | // leftMostNode with its unguarded version in the call to `this.visit()`.
|
7705 | if (leftMostSafe instanceof SafeMethodCall) {
|
7706 | this._nodeMap.set(leftMostSafe, new MethodCall(leftMostSafe.span, leftMostSafe.sourceSpan, leftMostSafe.nameSpan, leftMostSafe.receiver, leftMostSafe.name, leftMostSafe.args));
|
7707 | }
|
7708 | else {
|
7709 | this._nodeMap.set(leftMostSafe, new PropertyRead(leftMostSafe.span, leftMostSafe.sourceSpan, leftMostSafe.nameSpan, leftMostSafe.receiver, leftMostSafe.name));
|
7710 | }
|
7711 | // Recursively convert the node now without the guarded member access.
|
7712 | const access = this._visit(ast, _Mode.Expression);
|
7713 | // Remove the mapping. This is not strictly required as the converter only traverses each node
|
7714 | // once but is safer if the conversion is changed to traverse the nodes more than once.
|
7715 | this._nodeMap.delete(leftMostSafe);
|
7716 | // If we allocated a temporary, release it.
|
7717 | if (temporary) {
|
7718 | this.releaseTemporary(temporary);
|
7719 | }
|
7720 | // Produce the conditional
|
7721 | return convertToStatementIfNeeded(mode, condition.conditional(literal(null), access));
|
7722 | }
|
7723 | // Given an expression of the form a?.b.c?.d.e then the left most safe node is
|
7724 | // the (a?.b). The . and ?. are left associative thus can be rewritten as:
|
7725 | // ((((a?.c).b).c)?.d).e. This returns the most deeply nested safe read or
|
7726 | // safe method call as this needs to be transformed initially to:
|
7727 | // a == null ? null : a.c.b.c?.d.e
|
7728 | // then to:
|
7729 | // a == null ? null : a.b.c == null ? null : a.b.c.d.e
|
7730 | leftMostSafeNode(ast) {
|
7731 | const visit = (visitor, ast) => {
|
7732 | return (this._nodeMap.get(ast) || ast).visit(visitor);
|
7733 | };
|
7734 | return ast.visit({
|
7735 | visitUnary(ast) {
|
7736 | return null;
|
7737 | },
|
7738 | visitBinary(ast) {
|
7739 | return null;
|
7740 | },
|
7741 | visitChain(ast) {
|
7742 | return null;
|
7743 | },
|
7744 | visitConditional(ast) {
|
7745 | return null;
|
7746 | },
|
7747 | visitFunctionCall(ast) {
|
7748 | return null;
|
7749 | },
|
7750 | visitImplicitReceiver(ast) {
|
7751 | return null;
|
7752 | },
|
7753 | visitThisReceiver(ast) {
|
7754 | return null;
|
7755 | },
|
7756 | visitInterpolation(ast) {
|
7757 | return null;
|
7758 | },
|
7759 | visitKeyedRead(ast) {
|
7760 | return visit(this, ast.obj);
|
7761 | },
|
7762 | visitKeyedWrite(ast) {
|
7763 | return null;
|
7764 | },
|
7765 | visitLiteralArray(ast) {
|
7766 | return null;
|
7767 | },
|
7768 | visitLiteralMap(ast) {
|
7769 | return null;
|
7770 | },
|
7771 | visitLiteralPrimitive(ast) {
|
7772 | return null;
|
7773 | },
|
7774 | visitMethodCall(ast) {
|
7775 | return visit(this, ast.receiver);
|
7776 | },
|
7777 | visitPipe(ast) {
|
7778 | return null;
|
7779 | },
|
7780 | visitPrefixNot(ast) {
|
7781 | return null;
|
7782 | },
|
7783 | visitNonNullAssert(ast) {
|
7784 | return null;
|
7785 | },
|
7786 | visitPropertyRead(ast) {
|
7787 | return visit(this, ast.receiver);
|
7788 | },
|
7789 | visitPropertyWrite(ast) {
|
7790 | return null;
|
7791 | },
|
7792 | visitQuote(ast) {
|
7793 | return null;
|
7794 | },
|
7795 | visitSafeMethodCall(ast) {
|
7796 | return visit(this, ast.receiver) || ast;
|
7797 | },
|
7798 | visitSafePropertyRead(ast) {
|
7799 | return visit(this, ast.receiver) || ast;
|
7800 | }
|
7801 | });
|
7802 | }
|
7803 | // Returns true of the AST includes a method or a pipe indicating that, if the
|
7804 | // expression is used as the target of a safe property or method access then
|
7805 | // the expression should be stored into a temporary variable.
|
7806 | needsTemporary(ast) {
|
7807 | const visit = (visitor, ast) => {
|
7808 | return ast && (this._nodeMap.get(ast) || ast).visit(visitor);
|
7809 | };
|
7810 | const visitSome = (visitor, ast) => {
|
7811 | return ast.some(ast => visit(visitor, ast));
|
7812 | };
|
7813 | return ast.visit({
|
7814 | visitUnary(ast) {
|
7815 | return visit(this, ast.expr);
|
7816 | },
|
7817 | visitBinary(ast) {
|
7818 | return visit(this, ast.left) || visit(this, ast.right);
|
7819 | },
|
7820 | visitChain(ast) {
|
7821 | return false;
|
7822 | },
|
7823 | visitConditional(ast) {
|
7824 | return visit(this, ast.condition) || visit(this, ast.trueExp) || visit(this, ast.falseExp);
|
7825 | },
|
7826 | visitFunctionCall(ast) {
|
7827 | return true;
|
7828 | },
|
7829 | visitImplicitReceiver(ast) {
|
7830 | return false;
|
7831 | },
|
7832 | visitThisReceiver(ast) {
|
7833 | return false;
|
7834 | },
|
7835 | visitInterpolation(ast) {
|
7836 | return visitSome(this, ast.expressions);
|
7837 | },
|
7838 | visitKeyedRead(ast) {
|
7839 | return false;
|
7840 | },
|
7841 | visitKeyedWrite(ast) {
|
7842 | return false;
|
7843 | },
|
7844 | visitLiteralArray(ast) {
|
7845 | return true;
|
7846 | },
|
7847 | visitLiteralMap(ast) {
|
7848 | return true;
|
7849 | },
|
7850 | visitLiteralPrimitive(ast) {
|
7851 | return false;
|
7852 | },
|
7853 | visitMethodCall(ast) {
|
7854 | return true;
|
7855 | },
|
7856 | visitPipe(ast) {
|
7857 | return true;
|
7858 | },
|
7859 | visitPrefixNot(ast) {
|
7860 | return visit(this, ast.expression);
|
7861 | },
|
7862 | visitNonNullAssert(ast) {
|
7863 | return visit(this, ast.expression);
|
7864 | },
|
7865 | visitPropertyRead(ast) {
|
7866 | return false;
|
7867 | },
|
7868 | visitPropertyWrite(ast) {
|
7869 | return false;
|
7870 | },
|
7871 | visitQuote(ast) {
|
7872 | return false;
|
7873 | },
|
7874 | visitSafeMethodCall(ast) {
|
7875 | return true;
|
7876 | },
|
7877 | visitSafePropertyRead(ast) {
|
7878 | return false;
|
7879 | }
|
7880 | });
|
7881 | }
|
7882 | allocateTemporary() {
|
7883 | const tempNumber = this._currentTemporary++;
|
7884 | this.temporaryCount = Math.max(this._currentTemporary, this.temporaryCount);
|
7885 | return new ReadVarExpr(temporaryName(this.bindingId, tempNumber));
|
7886 | }
|
7887 | releaseTemporary(temporary) {
|
7888 | this._currentTemporary--;
|
7889 | if (temporary.name != temporaryName(this.bindingId, this._currentTemporary)) {
|
7890 | throw new Error(`Temporary ${temporary.name} released out of order`);
|
7891 | }
|
7892 | }
|
7893 | /**
|
7894 | * Creates an absolute `ParseSourceSpan` from the relative `ParseSpan`.
|
7895 | *
|
7896 | * `ParseSpan` objects are relative to the start of the expression.
|
7897 | * This method converts these to full `ParseSourceSpan` objects that
|
7898 | * show where the span is within the overall source file.
|
7899 | *
|
7900 | * @param span the relative span to convert.
|
7901 | * @returns a `ParseSourceSpan` for the given span or null if no
|
7902 | * `baseSourceSpan` was provided to this class.
|
7903 | */
|
7904 | convertSourceSpan(span) {
|
7905 | if (this.baseSourceSpan) {
|
7906 | const start = this.baseSourceSpan.start.moveBy(span.start);
|
7907 | const end = this.baseSourceSpan.start.moveBy(span.end);
|
7908 | const fullStart = this.baseSourceSpan.fullStart.moveBy(span.start);
|
7909 | return new ParseSourceSpan(start, end, fullStart);
|
7910 | }
|
7911 | else {
|
7912 | return null;
|
7913 | }
|
7914 | }
|
7915 | /** Adds the name of an AST to the list of implicit receiver accesses. */
|
7916 | addImplicitReceiverAccess(name) {
|
7917 | if (this.implicitReceiverAccesses) {
|
7918 | this.implicitReceiverAccesses.add(name);
|
7919 | }
|
7920 | }
|
7921 | }
|
7922 | function flattenStatements(arg, output) {
|
7923 | if (Array.isArray(arg)) {
|
7924 | arg.forEach((entry) => flattenStatements(entry, output));
|
7925 | }
|
7926 | else {
|
7927 | output.push(arg);
|
7928 | }
|
7929 | }
|
7930 | class DefaultLocalResolver {
|
7931 | constructor(globals) {
|
7932 | this.globals = globals;
|
7933 | }
|
7934 | notifyImplicitReceiverUse() { }
|
7935 | getLocal(name) {
|
7936 | if (name === EventHandlerVars.event.name) {
|
7937 | return EventHandlerVars.event;
|
7938 | }
|
7939 | return null;
|
7940 | }
|
7941 | }
|
7942 | function createCurrValueExpr(bindingId) {
|
7943 | return variable(`currVal_${bindingId}`); // fix syntax highlighting: `
|
7944 | }
|
7945 | function createPreventDefaultVar(bindingId) {
|
7946 | return variable(`pd_${bindingId}`);
|
7947 | }
|
7948 | function convertStmtIntoExpression(stmt) {
|
7949 | if (stmt instanceof ExpressionStatement) {
|
7950 | return stmt.expr;
|
7951 | }
|
7952 | else if (stmt instanceof ReturnStatement) {
|
7953 | return stmt.value;
|
7954 | }
|
7955 | return null;
|
7956 | }
|
7957 | class BuiltinFunctionCall extends FunctionCall {
|
7958 | constructor(span, sourceSpan, args, converter) {
|
7959 | super(span, sourceSpan, null, args);
|
7960 | this.args = args;
|
7961 | this.converter = converter;
|
7962 | }
|
7963 | }
|
7964 |
|
7965 | /**
|
7966 | * @license
|
7967 | * Copyright Google LLC All Rights Reserved.
|
7968 | *
|
7969 | * Use of this source code is governed by an MIT-style license that can be
|
7970 | * found in the LICENSE file at https://angular.io/license
|
7971 | */
|
7972 | /**
|
7973 | * This file is a port of shadowCSS from webcomponents.js to TypeScript.
|
7974 | *
|
7975 | * Please make sure to keep to edits in sync with the source file.
|
7976 | *
|
7977 | * Source:
|
7978 | * https://github.com/webcomponents/webcomponentsjs/blob/4efecd7e0e/src/ShadowCSS/ShadowCSS.js
|
7979 | *
|
7980 | * The original file level comment is reproduced below
|
7981 | */
|
7982 | /*
|
7983 | This is a limited shim for ShadowDOM css styling.
|
7984 | https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/shadow/index.html#styles
|
7985 |
|
7986 | The intention here is to support only the styling features which can be
|
7987 | relatively simply implemented. The goal is to allow users to avoid the
|
7988 | most obvious pitfalls and do so without compromising performance significantly.
|
7989 | For ShadowDOM styling that's not covered here, a set of best practices
|
7990 | can be provided that should allow users to accomplish more complex styling.
|
7991 |
|
7992 | The following is a list of specific ShadowDOM styling features and a brief
|
7993 | discussion of the approach used to shim.
|
7994 |
|
7995 | Shimmed features:
|
7996 |
|
7997 | * :host, :host-context: ShadowDOM allows styling of the shadowRoot's host
|
7998 | element using the :host rule. To shim this feature, the :host styles are
|
7999 | reformatted and prefixed with a given scope name and promoted to a
|
8000 | document level stylesheet.
|
8001 | For example, given a scope name of .foo, a rule like this:
|
8002 |
|
8003 | :host {
|
8004 | background: red;
|
8005 | }
|
8006 | }
|
8007 |
|
8008 | becomes:
|
8009 |
|
8010 | .foo {
|
8011 | background: red;
|
8012 | }
|
8013 |
|
8014 | * encapsulation: Styles defined within ShadowDOM, apply only to
|
8015 | dom inside the ShadowDOM. Polymer uses one of two techniques to implement
|
8016 | this feature.
|
8017 |
|
8018 | By default, rules are prefixed with the host element tag name
|
8019 | as a descendant selector. This ensures styling does not leak out of the 'top'
|
8020 | of the element's ShadowDOM. For example,
|
8021 |
|
8022 | div {
|
8023 | font-weight: bold;
|
8024 | }
|
8025 |
|
8026 | becomes:
|
8027 |
|
8028 | x-foo div {
|
8029 | font-weight: bold;
|
8030 | }
|
8031 |
|
8032 | becomes:
|
8033 |
|
8034 |
|
8035 | Alternatively, if WebComponents.ShadowCSS.strictStyling is set to true then
|
8036 | selectors are scoped by adding an attribute selector suffix to each
|
8037 | simple selector that contains the host element tag name. Each element
|
8038 | in the element's ShadowDOM template is also given the scope attribute.
|
8039 | Thus, these rules match only elements that have the scope attribute.
|
8040 | For example, given a scope name of x-foo, a rule like this:
|
8041 |
|
8042 | div {
|
8043 | font-weight: bold;
|
8044 | }
|
8045 |
|
8046 | becomes:
|
8047 |
|
8048 | div[x-foo] {
|
8049 | font-weight: bold;
|
8050 | }
|
8051 |
|
8052 | Note that elements that are dynamically added to a scope must have the scope
|
8053 | selector added to them manually.
|
8054 |
|
8055 | * upper/lower bound encapsulation: Styles which are defined outside a
|
8056 | shadowRoot should not cross the ShadowDOM boundary and should not apply
|
8057 | inside a shadowRoot.
|
8058 |
|
8059 | This styling behavior is not emulated. Some possible ways to do this that
|
8060 | were rejected due to complexity and/or performance concerns include: (1) reset
|
8061 | every possible property for every possible selector for a given scope name;
|
8062 | (2) re-implement css in javascript.
|
8063 |
|
8064 | As an alternative, users should make sure to use selectors
|
8065 | specific to the scope in which they are working.
|
8066 |
|
8067 | * ::distributed: This behavior is not emulated. It's often not necessary
|
8068 | to style the contents of a specific insertion point and instead, descendants
|
8069 | of the host element can be styled selectively. Users can also create an
|
8070 | extra node around an insertion point and style that node's contents
|
8071 | via descendent selectors. For example, with a shadowRoot like this:
|
8072 |
|
8073 | <style>
|
8074 | ::content(div) {
|
8075 | background: red;
|
8076 | }
|
8077 | </style>
|
8078 | <content></content>
|
8079 |
|
8080 | could become:
|
8081 |
|
8082 | <style>
|
8083 | / *@polyfill .content-container div * /
|
8084 | ::content(div) {
|
8085 | background: red;
|
8086 | }
|
8087 | </style>
|
8088 | <div class="content-container">
|
8089 | <content></content>
|
8090 | </div>
|
8091 |
|
8092 | Note the use of @polyfill in the comment above a ShadowDOM specific style
|
8093 | declaration. This is a directive to the styling shim to use the selector
|
8094 | in comments in lieu of the next selector when running under polyfill.
|
8095 | */
|
8096 | class ShadowCss {
|
8097 | constructor() {
|
8098 | this.strictStyling = true;
|
8099 | }
|
8100 | /*
|
8101 | * Shim some cssText with the given selector. Returns cssText that can
|
8102 | * be included in the document via WebComponents.ShadowCSS.addCssToDocument(css).
|
8103 | *
|
8104 | * When strictStyling is true:
|
8105 | * - selector is the attribute added to all elements inside the host,
|
8106 | * - hostSelector is the attribute added to the host itself.
|
8107 | */
|
8108 | shimCssText(cssText, selector, hostSelector = '') {
|
8109 | const commentsWithHash = extractCommentsWithHash(cssText);
|
8110 | cssText = stripComments(cssText);
|
8111 | cssText = this._insertDirectives(cssText);
|
8112 | const scopedCssText = this._scopeCssText(cssText, selector, hostSelector);
|
8113 | return [scopedCssText, ...commentsWithHash].join('\n');
|
8114 | }
|
8115 | _insertDirectives(cssText) {
|
8116 | cssText = this._insertPolyfillDirectivesInCssText(cssText);
|
8117 | return this._insertPolyfillRulesInCssText(cssText);
|
8118 | }
|
8119 | /*
|
8120 | * Process styles to convert native ShadowDOM rules that will trip
|
8121 | * up the css parser; we rely on decorating the stylesheet with inert rules.
|
8122 | *
|
8123 | * For example, we convert this rule:
|
8124 | *
|
8125 | * polyfill-next-selector { content: ':host menu-item'; }
|
8126 | * ::content menu-item {
|
8127 | *
|
8128 | * to this:
|
8129 | *
|
8130 | * scopeName menu-item {
|
8131 | *
|
8132 | **/
|
8133 | _insertPolyfillDirectivesInCssText(cssText) {
|
8134 | // Difference with webcomponents.js: does not handle comments
|
8135 | return cssText.replace(_cssContentNextSelectorRe, function (...m) {
|
8136 | return m[2] + '{';
|
8137 | });
|
8138 | }
|
8139 | /*
|
8140 | * Process styles to add rules which will only apply under the polyfill
|
8141 | *
|
8142 | * For example, we convert this rule:
|
8143 | *
|
8144 | * polyfill-rule {
|
8145 | * content: ':host menu-item';
|
8146 | * ...
|
8147 | * }
|
8148 | *
|
8149 | * to this:
|
8150 | *
|
8151 | * scopeName menu-item {...}
|
8152 | *
|
8153 | **/
|
8154 | _insertPolyfillRulesInCssText(cssText) {
|
8155 | // Difference with webcomponents.js: does not handle comments
|
8156 | return cssText.replace(_cssContentRuleRe, (...m) => {
|
8157 | const rule = m[0].replace(m[1], '').replace(m[2], '');
|
8158 | return m[4] + rule;
|
8159 | });
|
8160 | }
|
8161 | /* Ensure styles are scoped. Pseudo-scoping takes a rule like:
|
8162 | *
|
8163 | * .foo {... }
|
8164 | *
|
8165 | * and converts this to
|
8166 | *
|
8167 | * scopeName .foo { ... }
|
8168 | */
|
8169 | _scopeCssText(cssText, scopeSelector, hostSelector) {
|
8170 | const unscopedRules = this._extractUnscopedRulesFromCssText(cssText);
|
8171 | // replace :host and :host-context -shadowcsshost and -shadowcsshost respectively
|
8172 | cssText = this._insertPolyfillHostInCssText(cssText);
|
8173 | cssText = this._convertColonHost(cssText);
|
8174 | cssText = this._convertColonHostContext(cssText);
|
8175 | cssText = this._convertShadowDOMSelectors(cssText);
|
8176 | if (scopeSelector) {
|
8177 | cssText = this._scopeSelectors(cssText, scopeSelector, hostSelector);
|
8178 | }
|
8179 | cssText = cssText + '\n' + unscopedRules;
|
8180 | return cssText.trim();
|
8181 | }
|
8182 | /*
|
8183 | * Process styles to add rules which will only apply under the polyfill
|
8184 | * and do not process via CSSOM. (CSSOM is destructive to rules on rare
|
8185 | * occasions, e.g. -webkit-calc on Safari.)
|
8186 | * For example, we convert this rule:
|
8187 | *
|
8188 | * @polyfill-unscoped-rule {
|
8189 | * content: 'menu-item';
|
8190 | * ... }
|
8191 | *
|
8192 | * to this:
|
8193 | *
|
8194 | * menu-item {...}
|
8195 | *
|
8196 | **/
|
8197 | _extractUnscopedRulesFromCssText(cssText) {
|
8198 | // Difference with webcomponents.js: does not handle comments
|
8199 | let r = '';
|
8200 | let m;
|
8201 | _cssContentUnscopedRuleRe.lastIndex = 0;
|
8202 | while ((m = _cssContentUnscopedRuleRe.exec(cssText)) !== null) {
|
8203 | const rule = m[0].replace(m[2], '').replace(m[1], m[4]);
|
8204 | r += rule + '\n\n';
|
8205 | }
|
8206 | return r;
|
8207 | }
|
8208 | /*
|
8209 | * convert a rule like :host(.foo) > .bar { }
|
8210 | *
|
8211 | * to
|
8212 | *
|
8213 | * .foo<scopeName> > .bar
|
8214 | */
|
8215 | _convertColonHost(cssText) {
|
8216 | return this._convertColonRule(cssText, _cssColonHostRe, this._colonHostPartReplacer);
|
8217 | }
|
8218 | /*
|
8219 | * convert a rule like :host-context(.foo) > .bar { }
|
8220 | *
|
8221 | * to
|
8222 | *
|
8223 | * .foo<scopeName> > .bar, .foo scopeName > .bar { }
|
8224 | *
|
8225 | * and
|
8226 | *
|
8227 | * :host-context(.foo:host) .bar { ... }
|
8228 | *
|
8229 | * to
|
8230 | *
|
8231 | * .foo<scopeName> .bar { ... }
|
8232 | */
|
8233 | _convertColonHostContext(cssText) {
|
8234 | return this._convertColonRule(cssText, _cssColonHostContextRe, this._colonHostContextPartReplacer);
|
8235 | }
|
8236 | _convertColonRule(cssText, regExp, partReplacer) {
|
8237 | // m[1] = :host(-context), m[2] = contents of (), m[3] rest of rule
|
8238 | return cssText.replace(regExp, function (...m) {
|
8239 | if (m[2]) {
|
8240 | const parts = m[2].split(',');
|
8241 | const r = [];
|
8242 | for (let i = 0; i < parts.length; i++) {
|
8243 | const p = parts[i].trim();
|
8244 | if (!p)
|
8245 | break;
|
8246 | r.push(partReplacer(_polyfillHostNoCombinator, p, m[3]));
|
8247 | }
|
8248 | return r.join(',');
|
8249 | }
|
8250 | else {
|
8251 | return _polyfillHostNoCombinator + m[3];
|
8252 | }
|
8253 | });
|
8254 | }
|
8255 | _colonHostContextPartReplacer(host, part, suffix) {
|
8256 | if (part.indexOf(_polyfillHost) > -1) {
|
8257 | return this._colonHostPartReplacer(host, part, suffix);
|
8258 | }
|
8259 | else {
|
8260 | return host + part + suffix + ', ' + part + ' ' + host + suffix;
|
8261 | }
|
8262 | }
|
8263 | _colonHostPartReplacer(host, part, suffix) {
|
8264 | return host + part.replace(_polyfillHost, '') + suffix;
|
8265 | }
|
8266 | /*
|
8267 | * Convert combinators like ::shadow and pseudo-elements like ::content
|
8268 | * by replacing with space.
|
8269 | */
|
8270 | _convertShadowDOMSelectors(cssText) {
|
8271 | return _shadowDOMSelectorsRe.reduce((result, pattern) => result.replace(pattern, ' '), cssText);
|
8272 | }
|
8273 | // change a selector like 'div' to 'name div'
|
8274 | _scopeSelectors(cssText, scopeSelector, hostSelector) {
|
8275 | return processRules(cssText, (rule) => {
|
8276 | let selector = rule.selector;
|
8277 | let content = rule.content;
|
8278 | if (rule.selector[0] != '@') {
|
8279 | selector =
|
8280 | this._scopeSelector(rule.selector, scopeSelector, hostSelector, this.strictStyling);
|
8281 | }
|
8282 | else if (rule.selector.startsWith('@media') || rule.selector.startsWith('@supports') ||
|
8283 | rule.selector.startsWith('@page') || rule.selector.startsWith('@document')) {
|
8284 | content = this._scopeSelectors(rule.content, scopeSelector, hostSelector);
|
8285 | }
|
8286 | return new CssRule(selector, content);
|
8287 | });
|
8288 | }
|
8289 | _scopeSelector(selector, scopeSelector, hostSelector, strict) {
|
8290 | return selector.split(',')
|
8291 | .map(part => part.trim().split(_shadowDeepSelectors))
|
8292 | .map((deepParts) => {
|
8293 | const [shallowPart, ...otherParts] = deepParts;
|
8294 | const applyScope = (shallowPart) => {
|
8295 | if (this._selectorNeedsScoping(shallowPart, scopeSelector)) {
|
8296 | return strict ?
|
8297 | this._applyStrictSelectorScope(shallowPart, scopeSelector, hostSelector) :
|
8298 | this._applySelectorScope(shallowPart, scopeSelector, hostSelector);
|
8299 | }
|
8300 | else {
|
8301 | return shallowPart;
|
8302 | }
|
8303 | };
|
8304 | return [applyScope(shallowPart), ...otherParts].join(' ');
|
8305 | })
|
8306 | .join(', ');
|
8307 | }
|
8308 | _selectorNeedsScoping(selector, scopeSelector) {
|
8309 | const re = this._makeScopeMatcher(scopeSelector);
|
8310 | return !re.test(selector);
|
8311 | }
|
8312 | _makeScopeMatcher(scopeSelector) {
|
8313 | const lre = /\[/g;
|
8314 | const rre = /\]/g;
|
8315 | scopeSelector = scopeSelector.replace(lre, '\\[').replace(rre, '\\]');
|
8316 | return new RegExp('^(' + scopeSelector + ')' + _selectorReSuffix, 'm');
|
8317 | }
|
8318 | _applySelectorScope(selector, scopeSelector, hostSelector) {
|
8319 | // Difference from webcomponents.js: scopeSelector could not be an array
|
8320 | return this._applySimpleSelectorScope(selector, scopeSelector, hostSelector);
|
8321 | }
|
8322 | // scope via name and [is=name]
|
8323 | _applySimpleSelectorScope(selector, scopeSelector, hostSelector) {
|
8324 | // In Android browser, the lastIndex is not reset when the regex is used in String.replace()
|
8325 | _polyfillHostRe.lastIndex = 0;
|
8326 | if (_polyfillHostRe.test(selector)) {
|
8327 | const replaceBy = this.strictStyling ? `[${hostSelector}]` : scopeSelector;
|
8328 | return selector
|
8329 | .replace(_polyfillHostNoCombinatorRe, (hnc, selector) => {
|
8330 | return selector.replace(/([^:]*)(:*)(.*)/, (_, before, colon, after) => {
|
8331 | return before + replaceBy + colon + after;
|
8332 | });
|
8333 | })
|
8334 | .replace(_polyfillHostRe, replaceBy + ' ');
|
8335 | }
|
8336 | return scopeSelector + ' ' + selector;
|
8337 | }
|
8338 | // return a selector with [name] suffix on each simple selector
|
8339 | // e.g. .foo.bar > .zot becomes .foo[name].bar[name] > .zot[name] /** @internal */
|
8340 | _applyStrictSelectorScope(selector, scopeSelector, hostSelector) {
|
8341 | const isRe = /\[is=([^\]]*)\]/g;
|
8342 | scopeSelector = scopeSelector.replace(isRe, (_, ...parts) => parts[0]);
|
8343 | const attrName = '[' + scopeSelector + ']';
|
8344 | const _scopeSelectorPart = (p) => {
|
8345 | let scopedP = p.trim();
|
8346 | if (!scopedP) {
|
8347 | return '';
|
8348 | }
|
8349 | if (p.indexOf(_polyfillHostNoCombinator) > -1) {
|
8350 | scopedP = this._applySimpleSelectorScope(p, scopeSelector, hostSelector);
|
8351 | }
|
8352 | else {
|
8353 | // remove :host since it should be unnecessary
|
8354 | const t = p.replace(_polyfillHostRe, '');
|
8355 | if (t.length > 0) {
|
8356 | const matches = t.match(/([^:]*)(:*)(.*)/);
|
8357 | if (matches) {
|
8358 | scopedP = matches[1] + attrName + matches[2] + matches[3];
|
8359 | }
|
8360 | }
|
8361 | }
|
8362 | return scopedP;
|
8363 | };
|
8364 | const safeContent = new SafeSelector(selector);
|
8365 | selector = safeContent.content();
|
8366 | let scopedSelector = '';
|
8367 | let startIndex = 0;
|
8368 | let res;
|
8369 | const sep = /( |>|\+|~(?!=))\s*/g;
|
8370 | // If a selector appears before :host it should not be shimmed as it
|
8371 | // matches on ancestor elements and not on elements in the host's shadow
|
8372 | // `:host-context(div)` is transformed to
|
8373 | // `-shadowcsshost-no-combinatordiv, div -shadowcsshost-no-combinator`
|
8374 | // the `div` is not part of the component in the 2nd selectors and should not be scoped.
|
8375 | // Historically `component-tag:host` was matching the component so we also want to preserve
|
8376 | // this behavior to avoid breaking legacy apps (it should not match).
|
8377 | // The behavior should be:
|
8378 | // - `tag:host` -> `tag[h]` (this is to avoid breaking legacy apps, should not match anything)
|
8379 | // - `tag :host` -> `tag [h]` (`tag` is not scoped because it's considered part of a
|
8380 | // `:host-context(tag)`)
|
8381 | const hasHost = selector.indexOf(_polyfillHostNoCombinator) > -1;
|
8382 | // Only scope parts after the first `-shadowcsshost-no-combinator` when it is present
|
8383 | let shouldScope = !hasHost;
|
8384 | while ((res = sep.exec(selector)) !== null) {
|
8385 | const separator = res[1];
|
8386 | const part = selector.slice(startIndex, res.index).trim();
|
8387 | shouldScope = shouldScope || part.indexOf(_polyfillHostNoCombinator) > -1;
|
8388 | const scopedPart = shouldScope ? _scopeSelectorPart(part) : part;
|
8389 | scopedSelector += `${scopedPart} ${separator} `;
|
8390 | startIndex = sep.lastIndex;
|
8391 | }
|
8392 | const part = selector.substring(startIndex);
|
8393 | shouldScope = shouldScope || part.indexOf(_polyfillHostNoCombinator) > -1;
|
8394 | scopedSelector += shouldScope ? _scopeSelectorPart(part) : part;
|
8395 | // replace the placeholders with their original values
|
8396 | return safeContent.restore(scopedSelector);
|
8397 | }
|
8398 | _insertPolyfillHostInCssText(selector) {
|
8399 | return selector.replace(_colonHostContextRe, _polyfillHostContext)
|
8400 | .replace(_colonHostRe, _polyfillHost);
|
8401 | }
|
8402 | }
|
8403 | class SafeSelector {
|
8404 | constructor(selector) {
|
8405 | this.placeholders = [];
|
8406 | this.index = 0;
|
8407 | // Replaces attribute selectors with placeholders.
|
8408 | // The WS in [attr="va lue"] would otherwise be interpreted as a selector separator.
|
8409 | selector = this._escapeRegexMatches(selector, /(\[[^\]]*\])/g);
|
8410 | // CSS allows for certain special characters to be used in selectors if they're escaped.
|
8411 | // E.g. `.foo:blue` won't match a class called `foo:blue`, because the colon denotes a
|
8412 | // pseudo-class, but writing `.foo\:blue` will match, because the colon was escaped.
|
8413 | // Replace all escape sequences (`\` followed by a character) with a placeholder so
|
8414 | // that our handling of pseudo-selectors doesn't mess with them.
|
8415 | selector = this._escapeRegexMatches(selector, /(\\.)/g);
|
8416 | // Replaces the expression in `:nth-child(2n + 1)` with a placeholder.
|
8417 | // WS and "+" would otherwise be interpreted as selector separators.
|
8418 | this._content = selector.replace(/(:nth-[-\w]+)(\([^)]+\))/g, (_, pseudo, exp) => {
|
8419 | const replaceBy = `__ph-${this.index}__`;
|
8420 | this.placeholders.push(exp);
|
8421 | this.index++;
|
8422 | return pseudo + replaceBy;
|
8423 | });
|
8424 | }
|
8425 | restore(content) {
|
8426 | return content.replace(/__ph-(\d+)__/g, (_ph, index) => this.placeholders[+index]);
|
8427 | }
|
8428 | content() {
|
8429 | return this._content;
|
8430 | }
|
8431 | /**
|
8432 | * Replaces all of the substrings that match a regex within a
|
8433 | * special string (e.g. `__ph-0__`, `__ph-1__`, etc).
|
8434 | */
|
8435 | _escapeRegexMatches(content, pattern) {
|
8436 | return content.replace(pattern, (_, keep) => {
|
8437 | const replaceBy = `__ph-${this.index}__`;
|
8438 | this.placeholders.push(keep);
|
8439 | this.index++;
|
8440 | return replaceBy;
|
8441 | });
|
8442 | }
|
8443 | }
|
8444 | const _cssContentNextSelectorRe = /polyfill-next-selector[^}]*content:[\s]*?(['"])(.*?)\1[;\s]*}([^{]*?){/gim;
|
8445 | const _cssContentRuleRe = /(polyfill-rule)[^}]*(content:[\s]*(['"])(.*?)\3)[;\s]*[^}]*}/gim;
|
8446 | const _cssContentUnscopedRuleRe = /(polyfill-unscoped-rule)[^}]*(content:[\s]*(['"])(.*?)\3)[;\s]*[^}]*}/gim;
|
8447 | const _polyfillHost = '-shadowcsshost';
|
8448 | // note: :host-context pre-processed to -shadowcsshostcontext.
|
8449 | const _polyfillHostContext = '-shadowcsscontext';
|
8450 | const _parenSuffix = ')(?:\\((' +
|
8451 | '(?:\\([^)(]*\\)|[^)(]*)+?' +
|
8452 | ')\\))?([^,{]*)';
|
8453 | const _cssColonHostRe = new RegExp('(' + _polyfillHost + _parenSuffix, 'gim');
|
8454 | const _cssColonHostContextRe = new RegExp('(' + _polyfillHostContext + _parenSuffix, 'gim');
|
8455 | const _polyfillHostNoCombinator = _polyfillHost + '-no-combinator';
|
8456 | const _polyfillHostNoCombinatorRe = /-shadowcsshost-no-combinator([^\s]*)/;
|
8457 | const _shadowDOMSelectorsRe = [
|
8458 | /::shadow/g,
|
8459 | /::content/g,
|
8460 | // Deprecated selectors
|
8461 | /\/shadow-deep\//g,
|
8462 | /\/shadow\//g,
|
8463 | ];
|
8464 | // The deep combinator is deprecated in the CSS spec
|
8465 | // Support for `>>>`, `deep`, `::ng-deep` is then also deprecated and will be removed in the future.
|
8466 | // see https://github.com/angular/angular/pull/17677
|
8467 | const _shadowDeepSelectors = /(?:>>>)|(?:\/deep\/)|(?:::ng-deep)/g;
|
8468 | const _selectorReSuffix = '([>\\s~+\[.,{:][\\s\\S]*)?$';
|
8469 | const _polyfillHostRe = /-shadowcsshost/gim;
|
8470 | const _colonHostRe = /:host/gim;
|
8471 | const _colonHostContextRe = /:host-context/gim;
|
8472 | const _commentRe = /\/\*\s*[\s\S]*?\*\//g;
|
8473 | function stripComments(input) {
|
8474 | return input.replace(_commentRe, '');
|
8475 | }
|
8476 | const _commentWithHashRe = /\/\*\s*#\s*source(Mapping)?URL=[\s\S]+?\*\//g;
|
8477 | function extractCommentsWithHash(input) {
|
8478 | return input.match(_commentWithHashRe) || [];
|
8479 | }
|
8480 | const BLOCK_PLACEHOLDER = '%BLOCK%';
|
8481 | const QUOTE_PLACEHOLDER = '%QUOTED%';
|
8482 | const _ruleRe = /(\s*)([^;\{\}]+?)(\s*)((?:{%BLOCK%}?\s*;?)|(?:\s*;))/g;
|
8483 | const _quotedRe = /%QUOTED%/g;
|
8484 | const CONTENT_PAIRS = new Map([['{', '}']]);
|
8485 | const QUOTE_PAIRS = new Map([[`"`, `"`], [`'`, `'`]]);
|
8486 | class CssRule {
|
8487 | constructor(selector, content) {
|
8488 | this.selector = selector;
|
8489 | this.content = content;
|
8490 | }
|
8491 | }
|
8492 | function processRules(input, ruleCallback) {
|
8493 | const inputWithEscapedQuotes = escapeBlocks(input, QUOTE_PAIRS, QUOTE_PLACEHOLDER);
|
8494 | const inputWithEscapedBlocks = escapeBlocks(inputWithEscapedQuotes.escapedString, CONTENT_PAIRS, BLOCK_PLACEHOLDER);
|
8495 | let nextBlockIndex = 0;
|
8496 | let nextQuoteIndex = 0;
|
8497 | return inputWithEscapedBlocks.escapedString
|
8498 | .replace(_ruleRe, (...m) => {
|
8499 | const selector = m[2];
|
8500 | let content = '';
|
8501 | let suffix = m[4];
|
8502 | let contentPrefix = '';
|
8503 | if (suffix && suffix.startsWith('{' + BLOCK_PLACEHOLDER)) {
|
8504 | content = inputWithEscapedBlocks.blocks[nextBlockIndex++];
|
8505 | suffix = suffix.substring(BLOCK_PLACEHOLDER.length + 1);
|
8506 | contentPrefix = '{';
|
8507 | }
|
8508 | const rule = ruleCallback(new CssRule(selector, content));
|
8509 | return `${m[1]}${rule.selector}${m[3]}${contentPrefix}${rule.content}${suffix}`;
|
8510 | })
|
8511 | .replace(_quotedRe, () => inputWithEscapedQuotes.blocks[nextQuoteIndex++]);
|
8512 | }
|
8513 | class StringWithEscapedBlocks {
|
8514 | constructor(escapedString, blocks) {
|
8515 | this.escapedString = escapedString;
|
8516 | this.blocks = blocks;
|
8517 | }
|
8518 | }
|
8519 | function escapeBlocks(input, charPairs, placeholder) {
|
8520 | const resultParts = [];
|
8521 | const escapedBlocks = [];
|
8522 | let openCharCount = 0;
|
8523 | let nonBlockStartIndex = 0;
|
8524 | let blockStartIndex = -1;
|
8525 | let openChar;
|
8526 | let closeChar;
|
8527 | for (let i = 0; i < input.length; i++) {
|
8528 | const char = input[i];
|
8529 | if (char === '\\') {
|
8530 | i++;
|
8531 | }
|
8532 | else if (char === closeChar) {
|
8533 | openCharCount--;
|
8534 | if (openCharCount === 0) {
|
8535 | escapedBlocks.push(input.substring(blockStartIndex, i));
|
8536 | resultParts.push(placeholder);
|
8537 | nonBlockStartIndex = i;
|
8538 | blockStartIndex = -1;
|
8539 | openChar = closeChar = undefined;
|
8540 | }
|
8541 | }
|
8542 | else if (char === openChar) {
|
8543 | openCharCount++;
|
8544 | }
|
8545 | else if (openCharCount === 0 && charPairs.has(char)) {
|
8546 | openChar = char;
|
8547 | closeChar = charPairs.get(char);
|
8548 | openCharCount = 1;
|
8549 | blockStartIndex = i + 1;
|
8550 | resultParts.push(input.substring(nonBlockStartIndex, blockStartIndex));
|
8551 | }
|
8552 | }
|
8553 | if (blockStartIndex !== -1) {
|
8554 | escapedBlocks.push(input.substring(blockStartIndex));
|
8555 | resultParts.push(placeholder);
|
8556 | }
|
8557 | else {
|
8558 | resultParts.push(input.substring(nonBlockStartIndex));
|
8559 | }
|
8560 | return new StringWithEscapedBlocks(resultParts.join(''), escapedBlocks);
|
8561 | }
|
8562 |
|
8563 | /**
|
8564 | * @license
|
8565 | * Copyright Google LLC All Rights Reserved.
|
8566 | *
|
8567 | * Use of this source code is governed by an MIT-style license that can be
|
8568 | * found in the LICENSE file at https://angular.io/license
|
8569 | */
|
8570 | const COMPONENT_VARIABLE = '%COMP%';
|
8571 | const HOST_ATTR = `_nghost-${COMPONENT_VARIABLE}`;
|
8572 | const CONTENT_ATTR = `_ngcontent-${COMPONENT_VARIABLE}`;
|
8573 |
|
8574 | /**
|
8575 | * @license
|
8576 | * Copyright Google LLC All Rights Reserved.
|
8577 | *
|
8578 | * Use of this source code is governed by an MIT-style license that can be
|
8579 | * found in the LICENSE file at https://angular.io/license
|
8580 | */
|
8581 | /**
|
8582 | * A path is an ordered set of elements. Typically a path is to a
|
8583 | * particular offset in a source file. The head of the list is the top
|
8584 | * most node. The tail is the node that contains the offset directly.
|
8585 | *
|
8586 | * For example, the expression `a + b + c` might have an ast that looks
|
8587 | * like:
|
8588 | * +
|
8589 | * / \
|
8590 | * a +
|
8591 | * / \
|
8592 | * b c
|
8593 | *
|
8594 | * The path to the node at offset 9 would be `['+' at 1-10, '+' at 7-10,
|
8595 | * 'c' at 9-10]` and the path the node at offset 1 would be
|
8596 | * `['+' at 1-10, 'a' at 1-2]`.
|
8597 | */
|
8598 | class AstPath {
|
8599 | constructor(path, position = -1) {
|
8600 | this.path = path;
|
8601 | this.position = position;
|
8602 | }
|
8603 | get empty() {
|
8604 | return !this.path || !this.path.length;
|
8605 | }
|
8606 | get head() {
|
8607 | return this.path[0];
|
8608 | }
|
8609 | get tail() {
|
8610 | return this.path[this.path.length - 1];
|
8611 | }
|
8612 | parentOf(node) {
|
8613 | return node && this.path[this.path.indexOf(node) - 1];
|
8614 | }
|
8615 | childOf(node) {
|
8616 | return this.path[this.path.indexOf(node) + 1];
|
8617 | }
|
8618 | first(ctor) {
|
8619 | for (let i = this.path.length - 1; i >= 0; i--) {
|
8620 | let item = this.path[i];
|
8621 | if (item instanceof ctor)
|
8622 | return item;
|
8623 | }
|
8624 | }
|
8625 | push(node) {
|
8626 | this.path.push(node);
|
8627 | }
|
8628 | pop() {
|
8629 | return this.path.pop();
|
8630 | }
|
8631 | }
|
8632 |
|
8633 | /**
|
8634 | * @license
|
8635 | * Copyright Google LLC All Rights Reserved.
|
8636 | *
|
8637 | * Use of this source code is governed by an MIT-style license that can be
|
8638 | * found in the LICENSE file at https://angular.io/license
|
8639 | */
|
8640 | class NodeWithI18n {
|
8641 | constructor(sourceSpan, i18n) {
|
8642 | this.sourceSpan = sourceSpan;
|
8643 | this.i18n = i18n;
|
8644 | }
|
8645 | }
|
8646 | class Text$2 extends NodeWithI18n {
|
8647 | constructor(value, sourceSpan, i18n) {
|
8648 | super(sourceSpan, i18n);
|
8649 | this.value = value;
|
8650 | }
|
8651 | visit(visitor, context) {
|
8652 | return visitor.visitText(this, context);
|
8653 | }
|
8654 | }
|
8655 | class Expansion extends NodeWithI18n {
|
8656 | constructor(switchValue, type, cases, sourceSpan, switchValueSourceSpan, i18n) {
|
8657 | super(sourceSpan, i18n);
|
8658 | this.switchValue = switchValue;
|
8659 | this.type = type;
|
8660 | this.cases = cases;
|
8661 | this.switchValueSourceSpan = switchValueSourceSpan;
|
8662 | }
|
8663 | visit(visitor, context) {
|
8664 | return visitor.visitExpansion(this, context);
|
8665 | }
|
8666 | }
|
8667 | class ExpansionCase {
|
8668 | constructor(value, expression, sourceSpan, valueSourceSpan, expSourceSpan) {
|
8669 | this.value = value;
|
8670 | this.expression = expression;
|
8671 | this.sourceSpan = sourceSpan;
|
8672 | this.valueSourceSpan = valueSourceSpan;
|
8673 | this.expSourceSpan = expSourceSpan;
|
8674 | }
|
8675 | visit(visitor, context) {
|
8676 | return visitor.visitExpansionCase(this, context);
|
8677 | }
|
8678 | }
|
8679 | class Attribute extends NodeWithI18n {
|
8680 | constructor(name, value, sourceSpan, keySpan, valueSpan, i18n) {
|
8681 | super(sourceSpan, i18n);
|
8682 | this.name = name;
|
8683 | this.value = value;
|
8684 | this.keySpan = keySpan;
|
8685 | this.valueSpan = valueSpan;
|
8686 | }
|
8687 | visit(visitor, context) {
|
8688 | return visitor.visitAttribute(this, context);
|
8689 | }
|
8690 | }
|
8691 | class Element$1 extends NodeWithI18n {
|
8692 | constructor(name, attrs, children, sourceSpan, startSourceSpan, endSourceSpan = null, i18n) {
|
8693 | super(sourceSpan, i18n);
|
8694 | this.name = name;
|
8695 | this.attrs = attrs;
|
8696 | this.children = children;
|
8697 | this.startSourceSpan = startSourceSpan;
|
8698 | this.endSourceSpan = endSourceSpan;
|
8699 | }
|
8700 | visit(visitor, context) {
|
8701 | return visitor.visitElement(this, context);
|
8702 | }
|
8703 | }
|
8704 | class Comment {
|
8705 | constructor(value, sourceSpan) {
|
8706 | this.value = value;
|
8707 | this.sourceSpan = sourceSpan;
|
8708 | }
|
8709 | visit(visitor, context) {
|
8710 | return visitor.visitComment(this, context);
|
8711 | }
|
8712 | }
|
8713 | function visitAll$1(visitor, nodes, context = null) {
|
8714 | const result = [];
|
8715 | const visit = visitor.visit ?
|
8716 | (ast) => visitor.visit(ast, context) || ast.visit(visitor, context) :
|
8717 | (ast) => ast.visit(visitor, context);
|
8718 | nodes.forEach(ast => {
|
8719 | const astResult = visit(ast);
|
8720 | if (astResult) {
|
8721 | result.push(astResult);
|
8722 | }
|
8723 | });
|
8724 | return result;
|
8725 | }
|
8726 | class RecursiveVisitor {
|
8727 | constructor() { }
|
8728 | visitElement(ast, context) {
|
8729 | this.visitChildren(context, visit => {
|
8730 | visit(ast.attrs);
|
8731 | visit(ast.children);
|
8732 | });
|
8733 | }
|
8734 | visitAttribute(ast, context) { }
|
8735 | visitText(ast, context) { }
|
8736 | visitComment(ast, context) { }
|
8737 | visitExpansion(ast, context) {
|
8738 | return this.visitChildren(context, visit => {
|
8739 | visit(ast.cases);
|
8740 | });
|
8741 | }
|
8742 | visitExpansionCase(ast, context) { }
|
8743 | visitChildren(context, cb) {
|
8744 | let results = [];
|
8745 | let t = this;
|
8746 | function visit(children) {
|
8747 | if (children)
|
8748 | results.push(visitAll$1(t, children, context));
|
8749 | }
|
8750 | cb(visit);
|
8751 | return Array.prototype.concat.apply([], results);
|
8752 | }
|
8753 | }
|
8754 |
|
8755 | /**
|
8756 | * @license
|
8757 | * Copyright Google LLC All Rights Reserved.
|
8758 | *
|
8759 | * Use of this source code is governed by an MIT-style license that can be
|
8760 | * found in the LICENSE file at https://angular.io/license
|
8761 | */
|
8762 | var TokenType;
|
8763 | (function (TokenType) {
|
8764 | TokenType[TokenType["TAG_OPEN_START"] = 0] = "TAG_OPEN_START";
|
8765 | TokenType[TokenType["TAG_OPEN_END"] = 1] = "TAG_OPEN_END";
|
8766 | TokenType[TokenType["TAG_OPEN_END_VOID"] = 2] = "TAG_OPEN_END_VOID";
|
8767 | TokenType[TokenType["TAG_CLOSE"] = 3] = "TAG_CLOSE";
|
8768 | TokenType[TokenType["INCOMPLETE_TAG_OPEN"] = 4] = "INCOMPLETE_TAG_OPEN";
|
8769 | TokenType[TokenType["TEXT"] = 5] = "TEXT";
|
8770 | TokenType[TokenType["ESCAPABLE_RAW_TEXT"] = 6] = "ESCAPABLE_RAW_TEXT";
|
8771 | TokenType[TokenType["RAW_TEXT"] = 7] = "RAW_TEXT";
|
8772 | TokenType[TokenType["COMMENT_START"] = 8] = "COMMENT_START";
|
8773 | TokenType[TokenType["COMMENT_END"] = 9] = "COMMENT_END";
|
8774 | TokenType[TokenType["CDATA_START"] = 10] = "CDATA_START";
|
8775 | TokenType[TokenType["CDATA_END"] = 11] = "CDATA_END";
|
8776 | TokenType[TokenType["ATTR_NAME"] = 12] = "ATTR_NAME";
|
8777 | TokenType[TokenType["ATTR_QUOTE"] = 13] = "ATTR_QUOTE";
|
8778 | TokenType[TokenType["ATTR_VALUE"] = 14] = "ATTR_VALUE";
|
8779 | TokenType[TokenType["DOC_TYPE"] = 15] = "DOC_TYPE";
|
8780 | TokenType[TokenType["EXPANSION_FORM_START"] = 16] = "EXPANSION_FORM_START";
|
8781 | TokenType[TokenType["EXPANSION_CASE_VALUE"] = 17] = "EXPANSION_CASE_VALUE";
|
8782 | TokenType[TokenType["EXPANSION_CASE_EXP_START"] = 18] = "EXPANSION_CASE_EXP_START";
|
8783 | TokenType[TokenType["EXPANSION_CASE_EXP_END"] = 19] = "EXPANSION_CASE_EXP_END";
|
8784 | TokenType[TokenType["EXPANSION_FORM_END"] = 20] = "EXPANSION_FORM_END";
|
8785 | TokenType[TokenType["EOF"] = 21] = "EOF";
|
8786 | })(TokenType || (TokenType = {}));
|
8787 | class Token {
|
8788 | constructor(type, parts, sourceSpan) {
|
8789 | this.type = type;
|
8790 | this.parts = parts;
|
8791 | this.sourceSpan = sourceSpan;
|
8792 | }
|
8793 | }
|
8794 | class TokenError extends ParseError {
|
8795 | constructor(errorMsg, tokenType, span) {
|
8796 | super(span, errorMsg);
|
8797 | this.tokenType = tokenType;
|
8798 | }
|
8799 | }
|
8800 | class TokenizeResult {
|
8801 | constructor(tokens, errors, nonNormalizedIcuExpressions) {
|
8802 | this.tokens = tokens;
|
8803 | this.errors = errors;
|
8804 | this.nonNormalizedIcuExpressions = nonNormalizedIcuExpressions;
|
8805 | }
|
8806 | }
|
8807 | function tokenize(source, url, getTagDefinition, options = {}) {
|
8808 | const tokenizer = new _Tokenizer(new ParseSourceFile(source, url), getTagDefinition, options);
|
8809 | tokenizer.tokenize();
|
8810 | return new TokenizeResult(mergeTextTokens(tokenizer.tokens), tokenizer.errors, tokenizer.nonNormalizedIcuExpressions);
|
8811 | }
|
8812 | const _CR_OR_CRLF_REGEXP = /\r\n?/g;
|
8813 | function _unexpectedCharacterErrorMsg(charCode) {
|
8814 | const char = charCode === $EOF ? 'EOF' : String.fromCharCode(charCode);
|
8815 | return `Unexpected character "${char}"`;
|
8816 | }
|
8817 | function _unknownEntityErrorMsg(entitySrc) {
|
8818 | return `Unknown entity "${entitySrc}" - use the "&#<decimal>;" or "&#x<hex>;" syntax`;
|
8819 | }
|
8820 | function _unparsableEntityErrorMsg(type, entityStr) {
|
8821 | return `Unable to parse entity "${entityStr}" - ${type} character reference entities must end with ";"`;
|
8822 | }
|
8823 | var CharacterReferenceType;
|
8824 | (function (CharacterReferenceType) {
|
8825 | CharacterReferenceType["HEX"] = "hexadecimal";
|
8826 | CharacterReferenceType["DEC"] = "decimal";
|
8827 | })(CharacterReferenceType || (CharacterReferenceType = {}));
|
8828 | class _ControlFlowError {
|
8829 | constructor(error) {
|
8830 | this.error = error;
|
8831 | }
|
8832 | }
|
8833 | // See https://www.w3.org/TR/html51/syntax.html#writing-html-documents
|
8834 | class _Tokenizer {
|
8835 | /**
|
8836 | * @param _file The html source file being tokenized.
|
8837 | * @param _getTagDefinition A function that will retrieve a tag definition for a given tag name.
|
8838 | * @param options Configuration of the tokenization.
|
8839 | */
|
8840 | constructor(_file, _getTagDefinition, options) {
|
8841 | this._getTagDefinition = _getTagDefinition;
|
8842 | this._currentTokenStart = null;
|
8843 | this._currentTokenType = null;
|
8844 | this._expansionCaseStack = [];
|
8845 | this._inInterpolation = false;
|
8846 | this.tokens = [];
|
8847 | this.errors = [];
|
8848 | this.nonNormalizedIcuExpressions = [];
|
8849 | this._tokenizeIcu = options.tokenizeExpansionForms || false;
|
8850 | this._interpolationConfig = options.interpolationConfig || DEFAULT_INTERPOLATION_CONFIG;
|
8851 | this._leadingTriviaCodePoints =
|
8852 | options.leadingTriviaChars && options.leadingTriviaChars.map(c => c.codePointAt(0) || 0);
|
8853 | const range = options.range || { endPos: _file.content.length, startPos: 0, startLine: 0, startCol: 0 };
|
8854 | this._cursor = options.escapedString ? new EscapedCharacterCursor(_file, range) :
|
8855 | new PlainCharacterCursor(_file, range);
|
8856 | this._preserveLineEndings = options.preserveLineEndings || false;
|
8857 | this._escapedString = options.escapedString || false;
|
8858 | this._i18nNormalizeLineEndingsInICUs = options.i18nNormalizeLineEndingsInICUs || false;
|
8859 | try {
|
8860 | this._cursor.init();
|
8861 | }
|
8862 | catch (e) {
|
8863 | this.handleError(e);
|
8864 | }
|
8865 | }
|
8866 | _processCarriageReturns(content) {
|
8867 | if (this._preserveLineEndings) {
|
8868 | return content;
|
8869 | }
|
8870 | // https://www.w3.org/TR/html51/syntax.html#preprocessing-the-input-stream
|
8871 | // In order to keep the original position in the source, we can not
|
8872 | // pre-process it.
|
8873 | // Instead CRs are processed right before instantiating the tokens.
|
8874 | return content.replace(_CR_OR_CRLF_REGEXP, '\n');
|
8875 | }
|
8876 | tokenize() {
|
8877 | while (this._cursor.peek() !== $EOF) {
|
8878 | const start = this._cursor.clone();
|
8879 | try {
|
8880 | if (this._attemptCharCode($LT)) {
|
8881 | if (this._attemptCharCode($BANG)) {
|
8882 | if (this._attemptCharCode($LBRACKET)) {
|
8883 | this._consumeCdata(start);
|
8884 | }
|
8885 | else if (this._attemptCharCode($MINUS)) {
|
8886 | this._consumeComment(start);
|
8887 | }
|
8888 | else {
|
8889 | this._consumeDocType(start);
|
8890 | }
|
8891 | }
|
8892 | else if (this._attemptCharCode($SLASH)) {
|
8893 | this._consumeTagClose(start);
|
8894 | }
|
8895 | else {
|
8896 | this._consumeTagOpen(start);
|
8897 | }
|
8898 | }
|
8899 | else if (!(this._tokenizeIcu && this._tokenizeExpansionForm())) {
|
8900 | this._consumeText();
|
8901 | }
|
8902 | }
|
8903 | catch (e) {
|
8904 | this.handleError(e);
|
8905 | }
|
8906 | }
|
8907 | this._beginToken(TokenType.EOF);
|
8908 | this._endToken([]);
|
8909 | }
|
8910 | /**
|
8911 | * @returns whether an ICU token has been created
|
8912 | * @internal
|
8913 | */
|
8914 | _tokenizeExpansionForm() {
|
8915 | if (this.isExpansionFormStart()) {
|
8916 | this._consumeExpansionFormStart();
|
8917 | return true;
|
8918 | }
|
8919 | if (isExpansionCaseStart(this._cursor.peek()) && this._isInExpansionForm()) {
|
8920 | this._consumeExpansionCaseStart();
|
8921 | return true;
|
8922 | }
|
8923 | if (this._cursor.peek() === $RBRACE) {
|
8924 | if (this._isInExpansionCase()) {
|
8925 | this._consumeExpansionCaseEnd();
|
8926 | return true;
|
8927 | }
|
8928 | if (this._isInExpansionForm()) {
|
8929 | this._consumeExpansionFormEnd();
|
8930 | return true;
|
8931 | }
|
8932 | }
|
8933 | return false;
|
8934 | }
|
8935 | _beginToken(type, start = this._cursor.clone()) {
|
8936 | this._currentTokenStart = start;
|
8937 | this._currentTokenType = type;
|
8938 | }
|
8939 | _endToken(parts, end) {
|
8940 | if (this._currentTokenStart === null) {
|
8941 | throw new TokenError('Programming error - attempted to end a token when there was no start to the token', this._currentTokenType, this._cursor.getSpan(end));
|
8942 | }
|
8943 | if (this._currentTokenType === null) {
|
8944 | throw new TokenError('Programming error - attempted to end a token which has no token type', null, this._cursor.getSpan(this._currentTokenStart));
|
8945 | }
|
8946 | const token = new Token(this._currentTokenType, parts, this._cursor.getSpan(this._currentTokenStart, this._leadingTriviaCodePoints));
|
8947 | this.tokens.push(token);
|
8948 | this._currentTokenStart = null;
|
8949 | this._currentTokenType = null;
|
8950 | return token;
|
8951 | }
|
8952 | _createError(msg, span) {
|
8953 | if (this._isInExpansionForm()) {
|
8954 | msg += ` (Do you have an unescaped "{" in your template? Use "{{ '{' }}") to escape it.)`;
|
8955 | }
|
8956 | const error = new TokenError(msg, this._currentTokenType, span);
|
8957 | this._currentTokenStart = null;
|
8958 | this._currentTokenType = null;
|
8959 | return new _ControlFlowError(error);
|
8960 | }
|
8961 | handleError(e) {
|
8962 | if (e instanceof CursorError) {
|
8963 | e = this._createError(e.msg, this._cursor.getSpan(e.cursor));
|
8964 | }
|
8965 | if (e instanceof _ControlFlowError) {
|
8966 | this.errors.push(e.error);
|
8967 | }
|
8968 | else {
|
8969 | throw e;
|
8970 | }
|
8971 | }
|
8972 | _attemptCharCode(charCode) {
|
8973 | if (this._cursor.peek() === charCode) {
|
8974 | this._cursor.advance();
|
8975 | return true;
|
8976 | }
|
8977 | return false;
|
8978 | }
|
8979 | _attemptCharCodeCaseInsensitive(charCode) {
|
8980 | if (compareCharCodeCaseInsensitive(this._cursor.peek(), charCode)) {
|
8981 | this._cursor.advance();
|
8982 | return true;
|
8983 | }
|
8984 | return false;
|
8985 | }
|
8986 | _requireCharCode(charCode) {
|
8987 | const location = this._cursor.clone();
|
8988 | if (!this._attemptCharCode(charCode)) {
|
8989 | throw this._createError(_unexpectedCharacterErrorMsg(this._cursor.peek()), this._cursor.getSpan(location));
|
8990 | }
|
8991 | }
|
8992 | _attemptStr(chars) {
|
8993 | const len = chars.length;
|
8994 | if (this._cursor.charsLeft() < len) {
|
8995 | return false;
|
8996 | }
|
8997 | const initialPosition = this._cursor.clone();
|
8998 | for (let i = 0; i < len; i++) {
|
8999 | if (!this._attemptCharCode(chars.charCodeAt(i))) {
|
9000 | // If attempting to parse the string fails, we want to reset the parser
|
9001 | // to where it was before the attempt
|
9002 | this._cursor = initialPosition;
|
9003 | return false;
|
9004 | }
|
9005 | }
|
9006 | return true;
|
9007 | }
|
9008 | _attemptStrCaseInsensitive(chars) {
|
9009 | for (let i = 0; i < chars.length; i++) {
|
9010 | if (!this._attemptCharCodeCaseInsensitive(chars.charCodeAt(i))) {
|
9011 | return false;
|
9012 | }
|
9013 | }
|
9014 | return true;
|
9015 | }
|
9016 | _requireStr(chars) {
|
9017 | const location = this._cursor.clone();
|
9018 | if (!this._attemptStr(chars)) {
|
9019 | throw this._createError(_unexpectedCharacterErrorMsg(this._cursor.peek()), this._cursor.getSpan(location));
|
9020 | }
|
9021 | }
|
9022 | _attemptCharCodeUntilFn(predicate) {
|
9023 | while (!predicate(this._cursor.peek())) {
|
9024 | this._cursor.advance();
|
9025 | }
|
9026 | }
|
9027 | _requireCharCodeUntilFn(predicate, len) {
|
9028 | const start = this._cursor.clone();
|
9029 | this._attemptCharCodeUntilFn(predicate);
|
9030 | if (this._cursor.diff(start) < len) {
|
9031 | throw this._createError(_unexpectedCharacterErrorMsg(this._cursor.peek()), this._cursor.getSpan(start));
|
9032 | }
|
9033 | }
|
9034 | _attemptUntilChar(char) {
|
9035 | while (this._cursor.peek() !== char) {
|
9036 | this._cursor.advance();
|
9037 | }
|
9038 | }
|
9039 | _readChar(decodeEntities) {
|
9040 | if (decodeEntities && this._cursor.peek() === $AMPERSAND) {
|
9041 | return this._decodeEntity();
|
9042 | }
|
9043 | else {
|
9044 | // Don't rely upon reading directly from `_input` as the actual char value
|
9045 | // may have been generated from an escape sequence.
|
9046 | const char = String.fromCodePoint(this._cursor.peek());
|
9047 | this._cursor.advance();
|
9048 | return char;
|
9049 | }
|
9050 | }
|
9051 | _decodeEntity() {
|
9052 | const start = this._cursor.clone();
|
9053 | this._cursor.advance();
|
9054 | if (this._attemptCharCode($HASH)) {
|
9055 | const isHex = this._attemptCharCode($x) || this._attemptCharCode($X);
|
9056 | const codeStart = this._cursor.clone();
|
9057 | this._attemptCharCodeUntilFn(isDigitEntityEnd);
|
9058 | if (this._cursor.peek() != $SEMICOLON) {
|
9059 | // Advance cursor to include the peeked character in the string provided to the error
|
9060 | // message.
|
9061 | this._cursor.advance();
|
9062 | const entityType = isHex ? CharacterReferenceType.HEX : CharacterReferenceType.DEC;
|
9063 | throw this._createError(_unparsableEntityErrorMsg(entityType, this._cursor.getChars(start)), this._cursor.getSpan());
|
9064 | }
|
9065 | const strNum = this._cursor.getChars(codeStart);
|
9066 | this._cursor.advance();
|
9067 | try {
|
9068 | const charCode = parseInt(strNum, isHex ? 16 : 10);
|
9069 | return String.fromCharCode(charCode);
|
9070 | }
|
9071 | catch (_a) {
|
9072 | throw this._createError(_unknownEntityErrorMsg(this._cursor.getChars(start)), this._cursor.getSpan());
|
9073 | }
|
9074 | }
|
9075 | else {
|
9076 | const nameStart = this._cursor.clone();
|
9077 | this._attemptCharCodeUntilFn(isNamedEntityEnd);
|
9078 | if (this._cursor.peek() != $SEMICOLON) {
|
9079 | this._cursor = nameStart;
|
9080 | return '&';
|
9081 | }
|
9082 | const name = this._cursor.getChars(nameStart);
|
9083 | this._cursor.advance();
|
9084 | const char = NAMED_ENTITIES[name];
|
9085 | if (!char) {
|
9086 | throw this._createError(_unknownEntityErrorMsg(name), this._cursor.getSpan(start));
|
9087 | }
|
9088 | return char;
|
9089 | }
|
9090 | }
|
9091 | _consumeRawText(decodeEntities, endMarkerPredicate) {
|
9092 | this._beginToken(decodeEntities ? TokenType.ESCAPABLE_RAW_TEXT : TokenType.RAW_TEXT);
|
9093 | const parts = [];
|
9094 | while (true) {
|
9095 | const tagCloseStart = this._cursor.clone();
|
9096 | const foundEndMarker = endMarkerPredicate();
|
9097 | this._cursor = tagCloseStart;
|
9098 | if (foundEndMarker) {
|
9099 | break;
|
9100 | }
|
9101 | parts.push(this._readChar(decodeEntities));
|
9102 | }
|
9103 | return this._endToken([this._processCarriageReturns(parts.join(''))]);
|
9104 | }
|
9105 | _consumeComment(start) {
|
9106 | this._beginToken(TokenType.COMMENT_START, start);
|
9107 | this._requireCharCode($MINUS);
|
9108 | this._endToken([]);
|
9109 | this._consumeRawText(false, () => this._attemptStr('-->'));
|
9110 | this._beginToken(TokenType.COMMENT_END);
|
9111 | this._requireStr('-->');
|
9112 | this._endToken([]);
|
9113 | }
|
9114 | _consumeCdata(start) {
|
9115 | this._beginToken(TokenType.CDATA_START, start);
|
9116 | this._requireStr('CDATA[');
|
9117 | this._endToken([]);
|
9118 | this._consumeRawText(false, () => this._attemptStr(']]>'));
|
9119 | this._beginToken(TokenType.CDATA_END);
|
9120 | this._requireStr(']]>');
|
9121 | this._endToken([]);
|
9122 | }
|
9123 | _consumeDocType(start) {
|
9124 | this._beginToken(TokenType.DOC_TYPE, start);
|
9125 | const contentStart = this._cursor.clone();
|
9126 | this._attemptUntilChar($GT);
|
9127 | const content = this._cursor.getChars(contentStart);
|
9128 | this._cursor.advance();
|
9129 | this._endToken([content]);
|
9130 | }
|
9131 | _consumePrefixAndName() {
|
9132 | const nameOrPrefixStart = this._cursor.clone();
|
9133 | let prefix = '';
|
9134 | while (this._cursor.peek() !== $COLON && !isPrefixEnd(this._cursor.peek())) {
|
9135 | this._cursor.advance();
|
9136 | }
|
9137 | let nameStart;
|
9138 | if (this._cursor.peek() === $COLON) {
|
9139 | prefix = this._cursor.getChars(nameOrPrefixStart);
|
9140 | this._cursor.advance();
|
9141 | nameStart = this._cursor.clone();
|
9142 | }
|
9143 | else {
|
9144 | nameStart = nameOrPrefixStart;
|
9145 | }
|
9146 | this._requireCharCodeUntilFn(isNameEnd, prefix === '' ? 0 : 1);
|
9147 | const name = this._cursor.getChars(nameStart);
|
9148 | return [prefix, name];
|
9149 | }
|
9150 | _consumeTagOpen(start) {
|
9151 | let tagName;
|
9152 | let prefix;
|
9153 | let openTagToken;
|
9154 | try {
|
9155 | if (!isAsciiLetter(this._cursor.peek())) {
|
9156 | throw this._createError(_unexpectedCharacterErrorMsg(this._cursor.peek()), this._cursor.getSpan(start));
|
9157 | }
|
9158 | openTagToken = this._consumeTagOpenStart(start);
|
9159 | prefix = openTagToken.parts[0];
|
9160 | tagName = openTagToken.parts[1];
|
9161 | this._attemptCharCodeUntilFn(isNotWhitespace);
|
9162 | while (this._cursor.peek() !== $SLASH && this._cursor.peek() !== $GT &&
|
9163 | this._cursor.peek() !== $LT) {
|
9164 | this._consumeAttributeName();
|
9165 | this._attemptCharCodeUntilFn(isNotWhitespace);
|
9166 | if (this._attemptCharCode($EQ)) {
|
9167 | this._attemptCharCodeUntilFn(isNotWhitespace);
|
9168 | this._consumeAttributeValue();
|
9169 | }
|
9170 | this._attemptCharCodeUntilFn(isNotWhitespace);
|
9171 | }
|
9172 | this._consumeTagOpenEnd();
|
9173 | }
|
9174 | catch (e) {
|
9175 | if (e instanceof _ControlFlowError) {
|
9176 | if (openTagToken) {
|
9177 | // We errored before we could close the opening tag, so it is incomplete.
|
9178 | openTagToken.type = TokenType.INCOMPLETE_TAG_OPEN;
|
9179 | }
|
9180 | else {
|
9181 | // When the start tag is invalid, assume we want a "<" as text.
|
9182 | // Back to back text tokens are merged at the end.
|
9183 | this._beginToken(TokenType.TEXT, start);
|
9184 | this._endToken(['<']);
|
9185 | }
|
9186 | return;
|
9187 | }
|
9188 | throw e;
|
9189 | }
|
9190 | const contentTokenType = this._getTagDefinition(tagName).getContentType(prefix);
|
9191 | if (contentTokenType === TagContentType.RAW_TEXT) {
|
9192 | this._consumeRawTextWithTagClose(prefix, tagName, false);
|
9193 | }
|
9194 | else if (contentTokenType === TagContentType.ESCAPABLE_RAW_TEXT) {
|
9195 | this._consumeRawTextWithTagClose(prefix, tagName, true);
|
9196 | }
|
9197 | }
|
9198 | _consumeRawTextWithTagClose(prefix, tagName, decodeEntities) {
|
9199 | this._consumeRawText(decodeEntities, () => {
|
9200 | if (!this._attemptCharCode($LT))
|
9201 | return false;
|
9202 | if (!this._attemptCharCode($SLASH))
|
9203 | return false;
|
9204 | this._attemptCharCodeUntilFn(isNotWhitespace);
|
9205 | if (!this._attemptStrCaseInsensitive(tagName))
|
9206 | return false;
|
9207 | this._attemptCharCodeUntilFn(isNotWhitespace);
|
9208 | return this._attemptCharCode($GT);
|
9209 | });
|
9210 | this._beginToken(TokenType.TAG_CLOSE);
|
9211 | this._requireCharCodeUntilFn(code => code === $GT, 3);
|
9212 | this._cursor.advance(); // Consume the `>`
|
9213 | this._endToken([prefix, tagName]);
|
9214 | }
|
9215 | _consumeTagOpenStart(start) {
|
9216 | this._beginToken(TokenType.TAG_OPEN_START, start);
|
9217 | const parts = this._consumePrefixAndName();
|
9218 | return this._endToken(parts);
|
9219 | }
|
9220 | _consumeAttributeName() {
|
9221 | const attrNameStart = this._cursor.peek();
|
9222 | if (attrNameStart === $SQ || attrNameStart === $DQ) {
|
9223 | throw this._createError(_unexpectedCharacterErrorMsg(attrNameStart), this._cursor.getSpan());
|
9224 | }
|
9225 | this._beginToken(TokenType.ATTR_NAME);
|
9226 | const prefixAndName = this._consumePrefixAndName();
|
9227 | this._endToken(prefixAndName);
|
9228 | }
|
9229 | _consumeAttributeValue() {
|
9230 | let value;
|
9231 | if (this._cursor.peek() === $SQ || this._cursor.peek() === $DQ) {
|
9232 | this._beginToken(TokenType.ATTR_QUOTE);
|
9233 | const quoteChar = this._cursor.peek();
|
9234 | this._cursor.advance();
|
9235 | this._endToken([String.fromCodePoint(quoteChar)]);
|
9236 | this._beginToken(TokenType.ATTR_VALUE);
|
9237 | const parts = [];
|
9238 | while (this._cursor.peek() !== quoteChar) {
|
9239 | parts.push(this._readChar(true));
|
9240 | }
|
9241 | value = parts.join('');
|
9242 | this._endToken([this._processCarriageReturns(value)]);
|
9243 | this._beginToken(TokenType.ATTR_QUOTE);
|
9244 | this._cursor.advance();
|
9245 | this._endToken([String.fromCodePoint(quoteChar)]);
|
9246 | }
|
9247 | else {
|
9248 | this._beginToken(TokenType.ATTR_VALUE);
|
9249 | const valueStart = this._cursor.clone();
|
9250 | this._requireCharCodeUntilFn(isNameEnd, 1);
|
9251 | value = this._cursor.getChars(valueStart);
|
9252 | this._endToken([this._processCarriageReturns(value)]);
|
9253 | }
|
9254 | }
|
9255 | _consumeTagOpenEnd() {
|
9256 | const tokenType = this._attemptCharCode($SLASH) ? TokenType.TAG_OPEN_END_VOID : TokenType.TAG_OPEN_END;
|
9257 | this._beginToken(tokenType);
|
9258 | this._requireCharCode($GT);
|
9259 | this._endToken([]);
|
9260 | }
|
9261 | _consumeTagClose(start) {
|
9262 | this._beginToken(TokenType.TAG_CLOSE, start);
|
9263 | this._attemptCharCodeUntilFn(isNotWhitespace);
|
9264 | const prefixAndName = this._consumePrefixAndName();
|
9265 | this._attemptCharCodeUntilFn(isNotWhitespace);
|
9266 | this._requireCharCode($GT);
|
9267 | this._endToken(prefixAndName);
|
9268 | }
|
9269 | _consumeExpansionFormStart() {
|
9270 | this._beginToken(TokenType.EXPANSION_FORM_START);
|
9271 | this._requireCharCode($LBRACE);
|
9272 | this._endToken([]);
|
9273 | this._expansionCaseStack.push(TokenType.EXPANSION_FORM_START);
|
9274 | this._beginToken(TokenType.RAW_TEXT);
|
9275 | const condition = this._readUntil($COMMA);
|
9276 | const normalizedCondition = this._processCarriageReturns(condition);
|
9277 | if (this._i18nNormalizeLineEndingsInICUs) {
|
9278 | // We explicitly want to normalize line endings for this text.
|
9279 | this._endToken([normalizedCondition]);
|
9280 | }
|
9281 | else {
|
9282 | // We are not normalizing line endings.
|
9283 | const conditionToken = this._endToken([condition]);
|
9284 | if (normalizedCondition !== condition) {
|
9285 | this.nonNormalizedIcuExpressions.push(conditionToken);
|
9286 | }
|
9287 | }
|
9288 | this._requireCharCode($COMMA);
|
9289 | this._attemptCharCodeUntilFn(isNotWhitespace);
|
9290 | this._beginToken(TokenType.RAW_TEXT);
|
9291 | const type = this._readUntil($COMMA);
|
9292 | this._endToken([type]);
|
9293 | this._requireCharCode($COMMA);
|
9294 | this._attemptCharCodeUntilFn(isNotWhitespace);
|
9295 | }
|
9296 | _consumeExpansionCaseStart() {
|
9297 | this._beginToken(TokenType.EXPANSION_CASE_VALUE);
|
9298 | const value = this._readUntil($LBRACE).trim();
|
9299 | this._endToken([value]);
|
9300 | this._attemptCharCodeUntilFn(isNotWhitespace);
|
9301 | this._beginToken(TokenType.EXPANSION_CASE_EXP_START);
|
9302 | this._requireCharCode($LBRACE);
|
9303 | this._endToken([]);
|
9304 | this._attemptCharCodeUntilFn(isNotWhitespace);
|
9305 | this._expansionCaseStack.push(TokenType.EXPANSION_CASE_EXP_START);
|
9306 | }
|
9307 | _consumeExpansionCaseEnd() {
|
9308 | this._beginToken(TokenType.EXPANSION_CASE_EXP_END);
|
9309 | this._requireCharCode($RBRACE);
|
9310 | this._endToken([]);
|
9311 | this._attemptCharCodeUntilFn(isNotWhitespace);
|
9312 | this._expansionCaseStack.pop();
|
9313 | }
|
9314 | _consumeExpansionFormEnd() {
|
9315 | this._beginToken(TokenType.EXPANSION_FORM_END);
|
9316 | this._requireCharCode($RBRACE);
|
9317 | this._endToken([]);
|
9318 | this._expansionCaseStack.pop();
|
9319 | }
|
9320 | _consumeText() {
|
9321 | const start = this._cursor.clone();
|
9322 | this._beginToken(TokenType.TEXT, start);
|
9323 | const parts = [];
|
9324 | do {
|
9325 | if (this._interpolationConfig && this._attemptStr(this._interpolationConfig.start)) {
|
9326 | parts.push(this._interpolationConfig.start);
|
9327 | this._inInterpolation = true;
|
9328 | }
|
9329 | else if (this._interpolationConfig && this._inInterpolation &&
|
9330 | this._attemptStr(this._interpolationConfig.end)) {
|
9331 | parts.push(this._interpolationConfig.end);
|
9332 | this._inInterpolation = false;
|
9333 | }
|
9334 | else {
|
9335 | parts.push(this._readChar(true));
|
9336 | }
|
9337 | } while (!this._isTextEnd());
|
9338 | this._endToken([this._processCarriageReturns(parts.join(''))]);
|
9339 | }
|
9340 | _isTextEnd() {
|
9341 | if (this._cursor.peek() === $LT || this._cursor.peek() === $EOF) {
|
9342 | return true;
|
9343 | }
|
9344 | if (this._tokenizeIcu && !this._inInterpolation) {
|
9345 | if (this.isExpansionFormStart()) {
|
9346 | // start of an expansion form
|
9347 | return true;
|
9348 | }
|
9349 | if (this._cursor.peek() === $RBRACE && this._isInExpansionCase()) {
|
9350 | // end of and expansion case
|
9351 | return true;
|
9352 | }
|
9353 | }
|
9354 | return false;
|
9355 | }
|
9356 | _readUntil(char) {
|
9357 | const start = this._cursor.clone();
|
9358 | this._attemptUntilChar(char);
|
9359 | return this._cursor.getChars(start);
|
9360 | }
|
9361 | _isInExpansionCase() {
|
9362 | return this._expansionCaseStack.length > 0 &&
|
9363 | this._expansionCaseStack[this._expansionCaseStack.length - 1] ===
|
9364 | TokenType.EXPANSION_CASE_EXP_START;
|
9365 | }
|
9366 | _isInExpansionForm() {
|
9367 | return this._expansionCaseStack.length > 0 &&
|
9368 | this._expansionCaseStack[this._expansionCaseStack.length - 1] ===
|
9369 | TokenType.EXPANSION_FORM_START;
|
9370 | }
|
9371 | isExpansionFormStart() {
|
9372 | if (this._cursor.peek() !== $LBRACE) {
|
9373 | return false;
|
9374 | }
|
9375 | if (this._interpolationConfig) {
|
9376 | const start = this._cursor.clone();
|
9377 | const isInterpolation = this._attemptStr(this._interpolationConfig.start);
|
9378 | this._cursor = start;
|
9379 | return !isInterpolation;
|
9380 | }
|
9381 | return true;
|
9382 | }
|
9383 | }
|
9384 | function isNotWhitespace(code) {
|
9385 | return !isWhitespace(code) || code === $EOF;
|
9386 | }
|
9387 | function isNameEnd(code) {
|
9388 | return isWhitespace(code) || code === $GT || code === $LT ||
|
9389 | code === $SLASH || code === $SQ || code === $DQ || code === $EQ;
|
9390 | }
|
9391 | function isPrefixEnd(code) {
|
9392 | return (code < $a || $z < code) && (code < $A || $Z < code) &&
|
9393 | (code < $0 || code > $9);
|
9394 | }
|
9395 | function isDigitEntityEnd(code) {
|
9396 | return code == $SEMICOLON || code == $EOF || !isAsciiHexDigit(code);
|
9397 | }
|
9398 | function isNamedEntityEnd(code) {
|
9399 | return code == $SEMICOLON || code == $EOF || !isAsciiLetter(code);
|
9400 | }
|
9401 | function isExpansionCaseStart(peek) {
|
9402 | return peek !== $RBRACE;
|
9403 | }
|
9404 | function compareCharCodeCaseInsensitive(code1, code2) {
|
9405 | return toUpperCaseCharCode(code1) == toUpperCaseCharCode(code2);
|
9406 | }
|
9407 | function toUpperCaseCharCode(code) {
|
9408 | return code >= $a && code <= $z ? code - $a + $A : code;
|
9409 | }
|
9410 | function mergeTextTokens(srcTokens) {
|
9411 | const dstTokens = [];
|
9412 | let lastDstToken = undefined;
|
9413 | for (let i = 0; i < srcTokens.length; i++) {
|
9414 | const token = srcTokens[i];
|
9415 | if (lastDstToken && lastDstToken.type == TokenType.TEXT && token.type == TokenType.TEXT) {
|
9416 | lastDstToken.parts[0] += token.parts[0];
|
9417 | lastDstToken.sourceSpan.end = token.sourceSpan.end;
|
9418 | }
|
9419 | else {
|
9420 | lastDstToken = token;
|
9421 | dstTokens.push(lastDstToken);
|
9422 | }
|
9423 | }
|
9424 | return dstTokens;
|
9425 | }
|
9426 | class PlainCharacterCursor {
|
9427 | constructor(fileOrCursor, range) {
|
9428 | if (fileOrCursor instanceof PlainCharacterCursor) {
|
9429 | this.file = fileOrCursor.file;
|
9430 | this.input = fileOrCursor.input;
|
9431 | this.end = fileOrCursor.end;
|
9432 | const state = fileOrCursor.state;
|
9433 | // Note: avoid using `{...fileOrCursor.state}` here as that has a severe performance penalty.
|
9434 | // In ES5 bundles the object spread operator is translated into the `__assign` helper, which
|
9435 | // is not optimized by VMs as efficiently as a raw object literal. Since this constructor is
|
9436 | // called in tight loops, this difference matters.
|
9437 | this.state = {
|
9438 | peek: state.peek,
|
9439 | offset: state.offset,
|
9440 | line: state.line,
|
9441 | column: state.column,
|
9442 | };
|
9443 | }
|
9444 | else {
|
9445 | if (!range) {
|
9446 | throw new Error('Programming error: the range argument must be provided with a file argument.');
|
9447 | }
|
9448 | this.file = fileOrCursor;
|
9449 | this.input = fileOrCursor.content;
|
9450 | this.end = range.endPos;
|
9451 | this.state = {
|
9452 | peek: -1,
|
9453 | offset: range.startPos,
|
9454 | line: range.startLine,
|
9455 | column: range.startCol,
|
9456 | };
|
9457 | }
|
9458 | }
|
9459 | clone() {
|
9460 | return new PlainCharacterCursor(this);
|
9461 | }
|
9462 | peek() {
|
9463 | return this.state.peek;
|
9464 | }
|
9465 | charsLeft() {
|
9466 | return this.end - this.state.offset;
|
9467 | }
|
9468 | diff(other) {
|
9469 | return this.state.offset - other.state.offset;
|
9470 | }
|
9471 | advance() {
|
9472 | this.advanceState(this.state);
|
9473 | }
|
9474 | init() {
|
9475 | this.updatePeek(this.state);
|
9476 | }
|
9477 | getSpan(start, leadingTriviaCodePoints) {
|
9478 | start = start || this;
|
9479 | let fullStart = start;
|
9480 | if (leadingTriviaCodePoints) {
|
9481 | while (this.diff(start) > 0 && leadingTriviaCodePoints.indexOf(start.peek()) !== -1) {
|
9482 | if (fullStart === start) {
|
9483 | start = start.clone();
|
9484 | }
|
9485 | start.advance();
|
9486 | }
|
9487 | }
|
9488 | const startLocation = this.locationFromCursor(start);
|
9489 | const endLocation = this.locationFromCursor(this);
|
9490 | const fullStartLocation = fullStart !== start ? this.locationFromCursor(fullStart) : startLocation;
|
9491 | return new ParseSourceSpan(startLocation, endLocation, fullStartLocation);
|
9492 | }
|
9493 | getChars(start) {
|
9494 | return this.input.substring(start.state.offset, this.state.offset);
|
9495 | }
|
9496 | charAt(pos) {
|
9497 | return this.input.charCodeAt(pos);
|
9498 | }
|
9499 | advanceState(state) {
|
9500 | if (state.offset >= this.end) {
|
9501 | this.state = state;
|
9502 | throw new CursorError('Unexpected character "EOF"', this);
|
9503 | }
|
9504 | const currentChar = this.charAt(state.offset);
|
9505 | if (currentChar === $LF) {
|
9506 | state.line++;
|
9507 | state.column = 0;
|
9508 | }
|
9509 | else if (!isNewLine(currentChar)) {
|
9510 | state.column++;
|
9511 | }
|
9512 | state.offset++;
|
9513 | this.updatePeek(state);
|
9514 | }
|
9515 | updatePeek(state) {
|
9516 | state.peek = state.offset >= this.end ? $EOF : this.charAt(state.offset);
|
9517 | }
|
9518 | locationFromCursor(cursor) {
|
9519 | return new ParseLocation(cursor.file, cursor.state.offset, cursor.state.line, cursor.state.column);
|
9520 | }
|
9521 | }
|
9522 | class EscapedCharacterCursor extends PlainCharacterCursor {
|
9523 | constructor(fileOrCursor, range) {
|
9524 | if (fileOrCursor instanceof EscapedCharacterCursor) {
|
9525 | super(fileOrCursor);
|
9526 | this.internalState = Object.assign({}, fileOrCursor.internalState);
|
9527 | }
|
9528 | else {
|
9529 | super(fileOrCursor, range);
|
9530 | this.internalState = this.state;
|
9531 | }
|
9532 | }
|
9533 | advance() {
|
9534 | this.state = this.internalState;
|
9535 | super.advance();
|
9536 | this.processEscapeSequence();
|
9537 | }
|
9538 | init() {
|
9539 | super.init();
|
9540 | this.processEscapeSequence();
|
9541 | }
|
9542 | clone() {
|
9543 | return new EscapedCharacterCursor(this);
|
9544 | }
|
9545 | getChars(start) {
|
9546 | const cursor = start.clone();
|
9547 | let chars = '';
|
9548 | while (cursor.internalState.offset < this.internalState.offset) {
|
9549 | chars += String.fromCodePoint(cursor.peek());
|
9550 | cursor.advance();
|
9551 | }
|
9552 | return chars;
|
9553 | }
|
9554 | /**
|
9555 | * Process the escape sequence that starts at the current position in the text.
|
9556 | *
|
9557 | * This method is called to ensure that `peek` has the unescaped value of escape sequences.
|
9558 | */
|
9559 | processEscapeSequence() {
|
9560 | const peek = () => this.internalState.peek;
|
9561 | if (peek() === $BACKSLASH) {
|
9562 | // We have hit an escape sequence so we need the internal state to become independent
|
9563 | // of the external state.
|
9564 | this.internalState = Object.assign({}, this.state);
|
9565 | // Move past the backslash
|
9566 | this.advanceState(this.internalState);
|
9567 | // First check for standard control char sequences
|
9568 | if (peek() === $n) {
|
9569 | this.state.peek = $LF;
|
9570 | }
|
9571 | else if (peek() === $r) {
|
9572 | this.state.peek = $CR;
|
9573 | }
|
9574 | else if (peek() === $v) {
|
9575 | this.state.peek = $VTAB;
|
9576 | }
|
9577 | else if (peek() === $t) {
|
9578 | this.state.peek = $TAB;
|
9579 | }
|
9580 | else if (peek() === $b) {
|
9581 | this.state.peek = $BSPACE;
|
9582 | }
|
9583 | else if (peek() === $f) {
|
9584 | this.state.peek = $FF;
|
9585 | }
|
9586 | // Now consider more complex sequences
|
9587 | else if (peek() === $u) {
|
9588 | // Unicode code-point sequence
|
9589 | this.advanceState(this.internalState); // advance past the `u` char
|
9590 | if (peek() === $LBRACE) {
|
9591 | // Variable length Unicode, e.g. `\x{123}`
|
9592 | this.advanceState(this.internalState); // advance past the `{` char
|
9593 | // Advance past the variable number of hex digits until we hit a `}` char
|
9594 | const digitStart = this.clone();
|
9595 | let length = 0;
|
9596 | while (peek() !== $RBRACE) {
|
9597 | this.advanceState(this.internalState);
|
9598 | length++;
|
9599 | }
|
9600 | this.state.peek = this.decodeHexDigits(digitStart, length);
|
9601 | }
|
9602 | else {
|
9603 | // Fixed length Unicode, e.g. `\u1234`
|
9604 | const digitStart = this.clone();
|
9605 | this.advanceState(this.internalState);
|
9606 | this.advanceState(this.internalState);
|
9607 | this.advanceState(this.internalState);
|
9608 | this.state.peek = this.decodeHexDigits(digitStart, 4);
|
9609 | }
|
9610 | }
|
9611 | else if (peek() === $x) {
|
9612 | // Hex char code, e.g. `\x2F`
|
9613 | this.advanceState(this.internalState); // advance past the `x` char
|
9614 | const digitStart = this.clone();
|
9615 | this.advanceState(this.internalState);
|
9616 | this.state.peek = this.decodeHexDigits(digitStart, 2);
|
9617 | }
|
9618 | else if (isOctalDigit(peek())) {
|
9619 | // Octal char code, e.g. `\012`,
|
9620 | let octal = '';
|
9621 | let length = 0;
|
9622 | let previous = this.clone();
|
9623 | while (isOctalDigit(peek()) && length < 3) {
|
9624 | previous = this.clone();
|
9625 | octal += String.fromCodePoint(peek());
|
9626 | this.advanceState(this.internalState);
|
9627 | length++;
|
9628 | }
|
9629 | this.state.peek = parseInt(octal, 8);
|
9630 | // Backup one char
|
9631 | this.internalState = previous.internalState;
|
9632 | }
|
9633 | else if (isNewLine(this.internalState.peek)) {
|
9634 | // Line continuation `\` followed by a new line
|
9635 | this.advanceState(this.internalState); // advance over the newline
|
9636 | this.state = this.internalState;
|
9637 | }
|
9638 | else {
|
9639 | // If none of the `if` blocks were executed then we just have an escaped normal character.
|
9640 | // In that case we just, effectively, skip the backslash from the character.
|
9641 | this.state.peek = this.internalState.peek;
|
9642 | }
|
9643 | }
|
9644 | }
|
9645 | decodeHexDigits(start, length) {
|
9646 | const hex = this.input.substr(start.internalState.offset, length);
|
9647 | const charCode = parseInt(hex, 16);
|
9648 | if (!isNaN(charCode)) {
|
9649 | return charCode;
|
9650 | }
|
9651 | else {
|
9652 | start.state = start.internalState;
|
9653 | throw new CursorError('Invalid hexadecimal escape sequence', start);
|
9654 | }
|
9655 | }
|
9656 | }
|
9657 | class CursorError {
|
9658 | constructor(msg, cursor) {
|
9659 | this.msg = msg;
|
9660 | this.cursor = cursor;
|
9661 | }
|
9662 | }
|
9663 |
|
9664 | /**
|
9665 | * @license
|
9666 | * Copyright Google LLC All Rights Reserved.
|
9667 | *
|
9668 | * Use of this source code is governed by an MIT-style license that can be
|
9669 | * found in the LICENSE file at https://angular.io/license
|
9670 | */
|
9671 | class TreeError extends ParseError {
|
9672 | constructor(elementName, span, msg) {
|
9673 | super(span, msg);
|
9674 | this.elementName = elementName;
|
9675 | }
|
9676 | static create(elementName, span, msg) {
|
9677 | return new TreeError(elementName, span, msg);
|
9678 | }
|
9679 | }
|
9680 | class ParseTreeResult {
|
9681 | constructor(rootNodes, errors) {
|
9682 | this.rootNodes = rootNodes;
|
9683 | this.errors = errors;
|
9684 | }
|
9685 | }
|
9686 | class Parser {
|
9687 | constructor(getTagDefinition) {
|
9688 | this.getTagDefinition = getTagDefinition;
|
9689 | }
|
9690 | parse(source, url, options) {
|
9691 | const tokenizeResult = tokenize(source, url, this.getTagDefinition, options);
|
9692 | const parser = new _TreeBuilder(tokenizeResult.tokens, this.getTagDefinition);
|
9693 | parser.build();
|
9694 | return new ParseTreeResult(parser.rootNodes, tokenizeResult.errors.concat(parser.errors));
|
9695 | }
|
9696 | }
|
9697 | class _TreeBuilder {
|
9698 | constructor(tokens, getTagDefinition) {
|
9699 | this.tokens = tokens;
|
9700 | this.getTagDefinition = getTagDefinition;
|
9701 | this._index = -1;
|
9702 | this._elementStack = [];
|
9703 | this.rootNodes = [];
|
9704 | this.errors = [];
|
9705 | this._advance();
|
9706 | }
|
9707 | build() {
|
9708 | while (this._peek.type !== TokenType.EOF) {
|
9709 | if (this._peek.type === TokenType.TAG_OPEN_START ||
|
9710 | this._peek.type === TokenType.INCOMPLETE_TAG_OPEN) {
|
9711 | this._consumeStartTag(this._advance());
|
9712 | }
|
9713 | else if (this._peek.type === TokenType.TAG_CLOSE) {
|
9714 | this._consumeEndTag(this._advance());
|
9715 | }
|
9716 | else if (this._peek.type === TokenType.CDATA_START) {
|
9717 | this._closeVoidElement();
|
9718 | this._consumeCdata(this._advance());
|
9719 | }
|
9720 | else if (this._peek.type === TokenType.COMMENT_START) {
|
9721 | this._closeVoidElement();
|
9722 | this._consumeComment(this._advance());
|
9723 | }
|
9724 | else if (this._peek.type === TokenType.TEXT || this._peek.type === TokenType.RAW_TEXT ||
|
9725 | this._peek.type === TokenType.ESCAPABLE_RAW_TEXT) {
|
9726 | this._closeVoidElement();
|
9727 | this._consumeText(this._advance());
|
9728 | }
|
9729 | else if (this._peek.type === TokenType.EXPANSION_FORM_START) {
|
9730 | this._consumeExpansion(this._advance());
|
9731 | }
|
9732 | else {
|
9733 | // Skip all other tokens...
|
9734 | this._advance();
|
9735 | }
|
9736 | }
|
9737 | }
|
9738 | _advance() {
|
9739 | const prev = this._peek;
|
9740 | if (this._index < this.tokens.length - 1) {
|
9741 | // Note: there is always an EOF token at the end
|
9742 | this._index++;
|
9743 | }
|
9744 | this._peek = this.tokens[this._index];
|
9745 | return prev;
|
9746 | }
|
9747 | _advanceIf(type) {
|
9748 | if (this._peek.type === type) {
|
9749 | return this._advance();
|
9750 | }
|
9751 | return null;
|
9752 | }
|
9753 | _consumeCdata(_startToken) {
|
9754 | this._consumeText(this._advance());
|
9755 | this._advanceIf(TokenType.CDATA_END);
|
9756 | }
|
9757 | _consumeComment(token) {
|
9758 | const text = this._advanceIf(TokenType.RAW_TEXT);
|
9759 | this._advanceIf(TokenType.COMMENT_END);
|
9760 | const value = text != null ? text.parts[0].trim() : null;
|
9761 | this._addToParent(new Comment(value, token.sourceSpan));
|
9762 | }
|
9763 | _consumeExpansion(token) {
|
9764 | const switchValue = this._advance();
|
9765 | const type = this._advance();
|
9766 | const cases = [];
|
9767 | // read =
|
9768 | while (this._peek.type === TokenType.EXPANSION_CASE_VALUE) {
|
9769 | const expCase = this._parseExpansionCase();
|
9770 | if (!expCase)
|
9771 | return; // error
|
9772 | cases.push(expCase);
|
9773 | }
|
9774 | // read the final }
|
9775 | if (this._peek.type !== TokenType.EXPANSION_FORM_END) {
|
9776 | this.errors.push(TreeError.create(null, this._peek.sourceSpan, `Invalid ICU message. Missing '}'.`));
|
9777 | return;
|
9778 | }
|
9779 | const sourceSpan = new ParseSourceSpan(token.sourceSpan.start, this._peek.sourceSpan.end, token.sourceSpan.fullStart);
|
9780 | this._addToParent(new Expansion(switchValue.parts[0], type.parts[0], cases, sourceSpan, switchValue.sourceSpan));
|
9781 | this._advance();
|
9782 | }
|
9783 | _parseExpansionCase() {
|
9784 | const value = this._advance();
|
9785 | // read {
|
9786 | if (this._peek.type !== TokenType.EXPANSION_CASE_EXP_START) {
|
9787 | this.errors.push(TreeError.create(null, this._peek.sourceSpan, `Invalid ICU message. Missing '{'.`));
|
9788 | return null;
|
9789 | }
|
9790 | // read until }
|
9791 | const start = this._advance();
|
9792 | const exp = this._collectExpansionExpTokens(start);
|
9793 | if (!exp)
|
9794 | return null;
|
9795 | const end = this._advance();
|
9796 | exp.push(new Token(TokenType.EOF, [], end.sourceSpan));
|
9797 | // parse everything in between { and }
|
9798 | const expansionCaseParser = new _TreeBuilder(exp, this.getTagDefinition);
|
9799 | expansionCaseParser.build();
|
9800 | if (expansionCaseParser.errors.length > 0) {
|
9801 | this.errors = this.errors.concat(expansionCaseParser.errors);
|
9802 | return null;
|
9803 | }
|
9804 | const sourceSpan = new ParseSourceSpan(value.sourceSpan.start, end.sourceSpan.end, value.sourceSpan.fullStart);
|
9805 | const expSourceSpan = new ParseSourceSpan(start.sourceSpan.start, end.sourceSpan.end, start.sourceSpan.fullStart);
|
9806 | return new ExpansionCase(value.parts[0], expansionCaseParser.rootNodes, sourceSpan, value.sourceSpan, expSourceSpan);
|
9807 | }
|
9808 | _collectExpansionExpTokens(start) {
|
9809 | const exp = [];
|
9810 | const expansionFormStack = [TokenType.EXPANSION_CASE_EXP_START];
|
9811 | while (true) {
|
9812 | if (this._peek.type === TokenType.EXPANSION_FORM_START ||
|
9813 | this._peek.type === TokenType.EXPANSION_CASE_EXP_START) {
|
9814 | expansionFormStack.push(this._peek.type);
|
9815 | }
|
9816 | if (this._peek.type === TokenType.EXPANSION_CASE_EXP_END) {
|
9817 | if (lastOnStack(expansionFormStack, TokenType.EXPANSION_CASE_EXP_START)) {
|
9818 | expansionFormStack.pop();
|
9819 | if (expansionFormStack.length == 0)
|
9820 | return exp;
|
9821 | }
|
9822 | else {
|
9823 | this.errors.push(TreeError.create(null, start.sourceSpan, `Invalid ICU message. Missing '}'.`));
|
9824 | return null;
|
9825 | }
|
9826 | }
|
9827 | if (this._peek.type === TokenType.EXPANSION_FORM_END) {
|
9828 | if (lastOnStack(expansionFormStack, TokenType.EXPANSION_FORM_START)) {
|
9829 | expansionFormStack.pop();
|
9830 | }
|
9831 | else {
|
9832 | this.errors.push(TreeError.create(null, start.sourceSpan, `Invalid ICU message. Missing '}'.`));
|
9833 | return null;
|
9834 | }
|
9835 | }
|
9836 | if (this._peek.type === TokenType.EOF) {
|
9837 | this.errors.push(TreeError.create(null, start.sourceSpan, `Invalid ICU message. Missing '}'.`));
|
9838 | return null;
|
9839 | }
|
9840 | exp.push(this._advance());
|
9841 | }
|
9842 | }
|
9843 | _consumeText(token) {
|
9844 | let text = token.parts[0];
|
9845 | if (text.length > 0 && text[0] == '\n') {
|
9846 | const parent = this._getParentElement();
|
9847 | if (parent != null && parent.children.length == 0 &&
|
9848 | this.getTagDefinition(parent.name).ignoreFirstLf) {
|
9849 | text = text.substring(1);
|
9850 | }
|
9851 | }
|
9852 | if (text.length > 0) {
|
9853 | this._addToParent(new Text$2(text, token.sourceSpan));
|
9854 | }
|
9855 | }
|
9856 | _closeVoidElement() {
|
9857 | const el = this._getParentElement();
|
9858 | if (el && this.getTagDefinition(el.name).isVoid) {
|
9859 | this._elementStack.pop();
|
9860 | }
|
9861 | }
|
9862 | _consumeStartTag(startTagToken) {
|
9863 | const [prefix, name] = startTagToken.parts;
|
9864 | const attrs = [];
|
9865 | while (this._peek.type === TokenType.ATTR_NAME) {
|
9866 | attrs.push(this._consumeAttr(this._advance()));
|
9867 | }
|
9868 | const fullName = this._getElementFullName(prefix, name, this._getParentElement());
|
9869 | let selfClosing = false;
|
9870 | // Note: There could have been a tokenizer error
|
9871 | // so that we don't get a token for the end tag...
|
9872 | if (this._peek.type === TokenType.TAG_OPEN_END_VOID) {
|
9873 | this._advance();
|
9874 | selfClosing = true;
|
9875 | const tagDef = this.getTagDefinition(fullName);
|
9876 | if (!(tagDef.canSelfClose || getNsPrefix(fullName) !== null || tagDef.isVoid)) {
|
9877 | this.errors.push(TreeError.create(fullName, startTagToken.sourceSpan, `Only void and foreign elements can be self closed "${startTagToken.parts[1]}"`));
|
9878 | }
|
9879 | }
|
9880 | else if (this._peek.type === TokenType.TAG_OPEN_END) {
|
9881 | this._advance();
|
9882 | selfClosing = false;
|
9883 | }
|
9884 | const end = this._peek.sourceSpan.fullStart;
|
9885 | const span = new ParseSourceSpan(startTagToken.sourceSpan.start, end, startTagToken.sourceSpan.fullStart);
|
9886 | // Create a separate `startSpan` because `span` will be modified when there is an `end` span.
|
9887 | const startSpan = new ParseSourceSpan(startTagToken.sourceSpan.start, end, startTagToken.sourceSpan.fullStart);
|
9888 | const el = new Element$1(fullName, attrs, [], span, startSpan, undefined);
|
9889 | this._pushElement(el);
|
9890 | if (selfClosing) {
|
9891 | // Elements that are self-closed have their `endSourceSpan` set to the full span, as the
|
9892 | // element start tag also represents the end tag.
|
9893 | this._popElement(fullName, span);
|
9894 | }
|
9895 | else if (startTagToken.type === TokenType.INCOMPLETE_TAG_OPEN) {
|
9896 | // We already know the opening tag is not complete, so it is unlikely it has a corresponding
|
9897 | // close tag. Let's optimistically parse it as a full element and emit an error.
|
9898 | this._popElement(fullName, null);
|
9899 | this.errors.push(TreeError.create(fullName, span, `Opening tag "${fullName}" not terminated.`));
|
9900 | }
|
9901 | }
|
9902 | _pushElement(el) {
|
9903 | const parentEl = this._getParentElement();
|
9904 | if (parentEl && this.getTagDefinition(parentEl.name).isClosedByChild(el.name)) {
|
9905 | this._elementStack.pop();
|
9906 | }
|
9907 | this._addToParent(el);
|
9908 | this._elementStack.push(el);
|
9909 | }
|
9910 | _consumeEndTag(endTagToken) {
|
9911 | const fullName = this._getElementFullName(endTagToken.parts[0], endTagToken.parts[1], this._getParentElement());
|
9912 | if (this.getTagDefinition(fullName).isVoid) {
|
9913 | this.errors.push(TreeError.create(fullName, endTagToken.sourceSpan, `Void elements do not have end tags "${endTagToken.parts[1]}"`));
|
9914 | }
|
9915 | else if (!this._popElement(fullName, endTagToken.sourceSpan)) {
|
9916 | 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`;
|
9917 | this.errors.push(TreeError.create(fullName, endTagToken.sourceSpan, errMsg));
|
9918 | }
|
9919 | }
|
9920 | /**
|
9921 | * Closes the nearest element with the tag name `fullName` in the parse tree.
|
9922 | * `endSourceSpan` is the span of the closing tag, or null if the element does
|
9923 | * not have a closing tag (for example, this happens when an incomplete
|
9924 | * opening tag is recovered).
|
9925 | */
|
9926 | _popElement(fullName, endSourceSpan) {
|
9927 | for (let stackIndex = this._elementStack.length - 1; stackIndex >= 0; stackIndex--) {
|
9928 | const el = this._elementStack[stackIndex];
|
9929 | if (el.name == fullName) {
|
9930 | // Record the parse span with the element that is being closed. Any elements that are
|
9931 | // removed from the element stack at this point are closed implicitly, so they won't get
|
9932 | // an end source span (as there is no explicit closing element).
|
9933 | el.endSourceSpan = endSourceSpan;
|
9934 | el.sourceSpan.end = endSourceSpan !== null ? endSourceSpan.end : el.sourceSpan.end;
|
9935 | this._elementStack.splice(stackIndex, this._elementStack.length - stackIndex);
|
9936 | return true;
|
9937 | }
|
9938 | if (!this.getTagDefinition(el.name).closedByParent) {
|
9939 | return false;
|
9940 | }
|
9941 | }
|
9942 | return false;
|
9943 | }
|
9944 | _consumeAttr(attrName) {
|
9945 | const fullName = mergeNsAndName(attrName.parts[0], attrName.parts[1]);
|
9946 | let end = attrName.sourceSpan.end;
|
9947 | let value = '';
|
9948 | let valueSpan = undefined;
|
9949 | if (this._peek.type === TokenType.ATTR_QUOTE) {
|
9950 | this._advance();
|
9951 | }
|
9952 | if (this._peek.type === TokenType.ATTR_VALUE) {
|
9953 | const valueToken = this._advance();
|
9954 | value = valueToken.parts[0];
|
9955 | end = valueToken.sourceSpan.end;
|
9956 | valueSpan = valueToken.sourceSpan;
|
9957 | }
|
9958 | if (this._peek.type === TokenType.ATTR_QUOTE) {
|
9959 | const quoteToken = this._advance();
|
9960 | end = quoteToken.sourceSpan.end;
|
9961 | }
|
9962 | const keySpan = new ParseSourceSpan(attrName.sourceSpan.start, attrName.sourceSpan.end);
|
9963 | return new Attribute(fullName, value, new ParseSourceSpan(attrName.sourceSpan.start, end, attrName.sourceSpan.fullStart), keySpan, valueSpan);
|
9964 | }
|
9965 | _getParentElement() {
|
9966 | return this._elementStack.length > 0 ? this._elementStack[this._elementStack.length - 1] : null;
|
9967 | }
|
9968 | _addToParent(node) {
|
9969 | const parent = this._getParentElement();
|
9970 | if (parent != null) {
|
9971 | parent.children.push(node);
|
9972 | }
|
9973 | else {
|
9974 | this.rootNodes.push(node);
|
9975 | }
|
9976 | }
|
9977 | _getElementFullName(prefix, localName, parentElement) {
|
9978 | if (prefix === '') {
|
9979 | prefix = this.getTagDefinition(localName).implicitNamespacePrefix || '';
|
9980 | if (prefix === '' && parentElement != null) {
|
9981 | const parentTagName = splitNsName(parentElement.name)[1];
|
9982 | const parentTagDefinition = this.getTagDefinition(parentTagName);
|
9983 | if (!parentTagDefinition.preventNamespaceInheritance) {
|
9984 | prefix = getNsPrefix(parentElement.name);
|
9985 | }
|
9986 | }
|
9987 | }
|
9988 | return mergeNsAndName(prefix, localName);
|
9989 | }
|
9990 | }
|
9991 | function lastOnStack(stack, element) {
|
9992 | return stack.length > 0 && stack[stack.length - 1] === element;
|
9993 | }
|
9994 |
|
9995 | /**
|
9996 | * @license
|
9997 | * Copyright Google LLC All Rights Reserved.
|
9998 | *
|
9999 | * Use of this source code is governed by an MIT-style license that can be
|
10000 | * found in the LICENSE file at https://angular.io/license
|
10001 | */
|
10002 | class HtmlParser extends Parser {
|
10003 | constructor() {
|
10004 | super(getHtmlTagDefinition);
|
10005 | }
|
10006 | parse(source, url, options) {
|
10007 | return super.parse(source, url, options);
|
10008 | }
|
10009 | }
|
10010 |
|
10011 | /**
|
10012 | * @license
|
10013 | * Copyright Google LLC All Rights Reserved.
|
10014 | *
|
10015 | * Use of this source code is governed by an MIT-style license that can be
|
10016 | * found in the LICENSE file at https://angular.io/license
|
10017 | */
|
10018 | const PRESERVE_WS_ATTR_NAME = 'ngPreserveWhitespaces';
|
10019 | const SKIP_WS_TRIM_TAGS = new Set(['pre', 'template', 'textarea', 'script', 'style']);
|
10020 | // Equivalent to \s with \u00a0 (non-breaking space) excluded.
|
10021 | // Based on https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp
|
10022 | const WS_CHARS = ' \f\n\r\t\v\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff';
|
10023 | const NO_WS_REGEXP = new RegExp(`[^${WS_CHARS}]`);
|
10024 | const WS_REPLACE_REGEXP = new RegExp(`[${WS_CHARS}]{2,}`, 'g');
|
10025 | function hasPreserveWhitespacesAttr(attrs) {
|
10026 | return attrs.some((attr) => attr.name === PRESERVE_WS_ATTR_NAME);
|
10027 | }
|
10028 | /**
|
10029 | * Angular Dart introduced &ngsp; as a placeholder for non-removable space, see:
|
10030 | * https://github.com/dart-lang/angular/blob/0bb611387d29d65b5af7f9d2515ab571fd3fbee4/_tests/test/compiler/preserve_whitespace_test.dart#L25-L32
|
10031 | * In Angular Dart &ngsp; is converted to the 0xE500 PUA (Private Use Areas) unicode character
|
10032 | * and later on replaced by a space. We are re-implementing the same idea here.
|
10033 | */
|
10034 | function replaceNgsp(value) {
|
10035 | // lexer is replacing the &ngsp; pseudo-entity with NGSP_UNICODE
|
10036 | return value.replace(new RegExp(NGSP_UNICODE, 'g'), ' ');
|
10037 | }
|
10038 | /**
|
10039 | * This visitor can walk HTML parse tree and remove / trim text nodes using the following rules:
|
10040 | * - consider spaces, tabs and new lines as whitespace characters;
|
10041 | * - drop text nodes consisting of whitespace characters only;
|
10042 | * - for all other text nodes replace consecutive whitespace characters with one space;
|
10043 | * - convert &ngsp; pseudo-entity to a single space;
|
10044 | *
|
10045 | * Removal and trimming of whitespaces have positive performance impact (less code to generate
|
10046 | * while compiling templates, faster view creation). At the same time it can be "destructive"
|
10047 | * in some cases (whitespaces can influence layout). Because of the potential of breaking layout
|
10048 | * this visitor is not activated by default in Angular 5 and people need to explicitly opt-in for
|
10049 | * whitespace removal. The default option for whitespace removal will be revisited in Angular 6
|
10050 | * and might be changed to "on" by default.
|
10051 | */
|
10052 | class WhitespaceVisitor {
|
10053 | visitElement(element, context) {
|
10054 | if (SKIP_WS_TRIM_TAGS.has(element.name) || hasPreserveWhitespacesAttr(element.attrs)) {
|
10055 | // don't descent into elements where we need to preserve whitespaces
|
10056 | // but still visit all attributes to eliminate one used as a market to preserve WS
|
10057 | return new Element$1(element.name, visitAll$1(this, element.attrs), element.children, element.sourceSpan, element.startSourceSpan, element.endSourceSpan, element.i18n);
|
10058 | }
|
10059 | return new Element$1(element.name, element.attrs, visitAllWithSiblings(this, element.children), element.sourceSpan, element.startSourceSpan, element.endSourceSpan, element.i18n);
|
10060 | }
|
10061 | visitAttribute(attribute, context) {
|
10062 | return attribute.name !== PRESERVE_WS_ATTR_NAME ? attribute : null;
|
10063 | }
|
10064 | visitText(text, context) {
|
10065 | const isNotBlank = text.value.match(NO_WS_REGEXP);
|
10066 | const hasExpansionSibling = context &&
|
10067 | (context.prev instanceof Expansion || context.next instanceof Expansion);
|
10068 | if (isNotBlank || hasExpansionSibling) {
|
10069 | return new Text$2(replaceNgsp(text.value).replace(WS_REPLACE_REGEXP, ' '), text.sourceSpan, text.i18n);
|
10070 | }
|
10071 | return null;
|
10072 | }
|
10073 | visitComment(comment, context) {
|
10074 | return comment;
|
10075 | }
|
10076 | visitExpansion(expansion, context) {
|
10077 | return expansion;
|
10078 | }
|
10079 | visitExpansionCase(expansionCase, context) {
|
10080 | return expansionCase;
|
10081 | }
|
10082 | }
|
10083 | function removeWhitespaces(htmlAstWithErrors) {
|
10084 | return new ParseTreeResult(visitAll$1(new WhitespaceVisitor(), htmlAstWithErrors.rootNodes), htmlAstWithErrors.errors);
|
10085 | }
|
10086 | function visitAllWithSiblings(visitor, nodes) {
|
10087 | const result = [];
|
10088 | nodes.forEach((ast, i) => {
|
10089 | const context = { prev: nodes[i - 1], next: nodes[i + 1] };
|
10090 | const astResult = ast.visit(visitor, context);
|
10091 | if (astResult) {
|
10092 | result.push(astResult);
|
10093 | }
|
10094 | });
|
10095 | return result;
|
10096 | }
|
10097 |
|
10098 | /**
|
10099 | * @license
|
10100 | * Copyright Google LLC All Rights Reserved.
|
10101 | *
|
10102 | * Use of this source code is governed by an MIT-style license that can be
|
10103 | * found in the LICENSE file at https://angular.io/license
|
10104 | */
|
10105 | // http://cldr.unicode.org/index/cldr-spec/plural-rules
|
10106 | const PLURAL_CASES = ['zero', 'one', 'two', 'few', 'many', 'other'];
|
10107 | /**
|
10108 | * Expands special forms into elements.
|
10109 | *
|
10110 | * For example,
|
10111 | *
|
10112 | * ```
|
10113 | * { messages.length, plural,
|
10114 | * =0 {zero}
|
10115 | * =1 {one}
|
10116 | * other {more than one}
|
10117 | * }
|
10118 | * ```
|
10119 | *
|
10120 | * will be expanded into
|
10121 | *
|
10122 | * ```
|
10123 | * <ng-container [ngPlural]="messages.length">
|
10124 | * <ng-template ngPluralCase="=0">zero</ng-template>
|
10125 | * <ng-template ngPluralCase="=1">one</ng-template>
|
10126 | * <ng-template ngPluralCase="other">more than one</ng-template>
|
10127 | * </ng-container>
|
10128 | * ```
|
10129 | */
|
10130 | function expandNodes(nodes) {
|
10131 | const expander = new _Expander();
|
10132 | return new ExpansionResult(visitAll$1(expander, nodes), expander.isExpanded, expander.errors);
|
10133 | }
|
10134 | class ExpansionResult {
|
10135 | constructor(nodes, expanded, errors) {
|
10136 | this.nodes = nodes;
|
10137 | this.expanded = expanded;
|
10138 | this.errors = errors;
|
10139 | }
|
10140 | }
|
10141 | class ExpansionError extends ParseError {
|
10142 | constructor(span, errorMsg) {
|
10143 | super(span, errorMsg);
|
10144 | }
|
10145 | }
|
10146 | /**
|
10147 | * Expand expansion forms (plural, select) to directives
|
10148 | *
|
10149 | * @internal
|
10150 | */
|
10151 | class _Expander {
|
10152 | constructor() {
|
10153 | this.isExpanded = false;
|
10154 | this.errors = [];
|
10155 | }
|
10156 | visitElement(element, context) {
|
10157 | return new Element$1(element.name, element.attrs, visitAll$1(this, element.children), element.sourceSpan, element.startSourceSpan, element.endSourceSpan);
|
10158 | }
|
10159 | visitAttribute(attribute, context) {
|
10160 | return attribute;
|
10161 | }
|
10162 | visitText(text, context) {
|
10163 | return text;
|
10164 | }
|
10165 | visitComment(comment, context) {
|
10166 | return comment;
|
10167 | }
|
10168 | visitExpansion(icu, context) {
|
10169 | this.isExpanded = true;
|
10170 | return icu.type == 'plural' ? _expandPluralForm(icu, this.errors) :
|
10171 | _expandDefaultForm(icu, this.errors);
|
10172 | }
|
10173 | visitExpansionCase(icuCase, context) {
|
10174 | throw new Error('Should not be reached');
|
10175 | }
|
10176 | }
|
10177 | // Plural forms are expanded to `NgPlural` and `NgPluralCase`s
|
10178 | function _expandPluralForm(ast, errors) {
|
10179 | const children = ast.cases.map(c => {
|
10180 | if (PLURAL_CASES.indexOf(c.value) == -1 && !c.value.match(/^=\d+$/)) {
|
10181 | errors.push(new ExpansionError(c.valueSourceSpan, `Plural cases should be "=<number>" or one of ${PLURAL_CASES.join(', ')}`));
|
10182 | }
|
10183 | const expansionResult = expandNodes(c.expression);
|
10184 | errors.push(...expansionResult.errors);
|
10185 | 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);
|
10186 | });
|
10187 | const switchAttr = new Attribute('[ngPlural]', ast.switchValue, ast.switchValueSourceSpan, undefined /* keySpan */, undefined /* valueSpan */, undefined /* i18n */);
|
10188 | return new Element$1('ng-container', [switchAttr], children, ast.sourceSpan, ast.sourceSpan, ast.sourceSpan);
|
10189 | }
|
10190 | // ICU messages (excluding plural form) are expanded to `NgSwitch` and `NgSwitchCase`s
|
10191 | function _expandDefaultForm(ast, errors) {
|
10192 | const children = ast.cases.map(c => {
|
10193 | const expansionResult = expandNodes(c.expression);
|
10194 | errors.push(...expansionResult.errors);
|
10195 | if (c.value === 'other') {
|
10196 | // other is the default case when no values match
|
10197 | 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);
|
10198 | }
|
10199 | 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);
|
10200 | });
|
10201 | const switchAttr = new Attribute('[ngSwitch]', ast.switchValue, ast.switchValueSourceSpan, undefined /* keySpan */, undefined /* valueSpan */, undefined /* i18n */);
|
10202 | return new Element$1('ng-container', [switchAttr], children, ast.sourceSpan, ast.sourceSpan, ast.sourceSpan);
|
10203 | }
|
10204 |
|
10205 | /**
|
10206 | * @license
|
10207 | * Copyright Google LLC All Rights Reserved.
|
10208 | *
|
10209 | * Use of this source code is governed by an MIT-style license that can be
|
10210 | * found in the LICENSE file at https://angular.io/license
|
10211 | */
|
10212 | /**
|
10213 | * A segment of text within the template.
|
10214 | */
|
10215 | class TextAst {
|
10216 | constructor(value, ngContentIndex, sourceSpan) {
|
10217 | this.value = value;
|
10218 | this.ngContentIndex = ngContentIndex;
|
10219 | this.sourceSpan = sourceSpan;
|
10220 | }
|
10221 | visit(visitor, context) {
|
10222 | return visitor.visitText(this, context);
|
10223 | }
|
10224 | }
|
10225 | /**
|
10226 | * A bound expression within the text of a template.
|
10227 | */
|
10228 | class BoundTextAst {
|
10229 | constructor(value, ngContentIndex, sourceSpan) {
|
10230 | this.value = value;
|
10231 | this.ngContentIndex = ngContentIndex;
|
10232 | this.sourceSpan = sourceSpan;
|
10233 | }
|
10234 | visit(visitor, context) {
|
10235 | return visitor.visitBoundText(this, context);
|
10236 | }
|
10237 | }
|
10238 | /**
|
10239 | * A plain attribute on an element.
|
10240 | */
|
10241 | class AttrAst {
|
10242 | constructor(name, value, sourceSpan) {
|
10243 | this.name = name;
|
10244 | this.value = value;
|
10245 | this.sourceSpan = sourceSpan;
|
10246 | }
|
10247 | visit(visitor, context) {
|
10248 | return visitor.visitAttr(this, context);
|
10249 | }
|
10250 | }
|
10251 | const BoundPropertyMapping = {
|
10252 | [4 /* Animation */]: 4 /* Animation */,
|
10253 | [1 /* Attribute */]: 1 /* Attribute */,
|
10254 | [2 /* Class */]: 2 /* Class */,
|
10255 | [0 /* Property */]: 0 /* Property */,
|
10256 | [3 /* Style */]: 3 /* Style */,
|
10257 | };
|
10258 | /**
|
10259 | * A binding for an element property (e.g. `[property]="expression"`) or an animation trigger (e.g.
|
10260 | * `[@trigger]="stateExp"`)
|
10261 | */
|
10262 | class BoundElementPropertyAst {
|
10263 | constructor(name, type, securityContext, value, unit, sourceSpan) {
|
10264 | this.name = name;
|
10265 | this.type = type;
|
10266 | this.securityContext = securityContext;
|
10267 | this.value = value;
|
10268 | this.unit = unit;
|
10269 | this.sourceSpan = sourceSpan;
|
10270 | this.isAnimation = this.type === 4 /* Animation */;
|
10271 | }
|
10272 | static fromBoundProperty(prop) {
|
10273 | const type = BoundPropertyMapping[prop.type];
|
10274 | return new BoundElementPropertyAst(prop.name, type, prop.securityContext, prop.value, prop.unit, prop.sourceSpan);
|
10275 | }
|
10276 | visit(visitor, context) {
|
10277 | return visitor.visitElementProperty(this, context);
|
10278 | }
|
10279 | }
|
10280 | /**
|
10281 | * A binding for an element event (e.g. `(event)="handler()"`) or an animation trigger event (e.g.
|
10282 | * `(@trigger.phase)="callback($event)"`).
|
10283 | */
|
10284 | class BoundEventAst {
|
10285 | constructor(name, target, phase, handler, sourceSpan, handlerSpan) {
|
10286 | this.name = name;
|
10287 | this.target = target;
|
10288 | this.phase = phase;
|
10289 | this.handler = handler;
|
10290 | this.sourceSpan = sourceSpan;
|
10291 | this.handlerSpan = handlerSpan;
|
10292 | this.fullName = BoundEventAst.calcFullName(this.name, this.target, this.phase);
|
10293 | this.isAnimation = !!this.phase;
|
10294 | }
|
10295 | static calcFullName(name, target, phase) {
|
10296 | if (target) {
|
10297 | return `${target}:${name}`;
|
10298 | }
|
10299 | if (phase) {
|
10300 | return `@${name}.${phase}`;
|
10301 | }
|
10302 | return name;
|
10303 | }
|
10304 | static fromParsedEvent(event) {
|
10305 | const target = event.type === 0 /* Regular */ ? event.targetOrPhase : null;
|
10306 | const phase = event.type === 1 /* Animation */ ? event.targetOrPhase : null;
|
10307 | return new BoundEventAst(event.name, target, phase, event.handler, event.sourceSpan, event.handlerSpan);
|
10308 | }
|
10309 | visit(visitor, context) {
|
10310 | return visitor.visitEvent(this, context);
|
10311 | }
|
10312 | }
|
10313 | /**
|
10314 | * A reference declaration on an element (e.g. `let someName="expression"`).
|
10315 | */
|
10316 | class ReferenceAst {
|
10317 | constructor(name, value, originalValue, sourceSpan) {
|
10318 | this.name = name;
|
10319 | this.value = value;
|
10320 | this.originalValue = originalValue;
|
10321 | this.sourceSpan = sourceSpan;
|
10322 | }
|
10323 | visit(visitor, context) {
|
10324 | return visitor.visitReference(this, context);
|
10325 | }
|
10326 | }
|
10327 | /**
|
10328 | * A variable declaration on a <ng-template> (e.g. `var-someName="someLocalName"`).
|
10329 | */
|
10330 | class VariableAst {
|
10331 | constructor(name, value, sourceSpan, valueSpan) {
|
10332 | this.name = name;
|
10333 | this.value = value;
|
10334 | this.sourceSpan = sourceSpan;
|
10335 | this.valueSpan = valueSpan;
|
10336 | }
|
10337 | static fromParsedVariable(v) {
|
10338 | return new VariableAst(v.name, v.value, v.sourceSpan, v.valueSpan);
|
10339 | }
|
10340 | visit(visitor, context) {
|
10341 | return visitor.visitVariable(this, context);
|
10342 | }
|
10343 | }
|
10344 | /**
|
10345 | * An element declaration in a template.
|
10346 | */
|
10347 | class ElementAst {
|
10348 | constructor(name, attrs, inputs, outputs, references, directives, providers, hasViewContainer, queryMatches, children, ngContentIndex, sourceSpan, endSourceSpan) {
|
10349 | this.name = name;
|
10350 | this.attrs = attrs;
|
10351 | this.inputs = inputs;
|
10352 | this.outputs = outputs;
|
10353 | this.references = references;
|
10354 | this.directives = directives;
|
10355 | this.providers = providers;
|
10356 | this.hasViewContainer = hasViewContainer;
|
10357 | this.queryMatches = queryMatches;
|
10358 | this.children = children;
|
10359 | this.ngContentIndex = ngContentIndex;
|
10360 | this.sourceSpan = sourceSpan;
|
10361 | this.endSourceSpan = endSourceSpan;
|
10362 | }
|
10363 | visit(visitor, context) {
|
10364 | return visitor.visitElement(this, context);
|
10365 | }
|
10366 | }
|
10367 | /**
|
10368 | * A `<ng-template>` element included in an Angular template.
|
10369 | */
|
10370 | class EmbeddedTemplateAst {
|
10371 | constructor(attrs, outputs, references, variables, directives, providers, hasViewContainer, queryMatches, children, ngContentIndex, sourceSpan) {
|
10372 | this.attrs = attrs;
|
10373 | this.outputs = outputs;
|
10374 | this.references = references;
|
10375 | this.variables = variables;
|
10376 | this.directives = directives;
|
10377 | this.providers = providers;
|
10378 | this.hasViewContainer = hasViewContainer;
|
10379 | this.queryMatches = queryMatches;
|
10380 | this.children = children;
|
10381 | this.ngContentIndex = ngContentIndex;
|
10382 | this.sourceSpan = sourceSpan;
|
10383 | }
|
10384 | visit(visitor, context) {
|
10385 | return visitor.visitEmbeddedTemplate(this, context);
|
10386 | }
|
10387 | }
|
10388 | /**
|
10389 | * A directive property with a bound value (e.g. `*ngIf="condition").
|
10390 | */
|
10391 | class BoundDirectivePropertyAst {
|
10392 | constructor(directiveName, templateName, value, sourceSpan) {
|
10393 | this.directiveName = directiveName;
|
10394 | this.templateName = templateName;
|
10395 | this.value = value;
|
10396 | this.sourceSpan = sourceSpan;
|
10397 | }
|
10398 | visit(visitor, context) {
|
10399 | return visitor.visitDirectiveProperty(this, context);
|
10400 | }
|
10401 | }
|
10402 | /**
|
10403 | * A directive declared on an element.
|
10404 | */
|
10405 | class DirectiveAst {
|
10406 | constructor(directive, inputs, hostProperties, hostEvents, contentQueryStartId, sourceSpan) {
|
10407 | this.directive = directive;
|
10408 | this.inputs = inputs;
|
10409 | this.hostProperties = hostProperties;
|
10410 | this.hostEvents = hostEvents;
|
10411 | this.contentQueryStartId = contentQueryStartId;
|
10412 | this.sourceSpan = sourceSpan;
|
10413 | }
|
10414 | visit(visitor, context) {
|
10415 | return visitor.visitDirective(this, context);
|
10416 | }
|
10417 | }
|
10418 | /**
|
10419 | * A provider declared on an element
|
10420 | */
|
10421 | class ProviderAst {
|
10422 | constructor(token, multiProvider, eager, providers, providerType, lifecycleHooks, sourceSpan, isModule) {
|
10423 | this.token = token;
|
10424 | this.multiProvider = multiProvider;
|
10425 | this.eager = eager;
|
10426 | this.providers = providers;
|
10427 | this.providerType = providerType;
|
10428 | this.lifecycleHooks = lifecycleHooks;
|
10429 | this.sourceSpan = sourceSpan;
|
10430 | this.isModule = isModule;
|
10431 | }
|
10432 | visit(visitor, context) {
|
10433 | // No visit method in the visitor for now...
|
10434 | return null;
|
10435 | }
|
10436 | }
|
10437 | var ProviderAstType;
|
10438 | (function (ProviderAstType) {
|
10439 | ProviderAstType[ProviderAstType["PublicService"] = 0] = "PublicService";
|
10440 | ProviderAstType[ProviderAstType["PrivateService"] = 1] = "PrivateService";
|
10441 | ProviderAstType[ProviderAstType["Component"] = 2] = "Component";
|
10442 | ProviderAstType[ProviderAstType["Directive"] = 3] = "Directive";
|
10443 | ProviderAstType[ProviderAstType["Builtin"] = 4] = "Builtin";
|
10444 | })(ProviderAstType || (ProviderAstType = {}));
|
10445 | /**
|
10446 | * Position where content is to be projected (instance of `<ng-content>` in a template).
|
10447 | */
|
10448 | class NgContentAst {
|
10449 | constructor(index, ngContentIndex, sourceSpan) {
|
10450 | this.index = index;
|
10451 | this.ngContentIndex = ngContentIndex;
|
10452 | this.sourceSpan = sourceSpan;
|
10453 | }
|
10454 | visit(visitor, context) {
|
10455 | return visitor.visitNgContent(this, context);
|
10456 | }
|
10457 | }
|
10458 | /**
|
10459 | * A visitor that accepts each node but doesn't do anything. It is intended to be used
|
10460 | * as the base class for a visitor that is only interested in a subset of the node types.
|
10461 | */
|
10462 | class NullTemplateVisitor {
|
10463 | visitNgContent(ast, context) { }
|
10464 | visitEmbeddedTemplate(ast, context) { }
|
10465 | visitElement(ast, context) { }
|
10466 | visitReference(ast, context) { }
|
10467 | visitVariable(ast, context) { }
|
10468 | visitEvent(ast, context) { }
|
10469 | visitElementProperty(ast, context) { }
|
10470 | visitAttr(ast, context) { }
|
10471 | visitBoundText(ast, context) { }
|
10472 | visitText(ast, context) { }
|
10473 | visitDirective(ast, context) { }
|
10474 | visitDirectiveProperty(ast, context) { }
|
10475 | }
|
10476 | /**
|
10477 | * Base class that can be used to build a visitor that visits each node
|
10478 | * in an template ast recursively.
|
10479 | */
|
10480 | class RecursiveTemplateAstVisitor extends NullTemplateVisitor {
|
10481 | constructor() {
|
10482 | super();
|
10483 | }
|
10484 | // Nodes with children
|
10485 | visitEmbeddedTemplate(ast, context) {
|
10486 | return this.visitChildren(context, visit => {
|
10487 | visit(ast.attrs);
|
10488 | visit(ast.references);
|
10489 | visit(ast.variables);
|
10490 | visit(ast.directives);
|
10491 | visit(ast.providers);
|
10492 | visit(ast.children);
|
10493 | });
|
10494 | }
|
10495 | visitElement(ast, context) {
|
10496 | return this.visitChildren(context, visit => {
|
10497 | visit(ast.attrs);
|
10498 | visit(ast.inputs);
|
10499 | visit(ast.outputs);
|
10500 | visit(ast.references);
|
10501 | visit(ast.directives);
|
10502 | visit(ast.providers);
|
10503 | visit(ast.children);
|
10504 | });
|
10505 | }
|
10506 | visitDirective(ast, context) {
|
10507 | return this.visitChildren(context, visit => {
|
10508 | visit(ast.inputs);
|
10509 | visit(ast.hostProperties);
|
10510 | visit(ast.hostEvents);
|
10511 | });
|
10512 | }
|
10513 | visitChildren(context, cb) {
|
10514 | let results = [];
|
10515 | let t = this;
|
10516 | function visit(children) {
|
10517 | if (children && children.length)
|
10518 | results.push(templateVisitAll(t, children, context));
|
10519 | }
|
10520 | cb(visit);
|
10521 | return Array.prototype.concat.apply([], results);
|
10522 | }
|
10523 | }
|
10524 | /**
|
10525 | * Visit every node in a list of {@link TemplateAst}s with the given {@link TemplateAstVisitor}.
|
10526 | */
|
10527 | function templateVisitAll(visitor, asts, context = null) {
|
10528 | const result = [];
|
10529 | const visit = visitor.visit ?
|
10530 | (ast) => visitor.visit(ast, context) || ast.visit(visitor, context) :
|
10531 | (ast) => ast.visit(visitor, context);
|
10532 | asts.forEach(ast => {
|
10533 | const astResult = visit(ast);
|
10534 | if (astResult) {
|
10535 | result.push(astResult);
|
10536 | }
|
10537 | });
|
10538 | return result;
|
10539 | }
|
10540 |
|
10541 | /**
|
10542 | * @license
|
10543 | * Copyright Google LLC All Rights Reserved.
|
10544 | *
|
10545 | * Use of this source code is governed by an MIT-style license that can be
|
10546 | * found in the LICENSE file at https://angular.io/license
|
10547 | */
|
10548 | class ProviderError extends ParseError {
|
10549 | constructor(message, span) {
|
10550 | super(span, message);
|
10551 | }
|
10552 | }
|
10553 | class ProviderViewContext {
|
10554 | constructor(reflector, component) {
|
10555 | this.reflector = reflector;
|
10556 | this.component = component;
|
10557 | this.errors = [];
|
10558 | this.viewQueries = _getViewQueries(component);
|
10559 | this.viewProviders = new Map();
|
10560 | component.viewProviders.forEach((provider) => {
|
10561 | if (this.viewProviders.get(tokenReference(provider.token)) == null) {
|
10562 | this.viewProviders.set(tokenReference(provider.token), true);
|
10563 | }
|
10564 | });
|
10565 | }
|
10566 | }
|
10567 | class ProviderElementContext {
|
10568 | constructor(viewContext, _parent, _isViewRoot, _directiveAsts, attrs, refs, isTemplate, contentQueryStartId, _sourceSpan) {
|
10569 | this.viewContext = viewContext;
|
10570 | this._parent = _parent;
|
10571 | this._isViewRoot = _isViewRoot;
|
10572 | this._directiveAsts = _directiveAsts;
|
10573 | this._sourceSpan = _sourceSpan;
|
10574 | this._transformedProviders = new Map();
|
10575 | this._seenProviders = new Map();
|
10576 | this._queriedTokens = new Map();
|
10577 | this.transformedHasViewContainer = false;
|
10578 | this._attrs = {};
|
10579 | attrs.forEach((attrAst) => this._attrs[attrAst.name] = attrAst.value);
|
10580 | const directivesMeta = _directiveAsts.map(directiveAst => directiveAst.directive);
|
10581 | this._allProviders =
|
10582 | _resolveProvidersFromDirectives(directivesMeta, _sourceSpan, viewContext.errors);
|
10583 | this._contentQueries = _getContentQueries(contentQueryStartId, directivesMeta);
|
10584 | Array.from(this._allProviders.values()).forEach((provider) => {
|
10585 | this._addQueryReadsTo(provider.token, provider.token, this._queriedTokens);
|
10586 | });
|
10587 | if (isTemplate) {
|
10588 | const templateRefId = createTokenForExternalReference(this.viewContext.reflector, Identifiers.TemplateRef);
|
10589 | this._addQueryReadsTo(templateRefId, templateRefId, this._queriedTokens);
|
10590 | }
|
10591 | refs.forEach((refAst) => {
|
10592 | let defaultQueryValue = refAst.value ||
|
10593 | createTokenForExternalReference(this.viewContext.reflector, Identifiers.ElementRef);
|
10594 | this._addQueryReadsTo({ value: refAst.name }, defaultQueryValue, this._queriedTokens);
|
10595 | });
|
10596 | if (this._queriedTokens.get(this.viewContext.reflector.resolveExternalReference(Identifiers.ViewContainerRef))) {
|
10597 | this.transformedHasViewContainer = true;
|
10598 | }
|
10599 | // create the providers that we know are eager first
|
10600 | Array.from(this._allProviders.values()).forEach((provider) => {
|
10601 | const eager = provider.eager || this._queriedTokens.get(tokenReference(provider.token));
|
10602 | if (eager) {
|
10603 | this._getOrCreateLocalProvider(provider.providerType, provider.token, true);
|
10604 | }
|
10605 | });
|
10606 | }
|
10607 | afterElement() {
|
10608 | // collect lazy providers
|
10609 | Array.from(this._allProviders.values()).forEach((provider) => {
|
10610 | this._getOrCreateLocalProvider(provider.providerType, provider.token, false);
|
10611 | });
|
10612 | }
|
10613 | get transformProviders() {
|
10614 | // Note: Maps keep their insertion order.
|
10615 | const lazyProviders = [];
|
10616 | const eagerProviders = [];
|
10617 | this._transformedProviders.forEach(provider => {
|
10618 | if (provider.eager) {
|
10619 | eagerProviders.push(provider);
|
10620 | }
|
10621 | else {
|
10622 | lazyProviders.push(provider);
|
10623 | }
|
10624 | });
|
10625 | return lazyProviders.concat(eagerProviders);
|
10626 | }
|
10627 | get transformedDirectiveAsts() {
|
10628 | const sortedProviderTypes = this.transformProviders.map(provider => provider.token.identifier);
|
10629 | const sortedDirectives = this._directiveAsts.slice();
|
10630 | sortedDirectives.sort((dir1, dir2) => sortedProviderTypes.indexOf(dir1.directive.type) -
|
10631 | sortedProviderTypes.indexOf(dir2.directive.type));
|
10632 | return sortedDirectives;
|
10633 | }
|
10634 | get queryMatches() {
|
10635 | const allMatches = [];
|
10636 | this._queriedTokens.forEach((matches) => {
|
10637 | allMatches.push(...matches);
|
10638 | });
|
10639 | return allMatches;
|
10640 | }
|
10641 | _addQueryReadsTo(token, defaultValue, queryReadTokens) {
|
10642 | this._getQueriesFor(token).forEach((query) => {
|
10643 | const queryValue = query.meta.read || defaultValue;
|
10644 | const tokenRef = tokenReference(queryValue);
|
10645 | let queryMatches = queryReadTokens.get(tokenRef);
|
10646 | if (!queryMatches) {
|
10647 | queryMatches = [];
|
10648 | queryReadTokens.set(tokenRef, queryMatches);
|
10649 | }
|
10650 | queryMatches.push({ queryId: query.queryId, value: queryValue });
|
10651 | });
|
10652 | }
|
10653 | _getQueriesFor(token) {
|
10654 | const result = [];
|
10655 | let currentEl = this;
|
10656 | let distance = 0;
|
10657 | let queries;
|
10658 | while (currentEl !== null) {
|
10659 | queries = currentEl._contentQueries.get(tokenReference(token));
|
10660 | if (queries) {
|
10661 | result.push(...queries.filter((query) => query.meta.descendants || distance <= 1));
|
10662 | }
|
10663 | if (currentEl._directiveAsts.length > 0) {
|
10664 | distance++;
|
10665 | }
|
10666 | currentEl = currentEl._parent;
|
10667 | }
|
10668 | queries = this.viewContext.viewQueries.get(tokenReference(token));
|
10669 | if (queries) {
|
10670 | result.push(...queries);
|
10671 | }
|
10672 | return result;
|
10673 | }
|
10674 | _getOrCreateLocalProvider(requestingProviderType, token, eager) {
|
10675 | const resolvedProvider = this._allProviders.get(tokenReference(token));
|
10676 | if (!resolvedProvider ||
|
10677 | ((requestingProviderType === ProviderAstType.Directive ||
|
10678 | requestingProviderType === ProviderAstType.PublicService) &&
|
10679 | resolvedProvider.providerType === ProviderAstType.PrivateService) ||
|
10680 | ((requestingProviderType === ProviderAstType.PrivateService ||
|
10681 | requestingProviderType === ProviderAstType.PublicService) &&
|
10682 | resolvedProvider.providerType === ProviderAstType.Builtin)) {
|
10683 | return null;
|
10684 | }
|
10685 | let transformedProviderAst = this._transformedProviders.get(tokenReference(token));
|
10686 | if (transformedProviderAst) {
|
10687 | return transformedProviderAst;
|
10688 | }
|
10689 | if (this._seenProviders.get(tokenReference(token)) != null) {
|
10690 | this.viewContext.errors.push(new ProviderError(`Cannot instantiate cyclic dependency! ${tokenName(token)}`, this._sourceSpan));
|
10691 | return null;
|
10692 | }
|
10693 | this._seenProviders.set(tokenReference(token), true);
|
10694 | const transformedProviders = resolvedProvider.providers.map((provider) => {
|
10695 | let transformedUseValue = provider.useValue;
|
10696 | let transformedUseExisting = provider.useExisting;
|
10697 | let transformedDeps = undefined;
|
10698 | if (provider.useExisting != null) {
|
10699 | const existingDiDep = this._getDependency(resolvedProvider.providerType, { token: provider.useExisting }, eager);
|
10700 | if (existingDiDep.token != null) {
|
10701 | transformedUseExisting = existingDiDep.token;
|
10702 | }
|
10703 | else {
|
10704 | transformedUseExisting = null;
|
10705 | transformedUseValue = existingDiDep.value;
|
10706 | }
|
10707 | }
|
10708 | else if (provider.useFactory) {
|
10709 | const deps = provider.deps || provider.useFactory.diDeps;
|
10710 | transformedDeps =
|
10711 | deps.map((dep) => this._getDependency(resolvedProvider.providerType, dep, eager));
|
10712 | }
|
10713 | else if (provider.useClass) {
|
10714 | const deps = provider.deps || provider.useClass.diDeps;
|
10715 | transformedDeps =
|
10716 | deps.map((dep) => this._getDependency(resolvedProvider.providerType, dep, eager));
|
10717 | }
|
10718 | return _transformProvider(provider, {
|
10719 | useExisting: transformedUseExisting,
|
10720 | useValue: transformedUseValue,
|
10721 | deps: transformedDeps
|
10722 | });
|
10723 | });
|
10724 | transformedProviderAst =
|
10725 | _transformProviderAst(resolvedProvider, { eager: eager, providers: transformedProviders });
|
10726 | this._transformedProviders.set(tokenReference(token), transformedProviderAst);
|
10727 | return transformedProviderAst;
|
10728 | }
|
10729 | _getLocalDependency(requestingProviderType, dep, eager = false) {
|
10730 | if (dep.isAttribute) {
|
10731 | const attrValue = this._attrs[dep.token.value];
|
10732 | return { isValue: true, value: attrValue == null ? null : attrValue };
|
10733 | }
|
10734 | if (dep.token != null) {
|
10735 | // access builtints
|
10736 | if ((requestingProviderType === ProviderAstType.Directive ||
|
10737 | requestingProviderType === ProviderAstType.Component)) {
|
10738 | if (tokenReference(dep.token) ===
|
10739 | this.viewContext.reflector.resolveExternalReference(Identifiers.Renderer) ||
|
10740 | tokenReference(dep.token) ===
|
10741 | this.viewContext.reflector.resolveExternalReference(Identifiers.ElementRef) ||
|
10742 | tokenReference(dep.token) ===
|
10743 | this.viewContext.reflector.resolveExternalReference(Identifiers.ChangeDetectorRef) ||
|
10744 | tokenReference(dep.token) ===
|
10745 | this.viewContext.reflector.resolveExternalReference(Identifiers.TemplateRef)) {
|
10746 | return dep;
|
10747 | }
|
10748 | if (tokenReference(dep.token) ===
|
10749 | this.viewContext.reflector.resolveExternalReference(Identifiers.ViewContainerRef)) {
|
10750 | this.transformedHasViewContainer = true;
|
10751 | }
|
10752 | }
|
10753 | // access the injector
|
10754 | if (tokenReference(dep.token) ===
|
10755 | this.viewContext.reflector.resolveExternalReference(Identifiers.Injector)) {
|
10756 | return dep;
|
10757 | }
|
10758 | // access providers
|
10759 | if (this._getOrCreateLocalProvider(requestingProviderType, dep.token, eager) != null) {
|
10760 | return dep;
|
10761 | }
|
10762 | }
|
10763 | return null;
|
10764 | }
|
10765 | _getDependency(requestingProviderType, dep, eager = false) {
|
10766 | let currElement = this;
|
10767 | let currEager = eager;
|
10768 | let result = null;
|
10769 | if (!dep.isSkipSelf) {
|
10770 | result = this._getLocalDependency(requestingProviderType, dep, eager);
|
10771 | }
|
10772 | if (dep.isSelf) {
|
10773 | if (!result && dep.isOptional) {
|
10774 | result = { isValue: true, value: null };
|
10775 | }
|
10776 | }
|
10777 | else {
|
10778 | // check parent elements
|
10779 | while (!result && currElement._parent) {
|
10780 | const prevElement = currElement;
|
10781 | currElement = currElement._parent;
|
10782 | if (prevElement._isViewRoot) {
|
10783 | currEager = false;
|
10784 | }
|
10785 | result = currElement._getLocalDependency(ProviderAstType.PublicService, dep, currEager);
|
10786 | }
|
10787 | // check @Host restriction
|
10788 | if (!result) {
|
10789 | if (!dep.isHost || this.viewContext.component.isHost ||
|
10790 | this.viewContext.component.type.reference === tokenReference(dep.token) ||
|
10791 | this.viewContext.viewProviders.get(tokenReference(dep.token)) != null) {
|
10792 | result = dep;
|
10793 | }
|
10794 | else {
|
10795 | result = dep.isOptional ? { isValue: true, value: null } : null;
|
10796 | }
|
10797 | }
|
10798 | }
|
10799 | if (!result) {
|
10800 | this.viewContext.errors.push(new ProviderError(`No provider for ${tokenName(dep.token)}`, this._sourceSpan));
|
10801 | }
|
10802 | return result;
|
10803 | }
|
10804 | }
|
10805 | function _transformProvider(provider, { useExisting, useValue, deps }) {
|
10806 | return {
|
10807 | token: provider.token,
|
10808 | useClass: provider.useClass,
|
10809 | useExisting: useExisting,
|
10810 | useFactory: provider.useFactory,
|
10811 | useValue: useValue,
|
10812 | deps: deps,
|
10813 | multi: provider.multi
|
10814 | };
|
10815 | }
|
10816 | function _transformProviderAst(provider, { eager, providers }) {
|
10817 | return new ProviderAst(provider.token, provider.multiProvider, provider.eager || eager, providers, provider.providerType, provider.lifecycleHooks, provider.sourceSpan, provider.isModule);
|
10818 | }
|
10819 | function _resolveProvidersFromDirectives(directives, sourceSpan, targetErrors) {
|
10820 | const providersByToken = new Map();
|
10821 | directives.forEach((directive) => {
|
10822 | const dirProvider = { token: { identifier: directive.type }, useClass: directive.type };
|
10823 | _resolveProviders([dirProvider], directive.isComponent ? ProviderAstType.Component : ProviderAstType.Directive, true, sourceSpan, targetErrors, providersByToken, /* isModule */ false);
|
10824 | });
|
10825 | // Note: directives need to be able to overwrite providers of a component!
|
10826 | const directivesWithComponentFirst = directives.filter(dir => dir.isComponent).concat(directives.filter(dir => !dir.isComponent));
|
10827 | directivesWithComponentFirst.forEach((directive) => {
|
10828 | _resolveProviders(directive.providers, ProviderAstType.PublicService, false, sourceSpan, targetErrors, providersByToken, /* isModule */ false);
|
10829 | _resolveProviders(directive.viewProviders, ProviderAstType.PrivateService, false, sourceSpan, targetErrors, providersByToken, /* isModule */ false);
|
10830 | });
|
10831 | return providersByToken;
|
10832 | }
|
10833 | function _resolveProviders(providers, providerType, eager, sourceSpan, targetErrors, targetProvidersByToken, isModule) {
|
10834 | providers.forEach((provider) => {
|
10835 | let resolvedProvider = targetProvidersByToken.get(tokenReference(provider.token));
|
10836 | if (resolvedProvider != null && !!resolvedProvider.multiProvider !== !!provider.multi) {
|
10837 | targetErrors.push(new ProviderError(`Mixing multi and non multi provider is not possible for token ${tokenName(resolvedProvider.token)}`, sourceSpan));
|
10838 | }
|
10839 | if (!resolvedProvider) {
|
10840 | const lifecycleHooks = provider.token.identifier &&
|
10841 | provider.token.identifier.lifecycleHooks ?
|
10842 | provider.token.identifier.lifecycleHooks :
|
10843 | [];
|
10844 | const isUseValue = !(provider.useClass || provider.useExisting || provider.useFactory);
|
10845 | resolvedProvider = new ProviderAst(provider.token, !!provider.multi, eager || isUseValue, [provider], providerType, lifecycleHooks, sourceSpan, isModule);
|
10846 | targetProvidersByToken.set(tokenReference(provider.token), resolvedProvider);
|
10847 | }
|
10848 | else {
|
10849 | if (!provider.multi) {
|
10850 | resolvedProvider.providers.length = 0;
|
10851 | }
|
10852 | resolvedProvider.providers.push(provider);
|
10853 | }
|
10854 | });
|
10855 | }
|
10856 | function _getViewQueries(component) {
|
10857 | // Note: queries start with id 1 so we can use the number in a Bloom filter!
|
10858 | let viewQueryId = 1;
|
10859 | const viewQueries = new Map();
|
10860 | if (component.viewQueries) {
|
10861 | component.viewQueries.forEach((query) => _addQueryToTokenMap(viewQueries, { meta: query, queryId: viewQueryId++ }));
|
10862 | }
|
10863 | return viewQueries;
|
10864 | }
|
10865 | function _getContentQueries(contentQueryStartId, directives) {
|
10866 | let contentQueryId = contentQueryStartId;
|
10867 | const contentQueries = new Map();
|
10868 | directives.forEach((directive, directiveIndex) => {
|
10869 | if (directive.queries) {
|
10870 | directive.queries.forEach((query) => _addQueryToTokenMap(contentQueries, { meta: query, queryId: contentQueryId++ }));
|
10871 | }
|
10872 | });
|
10873 | return contentQueries;
|
10874 | }
|
10875 | function _addQueryToTokenMap(map, query) {
|
10876 | query.meta.selectors.forEach((token) => {
|
10877 | let entry = map.get(tokenReference(token));
|
10878 | if (!entry) {
|
10879 | entry = [];
|
10880 | map.set(tokenReference(token), entry);
|
10881 | }
|
10882 | entry.push(query);
|
10883 | });
|
10884 | }
|
10885 |
|
10886 | /**
|
10887 | * @license
|
10888 | * Copyright Google LLC All Rights Reserved.
|
10889 | *
|
10890 | * Use of this source code is governed by an MIT-style license that can be
|
10891 | * found in the LICENSE file at https://angular.io/license
|
10892 | */
|
10893 | class StyleWithImports {
|
10894 | constructor(style, styleUrls) {
|
10895 | this.style = style;
|
10896 | this.styleUrls = styleUrls;
|
10897 | }
|
10898 | }
|
10899 | function isStyleUrlResolvable(url) {
|
10900 | if (url == null || url.length === 0 || url[0] == '/')
|
10901 | return false;
|
10902 | const schemeMatch = url.match(URL_WITH_SCHEMA_REGEXP);
|
10903 | return schemeMatch === null || schemeMatch[1] == 'package' || schemeMatch[1] == 'asset';
|
10904 | }
|
10905 | /**
|
10906 | * Rewrites stylesheets by resolving and removing the @import urls that
|
10907 | * are either relative or don't have a `package:` scheme
|
10908 | */
|
10909 | function extractStyleUrls(resolver, baseUrl, cssText) {
|
10910 | const foundUrls = [];
|
10911 | const modifiedCssText = cssText.replace(CSS_STRIPPABLE_COMMENT_REGEXP, '')
|
10912 | .replace(CSS_IMPORT_REGEXP, (...m) => {
|
10913 | const url = m[1] || m[2];
|
10914 | if (!isStyleUrlResolvable(url)) {
|
10915 | // Do not attempt to resolve non-package absolute URLs with URI
|
10916 | // scheme
|
10917 | return m[0];
|
10918 | }
|
10919 | foundUrls.push(resolver.resolve(baseUrl, url));
|
10920 | return '';
|
10921 | });
|
10922 | return new StyleWithImports(modifiedCssText, foundUrls);
|
10923 | }
|
10924 | const CSS_IMPORT_REGEXP = /@import\s+(?:url\()?\s*(?:(?:['"]([^'"]*))|([^;\)\s]*))[^;]*;?/g;
|
10925 | const CSS_STRIPPABLE_COMMENT_REGEXP = /\/\*(?!#\s*(?:sourceURL|sourceMappingURL)=)[\s\S]+?\*\//g;
|
10926 | const URL_WITH_SCHEMA_REGEXP = /^([^:/?#]+):/;
|
10927 |
|
10928 | /**
|
10929 | * @license
|
10930 | * Copyright Google LLC All Rights Reserved.
|
10931 | *
|
10932 | * Use of this source code is governed by an MIT-style license that can be
|
10933 | * found in the LICENSE file at https://angular.io/license
|
10934 | */
|
10935 | const PROPERTY_PARTS_SEPARATOR = '.';
|
10936 | const ATTRIBUTE_PREFIX = 'attr';
|
10937 | const CLASS_PREFIX = 'class';
|
10938 | const STYLE_PREFIX = 'style';
|
10939 | const TEMPLATE_ATTR_PREFIX = '*';
|
10940 | const ANIMATE_PROP_PREFIX = 'animate-';
|
10941 | /**
|
10942 | * Parses bindings in templates and in the directive host area.
|
10943 | */
|
10944 | class BindingParser {
|
10945 | constructor(_exprParser, _interpolationConfig, _schemaRegistry, pipes, errors) {
|
10946 | this._exprParser = _exprParser;
|
10947 | this._interpolationConfig = _interpolationConfig;
|
10948 | this._schemaRegistry = _schemaRegistry;
|
10949 | this.errors = errors;
|
10950 | this.pipesByName = null;
|
10951 | this._usedPipes = new Map();
|
10952 | // When the `pipes` parameter is `null`, do not check for used pipes
|
10953 | // This is used in IVY when we might not know the available pipes at compile time
|
10954 | if (pipes) {
|
10955 | const pipesByName = new Map();
|
10956 | pipes.forEach(pipe => pipesByName.set(pipe.name, pipe));
|
10957 | this.pipesByName = pipesByName;
|
10958 | }
|
10959 | }
|
10960 | get interpolationConfig() {
|
10961 | return this._interpolationConfig;
|
10962 | }
|
10963 | getUsedPipes() {
|
10964 | return Array.from(this._usedPipes.values());
|
10965 | }
|
10966 | createBoundHostProperties(dirMeta, sourceSpan) {
|
10967 | if (dirMeta.hostProperties) {
|
10968 | const boundProps = [];
|
10969 | Object.keys(dirMeta.hostProperties).forEach(propName => {
|
10970 | const expression = dirMeta.hostProperties[propName];
|
10971 | if (typeof expression === 'string') {
|
10972 | this.parsePropertyBinding(propName, expression, true, sourceSpan, sourceSpan.start.offset, undefined, [],
|
10973 | // Use the `sourceSpan` for `keySpan`. This isn't really accurate, but neither is the
|
10974 | // sourceSpan, as it represents the sourceSpan of the host itself rather than the
|
10975 | // source of the host binding (which doesn't exist in the template). Regardless,
|
10976 | // neither of these values are used in Ivy but are only here to satisfy the function
|
10977 | // signature. This should likely be refactored in the future so that `sourceSpan`
|
10978 | // isn't being used inaccurately.
|
10979 | boundProps, sourceSpan);
|
10980 | }
|
10981 | else {
|
10982 | this._reportError(`Value of the host property binding "${propName}" needs to be a string representing an expression but got "${expression}" (${typeof expression})`, sourceSpan);
|
10983 | }
|
10984 | });
|
10985 | return boundProps;
|
10986 | }
|
10987 | return null;
|
10988 | }
|
10989 | createDirectiveHostPropertyAsts(dirMeta, elementSelector, sourceSpan) {
|
10990 | const boundProps = this.createBoundHostProperties(dirMeta, sourceSpan);
|
10991 | return boundProps &&
|
10992 | boundProps.map((prop) => this.createBoundElementProperty(elementSelector, prop));
|
10993 | }
|
10994 | createDirectiveHostEventAsts(dirMeta, sourceSpan) {
|
10995 | if (dirMeta.hostListeners) {
|
10996 | const targetEvents = [];
|
10997 | Object.keys(dirMeta.hostListeners).forEach(propName => {
|
10998 | const expression = dirMeta.hostListeners[propName];
|
10999 | if (typeof expression === 'string') {
|
11000 | // Use the `sourceSpan` for `keySpan` and `handlerSpan`. This isn't really accurate, but
|
11001 | // neither is the `sourceSpan`, as it represents the `sourceSpan` of the host itself
|
11002 | // rather than the source of the host binding (which doesn't exist in the template).
|
11003 | // Regardless, neither of these values are used in Ivy but are only here to satisfy the
|
11004 | // function signature. This should likely be refactored in the future so that `sourceSpan`
|
11005 | // isn't being used inaccurately.
|
11006 | this.parseEvent(propName, expression, sourceSpan, sourceSpan, [], targetEvents, sourceSpan);
|
11007 | }
|
11008 | else {
|
11009 | this._reportError(`Value of the host listener "${propName}" needs to be a string representing an expression but got "${expression}" (${typeof expression})`, sourceSpan);
|
11010 | }
|
11011 | });
|
11012 | return targetEvents;
|
11013 | }
|
11014 | return null;
|
11015 | }
|
11016 | parseInterpolation(value, sourceSpan) {
|
11017 | const sourceInfo = sourceSpan.start.toString();
|
11018 | const absoluteOffset = sourceSpan.fullStart.offset;
|
11019 | try {
|
11020 | const ast = this._exprParser.parseInterpolation(value, sourceInfo, absoluteOffset, this._interpolationConfig);
|
11021 | if (ast)
|
11022 | this._reportExpressionParserErrors(ast.errors, sourceSpan);
|
11023 | this._checkPipes(ast, sourceSpan);
|
11024 | return ast;
|
11025 | }
|
11026 | catch (e) {
|
11027 | this._reportError(`${e}`, sourceSpan);
|
11028 | return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo, absoluteOffset);
|
11029 | }
|
11030 | }
|
11031 | /**
|
11032 | * Similar to `parseInterpolation`, but treats the provided string as a single expression
|
11033 | * element that would normally appear within the interpolation prefix and suffix (`{{` and `}}`).
|
11034 | * This is used for parsing the switch expression in ICUs.
|
11035 | */
|
11036 | parseInterpolationExpression(expression, sourceSpan) {
|
11037 | const sourceInfo = sourceSpan.start.toString();
|
11038 | const absoluteOffset = sourceSpan.start.offset;
|
11039 | try {
|
11040 | const ast = this._exprParser.parseInterpolationExpression(expression, sourceInfo, absoluteOffset);
|
11041 | if (ast)
|
11042 | this._reportExpressionParserErrors(ast.errors, sourceSpan);
|
11043 | this._checkPipes(ast, sourceSpan);
|
11044 | return ast;
|
11045 | }
|
11046 | catch (e) {
|
11047 | this._reportError(`${e}`, sourceSpan);
|
11048 | return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo, absoluteOffset);
|
11049 | }
|
11050 | }
|
11051 | /**
|
11052 | * Parses the bindings in a microsyntax expression, and converts them to
|
11053 | * `ParsedProperty` or `ParsedVariable`.
|
11054 | *
|
11055 | * @param tplKey template binding name
|
11056 | * @param tplValue template binding value
|
11057 | * @param sourceSpan span of template binding relative to entire the template
|
11058 | * @param absoluteValueOffset start of the tplValue relative to the entire template
|
11059 | * @param targetMatchableAttrs potential attributes to match in the template
|
11060 | * @param targetProps target property bindings in the template
|
11061 | * @param targetVars target variables in the template
|
11062 | */
|
11063 | parseInlineTemplateBinding(tplKey, tplValue, sourceSpan, absoluteValueOffset, targetMatchableAttrs, targetProps, targetVars, isIvyAst) {
|
11064 | const absoluteKeyOffset = sourceSpan.start.offset + TEMPLATE_ATTR_PREFIX.length;
|
11065 | const bindings = this._parseTemplateBindings(tplKey, tplValue, sourceSpan, absoluteKeyOffset, absoluteValueOffset);
|
11066 | for (const binding of bindings) {
|
11067 | // sourceSpan is for the entire HTML attribute. bindingSpan is for a particular
|
11068 | // binding within the microsyntax expression so it's more narrow than sourceSpan.
|
11069 | const bindingSpan = moveParseSourceSpan(sourceSpan, binding.sourceSpan);
|
11070 | const key = binding.key.source;
|
11071 | const keySpan = moveParseSourceSpan(sourceSpan, binding.key.span);
|
11072 | if (binding instanceof VariableBinding) {
|
11073 | const value = binding.value ? binding.value.source : '$implicit';
|
11074 | const valueSpan = binding.value ? moveParseSourceSpan(sourceSpan, binding.value.span) : undefined;
|
11075 | targetVars.push(new ParsedVariable(key, value, bindingSpan, keySpan, valueSpan));
|
11076 | }
|
11077 | else if (binding.value) {
|
11078 | const srcSpan = isIvyAst ? bindingSpan : sourceSpan;
|
11079 | const valueSpan = moveParseSourceSpan(sourceSpan, binding.value.ast.sourceSpan);
|
11080 | this._parsePropertyAst(key, binding.value, srcSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps);
|
11081 | }
|
11082 | else {
|
11083 | targetMatchableAttrs.push([key, '' /* value */]);
|
11084 | // Since this is a literal attribute with no RHS, source span should be
|
11085 | // just the key span.
|
11086 | this.parseLiteralAttr(key, null /* value */, keySpan, absoluteValueOffset, undefined /* valueSpan */, targetMatchableAttrs, targetProps, keySpan);
|
11087 | }
|
11088 | }
|
11089 | }
|
11090 | /**
|
11091 | * Parses the bindings in a microsyntax expression, e.g.
|
11092 | * ```
|
11093 | * <tag *tplKey="let value1 = prop; let value2 = localVar">
|
11094 | * ```
|
11095 | *
|
11096 | * @param tplKey template binding name
|
11097 | * @param tplValue template binding value
|
11098 | * @param sourceSpan span of template binding relative to entire the template
|
11099 | * @param absoluteKeyOffset start of the `tplKey`
|
11100 | * @param absoluteValueOffset start of the `tplValue`
|
11101 | */
|
11102 | _parseTemplateBindings(tplKey, tplValue, sourceSpan, absoluteKeyOffset, absoluteValueOffset) {
|
11103 | const sourceInfo = sourceSpan.start.toString();
|
11104 | try {
|
11105 | const bindingsResult = this._exprParser.parseTemplateBindings(tplKey, tplValue, sourceInfo, absoluteKeyOffset, absoluteValueOffset);
|
11106 | this._reportExpressionParserErrors(bindingsResult.errors, sourceSpan);
|
11107 | bindingsResult.templateBindings.forEach((binding) => {
|
11108 | if (binding.value instanceof ASTWithSource) {
|
11109 | this._checkPipes(binding.value, sourceSpan);
|
11110 | }
|
11111 | });
|
11112 | bindingsResult.warnings.forEach((warning) => {
|
11113 | this._reportError(warning, sourceSpan, ParseErrorLevel.WARNING);
|
11114 | });
|
11115 | return bindingsResult.templateBindings;
|
11116 | }
|
11117 | catch (e) {
|
11118 | this._reportError(`${e}`, sourceSpan);
|
11119 | return [];
|
11120 | }
|
11121 | }
|
11122 | parseLiteralAttr(name, value, sourceSpan, absoluteOffset, valueSpan, targetMatchableAttrs,
|
11123 | // TODO(atscott): keySpan is only optional here so VE template parser implementation does not
|
11124 | // have to change This should be required when VE is removed.
|
11125 | targetProps, keySpan) {
|
11126 | if (isAnimationLabel(name)) {
|
11127 | name = name.substring(1);
|
11128 | if (keySpan !== undefined) {
|
11129 | keySpan = moveParseSourceSpan(keySpan, new AbsoluteSourceSpan(keySpan.start.offset + 1, keySpan.end.offset));
|
11130 | }
|
11131 | if (value) {
|
11132 | this._reportError(`Assigning animation triggers via @prop="exp" attributes with an expression is invalid.` +
|
11133 | ` Use property bindings (e.g. [@prop]="exp") or use an attribute without a value (e.g. @prop) instead.`, sourceSpan, ParseErrorLevel.ERROR);
|
11134 | }
|
11135 | this._parseAnimation(name, value, sourceSpan, absoluteOffset, keySpan, valueSpan, targetMatchableAttrs, targetProps);
|
11136 | }
|
11137 | else {
|
11138 | targetProps.push(new ParsedProperty(name, this._exprParser.wrapLiteralPrimitive(value, '', absoluteOffset), ParsedPropertyType.LITERAL_ATTR, sourceSpan, keySpan, valueSpan));
|
11139 | }
|
11140 | }
|
11141 | parsePropertyBinding(name, expression, isHost, sourceSpan, absoluteOffset, valueSpan,
|
11142 | // TODO(atscott): keySpan is only optional here so VE template parser implementation does not
|
11143 | // have to change This should be required when VE is removed.
|
11144 | targetMatchableAttrs, targetProps, keySpan) {
|
11145 | if (name.length === 0) {
|
11146 | this._reportError(`Property name is missing in binding`, sourceSpan);
|
11147 | }
|
11148 | let isAnimationProp = false;
|
11149 | if (name.startsWith(ANIMATE_PROP_PREFIX)) {
|
11150 | isAnimationProp = true;
|
11151 | name = name.substring(ANIMATE_PROP_PREFIX.length);
|
11152 | if (keySpan !== undefined) {
|
11153 | keySpan = moveParseSourceSpan(keySpan, new AbsoluteSourceSpan(keySpan.start.offset + ANIMATE_PROP_PREFIX.length, keySpan.end.offset));
|
11154 | }
|
11155 | }
|
11156 | else if (isAnimationLabel(name)) {
|
11157 | isAnimationProp = true;
|
11158 | name = name.substring(1);
|
11159 | if (keySpan !== undefined) {
|
11160 | keySpan = moveParseSourceSpan(keySpan, new AbsoluteSourceSpan(keySpan.start.offset + 1, keySpan.end.offset));
|
11161 | }
|
11162 | }
|
11163 | if (isAnimationProp) {
|
11164 | this._parseAnimation(name, expression, sourceSpan, absoluteOffset, keySpan, valueSpan, targetMatchableAttrs, targetProps);
|
11165 | }
|
11166 | else {
|
11167 | this._parsePropertyAst(name, this._parseBinding(expression, isHost, valueSpan || sourceSpan, absoluteOffset), sourceSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps);
|
11168 | }
|
11169 | }
|
11170 | parsePropertyInterpolation(name, value, sourceSpan, valueSpan, targetMatchableAttrs,
|
11171 | // TODO(atscott): keySpan is only optional here so VE template parser implementation does not
|
11172 | // have to change This should be required when VE is removed.
|
11173 | targetProps, keySpan) {
|
11174 | const expr = this.parseInterpolation(value, valueSpan || sourceSpan);
|
11175 | if (expr) {
|
11176 | this._parsePropertyAst(name, expr, sourceSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps);
|
11177 | return true;
|
11178 | }
|
11179 | return false;
|
11180 | }
|
11181 | _parsePropertyAst(name, ast, sourceSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps) {
|
11182 | targetMatchableAttrs.push([name, ast.source]);
|
11183 | targetProps.push(new ParsedProperty(name, ast, ParsedPropertyType.DEFAULT, sourceSpan, keySpan, valueSpan));
|
11184 | }
|
11185 | _parseAnimation(name, expression, sourceSpan, absoluteOffset, keySpan, valueSpan, targetMatchableAttrs, targetProps) {
|
11186 | if (name.length === 0) {
|
11187 | this._reportError('Animation trigger is missing', sourceSpan);
|
11188 | }
|
11189 | // This will occur when a @trigger is not paired with an expression.
|
11190 | // For animations it is valid to not have an expression since */void
|
11191 | // states will be applied by angular when the element is attached/detached
|
11192 | const ast = this._parseBinding(expression || 'undefined', false, valueSpan || sourceSpan, absoluteOffset);
|
11193 | targetMatchableAttrs.push([name, ast.source]);
|
11194 | targetProps.push(new ParsedProperty(name, ast, ParsedPropertyType.ANIMATION, sourceSpan, keySpan, valueSpan));
|
11195 | }
|
11196 | _parseBinding(value, isHostBinding, sourceSpan, absoluteOffset) {
|
11197 | const sourceInfo = (sourceSpan && sourceSpan.start || '(unknown)').toString();
|
11198 | try {
|
11199 | const ast = isHostBinding ?
|
11200 | this._exprParser.parseSimpleBinding(value, sourceInfo, absoluteOffset, this._interpolationConfig) :
|
11201 | this._exprParser.parseBinding(value, sourceInfo, absoluteOffset, this._interpolationConfig);
|
11202 | if (ast)
|
11203 | this._reportExpressionParserErrors(ast.errors, sourceSpan);
|
11204 | this._checkPipes(ast, sourceSpan);
|
11205 | return ast;
|
11206 | }
|
11207 | catch (e) {
|
11208 | this._reportError(`${e}`, sourceSpan);
|
11209 | return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo, absoluteOffset);
|
11210 | }
|
11211 | }
|
11212 | createBoundElementProperty(elementSelector, boundProp, skipValidation = false, mapPropertyName = true) {
|
11213 | if (boundProp.isAnimation) {
|
11214 | return new BoundElementProperty(boundProp.name, 4 /* Animation */, SecurityContext.NONE, boundProp.expression, null, boundProp.sourceSpan, boundProp.keySpan, boundProp.valueSpan);
|
11215 | }
|
11216 | let unit = null;
|
11217 | let bindingType = undefined;
|
11218 | let boundPropertyName = null;
|
11219 | const parts = boundProp.name.split(PROPERTY_PARTS_SEPARATOR);
|
11220 | let securityContexts = undefined;
|
11221 | // Check for special cases (prefix style, attr, class)
|
11222 | if (parts.length > 1) {
|
11223 | if (parts[0] == ATTRIBUTE_PREFIX) {
|
11224 | boundPropertyName = parts.slice(1).join(PROPERTY_PARTS_SEPARATOR);
|
11225 | if (!skipValidation) {
|
11226 | this._validatePropertyOrAttributeName(boundPropertyName, boundProp.sourceSpan, true);
|
11227 | }
|
11228 | securityContexts = calcPossibleSecurityContexts(this._schemaRegistry, elementSelector, boundPropertyName, true);
|
11229 | const nsSeparatorIdx = boundPropertyName.indexOf(':');
|
11230 | if (nsSeparatorIdx > -1) {
|
11231 | const ns = boundPropertyName.substring(0, nsSeparatorIdx);
|
11232 | const name = boundPropertyName.substring(nsSeparatorIdx + 1);
|
11233 | boundPropertyName = mergeNsAndName(ns, name);
|
11234 | }
|
11235 | bindingType = 1 /* Attribute */;
|
11236 | }
|
11237 | else if (parts[0] == CLASS_PREFIX) {
|
11238 | boundPropertyName = parts[1];
|
11239 | bindingType = 2 /* Class */;
|
11240 | securityContexts = [SecurityContext.NONE];
|
11241 | }
|
11242 | else if (parts[0] == STYLE_PREFIX) {
|
11243 | unit = parts.length > 2 ? parts[2] : null;
|
11244 | boundPropertyName = parts[1];
|
11245 | bindingType = 3 /* Style */;
|
11246 | securityContexts = [SecurityContext.STYLE];
|
11247 | }
|
11248 | }
|
11249 | // If not a special case, use the full property name
|
11250 | if (boundPropertyName === null) {
|
11251 | const mappedPropName = this._schemaRegistry.getMappedPropName(boundProp.name);
|
11252 | boundPropertyName = mapPropertyName ? mappedPropName : boundProp.name;
|
11253 | securityContexts = calcPossibleSecurityContexts(this._schemaRegistry, elementSelector, mappedPropName, false);
|
11254 | bindingType = 0 /* Property */;
|
11255 | if (!skipValidation) {
|
11256 | this._validatePropertyOrAttributeName(mappedPropName, boundProp.sourceSpan, false);
|
11257 | }
|
11258 | }
|
11259 | return new BoundElementProperty(boundPropertyName, bindingType, securityContexts[0], boundProp.expression, unit, boundProp.sourceSpan, boundProp.keySpan, boundProp.valueSpan);
|
11260 | }
|
11261 | // TODO: keySpan should be required but was made optional to avoid changing VE parser.
|
11262 | parseEvent(name, expression, sourceSpan, handlerSpan, targetMatchableAttrs, targetEvents, keySpan) {
|
11263 | if (name.length === 0) {
|
11264 | this._reportError(`Event name is missing in binding`, sourceSpan);
|
11265 | }
|
11266 | if (isAnimationLabel(name)) {
|
11267 | name = name.substr(1);
|
11268 | if (keySpan !== undefined) {
|
11269 | keySpan = moveParseSourceSpan(keySpan, new AbsoluteSourceSpan(keySpan.start.offset + 1, keySpan.end.offset));
|
11270 | }
|
11271 | this._parseAnimationEvent(name, expression, sourceSpan, handlerSpan, targetEvents, keySpan);
|
11272 | }
|
11273 | else {
|
11274 | this._parseRegularEvent(name, expression, sourceSpan, handlerSpan, targetMatchableAttrs, targetEvents, keySpan);
|
11275 | }
|
11276 | }
|
11277 | calcPossibleSecurityContexts(selector, propName, isAttribute) {
|
11278 | const prop = this._schemaRegistry.getMappedPropName(propName);
|
11279 | return calcPossibleSecurityContexts(this._schemaRegistry, selector, prop, isAttribute);
|
11280 | }
|
11281 | _parseAnimationEvent(name, expression, sourceSpan, handlerSpan, targetEvents, keySpan) {
|
11282 | const matches = splitAtPeriod(name, [name, '']);
|
11283 | const eventName = matches[0];
|
11284 | const phase = matches[1].toLowerCase();
|
11285 | const ast = this._parseAction(expression, handlerSpan);
|
11286 | targetEvents.push(new ParsedEvent(eventName, phase, 1 /* Animation */, ast, sourceSpan, handlerSpan, keySpan));
|
11287 | if (eventName.length === 0) {
|
11288 | this._reportError(`Animation event name is missing in binding`, sourceSpan);
|
11289 | }
|
11290 | if (phase) {
|
11291 | if (phase !== 'start' && phase !== 'done') {
|
11292 | this._reportError(`The provided animation output phase value "${phase}" for "@${eventName}" is not supported (use start or done)`, sourceSpan);
|
11293 | }
|
11294 | }
|
11295 | else {
|
11296 | this._reportError(`The animation trigger output event (@${eventName}) is missing its phase value name (start or done are currently supported)`, sourceSpan);
|
11297 | }
|
11298 | }
|
11299 | _parseRegularEvent(name, expression, sourceSpan, handlerSpan, targetMatchableAttrs, targetEvents, keySpan) {
|
11300 | // long format: 'target: eventName'
|
11301 | const [target, eventName] = splitAtColon(name, [null, name]);
|
11302 | const ast = this._parseAction(expression, handlerSpan);
|
11303 | targetMatchableAttrs.push([name, ast.source]);
|
11304 | targetEvents.push(new ParsedEvent(eventName, target, 0 /* Regular */, ast, sourceSpan, handlerSpan, keySpan));
|
11305 | // Don't detect directives for event names for now,
|
11306 | // so don't add the event name to the matchableAttrs
|
11307 | }
|
11308 | _parseAction(value, sourceSpan) {
|
11309 | const sourceInfo = (sourceSpan && sourceSpan.start || '(unknown').toString();
|
11310 | const absoluteOffset = (sourceSpan && sourceSpan.start) ? sourceSpan.start.offset : 0;
|
11311 | try {
|
11312 | const ast = this._exprParser.parseAction(value, sourceInfo, absoluteOffset, this._interpolationConfig);
|
11313 | if (ast) {
|
11314 | this._reportExpressionParserErrors(ast.errors, sourceSpan);
|
11315 | }
|
11316 | if (!ast || ast.ast instanceof EmptyExpr) {
|
11317 | this._reportError(`Empty expressions are not allowed`, sourceSpan);
|
11318 | return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo, absoluteOffset);
|
11319 | }
|
11320 | this._checkPipes(ast, sourceSpan);
|
11321 | return ast;
|
11322 | }
|
11323 | catch (e) {
|
11324 | this._reportError(`${e}`, sourceSpan);
|
11325 | return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo, absoluteOffset);
|
11326 | }
|
11327 | }
|
11328 | _reportError(message, sourceSpan, level = ParseErrorLevel.ERROR) {
|
11329 | this.errors.push(new ParseError(sourceSpan, message, level));
|
11330 | }
|
11331 | _reportExpressionParserErrors(errors, sourceSpan) {
|
11332 | for (const error of errors) {
|
11333 | this._reportError(error.message, sourceSpan);
|
11334 | }
|
11335 | }
|
11336 | // Make sure all the used pipes are known in `this.pipesByName`
|
11337 | _checkPipes(ast, sourceSpan) {
|
11338 | if (ast && this.pipesByName) {
|
11339 | const collector = new PipeCollector();
|
11340 | ast.visit(collector);
|
11341 | collector.pipes.forEach((ast, pipeName) => {
|
11342 | const pipeMeta = this.pipesByName.get(pipeName);
|
11343 | if (!pipeMeta) {
|
11344 | this._reportError(`The pipe '${pipeName}' could not be found`, new ParseSourceSpan(sourceSpan.start.moveBy(ast.span.start), sourceSpan.start.moveBy(ast.span.end)));
|
11345 | }
|
11346 | else {
|
11347 | this._usedPipes.set(pipeName, pipeMeta);
|
11348 | }
|
11349 | });
|
11350 | }
|
11351 | }
|
11352 | /**
|
11353 | * @param propName the name of the property / attribute
|
11354 | * @param sourceSpan
|
11355 | * @param isAttr true when binding to an attribute
|
11356 | */
|
11357 | _validatePropertyOrAttributeName(propName, sourceSpan, isAttr) {
|
11358 | const report = isAttr ? this._schemaRegistry.validateAttribute(propName) :
|
11359 | this._schemaRegistry.validateProperty(propName);
|
11360 | if (report.error) {
|
11361 | this._reportError(report.msg, sourceSpan, ParseErrorLevel.ERROR);
|
11362 | }
|
11363 | }
|
11364 | }
|
11365 | class PipeCollector extends RecursiveAstVisitor {
|
11366 | constructor() {
|
11367 | super(...arguments);
|
11368 | this.pipes = new Map();
|
11369 | }
|
11370 | visitPipe(ast, context) {
|
11371 | this.pipes.set(ast.name, ast);
|
11372 | ast.exp.visit(this);
|
11373 | this.visitAll(ast.args, context);
|
11374 | return null;
|
11375 | }
|
11376 | }
|
11377 | function isAnimationLabel(name) {
|
11378 | return name[0] == '@';
|
11379 | }
|
11380 | function calcPossibleSecurityContexts(registry, selector, propName, isAttribute) {
|
11381 | const ctxs = [];
|
11382 | CssSelector.parse(selector).forEach((selector) => {
|
11383 | const elementNames = selector.element ? [selector.element] : registry.allKnownElementNames();
|
11384 | const notElementNames = new Set(selector.notSelectors.filter(selector => selector.isElementSelector())
|
11385 | .map((selector) => selector.element));
|
11386 | const possibleElementNames = elementNames.filter(elementName => !notElementNames.has(elementName));
|
11387 | ctxs.push(...possibleElementNames.map(elementName => registry.securityContext(elementName, propName, isAttribute)));
|
11388 | });
|
11389 | return ctxs.length === 0 ? [SecurityContext.NONE] : Array.from(new Set(ctxs)).sort();
|
11390 | }
|
11391 | /**
|
11392 | * Compute a new ParseSourceSpan based off an original `sourceSpan` by using
|
11393 | * absolute offsets from the specified `absoluteSpan`.
|
11394 | *
|
11395 | * @param sourceSpan original source span
|
11396 | * @param absoluteSpan absolute source span to move to
|
11397 | */
|
11398 | function moveParseSourceSpan(sourceSpan, absoluteSpan) {
|
11399 | // The difference of two absolute offsets provide the relative offset
|
11400 | const startDiff = absoluteSpan.start - sourceSpan.start.offset;
|
11401 | const endDiff = absoluteSpan.end - sourceSpan.end.offset;
|
11402 | return new ParseSourceSpan(sourceSpan.start.moveBy(startDiff), sourceSpan.end.moveBy(endDiff), sourceSpan.fullStart.moveBy(startDiff), sourceSpan.details);
|
11403 | }
|
11404 |
|
11405 | /**
|
11406 | * @license
|
11407 | * Copyright Google LLC All Rights Reserved.
|
11408 | *
|
11409 | * Use of this source code is governed by an MIT-style license that can be
|
11410 | * found in the LICENSE file at https://angular.io/license
|
11411 | */
|
11412 | const NG_CONTENT_SELECT_ATTR = 'select';
|
11413 | const LINK_ELEMENT = 'link';
|
11414 | const LINK_STYLE_REL_ATTR = 'rel';
|
11415 | const LINK_STYLE_HREF_ATTR = 'href';
|
11416 | const LINK_STYLE_REL_VALUE = 'stylesheet';
|
11417 | const STYLE_ELEMENT = 'style';
|
11418 | const SCRIPT_ELEMENT = 'script';
|
11419 | const NG_NON_BINDABLE_ATTR = 'ngNonBindable';
|
11420 | const NG_PROJECT_AS = 'ngProjectAs';
|
11421 | function preparseElement(ast) {
|
11422 | let selectAttr = null;
|
11423 | let hrefAttr = null;
|
11424 | let relAttr = null;
|
11425 | let nonBindable = false;
|
11426 | let projectAs = '';
|
11427 | ast.attrs.forEach(attr => {
|
11428 | const lcAttrName = attr.name.toLowerCase();
|
11429 | if (lcAttrName == NG_CONTENT_SELECT_ATTR) {
|
11430 | selectAttr = attr.value;
|
11431 | }
|
11432 | else if (lcAttrName == LINK_STYLE_HREF_ATTR) {
|
11433 | hrefAttr = attr.value;
|
11434 | }
|
11435 | else if (lcAttrName == LINK_STYLE_REL_ATTR) {
|
11436 | relAttr = attr.value;
|
11437 | }
|
11438 | else if (attr.name == NG_NON_BINDABLE_ATTR) {
|
11439 | nonBindable = true;
|
11440 | }
|
11441 | else if (attr.name == NG_PROJECT_AS) {
|
11442 | if (attr.value.length > 0) {
|
11443 | projectAs = attr.value;
|
11444 | }
|
11445 | }
|
11446 | });
|
11447 | selectAttr = normalizeNgContentSelect(selectAttr);
|
11448 | const nodeName = ast.name.toLowerCase();
|
11449 | let type = PreparsedElementType.OTHER;
|
11450 | if (isNgContent(nodeName)) {
|
11451 | type = PreparsedElementType.NG_CONTENT;
|
11452 | }
|
11453 | else if (nodeName == STYLE_ELEMENT) {
|
11454 | type = PreparsedElementType.STYLE;
|
11455 | }
|
11456 | else if (nodeName == SCRIPT_ELEMENT) {
|
11457 | type = PreparsedElementType.SCRIPT;
|
11458 | }
|
11459 | else if (nodeName == LINK_ELEMENT && relAttr == LINK_STYLE_REL_VALUE) {
|
11460 | type = PreparsedElementType.STYLESHEET;
|
11461 | }
|
11462 | return new PreparsedElement(type, selectAttr, hrefAttr, nonBindable, projectAs);
|
11463 | }
|
11464 | var PreparsedElementType;
|
11465 | (function (PreparsedElementType) {
|
11466 | PreparsedElementType[PreparsedElementType["NG_CONTENT"] = 0] = "NG_CONTENT";
|
11467 | PreparsedElementType[PreparsedElementType["STYLE"] = 1] = "STYLE";
|
11468 | PreparsedElementType[PreparsedElementType["STYLESHEET"] = 2] = "STYLESHEET";
|
11469 | PreparsedElementType[PreparsedElementType["SCRIPT"] = 3] = "SCRIPT";
|
11470 | PreparsedElementType[PreparsedElementType["OTHER"] = 4] = "OTHER";
|
11471 | })(PreparsedElementType || (PreparsedElementType = {}));
|
11472 | class PreparsedElement {
|
11473 | constructor(type, selectAttr, hrefAttr, nonBindable, projectAs) {
|
11474 | this.type = type;
|
11475 | this.selectAttr = selectAttr;
|
11476 | this.hrefAttr = hrefAttr;
|
11477 | this.nonBindable = nonBindable;
|
11478 | this.projectAs = projectAs;
|
11479 | }
|
11480 | }
|
11481 | function normalizeNgContentSelect(selectAttr) {
|
11482 | if (selectAttr === null || selectAttr.length === 0) {
|
11483 | return '*';
|
11484 | }
|
11485 | return selectAttr;
|
11486 | }
|
11487 |
|
11488 | /**
|
11489 | * @license
|
11490 | * Copyright Google LLC All Rights Reserved.
|
11491 | *
|
11492 | * Use of this source code is governed by an MIT-style license that can be
|
11493 | * found in the LICENSE file at https://angular.io/license
|
11494 | */
|
11495 | const BIND_NAME_REGEXP = /^(?:(?:(?:(bind-)|(let-)|(ref-|#)|(on-)|(bindon-)|(@))(.*))|\[\(([^\)]+)\)\]|\[([^\]]+)\]|\(([^\)]+)\))$/;
|
11496 | // Group 1 = "bind-"
|
11497 | const KW_BIND_IDX = 1;
|
11498 | // Group 2 = "let-"
|
11499 | const KW_LET_IDX = 2;
|
11500 | // Group 3 = "ref-/#"
|
11501 | const KW_REF_IDX = 3;
|
11502 | // Group 4 = "on-"
|
11503 | const KW_ON_IDX = 4;
|
11504 | // Group 5 = "bindon-"
|
11505 | const KW_BINDON_IDX = 5;
|
11506 | // Group 6 = "@"
|
11507 | const KW_AT_IDX = 6;
|
11508 | // Group 7 = the identifier after "bind-", "let-", "ref-/#", "on-", "bindon-" or "@"
|
11509 | const IDENT_KW_IDX = 7;
|
11510 | // Group 8 = identifier inside [()]
|
11511 | const IDENT_BANANA_BOX_IDX = 8;
|
11512 | // Group 9 = identifier inside []
|
11513 | const IDENT_PROPERTY_IDX = 9;
|
11514 | // Group 10 = identifier inside ()
|
11515 | const IDENT_EVENT_IDX = 10;
|
11516 | const TEMPLATE_ATTR_PREFIX$1 = '*';
|
11517 | const CLASS_ATTR = 'class';
|
11518 | let _TEXT_CSS_SELECTOR;
|
11519 | function TEXT_CSS_SELECTOR() {
|
11520 | if (!_TEXT_CSS_SELECTOR) {
|
11521 | _TEXT_CSS_SELECTOR = CssSelector.parse('*')[0];
|
11522 | }
|
11523 | return _TEXT_CSS_SELECTOR;
|
11524 | }
|
11525 | class TemplateParseError extends ParseError {
|
11526 | constructor(message, span, level) {
|
11527 | super(span, message, level);
|
11528 | }
|
11529 | }
|
11530 | class TemplateParseResult {
|
11531 | constructor(templateAst, usedPipes, errors) {
|
11532 | this.templateAst = templateAst;
|
11533 | this.usedPipes = usedPipes;
|
11534 | this.errors = errors;
|
11535 | }
|
11536 | }
|
11537 | class TemplateParser {
|
11538 | constructor(_config, _reflector, _exprParser, _schemaRegistry, _htmlParser, _console, transforms) {
|
11539 | this._config = _config;
|
11540 | this._reflector = _reflector;
|
11541 | this._exprParser = _exprParser;
|
11542 | this._schemaRegistry = _schemaRegistry;
|
11543 | this._htmlParser = _htmlParser;
|
11544 | this._console = _console;
|
11545 | this.transforms = transforms;
|
11546 | }
|
11547 | get expressionParser() {
|
11548 | return this._exprParser;
|
11549 | }
|
11550 | parse(component, template, directives, pipes, schemas, templateUrl, preserveWhitespaces) {
|
11551 | var _a;
|
11552 | const result = this.tryParse(component, template, directives, pipes, schemas, templateUrl, preserveWhitespaces);
|
11553 | const warnings = result.errors.filter(error => error.level === ParseErrorLevel.WARNING);
|
11554 | const errors = result.errors.filter(error => error.level === ParseErrorLevel.ERROR);
|
11555 | if (warnings.length > 0) {
|
11556 | (_a = this._console) === null || _a === void 0 ? void 0 : _a.warn(`Template parse warnings:\n${warnings.join('\n')}`);
|
11557 | }
|
11558 | if (errors.length > 0) {
|
11559 | const errorString = errors.join('\n');
|
11560 | throw syntaxError(`Template parse errors:\n${errorString}`, errors);
|
11561 | }
|
11562 | return { template: result.templateAst, pipes: result.usedPipes };
|
11563 | }
|
11564 | tryParse(component, template, directives, pipes, schemas, templateUrl, preserveWhitespaces) {
|
11565 | let htmlParseResult = typeof template === 'string' ?
|
11566 | this._htmlParser.parse(template, templateUrl, {
|
11567 | tokenizeExpansionForms: true,
|
11568 | interpolationConfig: this.getInterpolationConfig(component)
|
11569 | }) :
|
11570 | template;
|
11571 | if (!preserveWhitespaces) {
|
11572 | htmlParseResult = removeWhitespaces(htmlParseResult);
|
11573 | }
|
11574 | return this.tryParseHtml(this.expandHtml(htmlParseResult), component, directives, pipes, schemas);
|
11575 | }
|
11576 | tryParseHtml(htmlAstWithErrors, component, directives, pipes, schemas) {
|
11577 | let result;
|
11578 | const errors = htmlAstWithErrors.errors;
|
11579 | const usedPipes = [];
|
11580 | if (htmlAstWithErrors.rootNodes.length > 0) {
|
11581 | const uniqDirectives = removeSummaryDuplicates(directives);
|
11582 | const uniqPipes = removeSummaryDuplicates(pipes);
|
11583 | const providerViewContext = new ProviderViewContext(this._reflector, component);
|
11584 | let interpolationConfig = undefined;
|
11585 | if (component.template && component.template.interpolation) {
|
11586 | interpolationConfig = {
|
11587 | start: component.template.interpolation[0],
|
11588 | end: component.template.interpolation[1]
|
11589 | };
|
11590 | }
|
11591 | const bindingParser = new BindingParser(this._exprParser, interpolationConfig, this._schemaRegistry, uniqPipes, errors);
|
11592 | const parseVisitor = new TemplateParseVisitor(this._reflector, this._config, providerViewContext, uniqDirectives, bindingParser, this._schemaRegistry, schemas, errors);
|
11593 | result = visitAll$1(parseVisitor, htmlAstWithErrors.rootNodes, EMPTY_ELEMENT_CONTEXT);
|
11594 | errors.push(...providerViewContext.errors);
|
11595 | usedPipes.push(...bindingParser.getUsedPipes());
|
11596 | }
|
11597 | else {
|
11598 | result = [];
|
11599 | }
|
11600 | this._assertNoReferenceDuplicationOnTemplate(result, errors);
|
11601 | if (errors.length > 0) {
|
11602 | return new TemplateParseResult(result, usedPipes, errors);
|
11603 | }
|
11604 | if (this.transforms) {
|
11605 | this.transforms.forEach((transform) => {
|
11606 | result = templateVisitAll(transform, result);
|
11607 | });
|
11608 | }
|
11609 | return new TemplateParseResult(result, usedPipes, errors);
|
11610 | }
|
11611 | expandHtml(htmlAstWithErrors, forced = false) {
|
11612 | const errors = htmlAstWithErrors.errors;
|
11613 | if (errors.length == 0 || forced) {
|
11614 | // Transform ICU messages to angular directives
|
11615 | const expandedHtmlAst = expandNodes(htmlAstWithErrors.rootNodes);
|
11616 | errors.push(...expandedHtmlAst.errors);
|
11617 | htmlAstWithErrors = new ParseTreeResult(expandedHtmlAst.nodes, errors);
|
11618 | }
|
11619 | return htmlAstWithErrors;
|
11620 | }
|
11621 | getInterpolationConfig(component) {
|
11622 | if (component.template) {
|
11623 | return InterpolationConfig.fromArray(component.template.interpolation);
|
11624 | }
|
11625 | return undefined;
|
11626 | }
|
11627 | /** @internal */
|
11628 | _assertNoReferenceDuplicationOnTemplate(result, errors) {
|
11629 | const existingReferences = [];
|
11630 | result.filter(element => !!element.references)
|
11631 | .forEach(element => element.references.forEach((reference) => {
|
11632 | const name = reference.name;
|
11633 | if (existingReferences.indexOf(name) < 0) {
|
11634 | existingReferences.push(name);
|
11635 | }
|
11636 | else {
|
11637 | const error = new TemplateParseError(`Reference "#${name}" is defined several times`, reference.sourceSpan, ParseErrorLevel.ERROR);
|
11638 | errors.push(error);
|
11639 | }
|
11640 | }));
|
11641 | }
|
11642 | }
|
11643 | class TemplateParseVisitor {
|
11644 | constructor(reflector, config, providerViewContext, directives, _bindingParser, _schemaRegistry, _schemas, _targetErrors) {
|
11645 | this.reflector = reflector;
|
11646 | this.config = config;
|
11647 | this.providerViewContext = providerViewContext;
|
11648 | this._bindingParser = _bindingParser;
|
11649 | this._schemaRegistry = _schemaRegistry;
|
11650 | this._schemas = _schemas;
|
11651 | this._targetErrors = _targetErrors;
|
11652 | this.selectorMatcher = new SelectorMatcher();
|
11653 | this.directivesIndex = new Map();
|
11654 | this.ngContentCount = 0;
|
11655 | // Note: queries start with id 1 so we can use the number in a Bloom filter!
|
11656 | this.contentQueryStartId = providerViewContext.component.viewQueries.length + 1;
|
11657 | directives.forEach((directive, index) => {
|
11658 | const selector = CssSelector.parse(directive.selector);
|
11659 | this.selectorMatcher.addSelectables(selector, directive);
|
11660 | this.directivesIndex.set(directive, index);
|
11661 | });
|
11662 | }
|
11663 | visitExpansion(expansion, context) {
|
11664 | return null;
|
11665 | }
|
11666 | visitExpansionCase(expansionCase, context) {
|
11667 | return null;
|
11668 | }
|
11669 | visitText(text, parent) {
|
11670 | const ngContentIndex = parent.findNgContentIndex(TEXT_CSS_SELECTOR());
|
11671 | const valueNoNgsp = replaceNgsp(text.value);
|
11672 | const expr = this._bindingParser.parseInterpolation(valueNoNgsp, text.sourceSpan);
|
11673 | return expr ? new BoundTextAst(expr, ngContentIndex, text.sourceSpan) :
|
11674 | new TextAst(valueNoNgsp, ngContentIndex, text.sourceSpan);
|
11675 | }
|
11676 | visitAttribute(attribute, context) {
|
11677 | return new AttrAst(attribute.name, attribute.value, attribute.sourceSpan);
|
11678 | }
|
11679 | visitComment(comment, context) {
|
11680 | return null;
|
11681 | }
|
11682 | visitElement(element, parent) {
|
11683 | const queryStartIndex = this.contentQueryStartId;
|
11684 | const elName = element.name;
|
11685 | const preparsedElement = preparseElement(element);
|
11686 | if (preparsedElement.type === PreparsedElementType.SCRIPT ||
|
11687 | preparsedElement.type === PreparsedElementType.STYLE) {
|
11688 | // Skipping <script> for security reasons
|
11689 | // Skipping <style> as we already processed them
|
11690 | // in the StyleCompiler
|
11691 | return null;
|
11692 | }
|
11693 | if (preparsedElement.type === PreparsedElementType.STYLESHEET &&
|
11694 | isStyleUrlResolvable(preparsedElement.hrefAttr)) {
|
11695 | // Skipping stylesheets with either relative urls or package scheme as we already processed
|
11696 | // them in the StyleCompiler
|
11697 | return null;
|
11698 | }
|
11699 | const matchableAttrs = [];
|
11700 | const elementOrDirectiveProps = [];
|
11701 | const elementOrDirectiveRefs = [];
|
11702 | const elementVars = [];
|
11703 | const events = [];
|
11704 | const templateElementOrDirectiveProps = [];
|
11705 | const templateMatchableAttrs = [];
|
11706 | const templateElementVars = [];
|
11707 | let hasInlineTemplates = false;
|
11708 | const attrs = [];
|
11709 | const isTemplateElement = isNgTemplate(element.name);
|
11710 | element.attrs.forEach(attr => {
|
11711 | const parsedVariables = [];
|
11712 | const hasBinding = this._parseAttr(isTemplateElement, attr, matchableAttrs, elementOrDirectiveProps, events, elementOrDirectiveRefs, elementVars);
|
11713 | elementVars.push(...parsedVariables.map(v => VariableAst.fromParsedVariable(v)));
|
11714 | let templateValue;
|
11715 | let templateKey;
|
11716 | const normalizedName = this._normalizeAttributeName(attr.name);
|
11717 | if (normalizedName.startsWith(TEMPLATE_ATTR_PREFIX$1)) {
|
11718 | templateValue = attr.value;
|
11719 | templateKey = normalizedName.substring(TEMPLATE_ATTR_PREFIX$1.length);
|
11720 | }
|
11721 | const hasTemplateBinding = templateValue != null;
|
11722 | if (hasTemplateBinding) {
|
11723 | if (hasInlineTemplates) {
|
11724 | this._reportError(`Can't have multiple template bindings on one element. Use only one attribute prefixed with *`, attr.sourceSpan);
|
11725 | }
|
11726 | hasInlineTemplates = true;
|
11727 | const parsedVariables = [];
|
11728 | const absoluteOffset = (attr.valueSpan || attr.sourceSpan).start.offset;
|
11729 | this._bindingParser.parseInlineTemplateBinding(templateKey, templateValue, attr.sourceSpan, absoluteOffset, templateMatchableAttrs, templateElementOrDirectiveProps, parsedVariables, false /* isIvyAst */);
|
11730 | templateElementVars.push(...parsedVariables.map(v => VariableAst.fromParsedVariable(v)));
|
11731 | }
|
11732 | if (!hasBinding && !hasTemplateBinding) {
|
11733 | // don't include the bindings as attributes as well in the AST
|
11734 | attrs.push(this.visitAttribute(attr, null));
|
11735 | matchableAttrs.push([attr.name, attr.value]);
|
11736 | }
|
11737 | });
|
11738 | const elementCssSelector = createElementCssSelector(elName, matchableAttrs);
|
11739 | const { directives: directiveMetas, matchElement } = this._parseDirectives(this.selectorMatcher, elementCssSelector);
|
11740 | const references = [];
|
11741 | const boundDirectivePropNames = new Set();
|
11742 | const directiveAsts = this._createDirectiveAsts(isTemplateElement, element.name, directiveMetas, elementOrDirectiveProps, elementOrDirectiveRefs, element.sourceSpan, references, boundDirectivePropNames);
|
11743 | const elementProps = this._createElementPropertyAsts(element.name, elementOrDirectiveProps, boundDirectivePropNames);
|
11744 | const isViewRoot = parent.isTemplateElement || hasInlineTemplates;
|
11745 | const providerContext = new ProviderElementContext(this.providerViewContext, parent.providerContext, isViewRoot, directiveAsts, attrs, references, isTemplateElement, queryStartIndex, element.sourceSpan);
|
11746 | const children = visitAll$1(preparsedElement.nonBindable ? NON_BINDABLE_VISITOR : this, element.children, ElementContext.create(isTemplateElement, directiveAsts, isTemplateElement ? parent.providerContext : providerContext));
|
11747 | providerContext.afterElement();
|
11748 | // Override the actual selector when the `ngProjectAs` attribute is provided
|
11749 | const projectionSelector = preparsedElement.projectAs != '' ?
|
11750 | CssSelector.parse(preparsedElement.projectAs)[0] :
|
11751 | elementCssSelector;
|
11752 | const ngContentIndex = parent.findNgContentIndex(projectionSelector);
|
11753 | let parsedElement;
|
11754 | if (preparsedElement.type === PreparsedElementType.NG_CONTENT) {
|
11755 | // `<ng-content>` element
|
11756 | if (element.children && !element.children.every(_isEmptyTextNode)) {
|
11757 | this._reportError(`<ng-content> element cannot have content.`, element.sourceSpan);
|
11758 | }
|
11759 | parsedElement = new NgContentAst(this.ngContentCount++, hasInlineTemplates ? null : ngContentIndex, element.sourceSpan);
|
11760 | }
|
11761 | else if (isTemplateElement) {
|
11762 | // `<ng-template>` element
|
11763 | this._assertAllEventsPublishedByDirectives(directiveAsts, events);
|
11764 | this._assertNoComponentsNorElementBindingsOnTemplate(directiveAsts, elementProps, element.sourceSpan);
|
11765 | parsedElement = new EmbeddedTemplateAst(attrs, events, references, elementVars, providerContext.transformedDirectiveAsts, providerContext.transformProviders, providerContext.transformedHasViewContainer, providerContext.queryMatches, children, hasInlineTemplates ? null : ngContentIndex, element.sourceSpan);
|
11766 | }
|
11767 | else {
|
11768 | // element other than `<ng-content>` and `<ng-template>`
|
11769 | this._assertElementExists(matchElement, element);
|
11770 | this._assertOnlyOneComponent(directiveAsts, element.sourceSpan);
|
11771 | const ngContentIndex = hasInlineTemplates ? null : parent.findNgContentIndex(projectionSelector);
|
11772 | 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);
|
11773 | }
|
11774 | if (hasInlineTemplates) {
|
11775 | // The element as a *-attribute
|
11776 | const templateQueryStartIndex = this.contentQueryStartId;
|
11777 | const templateSelector = createElementCssSelector('ng-template', templateMatchableAttrs);
|
11778 | const { directives } = this._parseDirectives(this.selectorMatcher, templateSelector);
|
11779 | const templateBoundDirectivePropNames = new Set();
|
11780 | const templateDirectiveAsts = this._createDirectiveAsts(true, elName, directives, templateElementOrDirectiveProps, [], element.sourceSpan, [], templateBoundDirectivePropNames);
|
11781 | const templateElementProps = this._createElementPropertyAsts(elName, templateElementOrDirectiveProps, templateBoundDirectivePropNames);
|
11782 | this._assertNoComponentsNorElementBindingsOnTemplate(templateDirectiveAsts, templateElementProps, element.sourceSpan);
|
11783 | const templateProviderContext = new ProviderElementContext(this.providerViewContext, parent.providerContext, parent.isTemplateElement, templateDirectiveAsts, [], [], true, templateQueryStartIndex, element.sourceSpan);
|
11784 | templateProviderContext.afterElement();
|
11785 | parsedElement = new EmbeddedTemplateAst([], [], [], templateElementVars, templateProviderContext.transformedDirectiveAsts, templateProviderContext.transformProviders, templateProviderContext.transformedHasViewContainer, templateProviderContext.queryMatches, [parsedElement], ngContentIndex, element.sourceSpan);
|
11786 | }
|
11787 | return parsedElement;
|
11788 | }
|
11789 | _parseAttr(isTemplateElement, attr, targetMatchableAttrs, targetProps, targetEvents, targetRefs, targetVars) {
|
11790 | const name = this._normalizeAttributeName(attr.name);
|
11791 | const value = attr.value;
|
11792 | const srcSpan = attr.sourceSpan;
|
11793 | const absoluteOffset = attr.valueSpan ? attr.valueSpan.start.offset : srcSpan.start.offset;
|
11794 | const boundEvents = [];
|
11795 | const bindParts = name.match(BIND_NAME_REGEXP);
|
11796 | let hasBinding = false;
|
11797 | if (bindParts !== null) {
|
11798 | hasBinding = true;
|
11799 | if (bindParts[KW_BIND_IDX] != null) {
|
11800 | this._bindingParser.parsePropertyBinding(bindParts[IDENT_KW_IDX], value, false, srcSpan, absoluteOffset, attr.valueSpan, targetMatchableAttrs, targetProps);
|
11801 | }
|
11802 | else if (bindParts[KW_LET_IDX]) {
|
11803 | if (isTemplateElement) {
|
11804 | const identifier = bindParts[IDENT_KW_IDX];
|
11805 | this._parseVariable(identifier, value, srcSpan, targetVars);
|
11806 | }
|
11807 | else {
|
11808 | this._reportError(`"let-" is only supported on ng-template elements.`, srcSpan);
|
11809 | }
|
11810 | }
|
11811 | else if (bindParts[KW_REF_IDX]) {
|
11812 | const identifier = bindParts[IDENT_KW_IDX];
|
11813 | this._parseReference(identifier, value, srcSpan, targetRefs);
|
11814 | }
|
11815 | else if (bindParts[KW_ON_IDX]) {
|
11816 | this._bindingParser.parseEvent(bindParts[IDENT_KW_IDX], value, srcSpan, attr.valueSpan || srcSpan, targetMatchableAttrs, boundEvents);
|
11817 | }
|
11818 | else if (bindParts[KW_BINDON_IDX]) {
|
11819 | this._bindingParser.parsePropertyBinding(bindParts[IDENT_KW_IDX], value, false, srcSpan, absoluteOffset, attr.valueSpan, targetMatchableAttrs, targetProps);
|
11820 | this._parseAssignmentEvent(bindParts[IDENT_KW_IDX], value, srcSpan, attr.valueSpan || srcSpan, targetMatchableAttrs, boundEvents);
|
11821 | }
|
11822 | else if (bindParts[KW_AT_IDX]) {
|
11823 | this._bindingParser.parseLiteralAttr(name, value, srcSpan, absoluteOffset, attr.valueSpan, targetMatchableAttrs, targetProps);
|
11824 | }
|
11825 | else if (bindParts[IDENT_BANANA_BOX_IDX]) {
|
11826 | this._bindingParser.parsePropertyBinding(bindParts[IDENT_BANANA_BOX_IDX], value, false, srcSpan, absoluteOffset, attr.valueSpan, targetMatchableAttrs, targetProps);
|
11827 | this._parseAssignmentEvent(bindParts[IDENT_BANANA_BOX_IDX], value, srcSpan, attr.valueSpan || srcSpan, targetMatchableAttrs, boundEvents);
|
11828 | }
|
11829 | else if (bindParts[IDENT_PROPERTY_IDX]) {
|
11830 | this._bindingParser.parsePropertyBinding(bindParts[IDENT_PROPERTY_IDX], value, false, srcSpan, absoluteOffset, attr.valueSpan, targetMatchableAttrs, targetProps);
|
11831 | }
|
11832 | else if (bindParts[IDENT_EVENT_IDX]) {
|
11833 | this._bindingParser.parseEvent(bindParts[IDENT_EVENT_IDX], value, srcSpan, attr.valueSpan || srcSpan, targetMatchableAttrs, boundEvents);
|
11834 | }
|
11835 | }
|
11836 | else {
|
11837 | hasBinding = this._bindingParser.parsePropertyInterpolation(name, value, srcSpan, attr.valueSpan, targetMatchableAttrs, targetProps);
|
11838 | }
|
11839 | if (!hasBinding) {
|
11840 | this._bindingParser.parseLiteralAttr(name, value, srcSpan, absoluteOffset, attr.valueSpan, targetMatchableAttrs, targetProps);
|
11841 | }
|
11842 | targetEvents.push(...boundEvents.map(e => BoundEventAst.fromParsedEvent(e)));
|
11843 | return hasBinding;
|
11844 | }
|
11845 | _normalizeAttributeName(attrName) {
|
11846 | return /^data-/i.test(attrName) ? attrName.substring(5) : attrName;
|
11847 | }
|
11848 | _parseVariable(identifier, value, sourceSpan, targetVars) {
|
11849 | if (identifier.indexOf('-') > -1) {
|
11850 | this._reportError(`"-" is not allowed in variable names`, sourceSpan);
|
11851 | }
|
11852 | else if (identifier.length === 0) {
|
11853 | this._reportError(`Variable does not have a name`, sourceSpan);
|
11854 | }
|
11855 | targetVars.push(new VariableAst(identifier, value, sourceSpan));
|
11856 | }
|
11857 | _parseReference(identifier, value, sourceSpan, targetRefs) {
|
11858 | if (identifier.indexOf('-') > -1) {
|
11859 | this._reportError(`"-" is not allowed in reference names`, sourceSpan);
|
11860 | }
|
11861 | else if (identifier.length === 0) {
|
11862 | this._reportError(`Reference does not have a name`, sourceSpan);
|
11863 | }
|
11864 | targetRefs.push(new ElementOrDirectiveRef(identifier, value, sourceSpan));
|
11865 | }
|
11866 | _parseAssignmentEvent(name, expression, sourceSpan, valueSpan, targetMatchableAttrs, targetEvents) {
|
11867 | this._bindingParser.parseEvent(`${name}Change`, `${expression}=$event`, sourceSpan, valueSpan, targetMatchableAttrs, targetEvents);
|
11868 | }
|
11869 | _parseDirectives(selectorMatcher, elementCssSelector) {
|
11870 | // Need to sort the directives so that we get consistent results throughout,
|
11871 | // as selectorMatcher uses Maps inside.
|
11872 | // Also deduplicate directives as they might match more than one time!
|
11873 | const directives = newArray(this.directivesIndex.size);
|
11874 | // Whether any directive selector matches on the element name
|
11875 | let matchElement = false;
|
11876 | selectorMatcher.match(elementCssSelector, (selector, directive) => {
|
11877 | directives[this.directivesIndex.get(directive)] = directive;
|
11878 | matchElement = matchElement || selector.hasElementSelector();
|
11879 | });
|
11880 | return {
|
11881 | directives: directives.filter(dir => !!dir),
|
11882 | matchElement,
|
11883 | };
|
11884 | }
|
11885 | _createDirectiveAsts(isTemplateElement, elementName, directives, props, elementOrDirectiveRefs, elementSourceSpan, targetReferences, targetBoundDirectivePropNames) {
|
11886 | const matchedReferences = new Set();
|
11887 | let component = null;
|
11888 | const directiveAsts = directives.map((directive) => {
|
11889 | const sourceSpan = new ParseSourceSpan(elementSourceSpan.start, elementSourceSpan.end, elementSourceSpan.fullStart, `Directive ${identifierName(directive.type)}`);
|
11890 | if (directive.isComponent) {
|
11891 | component = directive;
|
11892 | }
|
11893 | const directiveProperties = [];
|
11894 | const boundProperties = this._bindingParser.createDirectiveHostPropertyAsts(directive, elementName, sourceSpan);
|
11895 | let hostProperties = boundProperties.map(prop => BoundElementPropertyAst.fromBoundProperty(prop));
|
11896 | // Note: We need to check the host properties here as well,
|
11897 | // as we don't know the element name in the DirectiveWrapperCompiler yet.
|
11898 | hostProperties = this._checkPropertiesInSchema(elementName, hostProperties);
|
11899 | const parsedEvents = this._bindingParser.createDirectiveHostEventAsts(directive, sourceSpan);
|
11900 | this._createDirectivePropertyAsts(directive.inputs, props, directiveProperties, targetBoundDirectivePropNames);
|
11901 | elementOrDirectiveRefs.forEach((elOrDirRef) => {
|
11902 | if ((elOrDirRef.value.length === 0 && directive.isComponent) ||
|
11903 | (elOrDirRef.isReferenceToDirective(directive))) {
|
11904 | targetReferences.push(new ReferenceAst(elOrDirRef.name, createTokenForReference(directive.type.reference), elOrDirRef.value, elOrDirRef.sourceSpan));
|
11905 | matchedReferences.add(elOrDirRef.name);
|
11906 | }
|
11907 | });
|
11908 | const hostEvents = parsedEvents.map(e => BoundEventAst.fromParsedEvent(e));
|
11909 | const contentQueryStartId = this.contentQueryStartId;
|
11910 | this.contentQueryStartId += directive.queries.length;
|
11911 | return new DirectiveAst(directive, directiveProperties, hostProperties, hostEvents, contentQueryStartId, sourceSpan);
|
11912 | });
|
11913 | elementOrDirectiveRefs.forEach((elOrDirRef) => {
|
11914 | if (elOrDirRef.value.length > 0) {
|
11915 | if (!matchedReferences.has(elOrDirRef.name)) {
|
11916 | this._reportError(`There is no directive with "exportAs" set to "${elOrDirRef.value}"`, elOrDirRef.sourceSpan);
|
11917 | }
|
11918 | }
|
11919 | else if (!component) {
|
11920 | let refToken = null;
|
11921 | if (isTemplateElement) {
|
11922 | refToken = createTokenForExternalReference(this.reflector, Identifiers.TemplateRef);
|
11923 | }
|
11924 | targetReferences.push(new ReferenceAst(elOrDirRef.name, refToken, elOrDirRef.value, elOrDirRef.sourceSpan));
|
11925 | }
|
11926 | });
|
11927 | return directiveAsts;
|
11928 | }
|
11929 | _createDirectivePropertyAsts(directiveProperties, boundProps, targetBoundDirectiveProps, targetBoundDirectivePropNames) {
|
11930 | if (directiveProperties) {
|
11931 | const boundPropsByName = new Map();
|
11932 | boundProps.forEach(boundProp => {
|
11933 | const prevValue = boundPropsByName.get(boundProp.name);
|
11934 | if (!prevValue || prevValue.isLiteral) {
|
11935 | // give [a]="b" a higher precedence than a="b" on the same element
|
11936 | boundPropsByName.set(boundProp.name, boundProp);
|
11937 | }
|
11938 | });
|
11939 | Object.keys(directiveProperties).forEach(dirProp => {
|
11940 | const elProp = directiveProperties[dirProp];
|
11941 | const boundProp = boundPropsByName.get(elProp);
|
11942 | // Bindings are optional, so this binding only needs to be set up if an expression is given.
|
11943 | if (boundProp) {
|
11944 | targetBoundDirectivePropNames.add(boundProp.name);
|
11945 | if (!isEmptyExpression(boundProp.expression)) {
|
11946 | targetBoundDirectiveProps.push(new BoundDirectivePropertyAst(dirProp, boundProp.name, boundProp.expression, boundProp.sourceSpan));
|
11947 | }
|
11948 | }
|
11949 | });
|
11950 | }
|
11951 | }
|
11952 | _createElementPropertyAsts(elementName, props, boundDirectivePropNames) {
|
11953 | const boundElementProps = [];
|
11954 | props.forEach((prop) => {
|
11955 | if (!prop.isLiteral && !boundDirectivePropNames.has(prop.name)) {
|
11956 | const boundProp = this._bindingParser.createBoundElementProperty(elementName, prop);
|
11957 | boundElementProps.push(BoundElementPropertyAst.fromBoundProperty(boundProp));
|
11958 | }
|
11959 | });
|
11960 | return this._checkPropertiesInSchema(elementName, boundElementProps);
|
11961 | }
|
11962 | _findComponentDirectives(directives) {
|
11963 | return directives.filter(directive => directive.directive.isComponent);
|
11964 | }
|
11965 | _findComponentDirectiveNames(directives) {
|
11966 | return this._findComponentDirectives(directives)
|
11967 | .map(directive => identifierName(directive.directive.type));
|
11968 | }
|
11969 | _assertOnlyOneComponent(directives, sourceSpan) {
|
11970 | const componentTypeNames = this._findComponentDirectiveNames(directives);
|
11971 | if (componentTypeNames.length > 1) {
|
11972 | this._reportError(`More than one component matched on this element.\n` +
|
11973 | `Make sure that only one component's selector can match a given element.\n` +
|
11974 | `Conflicting components: ${componentTypeNames.join(',')}`, sourceSpan);
|
11975 | }
|
11976 | }
|
11977 | /**
|
11978 | * Make sure that non-angular tags conform to the schemas.
|
11979 | *
|
11980 | * Note: An element is considered an angular tag when at least one directive selector matches the
|
11981 | * tag name.
|
11982 | *
|
11983 | * @param matchElement Whether any directive has matched on the tag name
|
11984 | * @param element the html element
|
11985 | */
|
11986 | _assertElementExists(matchElement, element) {
|
11987 | const elName = element.name.replace(/^:xhtml:/, '');
|
11988 | if (!matchElement && !this._schemaRegistry.hasElement(elName, this._schemas)) {
|
11989 | let errorMsg = `'${elName}' is not a known element:\n`;
|
11990 | errorMsg += `1. If '${elName}' is an Angular component, then verify that it is part of this module.\n`;
|
11991 | if (elName.indexOf('-') > -1) {
|
11992 | errorMsg += `2. If '${elName}' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message.`;
|
11993 | }
|
11994 | else {
|
11995 | errorMsg +=
|
11996 | `2. To allow any element add 'NO_ERRORS_SCHEMA' to the '@NgModule.schemas' of this component.`;
|
11997 | }
|
11998 | this._reportError(errorMsg, element.sourceSpan);
|
11999 | }
|
12000 | }
|
12001 | _assertNoComponentsNorElementBindingsOnTemplate(directives, elementProps, sourceSpan) {
|
12002 | const componentTypeNames = this._findComponentDirectiveNames(directives);
|
12003 | if (componentTypeNames.length > 0) {
|
12004 | this._reportError(`Components on an embedded template: ${componentTypeNames.join(',')}`, sourceSpan);
|
12005 | }
|
12006 | elementProps.forEach(prop => {
|
12007 | 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);
|
12008 | });
|
12009 | }
|
12010 | _assertAllEventsPublishedByDirectives(directives, events) {
|
12011 | const allDirectiveEvents = new Set();
|
12012 | directives.forEach(directive => {
|
12013 | Object.keys(directive.directive.outputs).forEach(k => {
|
12014 | const eventName = directive.directive.outputs[k];
|
12015 | allDirectiveEvents.add(eventName);
|
12016 | });
|
12017 | });
|
12018 | events.forEach(event => {
|
12019 | if (event.target != null || !allDirectiveEvents.has(event.name)) {
|
12020 | this._reportError(`Event binding ${event
|
12021 | .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);
|
12022 | }
|
12023 | });
|
12024 | }
|
12025 | _checkPropertiesInSchema(elementName, boundProps) {
|
12026 | // Note: We can't filter out empty expressions before this method,
|
12027 | // as we still want to validate them!
|
12028 | return boundProps.filter((boundProp) => {
|
12029 | if (boundProp.type === 0 /* Property */ &&
|
12030 | !this._schemaRegistry.hasProperty(elementName, boundProp.name, this._schemas)) {
|
12031 | let errorMsg = `Can't bind to '${boundProp.name}' since it isn't a known property of '${elementName}'.`;
|
12032 | if (elementName.startsWith('ng-')) {
|
12033 | errorMsg +=
|
12034 | `\n1. If '${boundProp
|
12035 | .name}' is an Angular directive, then add 'CommonModule' to the '@NgModule.imports' of this component.` +
|
12036 | `\n2. To allow any property add 'NO_ERRORS_SCHEMA' to the '@NgModule.schemas' of this component.`;
|
12037 | }
|
12038 | else if (elementName.indexOf('-') > -1) {
|
12039 | errorMsg +=
|
12040 | `\n1. If '${elementName}' is an Angular component and it has '${boundProp.name}' input, then verify that it is part of this module.` +
|
12041 | `\n2. If '${elementName}' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message.` +
|
12042 | `\n3. To allow any property add 'NO_ERRORS_SCHEMA' to the '@NgModule.schemas' of this component.`;
|
12043 | }
|
12044 | this._reportError(errorMsg, boundProp.sourceSpan);
|
12045 | }
|
12046 | return !isEmptyExpression(boundProp.value);
|
12047 | });
|
12048 | }
|
12049 | _reportError(message, sourceSpan, level = ParseErrorLevel.ERROR) {
|
12050 | this._targetErrors.push(new ParseError(sourceSpan, message, level));
|
12051 | }
|
12052 | }
|
12053 | class NonBindableVisitor {
|
12054 | visitElement(ast, parent) {
|
12055 | const preparsedElement = preparseElement(ast);
|
12056 | if (preparsedElement.type === PreparsedElementType.SCRIPT ||
|
12057 | preparsedElement.type === PreparsedElementType.STYLE ||
|
12058 | preparsedElement.type === PreparsedElementType.STYLESHEET) {
|
12059 | // Skipping <script> for security reasons
|
12060 | // Skipping <style> and stylesheets as we already processed them
|
12061 | // in the StyleCompiler
|
12062 | return null;
|
12063 | }
|
12064 | const attrNameAndValues = ast.attrs.map((attr) => [attr.name, attr.value]);
|
12065 | const selector = createElementCssSelector(ast.name, attrNameAndValues);
|
12066 | const ngContentIndex = parent.findNgContentIndex(selector);
|
12067 | const children = visitAll$1(this, ast.children, EMPTY_ELEMENT_CONTEXT);
|
12068 | return new ElementAst(ast.name, visitAll$1(this, ast.attrs), [], [], [], [], [], false, [], children, ngContentIndex, ast.sourceSpan, ast.endSourceSpan);
|
12069 | }
|
12070 | visitComment(comment, context) {
|
12071 | return null;
|
12072 | }
|
12073 | visitAttribute(attribute, context) {
|
12074 | return new AttrAst(attribute.name, attribute.value, attribute.sourceSpan);
|
12075 | }
|
12076 | visitText(text, parent) {
|
12077 | const ngContentIndex = parent.findNgContentIndex(TEXT_CSS_SELECTOR());
|
12078 | return new TextAst(text.value, ngContentIndex, text.sourceSpan);
|
12079 | }
|
12080 | visitExpansion(expansion, context) {
|
12081 | return expansion;
|
12082 | }
|
12083 | visitExpansionCase(expansionCase, context) {
|
12084 | return expansionCase;
|
12085 | }
|
12086 | }
|
12087 | /**
|
12088 | * A reference to an element or directive in a template. E.g., the reference in this template:
|
12089 | *
|
12090 | * <div #myMenu="coolMenu">
|
12091 | *
|
12092 | * would be {name: 'myMenu', value: 'coolMenu', sourceSpan: ...}
|
12093 | */
|
12094 | class ElementOrDirectiveRef {
|
12095 | constructor(name, value, sourceSpan) {
|
12096 | this.name = name;
|
12097 | this.value = value;
|
12098 | this.sourceSpan = sourceSpan;
|
12099 | }
|
12100 | /** Gets whether this is a reference to the given directive. */
|
12101 | isReferenceToDirective(directive) {
|
12102 | return splitExportAs(directive.exportAs).indexOf(this.value) !== -1;
|
12103 | }
|
12104 | }
|
12105 | /** Splits a raw, potentially comma-delimited `exportAs` value into an array of names. */
|
12106 | function splitExportAs(exportAs) {
|
12107 | return exportAs ? exportAs.split(',').map(e => e.trim()) : [];
|
12108 | }
|
12109 | function splitClasses(classAttrValue) {
|
12110 | return classAttrValue.trim().split(/\s+/g);
|
12111 | }
|
12112 | class ElementContext {
|
12113 | constructor(isTemplateElement, _ngContentIndexMatcher, _wildcardNgContentIndex, providerContext) {
|
12114 | this.isTemplateElement = isTemplateElement;
|
12115 | this._ngContentIndexMatcher = _ngContentIndexMatcher;
|
12116 | this._wildcardNgContentIndex = _wildcardNgContentIndex;
|
12117 | this.providerContext = providerContext;
|
12118 | }
|
12119 | static create(isTemplateElement, directives, providerContext) {
|
12120 | const matcher = new SelectorMatcher();
|
12121 | let wildcardNgContentIndex = null;
|
12122 | const component = directives.find(directive => directive.directive.isComponent);
|
12123 | if (component) {
|
12124 | const ngContentSelectors = component.directive.template.ngContentSelectors;
|
12125 | for (let i = 0; i < ngContentSelectors.length; i++) {
|
12126 | const selector = ngContentSelectors[i];
|
12127 | if (selector === '*') {
|
12128 | wildcardNgContentIndex = i;
|
12129 | }
|
12130 | else {
|
12131 | matcher.addSelectables(CssSelector.parse(ngContentSelectors[i]), i);
|
12132 | }
|
12133 | }
|
12134 | }
|
12135 | return new ElementContext(isTemplateElement, matcher, wildcardNgContentIndex, providerContext);
|
12136 | }
|
12137 | findNgContentIndex(selector) {
|
12138 | const ngContentIndices = [];
|
12139 | this._ngContentIndexMatcher.match(selector, (selector, ngContentIndex) => {
|
12140 | ngContentIndices.push(ngContentIndex);
|
12141 | });
|
12142 | ngContentIndices.sort();
|
12143 | if (this._wildcardNgContentIndex != null) {
|
12144 | ngContentIndices.push(this._wildcardNgContentIndex);
|
12145 | }
|
12146 | return ngContentIndices.length > 0 ? ngContentIndices[0] : null;
|
12147 | }
|
12148 | }
|
12149 | function createElementCssSelector(elementName, attributes) {
|
12150 | const cssSelector = new CssSelector();
|
12151 | const elNameNoNs = splitNsName(elementName)[1];
|
12152 | cssSelector.setElement(elNameNoNs);
|
12153 | for (let i = 0; i < attributes.length; i++) {
|
12154 | const attrName = attributes[i][0];
|
12155 | const attrNameNoNs = splitNsName(attrName)[1];
|
12156 | const attrValue = attributes[i][1];
|
12157 | cssSelector.addAttribute(attrNameNoNs, attrValue);
|
12158 | if (attrName.toLowerCase() == CLASS_ATTR) {
|
12159 | const classes = splitClasses(attrValue);
|
12160 | classes.forEach(className => cssSelector.addClassName(className));
|
12161 | }
|
12162 | }
|
12163 | return cssSelector;
|
12164 | }
|
12165 | const EMPTY_ELEMENT_CONTEXT = new ElementContext(true, new SelectorMatcher(), null, null);
|
12166 | const NON_BINDABLE_VISITOR = new NonBindableVisitor();
|
12167 | function _isEmptyTextNode(node) {
|
12168 | return node instanceof Text$2 && node.value.trim().length == 0;
|
12169 | }
|
12170 | function removeSummaryDuplicates(items) {
|
12171 | const map = new Map();
|
12172 | items.forEach((item) => {
|
12173 | if (!map.get(item.type.reference)) {
|
12174 | map.set(item.type.reference, item);
|
12175 | }
|
12176 | });
|
12177 | return Array.from(map.values());
|
12178 | }
|
12179 | function isEmptyExpression(ast) {
|
12180 | if (ast instanceof ASTWithSource) {
|
12181 | ast = ast.ast;
|
12182 | }
|
12183 | return ast instanceof EmptyExpr;
|
12184 | }
|
12185 |
|
12186 | /**
|
12187 | * @license
|
12188 | * Copyright Google LLC All Rights Reserved.
|
12189 | *
|
12190 | * Use of this source code is governed by an MIT-style license that can be
|
12191 | * found in the LICENSE file at https://angular.io/license
|
12192 | */
|
12193 | /**
|
12194 | * Parses string representation of a style and converts it into object literal.
|
12195 | *
|
12196 | * @param value string representation of style as used in the `style` attribute in HTML.
|
12197 | * Example: `color: red; height: auto`.
|
12198 | * @returns An array of style property name and value pairs, e.g. `['color', 'red', 'height',
|
12199 | * 'auto']`
|
12200 | */
|
12201 | function parse(value) {
|
12202 | // we use a string array here instead of a string map
|
12203 | // because a string-map is not guaranteed to retain the
|
12204 | // order of the entries whereas a string array can be
|
12205 | // constructed in a [key, value, key, value] format.
|
12206 | const styles = [];
|
12207 | let i = 0;
|
12208 | let parenDepth = 0;
|
12209 | let quote = 0 /* QuoteNone */;
|
12210 | let valueStart = 0;
|
12211 | let propStart = 0;
|
12212 | let currentProp = null;
|
12213 | let valueHasQuotes = false;
|
12214 | while (i < value.length) {
|
12215 | const token = value.charCodeAt(i++);
|
12216 | switch (token) {
|
12217 | case 40 /* OpenParen */:
|
12218 | parenDepth++;
|
12219 | break;
|
12220 | case 41 /* CloseParen */:
|
12221 | parenDepth--;
|
12222 | break;
|
12223 | case 39 /* QuoteSingle */:
|
12224 | // valueStart needs to be there since prop values don't
|
12225 | // have quotes in CSS
|
12226 | valueHasQuotes = valueHasQuotes || valueStart > 0;
|
12227 | if (quote === 0 /* QuoteNone */) {
|
12228 | quote = 39 /* QuoteSingle */;
|
12229 | }
|
12230 | else if (quote === 39 /* QuoteSingle */ && value.charCodeAt(i - 1) !== 92 /* BackSlash */) {
|
12231 | quote = 0 /* QuoteNone */;
|
12232 | }
|
12233 | break;
|
12234 | case 34 /* QuoteDouble */:
|
12235 | // same logic as above
|
12236 | valueHasQuotes = valueHasQuotes || valueStart > 0;
|
12237 | if (quote === 0 /* QuoteNone */) {
|
12238 | quote = 34 /* QuoteDouble */;
|
12239 | }
|
12240 | else if (quote === 34 /* QuoteDouble */ && value.charCodeAt(i - 1) !== 92 /* BackSlash */) {
|
12241 | quote = 0 /* QuoteNone */;
|
12242 | }
|
12243 | break;
|
12244 | case 58 /* Colon */:
|
12245 | if (!currentProp && parenDepth === 0 && quote === 0 /* QuoteNone */) {
|
12246 | currentProp = hyphenate(value.substring(propStart, i - 1).trim());
|
12247 | valueStart = i;
|
12248 | }
|
12249 | break;
|
12250 | case 59 /* Semicolon */:
|
12251 | if (currentProp && valueStart > 0 && parenDepth === 0 && quote === 0 /* QuoteNone */) {
|
12252 | const styleVal = value.substring(valueStart, i - 1).trim();
|
12253 | styles.push(currentProp, valueHasQuotes ? stripUnnecessaryQuotes(styleVal) : styleVal);
|
12254 | propStart = i;
|
12255 | valueStart = 0;
|
12256 | currentProp = null;
|
12257 | valueHasQuotes = false;
|
12258 | }
|
12259 | break;
|
12260 | }
|
12261 | }
|
12262 | if (currentProp && valueStart) {
|
12263 | const styleVal = value.substr(valueStart).trim();
|
12264 | styles.push(currentProp, valueHasQuotes ? stripUnnecessaryQuotes(styleVal) : styleVal);
|
12265 | }
|
12266 | return styles;
|
12267 | }
|
12268 | function stripUnnecessaryQuotes(value) {
|
12269 | const qS = value.charCodeAt(0);
|
12270 | const qE = value.charCodeAt(value.length - 1);
|
12271 | if (qS == qE && (qS == 39 /* QuoteSingle */ || qS == 34 /* QuoteDouble */)) {
|
12272 | const tempValue = value.substring(1, value.length - 1);
|
12273 | // special case to avoid using a multi-quoted string that was just chomped
|
12274 | // (e.g. `font-family: "Verdana", "sans-serif"`)
|
12275 | if (tempValue.indexOf('\'') == -1 && tempValue.indexOf('"') == -1) {
|
12276 | value = tempValue;
|
12277 | }
|
12278 | }
|
12279 | return value;
|
12280 | }
|
12281 | function hyphenate(value) {
|
12282 | return value
|
12283 | .replace(/[a-z][A-Z]/g, v => {
|
12284 | return v.charAt(0) + '-' + v.charAt(1);
|
12285 | })
|
12286 | .toLowerCase();
|
12287 | }
|
12288 |
|
12289 | const IMPORTANT_FLAG = '!important';
|
12290 | /**
|
12291 | * Minimum amount of binding slots required in the runtime for style/class bindings.
|
12292 | *
|
12293 | * Styling in Angular uses up two slots in the runtime LView/TData data structures to
|
12294 | * record binding data, property information and metadata.
|
12295 | *
|
12296 | * When a binding is registered it will place the following information in the `LView`:
|
12297 | *
|
12298 | * slot 1) binding value
|
12299 | * slot 2) cached value (all other values collected before it in string form)
|
12300 | *
|
12301 | * When a binding is registered it will place the following information in the `TData`:
|
12302 | *
|
12303 | * slot 1) prop name
|
12304 | * slot 2) binding index that points to the previous style/class binding (and some extra config
|
12305 | * values)
|
12306 | *
|
12307 | * Let's imagine we have a binding that looks like so:
|
12308 | *
|
12309 | * ```
|
12310 | * <div [style.width]="x" [style.height]="y">
|
12311 | * ```
|
12312 | *
|
12313 | * Our `LView` and `TData` data-structures look like so:
|
12314 | *
|
12315 | * ```typescript
|
12316 | * LView = [
|
12317 | * // ...
|
12318 | * x, // value of x
|
12319 | * "width: x",
|
12320 | *
|
12321 | * y, // value of y
|
12322 | * "width: x; height: y",
|
12323 | * // ...
|
12324 | * ];
|
12325 | *
|
12326 | * TData = [
|
12327 | * // ...
|
12328 | * "width", // binding slot 20
|
12329 | * 0,
|
12330 | *
|
12331 | * "height",
|
12332 | * 20,
|
12333 | * // ...
|
12334 | * ];
|
12335 | * ```
|
12336 | *
|
12337 | * */
|
12338 | const MIN_STYLING_BINDING_SLOTS_REQUIRED = 2;
|
12339 | /**
|
12340 | * Produces creation/update instructions for all styling bindings (class and style)
|
12341 | *
|
12342 | * It also produces the creation instruction to register all initial styling values
|
12343 | * (which are all the static class="..." and style="..." attribute values that exist
|
12344 | * on an element within a template).
|
12345 | *
|
12346 | * The builder class below handles producing instructions for the following cases:
|
12347 | *
|
12348 | * - Static style/class attributes (style="..." and class="...")
|
12349 | * - Dynamic style/class map bindings ([style]="map" and [class]="map|string")
|
12350 | * - Dynamic style/class property bindings ([style.prop]="exp" and [class.name]="exp")
|
12351 | *
|
12352 | * Due to the complex relationship of all of these cases, the instructions generated
|
12353 | * for these attributes/properties/bindings must be done so in the correct order. The
|
12354 | * order which these must be generated is as follows:
|
12355 | *
|
12356 | * if (createMode) {
|
12357 | * styling(...)
|
12358 | * }
|
12359 | * if (updateMode) {
|
12360 | * styleMap(...)
|
12361 | * classMap(...)
|
12362 | * styleProp(...)
|
12363 | * classProp(...)
|
12364 | * }
|
12365 | *
|
12366 | * The creation/update methods within the builder class produce these instructions.
|
12367 | */
|
12368 | class StylingBuilder {
|
12369 | constructor(_directiveExpr) {
|
12370 | this._directiveExpr = _directiveExpr;
|
12371 | /** Whether or not there are any static styling values present */
|
12372 | this._hasInitialValues = false;
|
12373 | /**
|
12374 | * Whether or not there are any styling bindings present
|
12375 | * (i.e. `[style]`, `[class]`, `[style.prop]` or `[class.name]`)
|
12376 | */
|
12377 | this.hasBindings = false;
|
12378 | this.hasBindingsWithPipes = false;
|
12379 | /** the input for [class] (if it exists) */
|
12380 | this._classMapInput = null;
|
12381 | /** the input for [style] (if it exists) */
|
12382 | this._styleMapInput = null;
|
12383 | /** an array of each [style.prop] input */
|
12384 | this._singleStyleInputs = null;
|
12385 | /** an array of each [class.name] input */
|
12386 | this._singleClassInputs = null;
|
12387 | this._lastStylingInput = null;
|
12388 | this._firstStylingInput = null;
|
12389 | // maps are used instead of hash maps because a Map will
|
12390 | // retain the ordering of the keys
|
12391 | /**
|
12392 | * Represents the location of each style binding in the template
|
12393 | * (e.g. `<div [style.width]="w" [style.height]="h">` implies
|
12394 | * that `width=0` and `height=1`)
|
12395 | */
|
12396 | this._stylesIndex = new Map();
|
12397 | /**
|
12398 | * Represents the location of each class binding in the template
|
12399 | * (e.g. `<div [class.big]="b" [class.hidden]="h">` implies
|
12400 | * that `big=0` and `hidden=1`)
|
12401 | */
|
12402 | this._classesIndex = new Map();
|
12403 | this._initialStyleValues = [];
|
12404 | this._initialClassValues = [];
|
12405 | }
|
12406 | /**
|
12407 | * Registers a given input to the styling builder to be later used when producing AOT code.
|
12408 | *
|
12409 | * The code below will only accept the input if it is somehow tied to styling (whether it be
|
12410 | * style/class bindings or static style/class attributes).
|
12411 | */
|
12412 | registerBoundInput(input) {
|
12413 | // [attr.style] or [attr.class] are skipped in the code below,
|
12414 | // they should not be treated as styling-based bindings since
|
12415 | // they are intended to be written directly to the attr and
|
12416 | // will therefore skip all style/class resolution that is present
|
12417 | // with style="", [style]="" and [style.prop]="", class="",
|
12418 | // [class.prop]="". [class]="" assignments
|
12419 | let binding = null;
|
12420 | let name = input.name;
|
12421 | switch (input.type) {
|
12422 | case 0 /* Property */:
|
12423 | binding = this.registerInputBasedOnName(name, input.value, input.sourceSpan);
|
12424 | break;
|
12425 | case 3 /* Style */:
|
12426 | binding = this.registerStyleInput(name, false, input.value, input.sourceSpan, input.unit);
|
12427 | break;
|
12428 | case 2 /* Class */:
|
12429 | binding = this.registerClassInput(name, false, input.value, input.sourceSpan);
|
12430 | break;
|
12431 | }
|
12432 | return binding ? true : false;
|
12433 | }
|
12434 | registerInputBasedOnName(name, expression, sourceSpan) {
|
12435 | let binding = null;
|
12436 | const prefix = name.substring(0, 6);
|
12437 | const isStyle = name === 'style' || prefix === 'style.' || prefix === 'style!';
|
12438 | const isClass = !isStyle && (name === 'class' || prefix === 'class.' || prefix === 'class!');
|
12439 | if (isStyle || isClass) {
|
12440 | const isMapBased = name.charAt(5) !== '.'; // style.prop or class.prop makes this a no
|
12441 | const property = name.substr(isMapBased ? 5 : 6); // the dot explains why there's a +1
|
12442 | if (isStyle) {
|
12443 | binding = this.registerStyleInput(property, isMapBased, expression, sourceSpan);
|
12444 | }
|
12445 | else {
|
12446 | binding = this.registerClassInput(property, isMapBased, expression, sourceSpan);
|
12447 | }
|
12448 | }
|
12449 | return binding;
|
12450 | }
|
12451 | registerStyleInput(name, isMapBased, value, sourceSpan, suffix) {
|
12452 | if (isEmptyExpression(value)) {
|
12453 | return null;
|
12454 | }
|
12455 | name = normalizePropName(name);
|
12456 | const { property, hasOverrideFlag, suffix: bindingSuffix } = parseProperty(name);
|
12457 | suffix = typeof suffix === 'string' && suffix.length !== 0 ? suffix : bindingSuffix;
|
12458 | const entry = { name: property, suffix: suffix, value, sourceSpan, hasOverrideFlag };
|
12459 | if (isMapBased) {
|
12460 | this._styleMapInput = entry;
|
12461 | }
|
12462 | else {
|
12463 | (this._singleStyleInputs = this._singleStyleInputs || []).push(entry);
|
12464 | registerIntoMap(this._stylesIndex, property);
|
12465 | }
|
12466 | this._lastStylingInput = entry;
|
12467 | this._firstStylingInput = this._firstStylingInput || entry;
|
12468 | this._checkForPipes(value);
|
12469 | this.hasBindings = true;
|
12470 | return entry;
|
12471 | }
|
12472 | registerClassInput(name, isMapBased, value, sourceSpan) {
|
12473 | if (isEmptyExpression(value)) {
|
12474 | return null;
|
12475 | }
|
12476 | const { property, hasOverrideFlag } = parseProperty(name);
|
12477 | const entry = { name: property, value, sourceSpan, hasOverrideFlag, suffix: null };
|
12478 | if (isMapBased) {
|
12479 | if (this._classMapInput) {
|
12480 | throw new Error('[class] and [className] bindings cannot be used on the same element simultaneously');
|
12481 | }
|
12482 | this._classMapInput = entry;
|
12483 | }
|
12484 | else {
|
12485 | (this._singleClassInputs = this._singleClassInputs || []).push(entry);
|
12486 | registerIntoMap(this._classesIndex, property);
|
12487 | }
|
12488 | this._lastStylingInput = entry;
|
12489 | this._firstStylingInput = this._firstStylingInput || entry;
|
12490 | this._checkForPipes(value);
|
12491 | this.hasBindings = true;
|
12492 | return entry;
|
12493 | }
|
12494 | _checkForPipes(value) {
|
12495 | if ((value instanceof ASTWithSource) && (value.ast instanceof BindingPipe)) {
|
12496 | this.hasBindingsWithPipes = true;
|
12497 | }
|
12498 | }
|
12499 | /**
|
12500 | * Registers the element's static style string value to the builder.
|
12501 | *
|
12502 | * @param value the style string (e.g. `width:100px; height:200px;`)
|
12503 | */
|
12504 | registerStyleAttr(value) {
|
12505 | this._initialStyleValues = parse(value);
|
12506 | this._hasInitialValues = true;
|
12507 | }
|
12508 | /**
|
12509 | * Registers the element's static class string value to the builder.
|
12510 | *
|
12511 | * @param value the className string (e.g. `disabled gold zoom`)
|
12512 | */
|
12513 | registerClassAttr(value) {
|
12514 | this._initialClassValues = value.trim().split(/\s+/g);
|
12515 | this._hasInitialValues = true;
|
12516 | }
|
12517 | /**
|
12518 | * Appends all styling-related expressions to the provided attrs array.
|
12519 | *
|
12520 | * @param attrs an existing array where each of the styling expressions
|
12521 | * will be inserted into.
|
12522 | */
|
12523 | populateInitialStylingAttrs(attrs) {
|
12524 | // [CLASS_MARKER, 'foo', 'bar', 'baz' ...]
|
12525 | if (this._initialClassValues.length) {
|
12526 | attrs.push(literal(1 /* Classes */));
|
12527 | for (let i = 0; i < this._initialClassValues.length; i++) {
|
12528 | attrs.push(literal(this._initialClassValues[i]));
|
12529 | }
|
12530 | }
|
12531 | // [STYLE_MARKER, 'width', '200px', 'height', '100px', ...]
|
12532 | if (this._initialStyleValues.length) {
|
12533 | attrs.push(literal(2 /* Styles */));
|
12534 | for (let i = 0; i < this._initialStyleValues.length; i += 2) {
|
12535 | attrs.push(literal(this._initialStyleValues[i]), literal(this._initialStyleValues[i + 1]));
|
12536 | }
|
12537 | }
|
12538 | }
|
12539 | /**
|
12540 | * Builds an instruction with all the expressions and parameters for `elementHostAttrs`.
|
12541 | *
|
12542 | * The instruction generation code below is used for producing the AOT statement code which is
|
12543 | * responsible for registering initial styles (within a directive hostBindings' creation block),
|
12544 | * as well as any of the provided attribute values, to the directive host element.
|
12545 | */
|
12546 | assignHostAttrs(attrs, definitionMap) {
|
12547 | if (this._directiveExpr && (attrs.length || this._hasInitialValues)) {
|
12548 | this.populateInitialStylingAttrs(attrs);
|
12549 | definitionMap.set('hostAttrs', literalArr(attrs));
|
12550 | }
|
12551 | }
|
12552 | /**
|
12553 | * Builds an instruction with all the expressions and parameters for `classMap`.
|
12554 | *
|
12555 | * The instruction data will contain all expressions for `classMap` to function
|
12556 | * which includes the `[class]` expression params.
|
12557 | */
|
12558 | buildClassMapInstruction(valueConverter) {
|
12559 | if (this._classMapInput) {
|
12560 | return this._buildMapBasedInstruction(valueConverter, true, this._classMapInput);
|
12561 | }
|
12562 | return null;
|
12563 | }
|
12564 | /**
|
12565 | * Builds an instruction with all the expressions and parameters for `styleMap`.
|
12566 | *
|
12567 | * The instruction data will contain all expressions for `styleMap` to function
|
12568 | * which includes the `[style]` expression params.
|
12569 | */
|
12570 | buildStyleMapInstruction(valueConverter) {
|
12571 | if (this._styleMapInput) {
|
12572 | return this._buildMapBasedInstruction(valueConverter, false, this._styleMapInput);
|
12573 | }
|
12574 | return null;
|
12575 | }
|
12576 | _buildMapBasedInstruction(valueConverter, isClassBased, stylingInput) {
|
12577 | // each styling binding value is stored in the LView
|
12578 | // map-based bindings allocate two slots: one for the
|
12579 | // previous binding value and another for the previous
|
12580 | // className or style attribute value.
|
12581 | let totalBindingSlotsRequired = MIN_STYLING_BINDING_SLOTS_REQUIRED;
|
12582 | // these values must be outside of the update block so that they can
|
12583 | // be evaluated (the AST visit call) during creation time so that any
|
12584 | // pipes can be picked up in time before the template is built
|
12585 | const mapValue = stylingInput.value.visit(valueConverter);
|
12586 | let reference;
|
12587 | if (mapValue instanceof Interpolation) {
|
12588 | totalBindingSlotsRequired += mapValue.expressions.length;
|
12589 | reference = isClassBased ? getClassMapInterpolationExpression(mapValue) :
|
12590 | getStyleMapInterpolationExpression(mapValue);
|
12591 | }
|
12592 | else {
|
12593 | reference = isClassBased ? Identifiers$1.classMap : Identifiers$1.styleMap;
|
12594 | }
|
12595 | return {
|
12596 | reference,
|
12597 | calls: [{
|
12598 | supportsInterpolation: true,
|
12599 | sourceSpan: stylingInput.sourceSpan,
|
12600 | allocateBindingSlots: totalBindingSlotsRequired,
|
12601 | params: (convertFn) => {
|
12602 | const convertResult = convertFn(mapValue);
|
12603 | const params = Array.isArray(convertResult) ? convertResult : [convertResult];
|
12604 | return params;
|
12605 | }
|
12606 | }]
|
12607 | };
|
12608 | }
|
12609 | _buildSingleInputs(reference, inputs, valueConverter, getInterpolationExpressionFn, isClassBased) {
|
12610 | const instructions = [];
|
12611 | inputs.forEach(input => {
|
12612 | const previousInstruction = instructions[instructions.length - 1];
|
12613 | const value = input.value.visit(valueConverter);
|
12614 | let referenceForCall = reference;
|
12615 | // each styling binding value is stored in the LView
|
12616 | // but there are two values stored for each binding:
|
12617 | // 1) the value itself
|
12618 | // 2) an intermediate value (concatenation of style up to this point).
|
12619 | // We need to store the intermediate value so that we don't allocate
|
12620 | // the strings on each CD.
|
12621 | let totalBindingSlotsRequired = MIN_STYLING_BINDING_SLOTS_REQUIRED;
|
12622 | if (value instanceof Interpolation) {
|
12623 | totalBindingSlotsRequired += value.expressions.length;
|
12624 | if (getInterpolationExpressionFn) {
|
12625 | referenceForCall = getInterpolationExpressionFn(value);
|
12626 | }
|
12627 | }
|
12628 | const call = {
|
12629 | sourceSpan: input.sourceSpan,
|
12630 | allocateBindingSlots: totalBindingSlotsRequired,
|
12631 | supportsInterpolation: !!getInterpolationExpressionFn,
|
12632 | params: (convertFn) => {
|
12633 | // params => stylingProp(propName, value, suffix)
|
12634 | const params = [];
|
12635 | params.push(literal(input.name));
|
12636 | const convertResult = convertFn(value);
|
12637 | if (Array.isArray(convertResult)) {
|
12638 | params.push(...convertResult);
|
12639 | }
|
12640 | else {
|
12641 | params.push(convertResult);
|
12642 | }
|
12643 | // [style.prop] bindings may use suffix values (e.g. px, em, etc...), therefore,
|
12644 | // if that is detected then we need to pass that in as an optional param.
|
12645 | if (!isClassBased && input.suffix !== null) {
|
12646 | params.push(literal(input.suffix));
|
12647 | }
|
12648 | return params;
|
12649 | }
|
12650 | };
|
12651 | // If we ended up generating a call to the same instruction as the previous styling property
|
12652 | // we can chain the calls together safely to save some bytes, otherwise we have to generate
|
12653 | // a separate instruction call. This is primarily a concern with interpolation instructions
|
12654 | // where we may start off with one `reference`, but end up using another based on the
|
12655 | // number of interpolations.
|
12656 | if (previousInstruction && previousInstruction.reference === referenceForCall) {
|
12657 | previousInstruction.calls.push(call);
|
12658 | }
|
12659 | else {
|
12660 | instructions.push({ reference: referenceForCall, calls: [call] });
|
12661 | }
|
12662 | });
|
12663 | return instructions;
|
12664 | }
|
12665 | _buildClassInputs(valueConverter) {
|
12666 | if (this._singleClassInputs) {
|
12667 | return this._buildSingleInputs(Identifiers$1.classProp, this._singleClassInputs, valueConverter, null, true);
|
12668 | }
|
12669 | return [];
|
12670 | }
|
12671 | _buildStyleInputs(valueConverter) {
|
12672 | if (this._singleStyleInputs) {
|
12673 | return this._buildSingleInputs(Identifiers$1.styleProp, this._singleStyleInputs, valueConverter, getStylePropInterpolationExpression, false);
|
12674 | }
|
12675 | return [];
|
12676 | }
|
12677 | /**
|
12678 | * Constructs all instructions which contain the expressions that will be placed
|
12679 | * into the update block of a template function or a directive hostBindings function.
|
12680 | */
|
12681 | buildUpdateLevelInstructions(valueConverter) {
|
12682 | const instructions = [];
|
12683 | if (this.hasBindings) {
|
12684 | const styleMapInstruction = this.buildStyleMapInstruction(valueConverter);
|
12685 | if (styleMapInstruction) {
|
12686 | instructions.push(styleMapInstruction);
|
12687 | }
|
12688 | const classMapInstruction = this.buildClassMapInstruction(valueConverter);
|
12689 | if (classMapInstruction) {
|
12690 | instructions.push(classMapInstruction);
|
12691 | }
|
12692 | instructions.push(...this._buildStyleInputs(valueConverter));
|
12693 | instructions.push(...this._buildClassInputs(valueConverter));
|
12694 | }
|
12695 | return instructions;
|
12696 | }
|
12697 | }
|
12698 | function registerIntoMap(map, key) {
|
12699 | if (!map.has(key)) {
|
12700 | map.set(key, map.size);
|
12701 | }
|
12702 | }
|
12703 | function parseProperty(name) {
|
12704 | let hasOverrideFlag = false;
|
12705 | const overrideIndex = name.indexOf(IMPORTANT_FLAG);
|
12706 | if (overrideIndex !== -1) {
|
12707 | name = overrideIndex > 0 ? name.substring(0, overrideIndex) : '';
|
12708 | hasOverrideFlag = true;
|
12709 | }
|
12710 | let suffix = null;
|
12711 | let property = name;
|
12712 | const unitIndex = name.lastIndexOf('.');
|
12713 | if (unitIndex > 0) {
|
12714 | suffix = name.substr(unitIndex + 1);
|
12715 | property = name.substring(0, unitIndex);
|
12716 | }
|
12717 | return { property, suffix, hasOverrideFlag };
|
12718 | }
|
12719 | /**
|
12720 | * Gets the instruction to generate for an interpolated class map.
|
12721 | * @param interpolation An Interpolation AST
|
12722 | */
|
12723 | function getClassMapInterpolationExpression(interpolation) {
|
12724 | switch (getInterpolationArgsLength(interpolation)) {
|
12725 | case 1:
|
12726 | return Identifiers$1.classMap;
|
12727 | case 3:
|
12728 | return Identifiers$1.classMapInterpolate1;
|
12729 | case 5:
|
12730 | return Identifiers$1.classMapInterpolate2;
|
12731 | case 7:
|
12732 | return Identifiers$1.classMapInterpolate3;
|
12733 | case 9:
|
12734 | return Identifiers$1.classMapInterpolate4;
|
12735 | case 11:
|
12736 | return Identifiers$1.classMapInterpolate5;
|
12737 | case 13:
|
12738 | return Identifiers$1.classMapInterpolate6;
|
12739 | case 15:
|
12740 | return Identifiers$1.classMapInterpolate7;
|
12741 | case 17:
|
12742 | return Identifiers$1.classMapInterpolate8;
|
12743 | default:
|
12744 | return Identifiers$1.classMapInterpolateV;
|
12745 | }
|
12746 | }
|
12747 | /**
|
12748 | * Gets the instruction to generate for an interpolated style map.
|
12749 | * @param interpolation An Interpolation AST
|
12750 | */
|
12751 | function getStyleMapInterpolationExpression(interpolation) {
|
12752 | switch (getInterpolationArgsLength(interpolation)) {
|
12753 | case 1:
|
12754 | return Identifiers$1.styleMap;
|
12755 | case 3:
|
12756 | return Identifiers$1.styleMapInterpolate1;
|
12757 | case 5:
|
12758 | return Identifiers$1.styleMapInterpolate2;
|
12759 | case 7:
|
12760 | return Identifiers$1.styleMapInterpolate3;
|
12761 | case 9:
|
12762 | return Identifiers$1.styleMapInterpolate4;
|
12763 | case 11:
|
12764 | return Identifiers$1.styleMapInterpolate5;
|
12765 | case 13:
|
12766 | return Identifiers$1.styleMapInterpolate6;
|
12767 | case 15:
|
12768 | return Identifiers$1.styleMapInterpolate7;
|
12769 | case 17:
|
12770 | return Identifiers$1.styleMapInterpolate8;
|
12771 | default:
|
12772 | return Identifiers$1.styleMapInterpolateV;
|
12773 | }
|
12774 | }
|
12775 | /**
|
12776 | * Gets the instruction to generate for an interpolated style prop.
|
12777 | * @param interpolation An Interpolation AST
|
12778 | */
|
12779 | function getStylePropInterpolationExpression(interpolation) {
|
12780 | switch (getInterpolationArgsLength(interpolation)) {
|
12781 | case 1:
|
12782 | return Identifiers$1.styleProp;
|
12783 | case 3:
|
12784 | return Identifiers$1.stylePropInterpolate1;
|
12785 | case 5:
|
12786 | return Identifiers$1.stylePropInterpolate2;
|
12787 | case 7:
|
12788 | return Identifiers$1.stylePropInterpolate3;
|
12789 | case 9:
|
12790 | return Identifiers$1.stylePropInterpolate4;
|
12791 | case 11:
|
12792 | return Identifiers$1.stylePropInterpolate5;
|
12793 | case 13:
|
12794 | return Identifiers$1.stylePropInterpolate6;
|
12795 | case 15:
|
12796 | return Identifiers$1.stylePropInterpolate7;
|
12797 | case 17:
|
12798 | return Identifiers$1.stylePropInterpolate8;
|
12799 | default:
|
12800 | return Identifiers$1.stylePropInterpolateV;
|
12801 | }
|
12802 | }
|
12803 | function normalizePropName(prop) {
|
12804 | return hyphenate(prop);
|
12805 | }
|
12806 |
|
12807 | /**
|
12808 | * @license
|
12809 | * Copyright Google LLC All Rights Reserved.
|
12810 | *
|
12811 | * Use of this source code is governed by an MIT-style license that can be
|
12812 | * found in the LICENSE file at https://angular.io/license
|
12813 | */
|
12814 | var TokenType$1;
|
12815 | (function (TokenType) {
|
12816 | TokenType[TokenType["Character"] = 0] = "Character";
|
12817 | TokenType[TokenType["Identifier"] = 1] = "Identifier";
|
12818 | TokenType[TokenType["Keyword"] = 2] = "Keyword";
|
12819 | TokenType[TokenType["String"] = 3] = "String";
|
12820 | TokenType[TokenType["Operator"] = 4] = "Operator";
|
12821 | TokenType[TokenType["Number"] = 5] = "Number";
|
12822 | TokenType[TokenType["Error"] = 6] = "Error";
|
12823 | })(TokenType$1 || (TokenType$1 = {}));
|
12824 | const KEYWORDS = ['var', 'let', 'as', 'null', 'undefined', 'true', 'false', 'if', 'else', 'this'];
|
12825 | class Lexer {
|
12826 | tokenize(text) {
|
12827 | const scanner = new _Scanner(text);
|
12828 | const tokens = [];
|
12829 | let token = scanner.scanToken();
|
12830 | while (token != null) {
|
12831 | tokens.push(token);
|
12832 | token = scanner.scanToken();
|
12833 | }
|
12834 | return tokens;
|
12835 | }
|
12836 | }
|
12837 | class Token$1 {
|
12838 | constructor(index, end, type, numValue, strValue) {
|
12839 | this.index = index;
|
12840 | this.end = end;
|
12841 | this.type = type;
|
12842 | this.numValue = numValue;
|
12843 | this.strValue = strValue;
|
12844 | }
|
12845 | isCharacter(code) {
|
12846 | return this.type == TokenType$1.Character && this.numValue == code;
|
12847 | }
|
12848 | isNumber() {
|
12849 | return this.type == TokenType$1.Number;
|
12850 | }
|
12851 | isString() {
|
12852 | return this.type == TokenType$1.String;
|
12853 | }
|
12854 | isOperator(operator) {
|
12855 | return this.type == TokenType$1.Operator && this.strValue == operator;
|
12856 | }
|
12857 | isIdentifier() {
|
12858 | return this.type == TokenType$1.Identifier;
|
12859 | }
|
12860 | isKeyword() {
|
12861 | return this.type == TokenType$1.Keyword;
|
12862 | }
|
12863 | isKeywordLet() {
|
12864 | return this.type == TokenType$1.Keyword && this.strValue == 'let';
|
12865 | }
|
12866 | isKeywordAs() {
|
12867 | return this.type == TokenType$1.Keyword && this.strValue == 'as';
|
12868 | }
|
12869 | isKeywordNull() {
|
12870 | return this.type == TokenType$1.Keyword && this.strValue == 'null';
|
12871 | }
|
12872 | isKeywordUndefined() {
|
12873 | return this.type == TokenType$1.Keyword && this.strValue == 'undefined';
|
12874 | }
|
12875 | isKeywordTrue() {
|
12876 | return this.type == TokenType$1.Keyword && this.strValue == 'true';
|
12877 | }
|
12878 | isKeywordFalse() {
|
12879 | return this.type == TokenType$1.Keyword && this.strValue == 'false';
|
12880 | }
|
12881 | isKeywordThis() {
|
12882 | return this.type == TokenType$1.Keyword && this.strValue == 'this';
|
12883 | }
|
12884 | isError() {
|
12885 | return this.type == TokenType$1.Error;
|
12886 | }
|
12887 | toNumber() {
|
12888 | return this.type == TokenType$1.Number ? this.numValue : -1;
|
12889 | }
|
12890 | toString() {
|
12891 | switch (this.type) {
|
12892 | case TokenType$1.Character:
|
12893 | case TokenType$1.Identifier:
|
12894 | case TokenType$1.Keyword:
|
12895 | case TokenType$1.Operator:
|
12896 | case TokenType$1.String:
|
12897 | case TokenType$1.Error:
|
12898 | return this.strValue;
|
12899 | case TokenType$1.Number:
|
12900 | return this.numValue.toString();
|
12901 | default:
|
12902 | return null;
|
12903 | }
|
12904 | }
|
12905 | }
|
12906 | function newCharacterToken(index, end, code) {
|
12907 | return new Token$1(index, end, TokenType$1.Character, code, String.fromCharCode(code));
|
12908 | }
|
12909 | function newIdentifierToken(index, end, text) {
|
12910 | return new Token$1(index, end, TokenType$1.Identifier, 0, text);
|
12911 | }
|
12912 | function newKeywordToken(index, end, text) {
|
12913 | return new Token$1(index, end, TokenType$1.Keyword, 0, text);
|
12914 | }
|
12915 | function newOperatorToken(index, end, text) {
|
12916 | return new Token$1(index, end, TokenType$1.Operator, 0, text);
|
12917 | }
|
12918 | function newStringToken(index, end, text) {
|
12919 | return new Token$1(index, end, TokenType$1.String, 0, text);
|
12920 | }
|
12921 | function newNumberToken(index, end, n) {
|
12922 | return new Token$1(index, end, TokenType$1.Number, n, '');
|
12923 | }
|
12924 | function newErrorToken(index, end, message) {
|
12925 | return new Token$1(index, end, TokenType$1.Error, 0, message);
|
12926 | }
|
12927 | const EOF = new Token$1(-1, -1, TokenType$1.Character, 0, '');
|
12928 | class _Scanner {
|
12929 | constructor(input) {
|
12930 | this.input = input;
|
12931 | this.peek = 0;
|
12932 | this.index = -1;
|
12933 | this.length = input.length;
|
12934 | this.advance();
|
12935 | }
|
12936 | advance() {
|
12937 | this.peek = ++this.index >= this.length ? $EOF : this.input.charCodeAt(this.index);
|
12938 | }
|
12939 | scanToken() {
|
12940 | const input = this.input, length = this.length;
|
12941 | let peek = this.peek, index = this.index;
|
12942 | // Skip whitespace.
|
12943 | while (peek <= $SPACE) {
|
12944 | if (++index >= length) {
|
12945 | peek = $EOF;
|
12946 | break;
|
12947 | }
|
12948 | else {
|
12949 | peek = input.charCodeAt(index);
|
12950 | }
|
12951 | }
|
12952 | this.peek = peek;
|
12953 | this.index = index;
|
12954 | if (index >= length) {
|
12955 | return null;
|
12956 | }
|
12957 | // Handle identifiers and numbers.
|
12958 | if (isIdentifierStart(peek))
|
12959 | return this.scanIdentifier();
|
12960 | if (isDigit(peek))
|
12961 | return this.scanNumber(index);
|
12962 | const start = index;
|
12963 | switch (peek) {
|
12964 | case $PERIOD:
|
12965 | this.advance();
|
12966 | return isDigit(this.peek) ? this.scanNumber(start) :
|
12967 | newCharacterToken(start, this.index, $PERIOD);
|
12968 | case $LPAREN:
|
12969 | case $RPAREN:
|
12970 | case $LBRACE:
|
12971 | case $RBRACE:
|
12972 | case $LBRACKET:
|
12973 | case $RBRACKET:
|
12974 | case $COMMA:
|
12975 | case $COLON:
|
12976 | case $SEMICOLON:
|
12977 | return this.scanCharacter(start, peek);
|
12978 | case $SQ:
|
12979 | case $DQ:
|
12980 | return this.scanString();
|
12981 | case $HASH:
|
12982 | case $PLUS:
|
12983 | case $MINUS:
|
12984 | case $STAR:
|
12985 | case $SLASH:
|
12986 | case $PERCENT:
|
12987 | case $CARET:
|
12988 | return this.scanOperator(start, String.fromCharCode(peek));
|
12989 | case $QUESTION:
|
12990 | return this.scanComplexOperator(start, '?', $PERIOD, '.');
|
12991 | case $LT:
|
12992 | case $GT:
|
12993 | return this.scanComplexOperator(start, String.fromCharCode(peek), $EQ, '=');
|
12994 | case $BANG:
|
12995 | case $EQ:
|
12996 | return this.scanComplexOperator(start, String.fromCharCode(peek), $EQ, '=', $EQ, '=');
|
12997 | case $AMPERSAND:
|
12998 | return this.scanComplexOperator(start, '&', $AMPERSAND, '&');
|
12999 | case $BAR:
|
13000 | return this.scanComplexOperator(start, '|', $BAR, '|');
|
13001 | case $NBSP:
|
13002 | while (isWhitespace(this.peek))
|
13003 | this.advance();
|
13004 | return this.scanToken();
|
13005 | }
|
13006 | this.advance();
|
13007 | return this.error(`Unexpected character [${String.fromCharCode(peek)}]`, 0);
|
13008 | }
|
13009 | scanCharacter(start, code) {
|
13010 | this.advance();
|
13011 | return newCharacterToken(start, this.index, code);
|
13012 | }
|
13013 | scanOperator(start, str) {
|
13014 | this.advance();
|
13015 | return newOperatorToken(start, this.index, str);
|
13016 | }
|
13017 | /**
|
13018 | * Tokenize a 2/3 char long operator
|
13019 | *
|
13020 | * @param start start index in the expression
|
13021 | * @param one first symbol (always part of the operator)
|
13022 | * @param twoCode code point for the second symbol
|
13023 | * @param two second symbol (part of the operator when the second code point matches)
|
13024 | * @param threeCode code point for the third symbol
|
13025 | * @param three third symbol (part of the operator when provided and matches source expression)
|
13026 | */
|
13027 | scanComplexOperator(start, one, twoCode, two, threeCode, three) {
|
13028 | this.advance();
|
13029 | let str = one;
|
13030 | if (this.peek == twoCode) {
|
13031 | this.advance();
|
13032 | str += two;
|
13033 | }
|
13034 | if (threeCode != null && this.peek == threeCode) {
|
13035 | this.advance();
|
13036 | str += three;
|
13037 | }
|
13038 | return newOperatorToken(start, this.index, str);
|
13039 | }
|
13040 | scanIdentifier() {
|
13041 | const start = this.index;
|
13042 | this.advance();
|
13043 | while (isIdentifierPart(this.peek))
|
13044 | this.advance();
|
13045 | const str = this.input.substring(start, this.index);
|
13046 | return KEYWORDS.indexOf(str) > -1 ? newKeywordToken(start, this.index, str) :
|
13047 | newIdentifierToken(start, this.index, str);
|
13048 | }
|
13049 | scanNumber(start) {
|
13050 | let simple = (this.index === start);
|
13051 | this.advance(); // Skip initial digit.
|
13052 | while (true) {
|
13053 | if (isDigit(this.peek)) ;
|
13054 | else if (this.peek == $PERIOD) {
|
13055 | simple = false;
|
13056 | }
|
13057 | else if (isExponentStart(this.peek)) {
|
13058 | this.advance();
|
13059 | if (isExponentSign(this.peek))
|
13060 | this.advance();
|
13061 | if (!isDigit(this.peek))
|
13062 | return this.error('Invalid exponent', -1);
|
13063 | simple = false;
|
13064 | }
|
13065 | else {
|
13066 | break;
|
13067 | }
|
13068 | this.advance();
|
13069 | }
|
13070 | const str = this.input.substring(start, this.index);
|
13071 | const value = simple ? parseIntAutoRadix(str) : parseFloat(str);
|
13072 | return newNumberToken(start, this.index, value);
|
13073 | }
|
13074 | scanString() {
|
13075 | const start = this.index;
|
13076 | const quote = this.peek;
|
13077 | this.advance(); // Skip initial quote.
|
13078 | let buffer = '';
|
13079 | let marker = this.index;
|
13080 | const input = this.input;
|
13081 | while (this.peek != quote) {
|
13082 | if (this.peek == $BACKSLASH) {
|
13083 | buffer += input.substring(marker, this.index);
|
13084 | this.advance();
|
13085 | let unescapedCode;
|
13086 | // Workaround for TS2.1-introduced type strictness
|
13087 | this.peek = this.peek;
|
13088 | if (this.peek == $u) {
|
13089 | // 4 character hex code for unicode character.
|
13090 | const hex = input.substring(this.index + 1, this.index + 5);
|
13091 | if (/^[0-9a-f]+$/i.test(hex)) {
|
13092 | unescapedCode = parseInt(hex, 16);
|
13093 | }
|
13094 | else {
|
13095 | return this.error(`Invalid unicode escape [\\u${hex}]`, 0);
|
13096 | }
|
13097 | for (let i = 0; i < 5; i++) {
|
13098 | this.advance();
|
13099 | }
|
13100 | }
|
13101 | else {
|
13102 | unescapedCode = unescape(this.peek);
|
13103 | this.advance();
|
13104 | }
|
13105 | buffer += String.fromCharCode(unescapedCode);
|
13106 | marker = this.index;
|
13107 | }
|
13108 | else if (this.peek == $EOF) {
|
13109 | return this.error('Unterminated quote', 0);
|
13110 | }
|
13111 | else {
|
13112 | this.advance();
|
13113 | }
|
13114 | }
|
13115 | const last = input.substring(marker, this.index);
|
13116 | this.advance(); // Skip terminating quote.
|
13117 | return newStringToken(start, this.index, buffer + last);
|
13118 | }
|
13119 | error(message, offset) {
|
13120 | const position = this.index + offset;
|
13121 | return newErrorToken(position, this.index, `Lexer Error: ${message} at column ${position} in expression [${this.input}]`);
|
13122 | }
|
13123 | }
|
13124 | function isIdentifierStart(code) {
|
13125 | return ($a <= code && code <= $z) || ($A <= code && code <= $Z) ||
|
13126 | (code == $_) || (code == $$);
|
13127 | }
|
13128 | function isIdentifier(input) {
|
13129 | if (input.length == 0)
|
13130 | return false;
|
13131 | const scanner = new _Scanner(input);
|
13132 | if (!isIdentifierStart(scanner.peek))
|
13133 | return false;
|
13134 | scanner.advance();
|
13135 | while (scanner.peek !== $EOF) {
|
13136 | if (!isIdentifierPart(scanner.peek))
|
13137 | return false;
|
13138 | scanner.advance();
|
13139 | }
|
13140 | return true;
|
13141 | }
|
13142 | function isIdentifierPart(code) {
|
13143 | return isAsciiLetter(code) || isDigit(code) || (code == $_) ||
|
13144 | (code == $$);
|
13145 | }
|
13146 | function isExponentStart(code) {
|
13147 | return code == $e || code == $E;
|
13148 | }
|
13149 | function isExponentSign(code) {
|
13150 | return code == $MINUS || code == $PLUS;
|
13151 | }
|
13152 | function isQuote(code) {
|
13153 | return code === $SQ || code === $DQ || code === $BT;
|
13154 | }
|
13155 | function unescape(code) {
|
13156 | switch (code) {
|
13157 | case $n:
|
13158 | return $LF;
|
13159 | case $f:
|
13160 | return $FF;
|
13161 | case $r:
|
13162 | return $CR;
|
13163 | case $t:
|
13164 | return $TAB;
|
13165 | case $v:
|
13166 | return $VTAB;
|
13167 | default:
|
13168 | return code;
|
13169 | }
|
13170 | }
|
13171 | function parseIntAutoRadix(text) {
|
13172 | const result = parseInt(text);
|
13173 | if (isNaN(result)) {
|
13174 | throw new Error('Invalid integer literal when parsing ' + text);
|
13175 | }
|
13176 | return result;
|
13177 | }
|
13178 |
|
13179 | /**
|
13180 | * @license
|
13181 | * Copyright Google LLC All Rights Reserved.
|
13182 | *
|
13183 | * Use of this source code is governed by an MIT-style license that can be
|
13184 | * found in the LICENSE file at https://angular.io/license
|
13185 | */
|
13186 | class SplitInterpolation {
|
13187 | constructor(strings, expressions, offsets) {
|
13188 | this.strings = strings;
|
13189 | this.expressions = expressions;
|
13190 | this.offsets = offsets;
|
13191 | }
|
13192 | }
|
13193 | class TemplateBindingParseResult {
|
13194 | constructor(templateBindings, warnings, errors) {
|
13195 | this.templateBindings = templateBindings;
|
13196 | this.warnings = warnings;
|
13197 | this.errors = errors;
|
13198 | }
|
13199 | }
|
13200 | class Parser$1 {
|
13201 | constructor(_lexer) {
|
13202 | this._lexer = _lexer;
|
13203 | this.errors = [];
|
13204 | this.simpleExpressionChecker = SimpleExpressionChecker;
|
13205 | }
|
13206 | parseAction(input, location, absoluteOffset, interpolationConfig = DEFAULT_INTERPOLATION_CONFIG) {
|
13207 | this._checkNoInterpolation(input, location, interpolationConfig);
|
13208 | const sourceToLex = this._stripComments(input);
|
13209 | const tokens = this._lexer.tokenize(this._stripComments(input));
|
13210 | const ast = new _ParseAST(input, location, absoluteOffset, tokens, sourceToLex.length, true, this.errors, input.length - sourceToLex.length)
|
13211 | .parseChain();
|
13212 | return new ASTWithSource(ast, input, location, absoluteOffset, this.errors);
|
13213 | }
|
13214 | parseBinding(input, location, absoluteOffset, interpolationConfig = DEFAULT_INTERPOLATION_CONFIG) {
|
13215 | const ast = this._parseBindingAst(input, location, absoluteOffset, interpolationConfig);
|
13216 | return new ASTWithSource(ast, input, location, absoluteOffset, this.errors);
|
13217 | }
|
13218 | checkSimpleExpression(ast) {
|
13219 | const checker = new this.simpleExpressionChecker();
|
13220 | ast.visit(checker);
|
13221 | return checker.errors;
|
13222 | }
|
13223 | parseSimpleBinding(input, location, absoluteOffset, interpolationConfig = DEFAULT_INTERPOLATION_CONFIG) {
|
13224 | const ast = this._parseBindingAst(input, location, absoluteOffset, interpolationConfig);
|
13225 | const errors = this.checkSimpleExpression(ast);
|
13226 | if (errors.length > 0) {
|
13227 | this._reportError(`Host binding expression cannot contain ${errors.join(' ')}`, input, location);
|
13228 | }
|
13229 | return new ASTWithSource(ast, input, location, absoluteOffset, this.errors);
|
13230 | }
|
13231 | _reportError(message, input, errLocation, ctxLocation) {
|
13232 | this.errors.push(new ParserError(message, input, errLocation, ctxLocation));
|
13233 | }
|
13234 | _parseBindingAst(input, location, absoluteOffset, interpolationConfig) {
|
13235 | // Quotes expressions use 3rd-party expression language. We don't want to use
|
13236 | // our lexer or parser for that, so we check for that ahead of time.
|
13237 | const quote = this._parseQuote(input, location, absoluteOffset);
|
13238 | if (quote != null) {
|
13239 | return quote;
|
13240 | }
|
13241 | this._checkNoInterpolation(input, location, interpolationConfig);
|
13242 | const sourceToLex = this._stripComments(input);
|
13243 | const tokens = this._lexer.tokenize(sourceToLex);
|
13244 | return new _ParseAST(input, location, absoluteOffset, tokens, sourceToLex.length, false, this.errors, input.length - sourceToLex.length)
|
13245 | .parseChain();
|
13246 | }
|
13247 | _parseQuote(input, location, absoluteOffset) {
|
13248 | if (input == null)
|
13249 | return null;
|
13250 | const prefixSeparatorIndex = input.indexOf(':');
|
13251 | if (prefixSeparatorIndex == -1)
|
13252 | return null;
|
13253 | const prefix = input.substring(0, prefixSeparatorIndex).trim();
|
13254 | if (!isIdentifier(prefix))
|
13255 | return null;
|
13256 | const uninterpretedExpression = input.substring(prefixSeparatorIndex + 1);
|
13257 | const span = new ParseSpan(0, input.length);
|
13258 | return new Quote(span, span.toAbsolute(absoluteOffset), prefix, uninterpretedExpression, location);
|
13259 | }
|
13260 | /**
|
13261 | * Parse microsyntax template expression and return a list of bindings or
|
13262 | * parsing errors in case the given expression is invalid.
|
13263 | *
|
13264 | * For example,
|
13265 | * ```
|
13266 | * <div *ngFor="let item of items">
|
13267 | * ^ ^ absoluteValueOffset for `templateValue`
|
13268 | * absoluteKeyOffset for `templateKey`
|
13269 | * ```
|
13270 | * contains three bindings:
|
13271 | * 1. ngFor -> null
|
13272 | * 2. item -> NgForOfContext.$implicit
|
13273 | * 3. ngForOf -> items
|
13274 | *
|
13275 | * This is apparent from the de-sugared template:
|
13276 | * ```
|
13277 | * <ng-template ngFor let-item [ngForOf]="items">
|
13278 | * ```
|
13279 | *
|
13280 | * @param templateKey name of directive, without the * prefix. For example: ngIf, ngFor
|
13281 | * @param templateValue RHS of the microsyntax attribute
|
13282 | * @param templateUrl template filename if it's external, component filename if it's inline
|
13283 | * @param absoluteKeyOffset start of the `templateKey`
|
13284 | * @param absoluteValueOffset start of the `templateValue`
|
13285 | */
|
13286 | parseTemplateBindings(templateKey, templateValue, templateUrl, absoluteKeyOffset, absoluteValueOffset) {
|
13287 | const tokens = this._lexer.tokenize(templateValue);
|
13288 | const parser = new _ParseAST(templateValue, templateUrl, absoluteValueOffset, tokens, templateValue.length, false /* parseAction */, this.errors, 0 /* relative offset */);
|
13289 | return parser.parseTemplateBindings({
|
13290 | source: templateKey,
|
13291 | span: new AbsoluteSourceSpan(absoluteKeyOffset, absoluteKeyOffset + templateKey.length),
|
13292 | });
|
13293 | }
|
13294 | parseInterpolation(input, location, absoluteOffset, interpolationConfig = DEFAULT_INTERPOLATION_CONFIG) {
|
13295 | const { strings, expressions, offsets } = this.splitInterpolation(input, location, interpolationConfig);
|
13296 | if (expressions.length === 0)
|
13297 | return null;
|
13298 | const expressionNodes = [];
|
13299 | for (let i = 0; i < expressions.length; ++i) {
|
13300 | const expressionText = expressions[i].text;
|
13301 | const sourceToLex = this._stripComments(expressionText);
|
13302 | const tokens = this._lexer.tokenize(sourceToLex);
|
13303 | const ast = new _ParseAST(input, location, absoluteOffset, tokens, sourceToLex.length, false, this.errors, offsets[i] + (expressionText.length - sourceToLex.length))
|
13304 | .parseChain();
|
13305 | expressionNodes.push(ast);
|
13306 | }
|
13307 | return this.createInterpolationAst(strings.map(s => s.text), expressionNodes, input, location, absoluteOffset);
|
13308 | }
|
13309 | /**
|
13310 | * Similar to `parseInterpolation`, but treats the provided string as a single expression
|
13311 | * element that would normally appear within the interpolation prefix and suffix (`{{` and `}}`).
|
13312 | * This is used for parsing the switch expression in ICUs.
|
13313 | */
|
13314 | parseInterpolationExpression(expression, location, absoluteOffset) {
|
13315 | const sourceToLex = this._stripComments(expression);
|
13316 | const tokens = this._lexer.tokenize(sourceToLex);
|
13317 | const ast = new _ParseAST(expression, location, absoluteOffset, tokens, sourceToLex.length,
|
13318 | /* parseAction */ false, this.errors, 0)
|
13319 | .parseChain();
|
13320 | const strings = ['', '']; // The prefix and suffix strings are both empty
|
13321 | return this.createInterpolationAst(strings, [ast], expression, location, absoluteOffset);
|
13322 | }
|
13323 | createInterpolationAst(strings, expressions, input, location, absoluteOffset) {
|
13324 | const span = new ParseSpan(0, input.length);
|
13325 | const interpolation = new Interpolation(span, span.toAbsolute(absoluteOffset), strings, expressions);
|
13326 | return new ASTWithSource(interpolation, input, location, absoluteOffset, this.errors);
|
13327 | }
|
13328 | /**
|
13329 | * Splits a string of text into "raw" text segments and expressions present in interpolations in
|
13330 | * the string.
|
13331 | * Returns `null` if there are no interpolations, otherwise a
|
13332 | * `SplitInterpolation` with splits that look like
|
13333 | * <raw text> <expression> <raw text> ... <raw text> <expression> <raw text>
|
13334 | */
|
13335 | splitInterpolation(input, location, interpolationConfig = DEFAULT_INTERPOLATION_CONFIG) {
|
13336 | const strings = [];
|
13337 | const expressions = [];
|
13338 | const offsets = [];
|
13339 | let i = 0;
|
13340 | let atInterpolation = false;
|
13341 | let extendLastString = false;
|
13342 | let { start: interpStart, end: interpEnd } = interpolationConfig;
|
13343 | while (i < input.length) {
|
13344 | if (!atInterpolation) {
|
13345 | // parse until starting {{
|
13346 | const start = i;
|
13347 | i = input.indexOf(interpStart, i);
|
13348 | if (i === -1) {
|
13349 | i = input.length;
|
13350 | }
|
13351 | const text = input.substring(start, i);
|
13352 | strings.push({ text, start, end: i });
|
13353 | atInterpolation = true;
|
13354 | }
|
13355 | else {
|
13356 | // parse from starting {{ to ending }} while ignoring content inside quotes.
|
13357 | const fullStart = i;
|
13358 | const exprStart = fullStart + interpStart.length;
|
13359 | const exprEnd = this._getInterpolationEndIndex(input, interpEnd, exprStart);
|
13360 | if (exprEnd === -1) {
|
13361 | // Could not find the end of the interpolation; do not parse an expression.
|
13362 | // Instead we should extend the content on the last raw string.
|
13363 | atInterpolation = false;
|
13364 | extendLastString = true;
|
13365 | break;
|
13366 | }
|
13367 | const fullEnd = exprEnd + interpEnd.length;
|
13368 | const text = input.substring(exprStart, exprEnd);
|
13369 | if (text.trim().length === 0) {
|
13370 | this._reportError('Blank expressions are not allowed in interpolated strings', input, `at column ${i} in`, location);
|
13371 | }
|
13372 | expressions.push({ text, start: fullStart, end: fullEnd });
|
13373 | offsets.push(exprStart);
|
13374 | i = fullEnd;
|
13375 | atInterpolation = false;
|
13376 | }
|
13377 | }
|
13378 | if (!atInterpolation) {
|
13379 | // If we are now at a text section, add the remaining content as a raw string.
|
13380 | if (extendLastString) {
|
13381 | const piece = strings[strings.length - 1];
|
13382 | piece.text += input.substring(i);
|
13383 | piece.end = input.length;
|
13384 | }
|
13385 | else {
|
13386 | strings.push({ text: input.substring(i), start: i, end: input.length });
|
13387 | }
|
13388 | }
|
13389 | return new SplitInterpolation(strings, expressions, offsets);
|
13390 | }
|
13391 | wrapLiteralPrimitive(input, location, absoluteOffset) {
|
13392 | const span = new ParseSpan(0, input == null ? 0 : input.length);
|
13393 | return new ASTWithSource(new LiteralPrimitive(span, span.toAbsolute(absoluteOffset), input), input, location, absoluteOffset, this.errors);
|
13394 | }
|
13395 | _stripComments(input) {
|
13396 | const i = this._commentStart(input);
|
13397 | return i != null ? input.substring(0, i).trim() : input;
|
13398 | }
|
13399 | _commentStart(input) {
|
13400 | let outerQuote = null;
|
13401 | for (let i = 0; i < input.length - 1; i++) {
|
13402 | const char = input.charCodeAt(i);
|
13403 | const nextChar = input.charCodeAt(i + 1);
|
13404 | if (char === $SLASH && nextChar == $SLASH && outerQuote == null)
|
13405 | return i;
|
13406 | if (outerQuote === char) {
|
13407 | outerQuote = null;
|
13408 | }
|
13409 | else if (outerQuote == null && isQuote(char)) {
|
13410 | outerQuote = char;
|
13411 | }
|
13412 | }
|
13413 | return null;
|
13414 | }
|
13415 | _checkNoInterpolation(input, location, { start, end }) {
|
13416 | let startIndex = -1;
|
13417 | let endIndex = -1;
|
13418 | for (const charIndex of this._forEachUnquotedChar(input, 0)) {
|
13419 | if (startIndex === -1) {
|
13420 | if (input.startsWith(start)) {
|
13421 | startIndex = charIndex;
|
13422 | }
|
13423 | }
|
13424 | else {
|
13425 | endIndex = this._getInterpolationEndIndex(input, end, charIndex);
|
13426 | if (endIndex > -1) {
|
13427 | break;
|
13428 | }
|
13429 | }
|
13430 | }
|
13431 | if (startIndex > -1 && endIndex > -1) {
|
13432 | this._reportError(`Got interpolation (${start}${end}) where expression was expected`, input, `at column ${startIndex} in`, location);
|
13433 | }
|
13434 | }
|
13435 | /**
|
13436 | * Finds the index of the end of an interpolation expression
|
13437 | * while ignoring comments and quoted content.
|
13438 | */
|
13439 | _getInterpolationEndIndex(input, expressionEnd, start) {
|
13440 | for (const charIndex of this._forEachUnquotedChar(input, start)) {
|
13441 | if (input.startsWith(expressionEnd, charIndex)) {
|
13442 | return charIndex;
|
13443 | }
|
13444 | // Nothing else in the expression matters after we've
|
13445 | // hit a comment so look directly for the end token.
|
13446 | if (input.startsWith('//', charIndex)) {
|
13447 | return input.indexOf(expressionEnd, charIndex);
|
13448 | }
|
13449 | }
|
13450 | return -1;
|
13451 | }
|
13452 | /**
|
13453 | * Generator used to iterate over the character indexes of a string that are outside of quotes.
|
13454 | * @param input String to loop through.
|
13455 | * @param start Index within the string at which to start.
|
13456 | */
|
13457 | *_forEachUnquotedChar(input, start) {
|
13458 | let currentQuote = null;
|
13459 | let escapeCount = 0;
|
13460 | for (let i = start; i < input.length; i++) {
|
13461 | const char = input[i];
|
13462 | // Skip the characters inside quotes. Note that we only care about the outer-most
|
13463 | // quotes matching up and we need to account for escape characters.
|
13464 | if (isQuote(input.charCodeAt(i)) && (currentQuote === null || currentQuote === char) &&
|
13465 | escapeCount % 2 === 0) {
|
13466 | currentQuote = currentQuote === null ? char : null;
|
13467 | }
|
13468 | else if (currentQuote === null) {
|
13469 | yield i;
|
13470 | }
|
13471 | escapeCount = char === '\\' ? escapeCount + 1 : 0;
|
13472 | }
|
13473 | }
|
13474 | }
|
13475 | class IvyParser extends Parser$1 {
|
13476 | constructor() {
|
13477 | super(...arguments);
|
13478 | this.simpleExpressionChecker = IvySimpleExpressionChecker;
|
13479 | }
|
13480 | }
|
13481 | /** Describes a stateful context an expression parser is in. */
|
13482 | var ParseContextFlags;
|
13483 | (function (ParseContextFlags) {
|
13484 | ParseContextFlags[ParseContextFlags["None"] = 0] = "None";
|
13485 | /**
|
13486 | * A Writable context is one in which a value may be written to an lvalue.
|
13487 | * For example, after we see a property access, we may expect a write to the
|
13488 | * property via the "=" operator.
|
13489 | * prop
|
13490 | * ^ possible "=" after
|
13491 | */
|
13492 | ParseContextFlags[ParseContextFlags["Writable"] = 1] = "Writable";
|
13493 | })(ParseContextFlags || (ParseContextFlags = {}));
|
13494 | class _ParseAST {
|
13495 | constructor(input, location, absoluteOffset, tokens, inputLength, parseAction, errors, offset) {
|
13496 | this.input = input;
|
13497 | this.location = location;
|
13498 | this.absoluteOffset = absoluteOffset;
|
13499 | this.tokens = tokens;
|
13500 | this.inputLength = inputLength;
|
13501 | this.parseAction = parseAction;
|
13502 | this.errors = errors;
|
13503 | this.offset = offset;
|
13504 | this.rparensExpected = 0;
|
13505 | this.rbracketsExpected = 0;
|
13506 | this.rbracesExpected = 0;
|
13507 | this.context = ParseContextFlags.None;
|
13508 | // Cache of expression start and input indeces to the absolute source span they map to, used to
|
13509 | // prevent creating superfluous source spans in `sourceSpan`.
|
13510 | // A serial of the expression start and input index is used for mapping because both are stateful
|
13511 | // and may change for subsequent expressions visited by the parser.
|
13512 | this.sourceSpanCache = new Map();
|
13513 | this.index = 0;
|
13514 | }
|
13515 | peek(offset) {
|
13516 | const i = this.index + offset;
|
13517 | return i < this.tokens.length ? this.tokens[i] : EOF;
|
13518 | }
|
13519 | get next() {
|
13520 | return this.peek(0);
|
13521 | }
|
13522 | /** Whether all the parser input has been processed. */
|
13523 | get atEOF() {
|
13524 | return this.index >= this.tokens.length;
|
13525 | }
|
13526 | /**
|
13527 | * Index of the next token to be processed, or the end of the last token if all have been
|
13528 | * processed.
|
13529 | */
|
13530 | get inputIndex() {
|
13531 | return this.atEOF ? this.currentEndIndex : this.next.index + this.offset;
|
13532 | }
|
13533 | /**
|
13534 | * End index of the last processed token, or the start of the first token if none have been
|
13535 | * processed.
|
13536 | */
|
13537 | get currentEndIndex() {
|
13538 | if (this.index > 0) {
|
13539 | const curToken = this.peek(-1);
|
13540 | return curToken.end + this.offset;
|
13541 | }
|
13542 | // No tokens have been processed yet; return the next token's start or the length of the input
|
13543 | // if there is no token.
|
13544 | if (this.tokens.length === 0) {
|
13545 | return this.inputLength + this.offset;
|
13546 | }
|
13547 | return this.next.index + this.offset;
|
13548 | }
|
13549 | /**
|
13550 | * Returns the absolute offset of the start of the current token.
|
13551 | */
|
13552 | get currentAbsoluteOffset() {
|
13553 | return this.absoluteOffset + this.inputIndex;
|
13554 | }
|
13555 | /**
|
13556 | * Retrieve a `ParseSpan` from `start` to the current position (or to `artificialEndIndex` if
|
13557 | * provided).
|
13558 | *
|
13559 | * @param start Position from which the `ParseSpan` will start.
|
13560 | * @param artificialEndIndex Optional ending index to be used if provided (and if greater than the
|
13561 | * natural ending index)
|
13562 | */
|
13563 | span(start, artificialEndIndex) {
|
13564 | let endIndex = this.currentEndIndex;
|
13565 | if (artificialEndIndex !== undefined && artificialEndIndex > this.currentEndIndex) {
|
13566 | endIndex = artificialEndIndex;
|
13567 | }
|
13568 | return new ParseSpan(start, endIndex);
|
13569 | }
|
13570 | sourceSpan(start, artificialEndIndex) {
|
13571 | const serial = `${start}@${this.inputIndex}:${artificialEndIndex}`;
|
13572 | if (!this.sourceSpanCache.has(serial)) {
|
13573 | this.sourceSpanCache.set(serial, this.span(start, artificialEndIndex).toAbsolute(this.absoluteOffset));
|
13574 | }
|
13575 | return this.sourceSpanCache.get(serial);
|
13576 | }
|
13577 | advance() {
|
13578 | this.index++;
|
13579 | }
|
13580 | /**
|
13581 | * Executes a callback in the provided context.
|
13582 | */
|
13583 | withContext(context, cb) {
|
13584 | this.context |= context;
|
13585 | const ret = cb();
|
13586 | this.context ^= context;
|
13587 | return ret;
|
13588 | }
|
13589 | consumeOptionalCharacter(code) {
|
13590 | if (this.next.isCharacter(code)) {
|
13591 | this.advance();
|
13592 | return true;
|
13593 | }
|
13594 | else {
|
13595 | return false;
|
13596 | }
|
13597 | }
|
13598 | peekKeywordLet() {
|
13599 | return this.next.isKeywordLet();
|
13600 | }
|
13601 | peekKeywordAs() {
|
13602 | return this.next.isKeywordAs();
|
13603 | }
|
13604 | /**
|
13605 | * Consumes an expected character, otherwise emits an error about the missing expected character
|
13606 | * and skips over the token stream until reaching a recoverable point.
|
13607 | *
|
13608 | * See `this.error` and `this.skip` for more details.
|
13609 | */
|
13610 | expectCharacter(code) {
|
13611 | if (this.consumeOptionalCharacter(code))
|
13612 | return;
|
13613 | this.error(`Missing expected ${String.fromCharCode(code)}`);
|
13614 | }
|
13615 | consumeOptionalOperator(op) {
|
13616 | if (this.next.isOperator(op)) {
|
13617 | this.advance();
|
13618 | return true;
|
13619 | }
|
13620 | else {
|
13621 | return false;
|
13622 | }
|
13623 | }
|
13624 | expectOperator(operator) {
|
13625 | if (this.consumeOptionalOperator(operator))
|
13626 | return;
|
13627 | this.error(`Missing expected operator ${operator}`);
|
13628 | }
|
13629 | prettyPrintToken(tok) {
|
13630 | return tok === EOF ? 'end of input' : `token ${tok}`;
|
13631 | }
|
13632 | expectIdentifierOrKeyword() {
|
13633 | const n = this.next;
|
13634 | if (!n.isIdentifier() && !n.isKeyword()) {
|
13635 | this.error(`Unexpected ${this.prettyPrintToken(n)}, expected identifier or keyword`);
|
13636 | return null;
|
13637 | }
|
13638 | this.advance();
|
13639 | return n.toString();
|
13640 | }
|
13641 | expectIdentifierOrKeywordOrString() {
|
13642 | const n = this.next;
|
13643 | if (!n.isIdentifier() && !n.isKeyword() && !n.isString()) {
|
13644 | this.error(`Unexpected ${this.prettyPrintToken(n)}, expected identifier, keyword, or string`);
|
13645 | return '';
|
13646 | }
|
13647 | this.advance();
|
13648 | return n.toString();
|
13649 | }
|
13650 | parseChain() {
|
13651 | const exprs = [];
|
13652 | const start = this.inputIndex;
|
13653 | while (this.index < this.tokens.length) {
|
13654 | const expr = this.parsePipe();
|
13655 | exprs.push(expr);
|
13656 | if (this.consumeOptionalCharacter($SEMICOLON)) {
|
13657 | if (!this.parseAction) {
|
13658 | this.error('Binding expression cannot contain chained expression');
|
13659 | }
|
13660 | while (this.consumeOptionalCharacter($SEMICOLON)) {
|
13661 | } // read all semicolons
|
13662 | }
|
13663 | else if (this.index < this.tokens.length) {
|
13664 | this.error(`Unexpected token '${this.next}'`);
|
13665 | }
|
13666 | }
|
13667 | if (exprs.length == 0) {
|
13668 | // We have no expressions so create an empty expression that spans the entire input length
|
13669 | const artificialStart = this.offset;
|
13670 | const artificialEnd = this.offset + this.inputLength;
|
13671 | return new EmptyExpr(this.span(artificialStart, artificialEnd), this.sourceSpan(artificialStart, artificialEnd));
|
13672 | }
|
13673 | if (exprs.length == 1)
|
13674 | return exprs[0];
|
13675 | return new Chain(this.span(start), this.sourceSpan(start), exprs);
|
13676 | }
|
13677 | parsePipe() {
|
13678 | const start = this.inputIndex;
|
13679 | let result = this.parseExpression();
|
13680 | if (this.consumeOptionalOperator('|')) {
|
13681 | if (this.parseAction) {
|
13682 | this.error('Cannot have a pipe in an action expression');
|
13683 | }
|
13684 | do {
|
13685 | const nameStart = this.inputIndex;
|
13686 | let nameId = this.expectIdentifierOrKeyword();
|
13687 | let nameSpan;
|
13688 | let fullSpanEnd = undefined;
|
13689 | if (nameId !== null) {
|
13690 | nameSpan = this.sourceSpan(nameStart);
|
13691 | }
|
13692 | else {
|
13693 | // No valid identifier was found, so we'll assume an empty pipe name ('').
|
13694 | nameId = '';
|
13695 | // However, there may have been whitespace present between the pipe character and the next
|
13696 | // token in the sequence (or the end of input). We want to track this whitespace so that
|
13697 | // the `BindingPipe` we produce covers not just the pipe character, but any trailing
|
13698 | // whitespace beyond it. Another way of thinking about this is that the zero-length name
|
13699 | // is assumed to be at the end of any whitespace beyond the pipe character.
|
13700 | //
|
13701 | // Therefore, we push the end of the `ParseSpan` for this pipe all the way up to the
|
13702 | // beginning of the next token, or until the end of input if the next token is EOF.
|
13703 | fullSpanEnd = this.next.index !== -1 ? this.next.index : this.inputLength + this.offset;
|
13704 | // The `nameSpan` for an empty pipe name is zero-length at the end of any whitespace
|
13705 | // beyond the pipe character.
|
13706 | nameSpan = new ParseSpan(fullSpanEnd, fullSpanEnd).toAbsolute(this.absoluteOffset);
|
13707 | }
|
13708 | const args = [];
|
13709 | while (this.consumeOptionalCharacter($COLON)) {
|
13710 | args.push(this.parseExpression());
|
13711 | // If there are additional expressions beyond the name, then the artificial end for the
|
13712 | // name is no longer relevant.
|
13713 | }
|
13714 | result = new BindingPipe(this.span(start), this.sourceSpan(start, fullSpanEnd), result, nameId, args, nameSpan);
|
13715 | } while (this.consumeOptionalOperator('|'));
|
13716 | }
|
13717 | return result;
|
13718 | }
|
13719 | parseExpression() {
|
13720 | return this.parseConditional();
|
13721 | }
|
13722 | parseConditional() {
|
13723 | const start = this.inputIndex;
|
13724 | const result = this.parseLogicalOr();
|
13725 | if (this.consumeOptionalOperator('?')) {
|
13726 | const yes = this.parsePipe();
|
13727 | let no;
|
13728 | if (!this.consumeOptionalCharacter($COLON)) {
|
13729 | const end = this.inputIndex;
|
13730 | const expression = this.input.substring(start, end);
|
13731 | this.error(`Conditional expression ${expression} requires all 3 expressions`);
|
13732 | no = new EmptyExpr(this.span(start), this.sourceSpan(start));
|
13733 | }
|
13734 | else {
|
13735 | no = this.parsePipe();
|
13736 | }
|
13737 | return new Conditional(this.span(start), this.sourceSpan(start), result, yes, no);
|
13738 | }
|
13739 | else {
|
13740 | return result;
|
13741 | }
|
13742 | }
|
13743 | parseLogicalOr() {
|
13744 | // '||'
|
13745 | const start = this.inputIndex;
|
13746 | let result = this.parseLogicalAnd();
|
13747 | while (this.consumeOptionalOperator('||')) {
|
13748 | const right = this.parseLogicalAnd();
|
13749 | result = new Binary(this.span(start), this.sourceSpan(start), '||', result, right);
|
13750 | }
|
13751 | return result;
|
13752 | }
|
13753 | parseLogicalAnd() {
|
13754 | // '&&'
|
13755 | const start = this.inputIndex;
|
13756 | let result = this.parseEquality();
|
13757 | while (this.consumeOptionalOperator('&&')) {
|
13758 | const right = this.parseEquality();
|
13759 | result = new Binary(this.span(start), this.sourceSpan(start), '&&', result, right);
|
13760 | }
|
13761 | return result;
|
13762 | }
|
13763 | parseEquality() {
|
13764 | // '==','!=','===','!=='
|
13765 | const start = this.inputIndex;
|
13766 | let result = this.parseRelational();
|
13767 | while (this.next.type == TokenType$1.Operator) {
|
13768 | const operator = this.next.strValue;
|
13769 | switch (operator) {
|
13770 | case '==':
|
13771 | case '===':
|
13772 | case '!=':
|
13773 | case '!==':
|
13774 | this.advance();
|
13775 | const right = this.parseRelational();
|
13776 | result = new Binary(this.span(start), this.sourceSpan(start), operator, result, right);
|
13777 | continue;
|
13778 | }
|
13779 | break;
|
13780 | }
|
13781 | return result;
|
13782 | }
|
13783 | parseRelational() {
|
13784 | // '<', '>', '<=', '>='
|
13785 | const start = this.inputIndex;
|
13786 | let result = this.parseAdditive();
|
13787 | while (this.next.type == TokenType$1.Operator) {
|
13788 | const operator = this.next.strValue;
|
13789 | switch (operator) {
|
13790 | case '<':
|
13791 | case '>':
|
13792 | case '<=':
|
13793 | case '>=':
|
13794 | this.advance();
|
13795 | const right = this.parseAdditive();
|
13796 | result = new Binary(this.span(start), this.sourceSpan(start), operator, result, right);
|
13797 | continue;
|
13798 | }
|
13799 | break;
|
13800 | }
|
13801 | return result;
|
13802 | }
|
13803 | parseAdditive() {
|
13804 | // '+', '-'
|
13805 | const start = this.inputIndex;
|
13806 | let result = this.parseMultiplicative();
|
13807 | while (this.next.type == TokenType$1.Operator) {
|
13808 | const operator = this.next.strValue;
|
13809 | switch (operator) {
|
13810 | case '+':
|
13811 | case '-':
|
13812 | this.advance();
|
13813 | let right = this.parseMultiplicative();
|
13814 | result = new Binary(this.span(start), this.sourceSpan(start), operator, result, right);
|
13815 | continue;
|
13816 | }
|
13817 | break;
|
13818 | }
|
13819 | return result;
|
13820 | }
|
13821 | parseMultiplicative() {
|
13822 | // '*', '%', '/'
|
13823 | const start = this.inputIndex;
|
13824 | let result = this.parsePrefix();
|
13825 | while (this.next.type == TokenType$1.Operator) {
|
13826 | const operator = this.next.strValue;
|
13827 | switch (operator) {
|
13828 | case '*':
|
13829 | case '%':
|
13830 | case '/':
|
13831 | this.advance();
|
13832 | let right = this.parsePrefix();
|
13833 | result = new Binary(this.span(start), this.sourceSpan(start), operator, result, right);
|
13834 | continue;
|
13835 | }
|
13836 | break;
|
13837 | }
|
13838 | return result;
|
13839 | }
|
13840 | parsePrefix() {
|
13841 | if (this.next.type == TokenType$1.Operator) {
|
13842 | const start = this.inputIndex;
|
13843 | const operator = this.next.strValue;
|
13844 | let result;
|
13845 | switch (operator) {
|
13846 | case '+':
|
13847 | this.advance();
|
13848 | result = this.parsePrefix();
|
13849 | return Unary.createPlus(this.span(start), this.sourceSpan(start), result);
|
13850 | case '-':
|
13851 | this.advance();
|
13852 | result = this.parsePrefix();
|
13853 | return Unary.createMinus(this.span(start), this.sourceSpan(start), result);
|
13854 | case '!':
|
13855 | this.advance();
|
13856 | result = this.parsePrefix();
|
13857 | return new PrefixNot(this.span(start), this.sourceSpan(start), result);
|
13858 | }
|
13859 | }
|
13860 | return this.parseCallChain();
|
13861 | }
|
13862 | parseCallChain() {
|
13863 | const start = this.inputIndex;
|
13864 | let result = this.parsePrimary();
|
13865 | while (true) {
|
13866 | if (this.consumeOptionalCharacter($PERIOD)) {
|
13867 | result = this.parseAccessMemberOrMethodCall(result, start, false);
|
13868 | }
|
13869 | else if (this.consumeOptionalOperator('?.')) {
|
13870 | result = this.parseAccessMemberOrMethodCall(result, start, true);
|
13871 | }
|
13872 | else if (this.consumeOptionalCharacter($LBRACKET)) {
|
13873 | this.withContext(ParseContextFlags.Writable, () => {
|
13874 | this.rbracketsExpected++;
|
13875 | const key = this.parsePipe();
|
13876 | if (key instanceof EmptyExpr) {
|
13877 | this.error(`Key access cannot be empty`);
|
13878 | }
|
13879 | this.rbracketsExpected--;
|
13880 | this.expectCharacter($RBRACKET);
|
13881 | if (this.consumeOptionalOperator('=')) {
|
13882 | const value = this.parseConditional();
|
13883 | result = new KeyedWrite(this.span(start), this.sourceSpan(start), result, key, value);
|
13884 | }
|
13885 | else {
|
13886 | result = new KeyedRead(this.span(start), this.sourceSpan(start), result, key);
|
13887 | }
|
13888 | });
|
13889 | }
|
13890 | else if (this.consumeOptionalCharacter($LPAREN)) {
|
13891 | this.rparensExpected++;
|
13892 | const args = this.parseCallArguments();
|
13893 | this.rparensExpected--;
|
13894 | this.expectCharacter($RPAREN);
|
13895 | result = new FunctionCall(this.span(start), this.sourceSpan(start), result, args);
|
13896 | }
|
13897 | else if (this.consumeOptionalOperator('!')) {
|
13898 | result = new NonNullAssert(this.span(start), this.sourceSpan(start), result);
|
13899 | }
|
13900 | else {
|
13901 | return result;
|
13902 | }
|
13903 | }
|
13904 | }
|
13905 | parsePrimary() {
|
13906 | const start = this.inputIndex;
|
13907 | if (this.consumeOptionalCharacter($LPAREN)) {
|
13908 | this.rparensExpected++;
|
13909 | const result = this.parsePipe();
|
13910 | this.rparensExpected--;
|
13911 | this.expectCharacter($RPAREN);
|
13912 | return result;
|
13913 | }
|
13914 | else if (this.next.isKeywordNull()) {
|
13915 | this.advance();
|
13916 | return new LiteralPrimitive(this.span(start), this.sourceSpan(start), null);
|
13917 | }
|
13918 | else if (this.next.isKeywordUndefined()) {
|
13919 | this.advance();
|
13920 | return new LiteralPrimitive(this.span(start), this.sourceSpan(start), void 0);
|
13921 | }
|
13922 | else if (this.next.isKeywordTrue()) {
|
13923 | this.advance();
|
13924 | return new LiteralPrimitive(this.span(start), this.sourceSpan(start), true);
|
13925 | }
|
13926 | else if (this.next.isKeywordFalse()) {
|
13927 | this.advance();
|
13928 | return new LiteralPrimitive(this.span(start), this.sourceSpan(start), false);
|
13929 | }
|
13930 | else if (this.next.isKeywordThis()) {
|
13931 | this.advance();
|
13932 | return new ThisReceiver(this.span(start), this.sourceSpan(start));
|
13933 | }
|
13934 | else if (this.consumeOptionalCharacter($LBRACKET)) {
|
13935 | this.rbracketsExpected++;
|
13936 | const elements = this.parseExpressionList($RBRACKET);
|
13937 | this.rbracketsExpected--;
|
13938 | this.expectCharacter($RBRACKET);
|
13939 | return new LiteralArray(this.span(start), this.sourceSpan(start), elements);
|
13940 | }
|
13941 | else if (this.next.isCharacter($LBRACE)) {
|
13942 | return this.parseLiteralMap();
|
13943 | }
|
13944 | else if (this.next.isIdentifier()) {
|
13945 | return this.parseAccessMemberOrMethodCall(new ImplicitReceiver(this.span(start), this.sourceSpan(start)), start, false);
|
13946 | }
|
13947 | else if (this.next.isNumber()) {
|
13948 | const value = this.next.toNumber();
|
13949 | this.advance();
|
13950 | return new LiteralPrimitive(this.span(start), this.sourceSpan(start), value);
|
13951 | }
|
13952 | else if (this.next.isString()) {
|
13953 | const literalValue = this.next.toString();
|
13954 | this.advance();
|
13955 | return new LiteralPrimitive(this.span(start), this.sourceSpan(start), literalValue);
|
13956 | }
|
13957 | else if (this.index >= this.tokens.length) {
|
13958 | this.error(`Unexpected end of expression: ${this.input}`);
|
13959 | return new EmptyExpr(this.span(start), this.sourceSpan(start));
|
13960 | }
|
13961 | else {
|
13962 | this.error(`Unexpected token ${this.next}`);
|
13963 | return new EmptyExpr(this.span(start), this.sourceSpan(start));
|
13964 | }
|
13965 | }
|
13966 | parseExpressionList(terminator) {
|
13967 | const result = [];
|
13968 | do {
|
13969 | if (!this.next.isCharacter(terminator)) {
|
13970 | result.push(this.parsePipe());
|
13971 | }
|
13972 | else {
|
13973 | break;
|
13974 | }
|
13975 | } while (this.consumeOptionalCharacter($COMMA));
|
13976 | return result;
|
13977 | }
|
13978 | parseLiteralMap() {
|
13979 | const keys = [];
|
13980 | const values = [];
|
13981 | const start = this.inputIndex;
|
13982 | this.expectCharacter($LBRACE);
|
13983 | if (!this.consumeOptionalCharacter($RBRACE)) {
|
13984 | this.rbracesExpected++;
|
13985 | do {
|
13986 | const quoted = this.next.isString();
|
13987 | const key = this.expectIdentifierOrKeywordOrString();
|
13988 | keys.push({ key, quoted });
|
13989 | this.expectCharacter($COLON);
|
13990 | values.push(this.parsePipe());
|
13991 | } while (this.consumeOptionalCharacter($COMMA));
|
13992 | this.rbracesExpected--;
|
13993 | this.expectCharacter($RBRACE);
|
13994 | }
|
13995 | return new LiteralMap(this.span(start), this.sourceSpan(start), keys, values);
|
13996 | }
|
13997 | parseAccessMemberOrMethodCall(receiver, start, isSafe = false) {
|
13998 | const nameStart = this.inputIndex;
|
13999 | const id = this.withContext(ParseContextFlags.Writable, () => {
|
14000 | var _a;
|
14001 | const id = (_a = this.expectIdentifierOrKeyword()) !== null && _a !== void 0 ? _a : '';
|
14002 | if (id.length === 0) {
|
14003 | this.error(`Expected identifier for property access`, receiver.span.end);
|
14004 | }
|
14005 | return id;
|
14006 | });
|
14007 | const nameSpan = this.sourceSpan(nameStart);
|
14008 | if (this.consumeOptionalCharacter($LPAREN)) {
|
14009 | this.rparensExpected++;
|
14010 | const args = this.parseCallArguments();
|
14011 | this.expectCharacter($RPAREN);
|
14012 | this.rparensExpected--;
|
14013 | const span = this.span(start);
|
14014 | const sourceSpan = this.sourceSpan(start);
|
14015 | return isSafe ? new SafeMethodCall(span, sourceSpan, nameSpan, receiver, id, args) :
|
14016 | new MethodCall(span, sourceSpan, nameSpan, receiver, id, args);
|
14017 | }
|
14018 | else {
|
14019 | if (isSafe) {
|
14020 | if (this.consumeOptionalOperator('=')) {
|
14021 | this.error('The \'?.\' operator cannot be used in the assignment');
|
14022 | return new EmptyExpr(this.span(start), this.sourceSpan(start));
|
14023 | }
|
14024 | else {
|
14025 | return new SafePropertyRead(this.span(start), this.sourceSpan(start), nameSpan, receiver, id);
|
14026 | }
|
14027 | }
|
14028 | else {
|
14029 | if (this.consumeOptionalOperator('=')) {
|
14030 | if (!this.parseAction) {
|
14031 | this.error('Bindings cannot contain assignments');
|
14032 | return new EmptyExpr(this.span(start), this.sourceSpan(start));
|
14033 | }
|
14034 | const value = this.parseConditional();
|
14035 | return new PropertyWrite(this.span(start), this.sourceSpan(start), nameSpan, receiver, id, value);
|
14036 | }
|
14037 | else {
|
14038 | return new PropertyRead(this.span(start), this.sourceSpan(start), nameSpan, receiver, id);
|
14039 | }
|
14040 | }
|
14041 | }
|
14042 | }
|
14043 | parseCallArguments() {
|
14044 | if (this.next.isCharacter($RPAREN))
|
14045 | return [];
|
14046 | const positionals = [];
|
14047 | do {
|
14048 | positionals.push(this.parsePipe());
|
14049 | } while (this.consumeOptionalCharacter($COMMA));
|
14050 | return positionals;
|
14051 | }
|
14052 | /**
|
14053 | * Parses an identifier, a keyword, a string with an optional `-` in between,
|
14054 | * and returns the string along with its absolute source span.
|
14055 | */
|
14056 | expectTemplateBindingKey() {
|
14057 | let result = '';
|
14058 | let operatorFound = false;
|
14059 | const start = this.currentAbsoluteOffset;
|
14060 | do {
|
14061 | result += this.expectIdentifierOrKeywordOrString();
|
14062 | operatorFound = this.consumeOptionalOperator('-');
|
14063 | if (operatorFound) {
|
14064 | result += '-';
|
14065 | }
|
14066 | } while (operatorFound);
|
14067 | return {
|
14068 | source: result,
|
14069 | span: new AbsoluteSourceSpan(start, start + result.length),
|
14070 | };
|
14071 | }
|
14072 | /**
|
14073 | * Parse microsyntax template expression and return a list of bindings or
|
14074 | * parsing errors in case the given expression is invalid.
|
14075 | *
|
14076 | * For example,
|
14077 | * ```
|
14078 | * <div *ngFor="let item of items; index as i; trackBy: func">
|
14079 | * ```
|
14080 | * contains five bindings:
|
14081 | * 1. ngFor -> null
|
14082 | * 2. item -> NgForOfContext.$implicit
|
14083 | * 3. ngForOf -> items
|
14084 | * 4. i -> NgForOfContext.index
|
14085 | * 5. ngForTrackBy -> func
|
14086 | *
|
14087 | * For a full description of the microsyntax grammar, see
|
14088 | * https://gist.github.com/mhevery/d3530294cff2e4a1b3fe15ff75d08855
|
14089 | *
|
14090 | * @param templateKey name of the microsyntax directive, like ngIf, ngFor,
|
14091 | * without the *, along with its absolute span.
|
14092 | */
|
14093 | parseTemplateBindings(templateKey) {
|
14094 | const bindings = [];
|
14095 | // The first binding is for the template key itself
|
14096 | // In *ngFor="let item of items", key = "ngFor", value = null
|
14097 | // In *ngIf="cond | pipe", key = "ngIf", value = "cond | pipe"
|
14098 | bindings.push(...this.parseDirectiveKeywordBindings(templateKey));
|
14099 | while (this.index < this.tokens.length) {
|
14100 | // If it starts with 'let', then this must be variable declaration
|
14101 | const letBinding = this.parseLetBinding();
|
14102 | if (letBinding) {
|
14103 | bindings.push(letBinding);
|
14104 | }
|
14105 | else {
|
14106 | // Two possible cases here, either `value "as" key` or
|
14107 | // "directive-keyword expression". We don't know which case, but both
|
14108 | // "value" and "directive-keyword" are template binding key, so consume
|
14109 | // the key first.
|
14110 | const key = this.expectTemplateBindingKey();
|
14111 | // Peek at the next token, if it is "as" then this must be variable
|
14112 | // declaration.
|
14113 | const binding = this.parseAsBinding(key);
|
14114 | if (binding) {
|
14115 | bindings.push(binding);
|
14116 | }
|
14117 | else {
|
14118 | // Otherwise the key must be a directive keyword, like "of". Transform
|
14119 | // the key to actual key. Eg. of -> ngForOf, trackBy -> ngForTrackBy
|
14120 | key.source =
|
14121 | templateKey.source + key.source.charAt(0).toUpperCase() + key.source.substring(1);
|
14122 | bindings.push(...this.parseDirectiveKeywordBindings(key));
|
14123 | }
|
14124 | }
|
14125 | this.consumeStatementTerminator();
|
14126 | }
|
14127 | return new TemplateBindingParseResult(bindings, [] /* warnings */, this.errors);
|
14128 | }
|
14129 | /**
|
14130 | * Parse a directive keyword, followed by a mandatory expression.
|
14131 | * For example, "of items", "trackBy: func".
|
14132 | * The bindings are: ngForOf -> items, ngForTrackBy -> func
|
14133 | * There could be an optional "as" binding that follows the expression.
|
14134 | * For example,
|
14135 | * ```
|
14136 | * *ngFor="let item of items | slice:0:1 as collection".
|
14137 | * ^^ ^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^
|
14138 | * keyword bound target optional 'as' binding
|
14139 | * ```
|
14140 | *
|
14141 | * @param key binding key, for example, ngFor, ngIf, ngForOf, along with its
|
14142 | * absolute span.
|
14143 | */
|
14144 | parseDirectiveKeywordBindings(key) {
|
14145 | const bindings = [];
|
14146 | this.consumeOptionalCharacter($COLON); // trackBy: trackByFunction
|
14147 | const value = this.getDirectiveBoundTarget();
|
14148 | let spanEnd = this.currentAbsoluteOffset;
|
14149 | // The binding could optionally be followed by "as". For example,
|
14150 | // *ngIf="cond | pipe as x". In this case, the key in the "as" binding
|
14151 | // is "x" and the value is the template key itself ("ngIf"). Note that the
|
14152 | // 'key' in the current context now becomes the "value" in the next binding.
|
14153 | const asBinding = this.parseAsBinding(key);
|
14154 | if (!asBinding) {
|
14155 | this.consumeStatementTerminator();
|
14156 | spanEnd = this.currentAbsoluteOffset;
|
14157 | }
|
14158 | const sourceSpan = new AbsoluteSourceSpan(key.span.start, spanEnd);
|
14159 | bindings.push(new ExpressionBinding(sourceSpan, key, value));
|
14160 | if (asBinding) {
|
14161 | bindings.push(asBinding);
|
14162 | }
|
14163 | return bindings;
|
14164 | }
|
14165 | /**
|
14166 | * Return the expression AST for the bound target of a directive keyword
|
14167 | * binding. For example,
|
14168 | * ```
|
14169 | * *ngIf="condition | pipe"
|
14170 | * ^^^^^^^^^^^^^^^^ bound target for "ngIf"
|
14171 | * *ngFor="let item of items"
|
14172 | * ^^^^^ bound target for "ngForOf"
|
14173 | * ```
|
14174 | */
|
14175 | getDirectiveBoundTarget() {
|
14176 | if (this.next === EOF || this.peekKeywordAs() || this.peekKeywordLet()) {
|
14177 | return null;
|
14178 | }
|
14179 | const ast = this.parsePipe(); // example: "condition | async"
|
14180 | const { start, end } = ast.span;
|
14181 | const value = this.input.substring(start, end);
|
14182 | return new ASTWithSource(ast, value, this.location, this.absoluteOffset + start, this.errors);
|
14183 | }
|
14184 | /**
|
14185 | * Return the binding for a variable declared using `as`. Note that the order
|
14186 | * of the key-value pair in this declaration is reversed. For example,
|
14187 | * ```
|
14188 | * *ngFor="let item of items; index as i"
|
14189 | * ^^^^^ ^
|
14190 | * value key
|
14191 | * ```
|
14192 | *
|
14193 | * @param value name of the value in the declaration, "ngIf" in the example
|
14194 | * above, along with its absolute span.
|
14195 | */
|
14196 | parseAsBinding(value) {
|
14197 | if (!this.peekKeywordAs()) {
|
14198 | return null;
|
14199 | }
|
14200 | this.advance(); // consume the 'as' keyword
|
14201 | const key = this.expectTemplateBindingKey();
|
14202 | this.consumeStatementTerminator();
|
14203 | const sourceSpan = new AbsoluteSourceSpan(value.span.start, this.currentAbsoluteOffset);
|
14204 | return new VariableBinding(sourceSpan, key, value);
|
14205 | }
|
14206 | /**
|
14207 | * Return the binding for a variable declared using `let`. For example,
|
14208 | * ```
|
14209 | * *ngFor="let item of items; let i=index;"
|
14210 | * ^^^^^^^^ ^^^^^^^^^^^
|
14211 | * ```
|
14212 | * In the first binding, `item` is bound to `NgForOfContext.$implicit`.
|
14213 | * In the second binding, `i` is bound to `NgForOfContext.index`.
|
14214 | */
|
14215 | parseLetBinding() {
|
14216 | if (!this.peekKeywordLet()) {
|
14217 | return null;
|
14218 | }
|
14219 | const spanStart = this.currentAbsoluteOffset;
|
14220 | this.advance(); // consume the 'let' keyword
|
14221 | const key = this.expectTemplateBindingKey();
|
14222 | let value = null;
|
14223 | if (this.consumeOptionalOperator('=')) {
|
14224 | value = this.expectTemplateBindingKey();
|
14225 | }
|
14226 | this.consumeStatementTerminator();
|
14227 | const sourceSpan = new AbsoluteSourceSpan(spanStart, this.currentAbsoluteOffset);
|
14228 | return new VariableBinding(sourceSpan, key, value);
|
14229 | }
|
14230 | /**
|
14231 | * Consume the optional statement terminator: semicolon or comma.
|
14232 | */
|
14233 | consumeStatementTerminator() {
|
14234 | this.consumeOptionalCharacter($SEMICOLON) || this.consumeOptionalCharacter($COMMA);
|
14235 | }
|
14236 | /**
|
14237 | * Records an error and skips over the token stream until reaching a recoverable point. See
|
14238 | * `this.skip` for more details on token skipping.
|
14239 | */
|
14240 | error(message, index = null) {
|
14241 | this.errors.push(new ParserError(message, this.input, this.locationText(index), this.location));
|
14242 | this.skip();
|
14243 | }
|
14244 | locationText(index = null) {
|
14245 | if (index == null)
|
14246 | index = this.index;
|
14247 | return (index < this.tokens.length) ? `at column ${this.tokens[index].index + 1} in` :
|
14248 | `at the end of the expression`;
|
14249 | }
|
14250 | /**
|
14251 | * Error recovery should skip tokens until it encounters a recovery point.
|
14252 | *
|
14253 | * The following are treated as unconditional recovery points:
|
14254 | * - end of input
|
14255 | * - ';' (parseChain() is always the root production, and it expects a ';')
|
14256 | * - '|' (since pipes may be chained and each pipe expression may be treated independently)
|
14257 | *
|
14258 | * The following are conditional recovery points:
|
14259 | * - ')', '}', ']' if one of calling productions is expecting one of these symbols
|
14260 | * - This allows skip() to recover from errors such as '(a.) + 1' allowing more of the AST to
|
14261 | * be retained (it doesn't skip any tokens as the ')' is retained because of the '(' begins
|
14262 | * an '(' <expr> ')' production).
|
14263 | * The recovery points of grouping symbols must be conditional as they must be skipped if
|
14264 | * none of the calling productions are not expecting the closing token else we will never
|
14265 | * make progress in the case of an extraneous group closing symbol (such as a stray ')').
|
14266 | * That is, we skip a closing symbol if we are not in a grouping production.
|
14267 | * - '=' in a `Writable` context
|
14268 | * - In this context, we are able to recover after seeing the `=` operator, which
|
14269 | * signals the presence of an independent rvalue expression following the `=` operator.
|
14270 | *
|
14271 | * If a production expects one of these token it increments the corresponding nesting count,
|
14272 | * and then decrements it just prior to checking if the token is in the input.
|
14273 | */
|
14274 | skip() {
|
14275 | let n = this.next;
|
14276 | while (this.index < this.tokens.length && !n.isCharacter($SEMICOLON) &&
|
14277 | !n.isOperator('|') && (this.rparensExpected <= 0 || !n.isCharacter($RPAREN)) &&
|
14278 | (this.rbracesExpected <= 0 || !n.isCharacter($RBRACE)) &&
|
14279 | (this.rbracketsExpected <= 0 || !n.isCharacter($RBRACKET)) &&
|
14280 | (!(this.context & ParseContextFlags.Writable) || !n.isOperator('='))) {
|
14281 | if (this.next.isError()) {
|
14282 | this.errors.push(new ParserError(this.next.toString(), this.input, this.locationText(), this.location));
|
14283 | }
|
14284 | this.advance();
|
14285 | n = this.next;
|
14286 | }
|
14287 | }
|
14288 | }
|
14289 | class SimpleExpressionChecker {
|
14290 | constructor() {
|
14291 | this.errors = [];
|
14292 | }
|
14293 | visitImplicitReceiver(ast, context) { }
|
14294 | visitThisReceiver(ast, context) { }
|
14295 | visitInterpolation(ast, context) { }
|
14296 | visitLiteralPrimitive(ast, context) { }
|
14297 | visitPropertyRead(ast, context) { }
|
14298 | visitPropertyWrite(ast, context) { }
|
14299 | visitSafePropertyRead(ast, context) { }
|
14300 | visitMethodCall(ast, context) { }
|
14301 | visitSafeMethodCall(ast, context) { }
|
14302 | visitFunctionCall(ast, context) { }
|
14303 | visitLiteralArray(ast, context) {
|
14304 | this.visitAll(ast.expressions, context);
|
14305 | }
|
14306 | visitLiteralMap(ast, context) {
|
14307 | this.visitAll(ast.values, context);
|
14308 | }
|
14309 | visitUnary(ast, context) { }
|
14310 | visitBinary(ast, context) { }
|
14311 | visitPrefixNot(ast, context) { }
|
14312 | visitNonNullAssert(ast, context) { }
|
14313 | visitConditional(ast, context) { }
|
14314 | visitPipe(ast, context) {
|
14315 | this.errors.push('pipes');
|
14316 | }
|
14317 | visitKeyedRead(ast, context) { }
|
14318 | visitKeyedWrite(ast, context) { }
|
14319 | visitAll(asts, context) {
|
14320 | return asts.map(node => node.visit(this, context));
|
14321 | }
|
14322 | visitChain(ast, context) { }
|
14323 | visitQuote(ast, context) { }
|
14324 | }
|
14325 | /**
|
14326 | * This class implements SimpleExpressionChecker used in View Engine and performs more strict checks
|
14327 | * to make sure host bindings do not contain pipes. In View Engine, having pipes in host bindings is
|
14328 | * not supported as well, but in some cases (like `!(value | async)`) the error is not triggered at
|
14329 | * compile time. In order to preserve View Engine behavior, more strict checks are introduced for
|
14330 | * Ivy mode only.
|
14331 | */
|
14332 | class IvySimpleExpressionChecker extends RecursiveAstVisitor {
|
14333 | constructor() {
|
14334 | super(...arguments);
|
14335 | this.errors = [];
|
14336 | }
|
14337 | visitPipe() {
|
14338 | this.errors.push('pipes');
|
14339 | }
|
14340 | }
|
14341 |
|
14342 | /**
|
14343 | * @license
|
14344 | * Copyright Google LLC All Rights Reserved.
|
14345 | *
|
14346 | * Use of this source code is governed by an MIT-style license that can be
|
14347 | * found in the LICENSE file at https://angular.io/license
|
14348 | */
|
14349 | // =================================================================================================
|
14350 | // =================================================================================================
|
14351 | // =========== S T O P - S T O P - S T O P - S T O P - S T O P - S T O P ===========
|
14352 | // =================================================================================================
|
14353 | // =================================================================================================
|
14354 | //
|
14355 | // DO NOT EDIT THIS LIST OF SECURITY SENSITIVE PROPERTIES WITHOUT A SECURITY REVIEW!
|
14356 | // Reach out to mprobst for details.
|
14357 | //
|
14358 | // =================================================================================================
|
14359 | /** Map from tagName|propertyName to SecurityContext. Properties applying to all tags use '*'. */
|
14360 | let _SECURITY_SCHEMA;
|
14361 | function SECURITY_SCHEMA() {
|
14362 | if (!_SECURITY_SCHEMA) {
|
14363 | _SECURITY_SCHEMA = {};
|
14364 | // Case is insignificant below, all element and attribute names are lower-cased for lookup.
|
14365 | registerContext(SecurityContext.HTML, [
|
14366 | 'iframe|srcdoc',
|
14367 | '*|innerHTML',
|
14368 | '*|outerHTML',
|
14369 | ]);
|
14370 | registerContext(SecurityContext.STYLE, ['*|style']);
|
14371 | // NB: no SCRIPT contexts here, they are never allowed due to the parser stripping them.
|
14372 | registerContext(SecurityContext.URL, [
|
14373 | '*|formAction', 'area|href', 'area|ping', 'audio|src', 'a|href',
|
14374 | 'a|ping', 'blockquote|cite', 'body|background', 'del|cite', 'form|action',
|
14375 | 'img|src', 'img|srcset', 'input|src', 'ins|cite', 'q|cite',
|
14376 | 'source|src', 'source|srcset', 'track|src', 'video|poster', 'video|src',
|
14377 | ]);
|
14378 | registerContext(SecurityContext.RESOURCE_URL, [
|
14379 | 'applet|code',
|
14380 | 'applet|codebase',
|
14381 | 'base|href',
|
14382 | 'embed|src',
|
14383 | 'frame|src',
|
14384 | 'head|profile',
|
14385 | 'html|manifest',
|
14386 | 'iframe|src',
|
14387 | 'link|href',
|
14388 | 'media|src',
|
14389 | 'object|codebase',
|
14390 | 'object|data',
|
14391 | 'script|src',
|
14392 | ]);
|
14393 | }
|
14394 | return _SECURITY_SCHEMA;
|
14395 | }
|
14396 | function registerContext(ctx, specs) {
|
14397 | for (const spec of specs)
|
14398 | _SECURITY_SCHEMA[spec.toLowerCase()] = ctx;
|
14399 | }
|
14400 |
|
14401 | /**
|
14402 | * @license
|
14403 | * Copyright Google LLC All Rights Reserved.
|
14404 | *
|
14405 | * Use of this source code is governed by an MIT-style license that can be
|
14406 | * found in the LICENSE file at https://angular.io/license
|
14407 | */
|
14408 | class ElementSchemaRegistry {
|
14409 | }
|
14410 |
|
14411 | /**
|
14412 | * @license
|
14413 | * Copyright Google LLC All Rights Reserved.
|
14414 | *
|
14415 | * Use of this source code is governed by an MIT-style license that can be
|
14416 | * found in the LICENSE file at https://angular.io/license
|
14417 | */
|
14418 | const BOOLEAN = 'boolean';
|
14419 | const NUMBER = 'number';
|
14420 | const STRING = 'string';
|
14421 | const OBJECT = 'object';
|
14422 | /**
|
14423 | * This array represents the DOM schema. It encodes inheritance, properties, and events.
|
14424 | *
|
14425 | * ## Overview
|
14426 | *
|
14427 | * Each line represents one kind of element. The `element_inheritance` and properties are joined
|
14428 | * using `element_inheritance|properties` syntax.
|
14429 | *
|
14430 | * ## Element Inheritance
|
14431 | *
|
14432 | * The `element_inheritance` can be further subdivided as `element1,element2,...^parentElement`.
|
14433 | * Here the individual elements are separated by `,` (commas). Every element in the list
|
14434 | * has identical properties.
|
14435 | *
|
14436 | * An `element` may inherit additional properties from `parentElement` If no `^parentElement` is
|
14437 | * specified then `""` (blank) element is assumed.
|
14438 | *
|
14439 | * NOTE: The blank element inherits from root `[Element]` element, the super element of all
|
14440 | * elements.
|
14441 | *
|
14442 | * NOTE an element prefix such as `:svg:` has no special meaning to the schema.
|
14443 | *
|
14444 | * ## Properties
|
14445 | *
|
14446 | * Each element has a set of properties separated by `,` (commas). Each property can be prefixed
|
14447 | * by a special character designating its type:
|
14448 | *
|
14449 | * - (no prefix): property is a string.
|
14450 | * - `*`: property represents an event.
|
14451 | * - `!`: property is a boolean.
|
14452 | * - `#`: property is a number.
|
14453 | * - `%`: property is an object.
|
14454 | *
|
14455 | * ## Query
|
14456 | *
|
14457 | * The class creates an internal squas representation which allows to easily answer the query of
|
14458 | * if a given property exist on a given element.
|
14459 | *
|
14460 | * NOTE: We don't yet support querying for types or events.
|
14461 | * NOTE: This schema is auto extracted from `schema_extractor.ts` located in the test folder,
|
14462 | * see dom_element_schema_registry_spec.ts
|
14463 | */
|
14464 | // =================================================================================================
|
14465 | // =================================================================================================
|
14466 | // =========== S T O P - S T O P - S T O P - S T O P - S T O P - S T O P ===========
|
14467 | // =================================================================================================
|
14468 | // =================================================================================================
|
14469 | //
|
14470 | // DO NOT EDIT THIS DOM SCHEMA WITHOUT A SECURITY REVIEW!
|
14471 | //
|
14472 | // Newly added properties must be security reviewed and assigned an appropriate SecurityContext in
|
14473 | // dom_security_schema.ts. Reach out to mprobst & rjamet for details.
|
14474 | //
|
14475 | // =================================================================================================
|
14476 | const SCHEMA = [
|
14477 | '[Element]|textContent,%classList,className,id,innerHTML,*beforecopy,*beforecut,*beforepaste,*copy,*cut,*paste,*search,*selectstart,*webkitfullscreenchange,*webkitfullscreenerror,*wheel,outerHTML,#scrollLeft,#scrollTop,slot' +
|
14478 | /* added manually to avoid breaking changes */
|
14479 | ',*message,*mozfullscreenchange,*mozfullscreenerror,*mozpointerlockchange,*mozpointerlockerror,*webglcontextcreationerror,*webglcontextlost,*webglcontextrestored',
|
14480 | '[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',
|
14481 | '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',
|
14482 | 'media^[HTMLElement]|!autoplay,!controls,%controlsList,%crossOrigin,#currentTime,!defaultMuted,#defaultPlaybackRate,!disableRemotePlayback,!loop,!muted,*encrypted,*waitingforkey,#playbackRate,preload,src,%srcObject,#volume',
|
14483 | ':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',
|
14484 | ':svg:graphics^:svg:|',
|
14485 | ':svg:animation^:svg:|*begin,*end,*repeat',
|
14486 | ':svg:geometry^:svg:|',
|
14487 | ':svg:componentTransferFunction^:svg:|',
|
14488 | ':svg:gradient^:svg:|',
|
14489 | ':svg:textContent^:svg:graphics|',
|
14490 | ':svg:textPositioning^:svg:textContent|',
|
14491 | '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',
|
14492 | 'area^[HTMLElement]|alt,coords,download,hash,host,hostname,href,!noHref,password,pathname,ping,port,protocol,referrerPolicy,rel,search,shape,target,username',
|
14493 | 'audio^media|',
|
14494 | 'br^[HTMLElement]|clear',
|
14495 | 'base^[HTMLElement]|href,target',
|
14496 | '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',
|
14497 | 'button^[HTMLElement]|!autofocus,!disabled,formAction,formEnctype,formMethod,!formNoValidate,formTarget,name,type,value',
|
14498 | 'canvas^[HTMLElement]|#height,#width',
|
14499 | 'content^[HTMLElement]|select',
|
14500 | 'dl^[HTMLElement]|!compact',
|
14501 | 'datalist^[HTMLElement]|',
|
14502 | 'details^[HTMLElement]|!open',
|
14503 | 'dialog^[HTMLElement]|!open,returnValue',
|
14504 | 'dir^[HTMLElement]|!compact',
|
14505 | 'div^[HTMLElement]|align',
|
14506 | 'embed^[HTMLElement]|align,height,name,src,type,width',
|
14507 | 'fieldset^[HTMLElement]|!disabled,name',
|
14508 | 'font^[HTMLElement]|color,face,size',
|
14509 | 'form^[HTMLElement]|acceptCharset,action,autocomplete,encoding,enctype,method,name,!noValidate,target',
|
14510 | 'frame^[HTMLElement]|frameBorder,longDesc,marginHeight,marginWidth,name,!noResize,scrolling,src',
|
14511 | 'frameset^[HTMLElement]|cols,*beforeunload,*blur,*error,*focus,*hashchange,*languagechange,*load,*message,*offline,*online,*pagehide,*pageshow,*popstate,*rejectionhandled,*resize,*scroll,*storage,*unhandledrejection,*unload,rows',
|
14512 | 'hr^[HTMLElement]|align,color,!noShade,size,width',
|
14513 | 'head^[HTMLElement]|',
|
14514 | 'h1,h2,h3,h4,h5,h6^[HTMLElement]|align',
|
14515 | 'html^[HTMLElement]|version',
|
14516 | 'iframe^[HTMLElement]|align,!allowFullscreen,frameBorder,height,longDesc,marginHeight,marginWidth,name,referrerPolicy,%sandbox,scrolling,src,srcdoc,width',
|
14517 | 'img^[HTMLElement]|align,alt,border,%crossOrigin,#height,#hspace,!isMap,longDesc,lowsrc,name,referrerPolicy,sizes,src,srcset,useMap,#vspace,#width',
|
14518 | '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',
|
14519 | 'li^[HTMLElement]|type,#value',
|
14520 | 'label^[HTMLElement]|htmlFor',
|
14521 | 'legend^[HTMLElement]|align',
|
14522 | 'link^[HTMLElement]|as,charset,%crossOrigin,!disabled,href,hreflang,integrity,media,referrerPolicy,rel,%relList,rev,%sizes,target,type',
|
14523 | 'map^[HTMLElement]|name',
|
14524 | 'marquee^[HTMLElement]|behavior,bgColor,direction,height,#hspace,#loop,#scrollAmount,#scrollDelay,!trueSpeed,#vspace,width',
|
14525 | 'menu^[HTMLElement]|!compact',
|
14526 | 'meta^[HTMLElement]|content,httpEquiv,name,scheme',
|
14527 | 'meter^[HTMLElement]|#high,#low,#max,#min,#optimum,#value',
|
14528 | 'ins,del^[HTMLElement]|cite,dateTime',
|
14529 | 'ol^[HTMLElement]|!compact,!reversed,#start,type',
|
14530 | 'object^[HTMLElement]|align,archive,border,code,codeBase,codeType,data,!declare,height,#hspace,name,standby,type,useMap,#vspace,width',
|
14531 | 'optgroup^[HTMLElement]|!disabled,label',
|
14532 | 'option^[HTMLElement]|!defaultSelected,!disabled,label,!selected,text,value',
|
14533 | 'output^[HTMLElement]|defaultValue,%htmlFor,name,value',
|
14534 | 'p^[HTMLElement]|align',
|
14535 | 'param^[HTMLElement]|name,type,value,valueType',
|
14536 | 'picture^[HTMLElement]|',
|
14537 | 'pre^[HTMLElement]|#width',
|
14538 | 'progress^[HTMLElement]|#max,#value',
|
14539 | 'q,blockquote,cite^[HTMLElement]|',
|
14540 | 'script^[HTMLElement]|!async,charset,%crossOrigin,!defer,event,htmlFor,integrity,src,text,type',
|
14541 | 'select^[HTMLElement]|!autofocus,!disabled,#length,!multiple,name,!required,#selectedIndex,#size,value',
|
14542 | 'shadow^[HTMLElement]|',
|
14543 | 'slot^[HTMLElement]|name',
|
14544 | 'source^[HTMLElement]|media,sizes,src,srcset,type',
|
14545 | 'span^[HTMLElement]|',
|
14546 | 'style^[HTMLElement]|!disabled,media,type',
|
14547 | 'caption^[HTMLElement]|align',
|
14548 | 'th,td^[HTMLElement]|abbr,align,axis,bgColor,ch,chOff,#colSpan,headers,height,!noWrap,#rowSpan,scope,vAlign,width',
|
14549 | 'col,colgroup^[HTMLElement]|align,ch,chOff,#span,vAlign,width',
|
14550 | 'table^[HTMLElement]|align,bgColor,border,%caption,cellPadding,cellSpacing,frame,rules,summary,%tFoot,%tHead,width',
|
14551 | 'tr^[HTMLElement]|align,bgColor,ch,chOff,vAlign',
|
14552 | 'tfoot,thead,tbody^[HTMLElement]|align,ch,chOff,vAlign',
|
14553 | 'template^[HTMLElement]|',
|
14554 | 'textarea^[HTMLElement]|autocapitalize,!autofocus,#cols,defaultValue,dirName,!disabled,#maxLength,#minLength,name,placeholder,!readOnly,!required,#rows,selectionDirection,#selectionEnd,#selectionStart,value,wrap',
|
14555 | 'title^[HTMLElement]|text',
|
14556 | 'track^[HTMLElement]|!default,kind,label,src,srclang',
|
14557 | 'ul^[HTMLElement]|!compact,type',
|
14558 | 'unknown^[HTMLElement]|',
|
14559 | 'video^media|#height,poster,#width',
|
14560 | ':svg:a^:svg:graphics|',
|
14561 | ':svg:animate^:svg:animation|',
|
14562 | ':svg:animateMotion^:svg:animation|',
|
14563 | ':svg:animateTransform^:svg:animation|',
|
14564 | ':svg:circle^:svg:geometry|',
|
14565 | ':svg:clipPath^:svg:graphics|',
|
14566 | ':svg:defs^:svg:graphics|',
|
14567 | ':svg:desc^:svg:|',
|
14568 | ':svg:discard^:svg:|',
|
14569 | ':svg:ellipse^:svg:geometry|',
|
14570 | ':svg:feBlend^:svg:|',
|
14571 | ':svg:feColorMatrix^:svg:|',
|
14572 | ':svg:feComponentTransfer^:svg:|',
|
14573 | ':svg:feComposite^:svg:|',
|
14574 | ':svg:feConvolveMatrix^:svg:|',
|
14575 | ':svg:feDiffuseLighting^:svg:|',
|
14576 | ':svg:feDisplacementMap^:svg:|',
|
14577 | ':svg:feDistantLight^:svg:|',
|
14578 | ':svg:feDropShadow^:svg:|',
|
14579 | ':svg:feFlood^:svg:|',
|
14580 | ':svg:feFuncA^:svg:componentTransferFunction|',
|
14581 | ':svg:feFuncB^:svg:componentTransferFunction|',
|
14582 | ':svg:feFuncG^:svg:componentTransferFunction|',
|
14583 | ':svg:feFuncR^:svg:componentTransferFunction|',
|
14584 | ':svg:feGaussianBlur^:svg:|',
|
14585 | ':svg:feImage^:svg:|',
|
14586 | ':svg:feMerge^:svg:|',
|
14587 | ':svg:feMergeNode^:svg:|',
|
14588 | ':svg:feMorphology^:svg:|',
|
14589 | ':svg:feOffset^:svg:|',
|
14590 | ':svg:fePointLight^:svg:|',
|
14591 | ':svg:feSpecularLighting^:svg:|',
|
14592 | ':svg:feSpotLight^:svg:|',
|
14593 | ':svg:feTile^:svg:|',
|
14594 | ':svg:feTurbulence^:svg:|',
|
14595 | ':svg:filter^:svg:|',
|
14596 | ':svg:foreignObject^:svg:graphics|',
|
14597 | ':svg:g^:svg:graphics|',
|
14598 | ':svg:image^:svg:graphics|',
|
14599 | ':svg:line^:svg:geometry|',
|
14600 | ':svg:linearGradient^:svg:gradient|',
|
14601 | ':svg:mpath^:svg:|',
|
14602 | ':svg:marker^:svg:|',
|
14603 | ':svg:mask^:svg:|',
|
14604 | ':svg:metadata^:svg:|',
|
14605 | ':svg:path^:svg:geometry|',
|
14606 | ':svg:pattern^:svg:|',
|
14607 | ':svg:polygon^:svg:geometry|',
|
14608 | ':svg:polyline^:svg:geometry|',
|
14609 | ':svg:radialGradient^:svg:gradient|',
|
14610 | ':svg:rect^:svg:geometry|',
|
14611 | ':svg:svg^:svg:graphics|#currentScale,#zoomAndPan',
|
14612 | ':svg:script^:svg:|type',
|
14613 | ':svg:set^:svg:animation|',
|
14614 | ':svg:stop^:svg:|',
|
14615 | ':svg:style^:svg:|!disabled,media,title,type',
|
14616 | ':svg:switch^:svg:graphics|',
|
14617 | ':svg:symbol^:svg:|',
|
14618 | ':svg:tspan^:svg:textPositioning|',
|
14619 | ':svg:text^:svg:textPositioning|',
|
14620 | ':svg:textPath^:svg:textContent|',
|
14621 | ':svg:title^:svg:|',
|
14622 | ':svg:use^:svg:graphics|',
|
14623 | ':svg:view^:svg:|#zoomAndPan',
|
14624 | 'data^[HTMLElement]|value',
|
14625 | 'keygen^[HTMLElement]|!autofocus,challenge,!disabled,form,keytype,name',
|
14626 | 'menuitem^[HTMLElement]|type,label,icon,!disabled,!checked,radiogroup,!default',
|
14627 | 'summary^[HTMLElement]|',
|
14628 | 'time^[HTMLElement]|dateTime',
|
14629 | ':svg:cursor^:svg:|',
|
14630 | ];
|
14631 | const _ATTR_TO_PROP = {
|
14632 | 'class': 'className',
|
14633 | 'for': 'htmlFor',
|
14634 | 'formaction': 'formAction',
|
14635 | 'innerHtml': 'innerHTML',
|
14636 | 'readonly': 'readOnly',
|
14637 | 'tabindex': 'tabIndex',
|
14638 | };
|
14639 | // Invert _ATTR_TO_PROP.
|
14640 | const _PROP_TO_ATTR = Object.keys(_ATTR_TO_PROP).reduce((inverted, attr) => {
|
14641 | inverted[_ATTR_TO_PROP[attr]] = attr;
|
14642 | return inverted;
|
14643 | }, {});
|
14644 | class DomElementSchemaRegistry extends ElementSchemaRegistry {
|
14645 | constructor() {
|
14646 | super();
|
14647 | this._schema = {};
|
14648 | SCHEMA.forEach(encodedType => {
|
14649 | const type = {};
|
14650 | const [strType, strProperties] = encodedType.split('|');
|
14651 | const properties = strProperties.split(',');
|
14652 | const [typeNames, superName] = strType.split('^');
|
14653 | typeNames.split(',').forEach(tag => this._schema[tag.toLowerCase()] = type);
|
14654 | const superType = superName && this._schema[superName.toLowerCase()];
|
14655 | if (superType) {
|
14656 | Object.keys(superType).forEach((prop) => {
|
14657 | type[prop] = superType[prop];
|
14658 | });
|
14659 | }
|
14660 | properties.forEach((property) => {
|
14661 | if (property.length > 0) {
|
14662 | switch (property[0]) {
|
14663 | case '*':
|
14664 | // We don't yet support events.
|
14665 | // If ever allowing to bind to events, GO THROUGH A SECURITY REVIEW, allowing events
|
14666 | // will
|
14667 | // almost certainly introduce bad XSS vulnerabilities.
|
14668 | // type[property.substring(1)] = EVENT;
|
14669 | break;
|
14670 | case '!':
|
14671 | type[property.substring(1)] = BOOLEAN;
|
14672 | break;
|
14673 | case '#':
|
14674 | type[property.substring(1)] = NUMBER;
|
14675 | break;
|
14676 | case '%':
|
14677 | type[property.substring(1)] = OBJECT;
|
14678 | break;
|
14679 | default:
|
14680 | type[property] = STRING;
|
14681 | }
|
14682 | }
|
14683 | });
|
14684 | });
|
14685 | }
|
14686 | hasProperty(tagName, propName, schemaMetas) {
|
14687 | if (schemaMetas.some((schema) => schema.name === NO_ERRORS_SCHEMA.name)) {
|
14688 | return true;
|
14689 | }
|
14690 | if (tagName.indexOf('-') > -1) {
|
14691 | if (isNgContainer(tagName) || isNgContent(tagName)) {
|
14692 | return false;
|
14693 | }
|
14694 | if (schemaMetas.some((schema) => schema.name === CUSTOM_ELEMENTS_SCHEMA.name)) {
|
14695 | // Can't tell now as we don't know which properties a custom element will get
|
14696 | // once it is instantiated
|
14697 | return true;
|
14698 | }
|
14699 | }
|
14700 | const elementProperties = this._schema[tagName.toLowerCase()] || this._schema['unknown'];
|
14701 | return !!elementProperties[propName];
|
14702 | }
|
14703 | hasElement(tagName, schemaMetas) {
|
14704 | if (schemaMetas.some((schema) => schema.name === NO_ERRORS_SCHEMA.name)) {
|
14705 | return true;
|
14706 | }
|
14707 | if (tagName.indexOf('-') > -1) {
|
14708 | if (isNgContainer(tagName) || isNgContent(tagName)) {
|
14709 | return true;
|
14710 | }
|
14711 | if (schemaMetas.some((schema) => schema.name === CUSTOM_ELEMENTS_SCHEMA.name)) {
|
14712 | // Allow any custom elements
|
14713 | return true;
|
14714 | }
|
14715 | }
|
14716 | return !!this._schema[tagName.toLowerCase()];
|
14717 | }
|
14718 | /**
|
14719 | * securityContext returns the security context for the given property on the given DOM tag.
|
14720 | *
|
14721 | * Tag and property name are statically known and cannot change at runtime, i.e. it is not
|
14722 | * possible to bind a value into a changing attribute or tag name.
|
14723 | *
|
14724 | * The filtering is based on a list of allowed tags|attributes. All attributes in the schema
|
14725 | * above are assumed to have the 'NONE' security context, i.e. that they are safe inert
|
14726 | * string values. Only specific well known attack vectors are assigned their appropriate context.
|
14727 | */
|
14728 | securityContext(tagName, propName, isAttribute) {
|
14729 | if (isAttribute) {
|
14730 | // NB: For security purposes, use the mapped property name, not the attribute name.
|
14731 | propName = this.getMappedPropName(propName);
|
14732 | }
|
14733 | // Make sure comparisons are case insensitive, so that case differences between attribute and
|
14734 | // property names do not have a security impact.
|
14735 | tagName = tagName.toLowerCase();
|
14736 | propName = propName.toLowerCase();
|
14737 | let ctx = SECURITY_SCHEMA()[tagName + '|' + propName];
|
14738 | if (ctx) {
|
14739 | return ctx;
|
14740 | }
|
14741 | ctx = SECURITY_SCHEMA()['*|' + propName];
|
14742 | return ctx ? ctx : SecurityContext.NONE;
|
14743 | }
|
14744 | getMappedPropName(propName) {
|
14745 | return _ATTR_TO_PROP[propName] || propName;
|
14746 | }
|
14747 | getDefaultComponentElementName() {
|
14748 | return 'ng-component';
|
14749 | }
|
14750 | validateProperty(name) {
|
14751 | if (name.toLowerCase().startsWith('on')) {
|
14752 | const msg = `Binding to event property '${name}' is disallowed for security reasons, ` +
|
14753 | `please use (${name.slice(2)})=...` +
|
14754 | `\nIf '${name}' is a directive input, make sure the directive is imported by the` +
|
14755 | ` current module.`;
|
14756 | return { error: true, msg: msg };
|
14757 | }
|
14758 | else {
|
14759 | return { error: false };
|
14760 | }
|
14761 | }
|
14762 | validateAttribute(name) {
|
14763 | if (name.toLowerCase().startsWith('on')) {
|
14764 | const msg = `Binding to event attribute '${name}' is disallowed for security reasons, ` +
|
14765 | `please use (${name.slice(2)})=...`;
|
14766 | return { error: true, msg: msg };
|
14767 | }
|
14768 | else {
|
14769 | return { error: false };
|
14770 | }
|
14771 | }
|
14772 | allKnownElementNames() {
|
14773 | return Object.keys(this._schema);
|
14774 | }
|
14775 | allKnownAttributesOfElement(tagName) {
|
14776 | const elementProperties = this._schema[tagName.toLowerCase()] || this._schema['unknown'];
|
14777 | // Convert properties to attributes.
|
14778 | return Object.keys(elementProperties).map(prop => { var _a; return (_a = _PROP_TO_ATTR[prop]) !== null && _a !== void 0 ? _a : prop; });
|
14779 | }
|
14780 | normalizeAnimationStyleProperty(propName) {
|
14781 | return dashCaseToCamelCase(propName);
|
14782 | }
|
14783 | normalizeAnimationStyleValue(camelCaseProp, userProvidedProp, val) {
|
14784 | let unit = '';
|
14785 | const strVal = val.toString().trim();
|
14786 | let errorMsg = null;
|
14787 | if (_isPixelDimensionStyle(camelCaseProp) && val !== 0 && val !== '0') {
|
14788 | if (typeof val === 'number') {
|
14789 | unit = 'px';
|
14790 | }
|
14791 | else {
|
14792 | const valAndSuffixMatch = val.match(/^[+-]?[\d\.]+([a-z]*)$/);
|
14793 | if (valAndSuffixMatch && valAndSuffixMatch[1].length == 0) {
|
14794 | errorMsg = `Please provide a CSS unit value for ${userProvidedProp}:${val}`;
|
14795 | }
|
14796 | }
|
14797 | }
|
14798 | return { error: errorMsg, value: strVal + unit };
|
14799 | }
|
14800 | }
|
14801 | function _isPixelDimensionStyle(prop) {
|
14802 | switch (prop) {
|
14803 | case 'width':
|
14804 | case 'height':
|
14805 | case 'minWidth':
|
14806 | case 'minHeight':
|
14807 | case 'maxWidth':
|
14808 | case 'maxHeight':
|
14809 | case 'left':
|
14810 | case 'top':
|
14811 | case 'bottom':
|
14812 | case 'right':
|
14813 | case 'fontSize':
|
14814 | case 'outlineWidth':
|
14815 | case 'outlineOffset':
|
14816 | case 'paddingTop':
|
14817 | case 'paddingLeft':
|
14818 | case 'paddingBottom':
|
14819 | case 'paddingRight':
|
14820 | case 'marginTop':
|
14821 | case 'marginLeft':
|
14822 | case 'marginBottom':
|
14823 | case 'marginRight':
|
14824 | case 'borderRadius':
|
14825 | case 'borderWidth':
|
14826 | case 'borderTopWidth':
|
14827 | case 'borderLeftWidth':
|
14828 | case 'borderRightWidth':
|
14829 | case 'borderBottomWidth':
|
14830 | case 'textIndent':
|
14831 | return true;
|
14832 | default:
|
14833 | return false;
|
14834 | }
|
14835 | }
|
14836 |
|
14837 | /**
|
14838 | * @license
|
14839 | * Copyright Google LLC All Rights Reserved.
|
14840 | *
|
14841 | * Use of this source code is governed by an MIT-style license that can be
|
14842 | * found in the LICENSE file at https://angular.io/license
|
14843 | */
|
14844 | /**
|
14845 | * Set of tagName|propertyName corresponding to Trusted Types sinks. Properties applying to all
|
14846 | * tags use '*'.
|
14847 | *
|
14848 | * Extracted from, and should be kept in sync with
|
14849 | * https://w3c.github.io/webappsec-trusted-types/dist/spec/#integrations
|
14850 | */
|
14851 | const TRUSTED_TYPES_SINKS = new Set([
|
14852 | // NOTE: All strings in this set *must* be lowercase!
|
14853 | // TrustedHTML
|
14854 | 'iframe|srcdoc',
|
14855 | '*|innerhtml',
|
14856 | '*|outerhtml',
|
14857 | // NB: no TrustedScript here, as the corresponding tags are stripped by the compiler.
|
14858 | // TrustedScriptURL
|
14859 | 'embed|src',
|
14860 | 'object|codebase',
|
14861 | 'object|data',
|
14862 | ]);
|
14863 | /**
|
14864 | * isTrustedTypesSink returns true if the given property on the given DOM tag is a Trusted Types
|
14865 | * sink. In that case, use `ElementSchemaRegistry.securityContext` to determine which particular
|
14866 | * Trusted Type is required for values passed to the sink:
|
14867 | * - SecurityContext.HTML corresponds to TrustedHTML
|
14868 | * - SecurityContext.RESOURCE_URL corresponds to TrustedScriptURL
|
14869 | */
|
14870 | function isTrustedTypesSink(tagName, propName) {
|
14871 | // Make sure comparisons are case insensitive, so that case differences between attribute and
|
14872 | // property names do not have a security impact.
|
14873 | tagName = tagName.toLowerCase();
|
14874 | propName = propName.toLowerCase();
|
14875 | return TRUSTED_TYPES_SINKS.has(tagName + '|' + propName) ||
|
14876 | TRUSTED_TYPES_SINKS.has('*|' + propName);
|
14877 | }
|
14878 |
|
14879 | /**
|
14880 | * @license
|
14881 | * Copyright Google LLC All Rights Reserved.
|
14882 | *
|
14883 | * Use of this source code is governed by an MIT-style license that can be
|
14884 | * found in the LICENSE file at https://angular.io/license
|
14885 | */
|
14886 | const BIND_NAME_REGEXP$1 = /^(?:(bind-)|(let-)|(ref-|#)|(on-)|(bindon-)|(@))(.*)$/;
|
14887 | // Group 1 = "bind-"
|
14888 | const KW_BIND_IDX$1 = 1;
|
14889 | // Group 2 = "let-"
|
14890 | const KW_LET_IDX$1 = 2;
|
14891 | // Group 3 = "ref-/#"
|
14892 | const KW_REF_IDX$1 = 3;
|
14893 | // Group 4 = "on-"
|
14894 | const KW_ON_IDX$1 = 4;
|
14895 | // Group 5 = "bindon-"
|
14896 | const KW_BINDON_IDX$1 = 5;
|
14897 | // Group 6 = "@"
|
14898 | const KW_AT_IDX$1 = 6;
|
14899 | // Group 7 = the identifier after "bind-", "let-", "ref-/#", "on-", "bindon-" or "@"
|
14900 | const IDENT_KW_IDX$1 = 7;
|
14901 | const BINDING_DELIMS = {
|
14902 | BANANA_BOX: { start: '[(', end: ')]' },
|
14903 | PROPERTY: { start: '[', end: ']' },
|
14904 | EVENT: { start: '(', end: ')' },
|
14905 | };
|
14906 | const TEMPLATE_ATTR_PREFIX$2 = '*';
|
14907 | function htmlAstToRender3Ast(htmlNodes, bindingParser) {
|
14908 | const transformer = new HtmlAstToIvyAst(bindingParser);
|
14909 | const ivyNodes = visitAll$1(transformer, htmlNodes);
|
14910 | // Errors might originate in either the binding parser or the html to ivy transformer
|
14911 | const allErrors = bindingParser.errors.concat(transformer.errors);
|
14912 | return {
|
14913 | nodes: ivyNodes,
|
14914 | errors: allErrors,
|
14915 | styleUrls: transformer.styleUrls,
|
14916 | styles: transformer.styles,
|
14917 | ngContentSelectors: transformer.ngContentSelectors,
|
14918 | };
|
14919 | }
|
14920 | class HtmlAstToIvyAst {
|
14921 | constructor(bindingParser) {
|
14922 | this.bindingParser = bindingParser;
|
14923 | this.errors = [];
|
14924 | this.styles = [];
|
14925 | this.styleUrls = [];
|
14926 | this.ngContentSelectors = [];
|
14927 | this.inI18nBlock = false;
|
14928 | }
|
14929 | // HTML visitor
|
14930 | visitElement(element) {
|
14931 | const isI18nRootElement = isI18nRootNode(element.i18n);
|
14932 | if (isI18nRootElement) {
|
14933 | if (this.inI18nBlock) {
|
14934 | this.reportError('Cannot mark an element as translatable inside of a translatable section. Please remove the nested i18n marker.', element.sourceSpan);
|
14935 | }
|
14936 | this.inI18nBlock = true;
|
14937 | }
|
14938 | const preparsedElement = preparseElement(element);
|
14939 | if (preparsedElement.type === PreparsedElementType.SCRIPT) {
|
14940 | return null;
|
14941 | }
|
14942 | else if (preparsedElement.type === PreparsedElementType.STYLE) {
|
14943 | const contents = textContents(element);
|
14944 | if (contents !== null) {
|
14945 | this.styles.push(contents);
|
14946 | }
|
14947 | return null;
|
14948 | }
|
14949 | else if (preparsedElement.type === PreparsedElementType.STYLESHEET &&
|
14950 | isStyleUrlResolvable(preparsedElement.hrefAttr)) {
|
14951 | this.styleUrls.push(preparsedElement.hrefAttr);
|
14952 | return null;
|
14953 | }
|
14954 | // Whether the element is a `<ng-template>`
|
14955 | const isTemplateElement = isNgTemplate(element.name);
|
14956 | const parsedProperties = [];
|
14957 | const boundEvents = [];
|
14958 | const variables = [];
|
14959 | const references = [];
|
14960 | const attributes = [];
|
14961 | const i18nAttrsMeta = {};
|
14962 | const templateParsedProperties = [];
|
14963 | const templateVariables = [];
|
14964 | // Whether the element has any *-attribute
|
14965 | let elementHasInlineTemplate = false;
|
14966 | for (const attribute of element.attrs) {
|
14967 | let hasBinding = false;
|
14968 | const normalizedName = normalizeAttributeName(attribute.name);
|
14969 | // `*attr` defines template bindings
|
14970 | let isTemplateBinding = false;
|
14971 | if (attribute.i18n) {
|
14972 | i18nAttrsMeta[attribute.name] = attribute.i18n;
|
14973 | }
|
14974 | if (normalizedName.startsWith(TEMPLATE_ATTR_PREFIX$2)) {
|
14975 | // *-attributes
|
14976 | if (elementHasInlineTemplate) {
|
14977 | this.reportError(`Can't have multiple template bindings on one element. Use only one attribute prefixed with *`, attribute.sourceSpan);
|
14978 | }
|
14979 | isTemplateBinding = true;
|
14980 | elementHasInlineTemplate = true;
|
14981 | const templateValue = attribute.value;
|
14982 | const templateKey = normalizedName.substring(TEMPLATE_ATTR_PREFIX$2.length);
|
14983 | const parsedVariables = [];
|
14984 | const absoluteValueOffset = attribute.valueSpan ?
|
14985 | attribute.valueSpan.start.offset :
|
14986 | // If there is no value span the attribute does not have a value, like `attr` in
|
14987 | //`<div attr></div>`. In this case, point to one character beyond the last character of
|
14988 | // the attribute name.
|
14989 | attribute.sourceSpan.start.offset + attribute.name.length;
|
14990 | this.bindingParser.parseInlineTemplateBinding(templateKey, templateValue, attribute.sourceSpan, absoluteValueOffset, [], templateParsedProperties, parsedVariables, true /* isIvyAst */);
|
14991 | templateVariables.push(...parsedVariables.map(v => new Variable(v.name, v.value, v.sourceSpan, v.keySpan, v.valueSpan)));
|
14992 | }
|
14993 | else {
|
14994 | // Check for variables, events, property bindings, interpolation
|
14995 | hasBinding = this.parseAttribute(isTemplateElement, attribute, [], parsedProperties, boundEvents, variables, references);
|
14996 | }
|
14997 | if (!hasBinding && !isTemplateBinding) {
|
14998 | // don't include the bindings as attributes as well in the AST
|
14999 | attributes.push(this.visitAttribute(attribute));
|
15000 | }
|
15001 | }
|
15002 | const children = visitAll$1(preparsedElement.nonBindable ? NON_BINDABLE_VISITOR$1 : this, element.children);
|
15003 | let parsedElement;
|
15004 | if (preparsedElement.type === PreparsedElementType.NG_CONTENT) {
|
15005 | // `<ng-content>`
|
15006 | if (element.children &&
|
15007 | !element.children.every((node) => isEmptyTextNode(node) || isCommentNode(node))) {
|
15008 | this.reportError(`<ng-content> element cannot have content.`, element.sourceSpan);
|
15009 | }
|
15010 | const selector = preparsedElement.selectAttr;
|
15011 | const attrs = element.attrs.map(attr => this.visitAttribute(attr));
|
15012 | parsedElement = new Content(selector, attrs, element.sourceSpan, element.i18n);
|
15013 | this.ngContentSelectors.push(selector);
|
15014 | }
|
15015 | else if (isTemplateElement) {
|
15016 | // `<ng-template>`
|
15017 | const attrs = this.extractAttributes(element.name, parsedProperties, i18nAttrsMeta);
|
15018 | parsedElement = new Template(element.name, attributes, attrs.bound, boundEvents, [ /* no template attributes */], children, references, variables, element.sourceSpan, element.startSourceSpan, element.endSourceSpan, element.i18n);
|
15019 | }
|
15020 | else {
|
15021 | const attrs = this.extractAttributes(element.name, parsedProperties, i18nAttrsMeta);
|
15022 | parsedElement = new Element(element.name, attributes, attrs.bound, boundEvents, children, references, element.sourceSpan, element.startSourceSpan, element.endSourceSpan, element.i18n);
|
15023 | }
|
15024 | if (elementHasInlineTemplate) {
|
15025 | // If this node is an inline-template (e.g. has *ngFor) then we need to create a template
|
15026 | // node that contains this node.
|
15027 | // Moreover, if the node is an element, then we need to hoist its attributes to the template
|
15028 | // node for matching against content projection selectors.
|
15029 | const attrs = this.extractAttributes('ng-template', templateParsedProperties, i18nAttrsMeta);
|
15030 | const templateAttrs = [];
|
15031 | attrs.literal.forEach(attr => templateAttrs.push(attr));
|
15032 | attrs.bound.forEach(attr => templateAttrs.push(attr));
|
15033 | const hoistedAttrs = parsedElement instanceof Element ?
|
15034 | {
|
15035 | attributes: parsedElement.attributes,
|
15036 | inputs: parsedElement.inputs,
|
15037 | outputs: parsedElement.outputs,
|
15038 | } :
|
15039 | { attributes: [], inputs: [], outputs: [] };
|
15040 | // For <ng-template>s with structural directives on them, avoid passing i18n information to
|
15041 | // the wrapping template to prevent unnecessary i18n instructions from being generated. The
|
15042 | // necessary i18n meta information will be extracted from child elements.
|
15043 | const i18n = isTemplateElement && isI18nRootElement ? undefined : element.i18n;
|
15044 | // TODO(pk): test for this case
|
15045 | parsedElement = new Template(parsedElement.name, hoistedAttrs.attributes, hoistedAttrs.inputs, hoistedAttrs.outputs, templateAttrs, [parsedElement], [ /* no references */], templateVariables, element.sourceSpan, element.startSourceSpan, element.endSourceSpan, i18n);
|
15046 | }
|
15047 | if (isI18nRootElement) {
|
15048 | this.inI18nBlock = false;
|
15049 | }
|
15050 | return parsedElement;
|
15051 | }
|
15052 | visitAttribute(attribute) {
|
15053 | return new TextAttribute(attribute.name, attribute.value, attribute.sourceSpan, attribute.keySpan, attribute.valueSpan, attribute.i18n);
|
15054 | }
|
15055 | visitText(text) {
|
15056 | return this._visitTextWithInterpolation(text.value, text.sourceSpan, text.i18n);
|
15057 | }
|
15058 | visitExpansion(expansion) {
|
15059 | if (!expansion.i18n) {
|
15060 | // do not generate Icu in case it was created
|
15061 | // outside of i18n block in a template
|
15062 | return null;
|
15063 | }
|
15064 | if (!isI18nRootNode(expansion.i18n)) {
|
15065 | throw new Error(`Invalid type "${expansion.i18n.constructor}" for "i18n" property of ${expansion.sourceSpan.toString()}. Expected a "Message"`);
|
15066 | }
|
15067 | const message = expansion.i18n;
|
15068 | const vars = {};
|
15069 | const placeholders = {};
|
15070 | // extract VARs from ICUs - we process them separately while
|
15071 | // assembling resulting message via goog.getMsg function, since
|
15072 | // we need to pass them to top-level goog.getMsg call
|
15073 | Object.keys(message.placeholders).forEach(key => {
|
15074 | const value = message.placeholders[key];
|
15075 | if (key.startsWith(I18N_ICU_VAR_PREFIX)) {
|
15076 | // Currently when the `plural` or `select` keywords in an ICU contain trailing spaces (e.g.
|
15077 | // `{count, select , ...}`), these spaces are also included into the key names in ICU vars
|
15078 | // (e.g. "VAR_SELECT "). These trailing spaces are not desirable, since they will later be
|
15079 | // converted into `_` symbols while normalizing placeholder names, which might lead to
|
15080 | // mismatches at runtime (i.e. placeholder will not be replaced with the correct value).
|
15081 | const formattedKey = key.trim();
|
15082 | const ast = this.bindingParser.parseInterpolationExpression(value.text, value.sourceSpan);
|
15083 | vars[formattedKey] = new BoundText(ast, value.sourceSpan);
|
15084 | }
|
15085 | else {
|
15086 | placeholders[key] = this._visitTextWithInterpolation(value.text, value.sourceSpan);
|
15087 | }
|
15088 | });
|
15089 | return new Icu(vars, placeholders, expansion.sourceSpan, message);
|
15090 | }
|
15091 | visitExpansionCase(expansionCase) {
|
15092 | return null;
|
15093 | }
|
15094 | visitComment(comment) {
|
15095 | return null;
|
15096 | }
|
15097 | // convert view engine `ParsedProperty` to a format suitable for IVY
|
15098 | extractAttributes(elementName, properties, i18nPropsMeta) {
|
15099 | const bound = [];
|
15100 | const literal = [];
|
15101 | properties.forEach(prop => {
|
15102 | const i18n = i18nPropsMeta[prop.name];
|
15103 | if (prop.isLiteral) {
|
15104 | literal.push(new TextAttribute(prop.name, prop.expression.source || '', prop.sourceSpan, prop.keySpan, prop.valueSpan, i18n));
|
15105 | }
|
15106 | else {
|
15107 | // Note that validation is skipped and property mapping is disabled
|
15108 | // due to the fact that we need to make sure a given prop is not an
|
15109 | // input of a directive and directive matching happens at runtime.
|
15110 | const bep = this.bindingParser.createBoundElementProperty(elementName, prop, /* skipValidation */ true, /* mapPropertyName */ false);
|
15111 | bound.push(BoundAttribute.fromBoundElementProperty(bep, i18n));
|
15112 | }
|
15113 | });
|
15114 | return { bound, literal };
|
15115 | }
|
15116 | parseAttribute(isTemplateElement, attribute, matchableAttributes, parsedProperties, boundEvents, variables, references) {
|
15117 | const name = normalizeAttributeName(attribute.name);
|
15118 | const value = attribute.value;
|
15119 | const srcSpan = attribute.sourceSpan;
|
15120 | const absoluteOffset = attribute.valueSpan ? attribute.valueSpan.start.offset : srcSpan.start.offset;
|
15121 | function createKeySpan(srcSpan, prefix, identifier) {
|
15122 | // We need to adjust the start location for the keySpan to account for the removed 'data-'
|
15123 | // prefix from `normalizeAttributeName`.
|
15124 | const normalizationAdjustment = attribute.name.length - name.length;
|
15125 | const keySpanStart = srcSpan.start.moveBy(prefix.length + normalizationAdjustment);
|
15126 | const keySpanEnd = keySpanStart.moveBy(identifier.length);
|
15127 | return new ParseSourceSpan(keySpanStart, keySpanEnd, keySpanStart, identifier);
|
15128 | }
|
15129 | const bindParts = name.match(BIND_NAME_REGEXP$1);
|
15130 | if (bindParts) {
|
15131 | if (bindParts[KW_BIND_IDX$1] != null) {
|
15132 | const identifier = bindParts[IDENT_KW_IDX$1];
|
15133 | const keySpan = createKeySpan(srcSpan, bindParts[KW_BIND_IDX$1], identifier);
|
15134 | this.bindingParser.parsePropertyBinding(identifier, value, false, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
|
15135 | }
|
15136 | else if (bindParts[KW_LET_IDX$1]) {
|
15137 | if (isTemplateElement) {
|
15138 | const identifier = bindParts[IDENT_KW_IDX$1];
|
15139 | const keySpan = createKeySpan(srcSpan, bindParts[KW_LET_IDX$1], identifier);
|
15140 | this.parseVariable(identifier, value, srcSpan, keySpan, attribute.valueSpan, variables);
|
15141 | }
|
15142 | else {
|
15143 | this.reportError(`"let-" is only supported on ng-template elements.`, srcSpan);
|
15144 | }
|
15145 | }
|
15146 | else if (bindParts[KW_REF_IDX$1]) {
|
15147 | const identifier = bindParts[IDENT_KW_IDX$1];
|
15148 | const keySpan = createKeySpan(srcSpan, bindParts[KW_REF_IDX$1], identifier);
|
15149 | this.parseReference(identifier, value, srcSpan, keySpan, attribute.valueSpan, references);
|
15150 | }
|
15151 | else if (bindParts[KW_ON_IDX$1]) {
|
15152 | const events = [];
|
15153 | const identifier = bindParts[IDENT_KW_IDX$1];
|
15154 | const keySpan = createKeySpan(srcSpan, bindParts[KW_ON_IDX$1], identifier);
|
15155 | this.bindingParser.parseEvent(identifier, value, srcSpan, attribute.valueSpan || srcSpan, matchableAttributes, events, keySpan);
|
15156 | addEvents(events, boundEvents);
|
15157 | }
|
15158 | else if (bindParts[KW_BINDON_IDX$1]) {
|
15159 | const identifier = bindParts[IDENT_KW_IDX$1];
|
15160 | const keySpan = createKeySpan(srcSpan, bindParts[KW_BINDON_IDX$1], identifier);
|
15161 | this.bindingParser.parsePropertyBinding(identifier, value, false, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
|
15162 | this.parseAssignmentEvent(identifier, value, srcSpan, attribute.valueSpan, matchableAttributes, boundEvents, keySpan);
|
15163 | }
|
15164 | else if (bindParts[KW_AT_IDX$1]) {
|
15165 | const keySpan = createKeySpan(srcSpan, '', name);
|
15166 | this.bindingParser.parseLiteralAttr(name, value, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
|
15167 | }
|
15168 | return true;
|
15169 | }
|
15170 | // We didn't see a kw-prefixed property binding, but we have not yet checked
|
15171 | // for the []/()/[()] syntax.
|
15172 | let delims = null;
|
15173 | if (name.startsWith(BINDING_DELIMS.BANANA_BOX.start)) {
|
15174 | delims = BINDING_DELIMS.BANANA_BOX;
|
15175 | }
|
15176 | else if (name.startsWith(BINDING_DELIMS.PROPERTY.start)) {
|
15177 | delims = BINDING_DELIMS.PROPERTY;
|
15178 | }
|
15179 | else if (name.startsWith(BINDING_DELIMS.EVENT.start)) {
|
15180 | delims = BINDING_DELIMS.EVENT;
|
15181 | }
|
15182 | if (delims !== null &&
|
15183 | // NOTE: older versions of the parser would match a start/end delimited
|
15184 | // binding iff the property name was terminated by the ending delimiter
|
15185 | // and the identifier in the binding was non-empty.
|
15186 | // TODO(ayazhafiz): update this to handle malformed bindings.
|
15187 | name.endsWith(delims.end) && name.length > delims.start.length + delims.end.length) {
|
15188 | const identifier = name.substring(delims.start.length, name.length - delims.end.length);
|
15189 | const keySpan = createKeySpan(srcSpan, delims.start, identifier);
|
15190 | if (delims.start === BINDING_DELIMS.BANANA_BOX.start) {
|
15191 | this.bindingParser.parsePropertyBinding(identifier, value, false, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
|
15192 | this.parseAssignmentEvent(identifier, value, srcSpan, attribute.valueSpan, matchableAttributes, boundEvents, keySpan);
|
15193 | }
|
15194 | else if (delims.start === BINDING_DELIMS.PROPERTY.start) {
|
15195 | this.bindingParser.parsePropertyBinding(identifier, value, false, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
|
15196 | }
|
15197 | else {
|
15198 | const events = [];
|
15199 | this.bindingParser.parseEvent(identifier, value, srcSpan, attribute.valueSpan || srcSpan, matchableAttributes, events, keySpan);
|
15200 | addEvents(events, boundEvents);
|
15201 | }
|
15202 | return true;
|
15203 | }
|
15204 | // No explicit binding found.
|
15205 | const keySpan = createKeySpan(srcSpan, '' /* prefix */, name);
|
15206 | const hasBinding = this.bindingParser.parsePropertyInterpolation(name, value, srcSpan, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
|
15207 | return hasBinding;
|
15208 | }
|
15209 | _visitTextWithInterpolation(value, sourceSpan, i18n) {
|
15210 | const valueNoNgsp = replaceNgsp(value);
|
15211 | const expr = this.bindingParser.parseInterpolation(valueNoNgsp, sourceSpan);
|
15212 | return expr ? new BoundText(expr, sourceSpan, i18n) : new Text(valueNoNgsp, sourceSpan);
|
15213 | }
|
15214 | parseVariable(identifier, value, sourceSpan, keySpan, valueSpan, variables) {
|
15215 | if (identifier.indexOf('-') > -1) {
|
15216 | this.reportError(`"-" is not allowed in variable names`, sourceSpan);
|
15217 | }
|
15218 | else if (identifier.length === 0) {
|
15219 | this.reportError(`Variable does not have a name`, sourceSpan);
|
15220 | }
|
15221 | variables.push(new Variable(identifier, value, sourceSpan, keySpan, valueSpan));
|
15222 | }
|
15223 | parseReference(identifier, value, sourceSpan, keySpan, valueSpan, references) {
|
15224 | if (identifier.indexOf('-') > -1) {
|
15225 | this.reportError(`"-" is not allowed in reference names`, sourceSpan);
|
15226 | }
|
15227 | else if (identifier.length === 0) {
|
15228 | this.reportError(`Reference does not have a name`, sourceSpan);
|
15229 | }
|
15230 | else if (references.some(reference => reference.name === identifier)) {
|
15231 | this.reportError(`Reference "#${identifier}" is defined more than once`, sourceSpan);
|
15232 | }
|
15233 | references.push(new Reference(identifier, value, sourceSpan, keySpan, valueSpan));
|
15234 | }
|
15235 | parseAssignmentEvent(name, expression, sourceSpan, valueSpan, targetMatchableAttrs, boundEvents, keySpan) {
|
15236 | const events = [];
|
15237 | this.bindingParser.parseEvent(`${name}Change`, `${expression}=$event`, sourceSpan, valueSpan || sourceSpan, targetMatchableAttrs, events, keySpan);
|
15238 | addEvents(events, boundEvents);
|
15239 | }
|
15240 | reportError(message, sourceSpan, level = ParseErrorLevel.ERROR) {
|
15241 | this.errors.push(new ParseError(sourceSpan, message, level));
|
15242 | }
|
15243 | }
|
15244 | class NonBindableVisitor$1 {
|
15245 | visitElement(ast) {
|
15246 | const preparsedElement = preparseElement(ast);
|
15247 | if (preparsedElement.type === PreparsedElementType.SCRIPT ||
|
15248 | preparsedElement.type === PreparsedElementType.STYLE ||
|
15249 | preparsedElement.type === PreparsedElementType.STYLESHEET) {
|
15250 | // Skipping <script> for security reasons
|
15251 | // Skipping <style> and stylesheets as we already processed them
|
15252 | // in the StyleCompiler
|
15253 | return null;
|
15254 | }
|
15255 | const children = visitAll$1(this, ast.children, null);
|
15256 | return new Element(ast.name, visitAll$1(this, ast.attrs),
|
15257 | /* inputs */ [], /* outputs */ [], children, /* references */ [], ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan);
|
15258 | }
|
15259 | visitComment(comment) {
|
15260 | return null;
|
15261 | }
|
15262 | visitAttribute(attribute) {
|
15263 | return new TextAttribute(attribute.name, attribute.value, attribute.sourceSpan, attribute.keySpan, attribute.valueSpan, attribute.i18n);
|
15264 | }
|
15265 | visitText(text) {
|
15266 | return new Text(text.value, text.sourceSpan);
|
15267 | }
|
15268 | visitExpansion(expansion) {
|
15269 | return null;
|
15270 | }
|
15271 | visitExpansionCase(expansionCase) {
|
15272 | return null;
|
15273 | }
|
15274 | }
|
15275 | const NON_BINDABLE_VISITOR$1 = new NonBindableVisitor$1();
|
15276 | function normalizeAttributeName(attrName) {
|
15277 | return /^data-/i.test(attrName) ? attrName.substring(5) : attrName;
|
15278 | }
|
15279 | function addEvents(events, boundEvents) {
|
15280 | boundEvents.push(...events.map(e => BoundEvent.fromParsedEvent(e)));
|
15281 | }
|
15282 | function isEmptyTextNode(node) {
|
15283 | return node instanceof Text$2 && node.value.trim().length == 0;
|
15284 | }
|
15285 | function isCommentNode(node) {
|
15286 | return node instanceof Comment;
|
15287 | }
|
15288 | function textContents(node) {
|
15289 | if (node.children.length !== 1 || !(node.children[0] instanceof Text$2)) {
|
15290 | return null;
|
15291 | }
|
15292 | else {
|
15293 | return node.children[0].value;
|
15294 | }
|
15295 | }
|
15296 |
|
15297 | /**
|
15298 | * @license
|
15299 | * Copyright Google LLC All Rights Reserved.
|
15300 | *
|
15301 | * Use of this source code is governed by an MIT-style license that can be
|
15302 | * found in the LICENSE file at https://angular.io/license
|
15303 | */
|
15304 | var TagType;
|
15305 | (function (TagType) {
|
15306 | TagType[TagType["ELEMENT"] = 0] = "ELEMENT";
|
15307 | TagType[TagType["TEMPLATE"] = 1] = "TEMPLATE";
|
15308 | })(TagType || (TagType = {}));
|
15309 | /**
|
15310 | * Generates an object that is used as a shared state between parent and all child contexts.
|
15311 | */
|
15312 | function setupRegistry() {
|
15313 | return { getUniqueId: getSeqNumberGenerator(), icus: new Map() };
|
15314 | }
|
15315 | /**
|
15316 | * I18nContext is a helper class which keeps track of all i18n-related aspects
|
15317 | * (accumulates placeholders, bindings, etc) between i18nStart and i18nEnd instructions.
|
15318 | *
|
15319 | * When we enter a nested template, the top-level context is being passed down
|
15320 | * to the nested component, which uses this context to generate a child instance
|
15321 | * of I18nContext class (to handle nested template) and at the end, reconciles it back
|
15322 | * with the parent context.
|
15323 | *
|
15324 | * @param index Instruction index of i18nStart, which initiates this context
|
15325 | * @param ref Reference to a translation const that represents the content if thus context
|
15326 | * @param level Nestng level defined for child contexts
|
15327 | * @param templateIndex Instruction index of a template which this context belongs to
|
15328 | * @param meta Meta information (id, meaning, description, etc) associated with this context
|
15329 | */
|
15330 | class I18nContext {
|
15331 | constructor(index, ref, level = 0, templateIndex = null, meta, registry) {
|
15332 | this.index = index;
|
15333 | this.ref = ref;
|
15334 | this.level = level;
|
15335 | this.templateIndex = templateIndex;
|
15336 | this.meta = meta;
|
15337 | this.registry = registry;
|
15338 | this.bindings = new Set();
|
15339 | this.placeholders = new Map();
|
15340 | this.isEmitted = false;
|
15341 | this._unresolvedCtxCount = 0;
|
15342 | this._registry = registry || setupRegistry();
|
15343 | this.id = this._registry.getUniqueId();
|
15344 | }
|
15345 | appendTag(type, node, index, closed) {
|
15346 | if (node.isVoid && closed) {
|
15347 | return; // ignore "close" for void tags
|
15348 | }
|
15349 | const ph = node.isVoid || !closed ? node.startName : node.closeName;
|
15350 | const content = { type, index, ctx: this.id, isVoid: node.isVoid, closed };
|
15351 | updatePlaceholderMap(this.placeholders, ph, content);
|
15352 | }
|
15353 | get icus() {
|
15354 | return this._registry.icus;
|
15355 | }
|
15356 | get isRoot() {
|
15357 | return this.level === 0;
|
15358 | }
|
15359 | get isResolved() {
|
15360 | return this._unresolvedCtxCount === 0;
|
15361 | }
|
15362 | getSerializedPlaceholders() {
|
15363 | const result = new Map();
|
15364 | this.placeholders.forEach((values, key) => result.set(key, values.map(serializePlaceholderValue)));
|
15365 | return result;
|
15366 | }
|
15367 | // public API to accumulate i18n-related content
|
15368 | appendBinding(binding) {
|
15369 | this.bindings.add(binding);
|
15370 | }
|
15371 | appendIcu(name, ref) {
|
15372 | updatePlaceholderMap(this._registry.icus, name, ref);
|
15373 | }
|
15374 | appendBoundText(node) {
|
15375 | const phs = assembleBoundTextPlaceholders(node, this.bindings.size, this.id);
|
15376 | phs.forEach((values, key) => updatePlaceholderMap(this.placeholders, key, ...values));
|
15377 | }
|
15378 | appendTemplate(node, index) {
|
15379 | // add open and close tags at the same time,
|
15380 | // since we process nested templates separately
|
15381 | this.appendTag(TagType.TEMPLATE, node, index, false);
|
15382 | this.appendTag(TagType.TEMPLATE, node, index, true);
|
15383 | this._unresolvedCtxCount++;
|
15384 | }
|
15385 | appendElement(node, index, closed) {
|
15386 | this.appendTag(TagType.ELEMENT, node, index, closed);
|
15387 | }
|
15388 | appendProjection(node, index) {
|
15389 | // Add open and close tags at the same time, since `<ng-content>` has no content,
|
15390 | // so when we come across `<ng-content>` we can register both open and close tags.
|
15391 | // Note: runtime i18n logic doesn't distinguish `<ng-content>` tag placeholders and
|
15392 | // regular element tag placeholders, so we generate element placeholders for both types.
|
15393 | this.appendTag(TagType.ELEMENT, node, index, false);
|
15394 | this.appendTag(TagType.ELEMENT, node, index, true);
|
15395 | }
|
15396 | /**
|
15397 | * Generates an instance of a child context based on the root one,
|
15398 | * when we enter a nested template within I18n section.
|
15399 | *
|
15400 | * @param index Instruction index of corresponding i18nStart, which initiates this context
|
15401 | * @param templateIndex Instruction index of a template which this context belongs to
|
15402 | * @param meta Meta information (id, meaning, description, etc) associated with this context
|
15403 | *
|
15404 | * @returns I18nContext instance
|
15405 | */
|
15406 | forkChildContext(index, templateIndex, meta) {
|
15407 | return new I18nContext(index, this.ref, this.level + 1, templateIndex, meta, this._registry);
|
15408 | }
|
15409 | /**
|
15410 | * Reconciles child context into parent one once the end of the i18n block is reached (i18nEnd).
|
15411 | *
|
15412 | * @param context Child I18nContext instance to be reconciled with parent context.
|
15413 | */
|
15414 | reconcileChildContext(context) {
|
15415 | // set the right context id for open and close
|
15416 | // template tags, so we can use it as sub-block ids
|
15417 | ['start', 'close'].forEach((op) => {
|
15418 | const key = context.meta[`${op}Name`];
|
15419 | const phs = this.placeholders.get(key) || [];
|
15420 | const tag = phs.find(findTemplateFn(this.id, context.templateIndex));
|
15421 | if (tag) {
|
15422 | tag.ctx = context.id;
|
15423 | }
|
15424 | });
|
15425 | // reconcile placeholders
|
15426 | const childPhs = context.placeholders;
|
15427 | childPhs.forEach((values, key) => {
|
15428 | const phs = this.placeholders.get(key);
|
15429 | if (!phs) {
|
15430 | this.placeholders.set(key, values);
|
15431 | return;
|
15432 | }
|
15433 | // try to find matching template...
|
15434 | const tmplIdx = phs.findIndex(findTemplateFn(context.id, context.templateIndex));
|
15435 | if (tmplIdx >= 0) {
|
15436 | // ... if found - replace it with nested template content
|
15437 | const isCloseTag = key.startsWith('CLOSE');
|
15438 | const isTemplateTag = key.endsWith('NG-TEMPLATE');
|
15439 | if (isTemplateTag) {
|
15440 | // current template's content is placed before or after
|
15441 | // parent template tag, depending on the open/close atrribute
|
15442 | phs.splice(tmplIdx + (isCloseTag ? 0 : 1), 0, ...values);
|
15443 | }
|
15444 | else {
|
15445 | const idx = isCloseTag ? values.length - 1 : 0;
|
15446 | values[idx].tmpl = phs[tmplIdx];
|
15447 | phs.splice(tmplIdx, 1, ...values);
|
15448 | }
|
15449 | }
|
15450 | else {
|
15451 | // ... otherwise just append content to placeholder value
|
15452 | phs.push(...values);
|
15453 | }
|
15454 | this.placeholders.set(key, phs);
|
15455 | });
|
15456 | this._unresolvedCtxCount--;
|
15457 | }
|
15458 | }
|
15459 | //
|
15460 | // Helper methods
|
15461 | //
|
15462 | function wrap(symbol, index, contextId, closed) {
|
15463 | const state = closed ? '/' : '';
|
15464 | return wrapI18nPlaceholder(`${state}${symbol}${index}`, contextId);
|
15465 | }
|
15466 | function wrapTag(symbol, { index, ctx, isVoid }, closed) {
|
15467 | return isVoid ? wrap(symbol, index, ctx) + wrap(symbol, index, ctx, true) :
|
15468 | wrap(symbol, index, ctx, closed);
|
15469 | }
|
15470 | function findTemplateFn(ctx, templateIndex) {
|
15471 | return (token) => typeof token === 'object' && token.type === TagType.TEMPLATE &&
|
15472 | token.index === templateIndex && token.ctx === ctx;
|
15473 | }
|
15474 | function serializePlaceholderValue(value) {
|
15475 | const element = (data, closed) => wrapTag('#', data, closed);
|
15476 | const template = (data, closed) => wrapTag('*', data, closed);
|
15477 | switch (value.type) {
|
15478 | case TagType.ELEMENT:
|
15479 | // close element tag
|
15480 | if (value.closed) {
|
15481 | return element(value, true) + (value.tmpl ? template(value.tmpl, true) : '');
|
15482 | }
|
15483 | // open element tag that also initiates a template
|
15484 | if (value.tmpl) {
|
15485 | return template(value.tmpl) + element(value) +
|
15486 | (value.isVoid ? template(value.tmpl, true) : '');
|
15487 | }
|
15488 | return element(value);
|
15489 | case TagType.TEMPLATE:
|
15490 | return template(value, value.closed);
|
15491 | default:
|
15492 | return value;
|
15493 | }
|
15494 | }
|
15495 |
|
15496 | /**
|
15497 | * @license
|
15498 | * Copyright Google LLC All Rights Reserved.
|
15499 | *
|
15500 | * Use of this source code is governed by an MIT-style license that can be
|
15501 | * found in the LICENSE file at https://angular.io/license
|
15502 | */
|
15503 | class IcuSerializerVisitor {
|
15504 | visitText(text) {
|
15505 | return text.value;
|
15506 | }
|
15507 | visitContainer(container) {
|
15508 | return container.children.map(child => child.visit(this)).join('');
|
15509 | }
|
15510 | visitIcu(icu) {
|
15511 | const strCases = Object.keys(icu.cases).map((k) => `${k} {${icu.cases[k].visit(this)}}`);
|
15512 | const result = `{${icu.expressionPlaceholder}, ${icu.type}, ${strCases.join(' ')}}`;
|
15513 | return result;
|
15514 | }
|
15515 | visitTagPlaceholder(ph) {
|
15516 | return ph.isVoid ?
|
15517 | this.formatPh(ph.startName) :
|
15518 | `${this.formatPh(ph.startName)}${ph.children.map(child => child.visit(this)).join('')}${this.formatPh(ph.closeName)}`;
|
15519 | }
|
15520 | visitPlaceholder(ph) {
|
15521 | return this.formatPh(ph.name);
|
15522 | }
|
15523 | visitIcuPlaceholder(ph, context) {
|
15524 | return this.formatPh(ph.name);
|
15525 | }
|
15526 | formatPh(value) {
|
15527 | return `{${formatI18nPlaceholderName(value, /* useCamelCase */ false)}}`;
|
15528 | }
|
15529 | }
|
15530 | const serializer = new IcuSerializerVisitor();
|
15531 | function serializeIcuNode(icu) {
|
15532 | return icu.visit(serializer);
|
15533 | }
|
15534 |
|
15535 | /**
|
15536 | * @license
|
15537 | * Copyright Google LLC All Rights Reserved.
|
15538 | *
|
15539 | * Use of this source code is governed by an MIT-style license that can be
|
15540 | * found in the LICENSE file at https://angular.io/license
|
15541 | */
|
15542 | const TAG_TO_PLACEHOLDER_NAMES = {
|
15543 | 'A': 'LINK',
|
15544 | 'B': 'BOLD_TEXT',
|
15545 | 'BR': 'LINE_BREAK',
|
15546 | 'EM': 'EMPHASISED_TEXT',
|
15547 | 'H1': 'HEADING_LEVEL1',
|
15548 | 'H2': 'HEADING_LEVEL2',
|
15549 | 'H3': 'HEADING_LEVEL3',
|
15550 | 'H4': 'HEADING_LEVEL4',
|
15551 | 'H5': 'HEADING_LEVEL5',
|
15552 | 'H6': 'HEADING_LEVEL6',
|
15553 | 'HR': 'HORIZONTAL_RULE',
|
15554 | 'I': 'ITALIC_TEXT',
|
15555 | 'LI': 'LIST_ITEM',
|
15556 | 'LINK': 'MEDIA_LINK',
|
15557 | 'OL': 'ORDERED_LIST',
|
15558 | 'P': 'PARAGRAPH',
|
15559 | 'Q': 'QUOTATION',
|
15560 | 'S': 'STRIKETHROUGH_TEXT',
|
15561 | 'SMALL': 'SMALL_TEXT',
|
15562 | 'SUB': 'SUBSTRIPT',
|
15563 | 'SUP': 'SUPERSCRIPT',
|
15564 | 'TBODY': 'TABLE_BODY',
|
15565 | 'TD': 'TABLE_CELL',
|
15566 | 'TFOOT': 'TABLE_FOOTER',
|
15567 | 'TH': 'TABLE_HEADER_CELL',
|
15568 | 'THEAD': 'TABLE_HEADER',
|
15569 | 'TR': 'TABLE_ROW',
|
15570 | 'TT': 'MONOSPACED_TEXT',
|
15571 | 'U': 'UNDERLINED_TEXT',
|
15572 | 'UL': 'UNORDERED_LIST',
|
15573 | };
|
15574 | /**
|
15575 | * Creates unique names for placeholder with different content.
|
15576 | *
|
15577 | * Returns the same placeholder name when the content is identical.
|
15578 | */
|
15579 | class PlaceholderRegistry {
|
15580 | constructor() {
|
15581 | // Count the occurrence of the base name top generate a unique name
|
15582 | this._placeHolderNameCounts = {};
|
15583 | // Maps signature to placeholder names
|
15584 | this._signatureToName = {};
|
15585 | }
|
15586 | getStartTagPlaceholderName(tag, attrs, isVoid) {
|
15587 | const signature = this._hashTag(tag, attrs, isVoid);
|
15588 | if (this._signatureToName[signature]) {
|
15589 | return this._signatureToName[signature];
|
15590 | }
|
15591 | const upperTag = tag.toUpperCase();
|
15592 | const baseName = TAG_TO_PLACEHOLDER_NAMES[upperTag] || `TAG_${upperTag}`;
|
15593 | const name = this._generateUniqueName(isVoid ? baseName : `START_${baseName}`);
|
15594 | this._signatureToName[signature] = name;
|
15595 | return name;
|
15596 | }
|
15597 | getCloseTagPlaceholderName(tag) {
|
15598 | const signature = this._hashClosingTag(tag);
|
15599 | if (this._signatureToName[signature]) {
|
15600 | return this._signatureToName[signature];
|
15601 | }
|
15602 | const upperTag = tag.toUpperCase();
|
15603 | const baseName = TAG_TO_PLACEHOLDER_NAMES[upperTag] || `TAG_${upperTag}`;
|
15604 | const name = this._generateUniqueName(`CLOSE_${baseName}`);
|
15605 | this._signatureToName[signature] = name;
|
15606 | return name;
|
15607 | }
|
15608 | getPlaceholderName(name, content) {
|
15609 | const upperName = name.toUpperCase();
|
15610 | const signature = `PH: ${upperName}=${content}`;
|
15611 | if (this._signatureToName[signature]) {
|
15612 | return this._signatureToName[signature];
|
15613 | }
|
15614 | const uniqueName = this._generateUniqueName(upperName);
|
15615 | this._signatureToName[signature] = uniqueName;
|
15616 | return uniqueName;
|
15617 | }
|
15618 | getUniquePlaceholder(name) {
|
15619 | return this._generateUniqueName(name.toUpperCase());
|
15620 | }
|
15621 | // Generate a hash for a tag - does not take attribute order into account
|
15622 | _hashTag(tag, attrs, isVoid) {
|
15623 | const start = `<${tag}`;
|
15624 | const strAttrs = Object.keys(attrs).sort().map((name) => ` ${name}=${attrs[name]}`).join('');
|
15625 | const end = isVoid ? '/>' : `></${tag}>`;
|
15626 | return start + strAttrs + end;
|
15627 | }
|
15628 | _hashClosingTag(tag) {
|
15629 | return this._hashTag(`/${tag}`, {}, false);
|
15630 | }
|
15631 | _generateUniqueName(base) {
|
15632 | const seen = this._placeHolderNameCounts.hasOwnProperty(base);
|
15633 | if (!seen) {
|
15634 | this._placeHolderNameCounts[base] = 1;
|
15635 | return base;
|
15636 | }
|
15637 | const id = this._placeHolderNameCounts[base];
|
15638 | this._placeHolderNameCounts[base] = id + 1;
|
15639 | return `${base}_${id}`;
|
15640 | }
|
15641 | }
|
15642 |
|
15643 | /**
|
15644 | * @license
|
15645 | * Copyright Google LLC All Rights Reserved.
|
15646 | *
|
15647 | * Use of this source code is governed by an MIT-style license that can be
|
15648 | * found in the LICENSE file at https://angular.io/license
|
15649 | */
|
15650 | const _expParser = new Parser$1(new Lexer());
|
15651 | /**
|
15652 | * Returns a function converting html nodes to an i18n Message given an interpolationConfig
|
15653 | */
|
15654 | function createI18nMessageFactory(interpolationConfig) {
|
15655 | const visitor = new _I18nVisitor(_expParser, interpolationConfig);
|
15656 | return (nodes, meaning, description, customId, visitNodeFn) => visitor.toI18nMessage(nodes, meaning, description, customId, visitNodeFn);
|
15657 | }
|
15658 | function noopVisitNodeFn(_html, i18n) {
|
15659 | return i18n;
|
15660 | }
|
15661 | class _I18nVisitor {
|
15662 | constructor(_expressionParser, _interpolationConfig) {
|
15663 | this._expressionParser = _expressionParser;
|
15664 | this._interpolationConfig = _interpolationConfig;
|
15665 | }
|
15666 | toI18nMessage(nodes, meaning = '', description = '', customId = '', visitNodeFn) {
|
15667 | const context = {
|
15668 | isIcu: nodes.length == 1 && nodes[0] instanceof Expansion,
|
15669 | icuDepth: 0,
|
15670 | placeholderRegistry: new PlaceholderRegistry(),
|
15671 | placeholderToContent: {},
|
15672 | placeholderToMessage: {},
|
15673 | visitNodeFn: visitNodeFn || noopVisitNodeFn,
|
15674 | };
|
15675 | const i18nodes = visitAll$1(this, nodes, context);
|
15676 | return new Message(i18nodes, context.placeholderToContent, context.placeholderToMessage, meaning, description, customId);
|
15677 | }
|
15678 | visitElement(el, context) {
|
15679 | var _a;
|
15680 | const children = visitAll$1(this, el.children, context);
|
15681 | const attrs = {};
|
15682 | el.attrs.forEach(attr => {
|
15683 | // Do not visit the attributes, translatable ones are top-level ASTs
|
15684 | attrs[attr.name] = attr.value;
|
15685 | });
|
15686 | const isVoid = getHtmlTagDefinition(el.name).isVoid;
|
15687 | const startPhName = context.placeholderRegistry.getStartTagPlaceholderName(el.name, attrs, isVoid);
|
15688 | context.placeholderToContent[startPhName] = {
|
15689 | text: el.startSourceSpan.toString(),
|
15690 | sourceSpan: el.startSourceSpan,
|
15691 | };
|
15692 | let closePhName = '';
|
15693 | if (!isVoid) {
|
15694 | closePhName = context.placeholderRegistry.getCloseTagPlaceholderName(el.name);
|
15695 | context.placeholderToContent[closePhName] = {
|
15696 | text: `</${el.name}>`,
|
15697 | sourceSpan: (_a = el.endSourceSpan) !== null && _a !== void 0 ? _a : el.sourceSpan,
|
15698 | };
|
15699 | }
|
15700 | const node = new TagPlaceholder(el.name, attrs, startPhName, closePhName, children, isVoid, el.sourceSpan, el.startSourceSpan, el.endSourceSpan);
|
15701 | return context.visitNodeFn(el, node);
|
15702 | }
|
15703 | visitAttribute(attribute, context) {
|
15704 | const node = this._visitTextWithInterpolation(attribute.value, attribute.valueSpan || attribute.sourceSpan, context, attribute.i18n);
|
15705 | return context.visitNodeFn(attribute, node);
|
15706 | }
|
15707 | visitText(text, context) {
|
15708 | const node = this._visitTextWithInterpolation(text.value, text.sourceSpan, context, text.i18n);
|
15709 | return context.visitNodeFn(text, node);
|
15710 | }
|
15711 | visitComment(comment, context) {
|
15712 | return null;
|
15713 | }
|
15714 | visitExpansion(icu, context) {
|
15715 | context.icuDepth++;
|
15716 | const i18nIcuCases = {};
|
15717 | const i18nIcu = new Icu$1(icu.switchValue, icu.type, i18nIcuCases, icu.sourceSpan);
|
15718 | icu.cases.forEach((caze) => {
|
15719 | i18nIcuCases[caze.value] = new Container(caze.expression.map((node) => node.visit(this, context)), caze.expSourceSpan);
|
15720 | });
|
15721 | context.icuDepth--;
|
15722 | if (context.isIcu || context.icuDepth > 0) {
|
15723 | // Returns an ICU node when:
|
15724 | // - the message (vs a part of the message) is an ICU message, or
|
15725 | // - the ICU message is nested.
|
15726 | const expPh = context.placeholderRegistry.getUniquePlaceholder(`VAR_${icu.type}`);
|
15727 | i18nIcu.expressionPlaceholder = expPh;
|
15728 | context.placeholderToContent[expPh] = {
|
15729 | text: icu.switchValue,
|
15730 | sourceSpan: icu.switchValueSourceSpan,
|
15731 | };
|
15732 | return context.visitNodeFn(icu, i18nIcu);
|
15733 | }
|
15734 | // Else returns a placeholder
|
15735 | // ICU placeholders should not be replaced with their original content but with the their
|
15736 | // translations.
|
15737 | // TODO(vicb): add a html.Node -> i18n.Message cache to avoid having to re-create the msg
|
15738 | const phName = context.placeholderRegistry.getPlaceholderName('ICU', icu.sourceSpan.toString());
|
15739 | context.placeholderToMessage[phName] = this.toI18nMessage([icu], '', '', '', undefined);
|
15740 | const node = new IcuPlaceholder(i18nIcu, phName, icu.sourceSpan);
|
15741 | return context.visitNodeFn(icu, node);
|
15742 | }
|
15743 | visitExpansionCase(_icuCase, _context) {
|
15744 | throw new Error('Unreachable code');
|
15745 | }
|
15746 | /**
|
15747 | * Split the, potentially interpolated, text up into text and placeholder pieces.
|
15748 | *
|
15749 | * @param text The potentially interpolated string to be split.
|
15750 | * @param sourceSpan The span of the whole of the `text` string.
|
15751 | * @param context The current context of the visitor, used to compute and store placeholders.
|
15752 | * @param previousI18n Any i18n metadata associated with this `text` from a previous pass.
|
15753 | */
|
15754 | _visitTextWithInterpolation(text, sourceSpan, context, previousI18n) {
|
15755 | const { strings, expressions } = this._expressionParser.splitInterpolation(text, sourceSpan.start.toString(), this._interpolationConfig);
|
15756 | // No expressions, return a single text.
|
15757 | if (expressions.length === 0) {
|
15758 | return new Text$1(text, sourceSpan);
|
15759 | }
|
15760 | // Return a sequence of `Text` and `Placeholder` nodes grouped in a `Container`.
|
15761 | const nodes = [];
|
15762 | for (let i = 0; i < strings.length - 1; i++) {
|
15763 | this._addText(nodes, strings[i], sourceSpan);
|
15764 | this._addPlaceholder(nodes, context, expressions[i], sourceSpan);
|
15765 | }
|
15766 | // The last index contains no expression
|
15767 | this._addText(nodes, strings[strings.length - 1], sourceSpan);
|
15768 | // Whitespace removal may have invalidated the interpolation source-spans.
|
15769 | reusePreviousSourceSpans(nodes, previousI18n);
|
15770 | return new Container(nodes, sourceSpan);
|
15771 | }
|
15772 | /**
|
15773 | * Create a new `Text` node from the `textPiece` and add it to the `nodes` collection.
|
15774 | *
|
15775 | * @param nodes The nodes to which the created `Text` node should be added.
|
15776 | * @param textPiece The text and relative span information for this `Text` node.
|
15777 | * @param interpolationSpan The span of the whole interpolated text.
|
15778 | */
|
15779 | _addText(nodes, textPiece, interpolationSpan) {
|
15780 | if (textPiece.text.length > 0) {
|
15781 | // No need to add empty strings
|
15782 | const stringSpan = getOffsetSourceSpan(interpolationSpan, textPiece);
|
15783 | nodes.push(new Text$1(textPiece.text, stringSpan));
|
15784 | }
|
15785 | }
|
15786 | /**
|
15787 | * Create a new `Placeholder` node from the `expression` and add it to the `nodes` collection.
|
15788 | *
|
15789 | * @param nodes The nodes to which the created `Text` node should be added.
|
15790 | * @param context The current context of the visitor, used to compute and store placeholders.
|
15791 | * @param expression The expression text and relative span information for this `Placeholder`
|
15792 | * node.
|
15793 | * @param interpolationSpan The span of the whole interpolated text.
|
15794 | */
|
15795 | _addPlaceholder(nodes, context, expression, interpolationSpan) {
|
15796 | const sourceSpan = getOffsetSourceSpan(interpolationSpan, expression);
|
15797 | const baseName = extractPlaceholderName(expression.text) || 'INTERPOLATION';
|
15798 | const phName = context.placeholderRegistry.getPlaceholderName(baseName, expression.text);
|
15799 | const text = this._interpolationConfig.start + expression.text + this._interpolationConfig.end;
|
15800 | context.placeholderToContent[phName] = { text, sourceSpan };
|
15801 | nodes.push(new Placeholder(expression.text, phName, sourceSpan));
|
15802 | }
|
15803 | }
|
15804 | /**
|
15805 | * Re-use the source-spans from `previousI18n` metadata for the `nodes`.
|
15806 | *
|
15807 | * Whitespace removal can invalidate the source-spans of interpolation nodes, so we
|
15808 | * reuse the source-span stored from a previous pass before the whitespace was removed.
|
15809 | *
|
15810 | * @param nodes The `Text` and `Placeholder` nodes to be processed.
|
15811 | * @param previousI18n Any i18n metadata for these `nodes` stored from a previous pass.
|
15812 | */
|
15813 | function reusePreviousSourceSpans(nodes, previousI18n) {
|
15814 | if (previousI18n instanceof Message) {
|
15815 | // The `previousI18n` is an i18n `Message`, so we are processing an `Attribute` with i18n
|
15816 | // metadata. The `Message` should consist only of a single `Container` that contains the
|
15817 | // parts (`Text` and `Placeholder`) to process.
|
15818 | assertSingleContainerMessage(previousI18n);
|
15819 | previousI18n = previousI18n.nodes[0];
|
15820 | }
|
15821 | if (previousI18n instanceof Container) {
|
15822 | // The `previousI18n` is a `Container`, which means that this is a second i18n extraction pass
|
15823 | // after whitespace has been removed from the AST ndoes.
|
15824 | assertEquivalentNodes(previousI18n.children, nodes);
|
15825 | // Reuse the source-spans from the first pass.
|
15826 | for (let i = 0; i < nodes.length; i++) {
|
15827 | nodes[i].sourceSpan = previousI18n.children[i].sourceSpan;
|
15828 | }
|
15829 | }
|
15830 | }
|
15831 | /**
|
15832 | * Asserts that the `message` contains exactly one `Container` node.
|
15833 | */
|
15834 | function assertSingleContainerMessage(message) {
|
15835 | const nodes = message.nodes;
|
15836 | if (nodes.length !== 1 || !(nodes[0] instanceof Container)) {
|
15837 | throw new Error('Unexpected previous i18n message - expected it to consist of only a single `Container` node.');
|
15838 | }
|
15839 | }
|
15840 | /**
|
15841 | * Asserts that the `previousNodes` and `node` collections have the same number of elements and
|
15842 | * corresponding elements have the same node type.
|
15843 | */
|
15844 | function assertEquivalentNodes(previousNodes, nodes) {
|
15845 | if (previousNodes.length !== nodes.length) {
|
15846 | throw new Error('The number of i18n message children changed between first and second pass.');
|
15847 | }
|
15848 | if (previousNodes.some((node, i) => nodes[i].constructor !== node.constructor)) {
|
15849 | throw new Error('The types of the i18n message children changed between first and second pass.');
|
15850 | }
|
15851 | }
|
15852 | /**
|
15853 | * Create a new `ParseSourceSpan` from the `sourceSpan`, offset by the `start` and `end` values.
|
15854 | */
|
15855 | function getOffsetSourceSpan(sourceSpan, { start, end }) {
|
15856 | return new ParseSourceSpan(sourceSpan.fullStart.moveBy(start), sourceSpan.fullStart.moveBy(end));
|
15857 | }
|
15858 | const _CUSTOM_PH_EXP = /\/\/[\s\S]*i18n[\s\S]*\([\s\S]*ph[\s\S]*=[\s\S]*("|')([\s\S]*?)\1[\s\S]*\)/g;
|
15859 | function extractPlaceholderName(input) {
|
15860 | return input.split(_CUSTOM_PH_EXP)[2];
|
15861 | }
|
15862 |
|
15863 | /**
|
15864 | * @license
|
15865 | * Copyright Google LLC All Rights Reserved.
|
15866 | *
|
15867 | * Use of this source code is governed by an MIT-style license that can be
|
15868 | * found in the LICENSE file at https://angular.io/license
|
15869 | */
|
15870 | /**
|
15871 | * An i18n error.
|
15872 | */
|
15873 | class I18nError extends ParseError {
|
15874 | constructor(span, msg) {
|
15875 | super(span, msg);
|
15876 | }
|
15877 | }
|
15878 |
|
15879 | /**
|
15880 | * @license
|
15881 | * Copyright Google LLC All Rights Reserved.
|
15882 | *
|
15883 | * Use of this source code is governed by an MIT-style license that can be
|
15884 | * found in the LICENSE file at https://angular.io/license
|
15885 | */
|
15886 | const setI18nRefs = (htmlNode, i18nNode) => {
|
15887 | if (htmlNode instanceof NodeWithI18n) {
|
15888 | if (i18nNode instanceof IcuPlaceholder && htmlNode.i18n instanceof Message) {
|
15889 | // This html node represents an ICU but this is a second processing pass, and the legacy id
|
15890 | // was computed in the previous pass and stored in the `i18n` property as a message.
|
15891 | // We are about to wipe out that property so capture the previous message to be reused when
|
15892 | // generating the message for this ICU later. See `_generateI18nMessage()`.
|
15893 | i18nNode.previousMessage = htmlNode.i18n;
|
15894 | }
|
15895 | htmlNode.i18n = i18nNode;
|
15896 | }
|
15897 | return i18nNode;
|
15898 | };
|
15899 | /**
|
15900 | * This visitor walks over HTML parse tree and converts information stored in
|
15901 | * i18n-related attributes ("i18n" and "i18n-*") into i18n meta object that is
|
15902 | * stored with other element's and attribute's information.
|
15903 | */
|
15904 | class I18nMetaVisitor {
|
15905 | constructor(interpolationConfig = DEFAULT_INTERPOLATION_CONFIG, keepI18nAttrs = false, enableI18nLegacyMessageIdFormat = false) {
|
15906 | this.interpolationConfig = interpolationConfig;
|
15907 | this.keepI18nAttrs = keepI18nAttrs;
|
15908 | this.enableI18nLegacyMessageIdFormat = enableI18nLegacyMessageIdFormat;
|
15909 | // whether visited nodes contain i18n information
|
15910 | this.hasI18nMeta = false;
|
15911 | this._errors = [];
|
15912 | // i18n message generation factory
|
15913 | this._createI18nMessage = createI18nMessageFactory(this.interpolationConfig);
|
15914 | }
|
15915 | _generateI18nMessage(nodes, meta = '', visitNodeFn) {
|
15916 | const { meaning, description, customId } = this._parseMetadata(meta);
|
15917 | const message = this._createI18nMessage(nodes, meaning, description, customId, visitNodeFn);
|
15918 | this._setMessageId(message, meta);
|
15919 | this._setLegacyIds(message, meta);
|
15920 | return message;
|
15921 | }
|
15922 | visitAllWithErrors(nodes) {
|
15923 | const result = nodes.map(node => node.visit(this, null));
|
15924 | return new ParseTreeResult(result, this._errors);
|
15925 | }
|
15926 | visitElement(element) {
|
15927 | if (hasI18nAttrs(element)) {
|
15928 | this.hasI18nMeta = true;
|
15929 | const attrs = [];
|
15930 | const attrsMeta = {};
|
15931 | for (const attr of element.attrs) {
|
15932 | if (attr.name === I18N_ATTR) {
|
15933 | // root 'i18n' node attribute
|
15934 | const i18n = element.i18n || attr.value;
|
15935 | const message = this._generateI18nMessage(element.children, i18n, setI18nRefs);
|
15936 | // do not assign empty i18n meta
|
15937 | if (message.nodes.length) {
|
15938 | element.i18n = message;
|
15939 | }
|
15940 | }
|
15941 | else if (attr.name.startsWith(I18N_ATTR_PREFIX)) {
|
15942 | // 'i18n-*' attributes
|
15943 | const name = attr.name.slice(I18N_ATTR_PREFIX.length);
|
15944 | if (isTrustedTypesSink(element.name, name)) {
|
15945 | this._reportError(attr, `Translating attribute '${name}' is disallowed for security reasons.`);
|
15946 | }
|
15947 | else {
|
15948 | attrsMeta[name] = attr.value;
|
15949 | }
|
15950 | }
|
15951 | else {
|
15952 | // non-i18n attributes
|
15953 | attrs.push(attr);
|
15954 | }
|
15955 | }
|
15956 | // set i18n meta for attributes
|
15957 | if (Object.keys(attrsMeta).length) {
|
15958 | for (const attr of attrs) {
|
15959 | const meta = attrsMeta[attr.name];
|
15960 | // do not create translation for empty attributes
|
15961 | if (meta !== undefined && attr.value) {
|
15962 | attr.i18n = this._generateI18nMessage([attr], attr.i18n || meta);
|
15963 | }
|
15964 | }
|
15965 | }
|
15966 | if (!this.keepI18nAttrs) {
|
15967 | // update element's attributes,
|
15968 | // keeping only non-i18n related ones
|
15969 | element.attrs = attrs;
|
15970 | }
|
15971 | }
|
15972 | visitAll$1(this, element.children, element.i18n);
|
15973 | return element;
|
15974 | }
|
15975 | visitExpansion(expansion, currentMessage) {
|
15976 | let message;
|
15977 | const meta = expansion.i18n;
|
15978 | this.hasI18nMeta = true;
|
15979 | if (meta instanceof IcuPlaceholder) {
|
15980 | // set ICU placeholder name (e.g. "ICU_1"),
|
15981 | // generated while processing root element contents,
|
15982 | // so we can reference it when we output translation
|
15983 | const name = meta.name;
|
15984 | message = this._generateI18nMessage([expansion], meta);
|
15985 | const icu = icuFromI18nMessage(message);
|
15986 | icu.name = name;
|
15987 | }
|
15988 | else {
|
15989 | // ICU is a top level message, try to use metadata from container element if provided via
|
15990 | // `context` argument. Note: context may not be available for standalone ICUs (without
|
15991 | // wrapping element), so fallback to ICU metadata in this case.
|
15992 | message = this._generateI18nMessage([expansion], currentMessage || meta);
|
15993 | }
|
15994 | expansion.i18n = message;
|
15995 | return expansion;
|
15996 | }
|
15997 | visitText(text) {
|
15998 | return text;
|
15999 | }
|
16000 | visitAttribute(attribute) {
|
16001 | return attribute;
|
16002 | }
|
16003 | visitComment(comment) {
|
16004 | return comment;
|
16005 | }
|
16006 | visitExpansionCase(expansionCase) {
|
16007 | return expansionCase;
|
16008 | }
|
16009 | /**
|
16010 | * Parse the general form `meta` passed into extract the explicit metadata needed to create a
|
16011 | * `Message`.
|
16012 | *
|
16013 | * There are three possibilities for the `meta` variable
|
16014 | * 1) a string from an `i18n` template attribute: parse it to extract the metadata values.
|
16015 | * 2) a `Message` from a previous processing pass: reuse the metadata values in the message.
|
16016 | * 4) other: ignore this and just process the message metadata as normal
|
16017 | *
|
16018 | * @param meta the bucket that holds information about the message
|
16019 | * @returns the parsed metadata.
|
16020 | */
|
16021 | _parseMetadata(meta) {
|
16022 | return typeof meta === 'string' ? parseI18nMeta(meta) :
|
16023 | meta instanceof Message ? meta : {};
|
16024 | }
|
16025 | /**
|
16026 | * Generate (or restore) message id if not specified already.
|
16027 | */
|
16028 | _setMessageId(message, meta) {
|
16029 | if (!message.id) {
|
16030 | message.id = meta instanceof Message && meta.id || decimalDigest(message);
|
16031 | }
|
16032 | }
|
16033 | /**
|
16034 | * Update the `message` with a `legacyId` if necessary.
|
16035 | *
|
16036 | * @param message the message whose legacy id should be set
|
16037 | * @param meta information about the message being processed
|
16038 | */
|
16039 | _setLegacyIds(message, meta) {
|
16040 | if (this.enableI18nLegacyMessageIdFormat) {
|
16041 | message.legacyIds = [computeDigest(message), computeDecimalDigest(message)];
|
16042 | }
|
16043 | else if (typeof meta !== 'string') {
|
16044 | // This occurs if we are doing the 2nd pass after whitespace removal (see `parseTemplate()` in
|
16045 | // `packages/compiler/src/render3/view/template.ts`).
|
16046 | // In that case we want to reuse the legacy message generated in the 1st pass (see
|
16047 | // `setI18nRefs()`).
|
16048 | const previousMessage = meta instanceof Message ?
|
16049 | meta :
|
16050 | meta instanceof IcuPlaceholder ? meta.previousMessage : undefined;
|
16051 | message.legacyIds = previousMessage ? previousMessage.legacyIds : [];
|
16052 | }
|
16053 | }
|
16054 | _reportError(node, msg) {
|
16055 | this._errors.push(new I18nError(node.sourceSpan, msg));
|
16056 | }
|
16057 | }
|
16058 | /** I18n separators for metadata **/
|
16059 | const I18N_MEANING_SEPARATOR = '|';
|
16060 | const I18N_ID_SEPARATOR = '@@';
|
16061 | /**
|
16062 | * Parses i18n metas like:
|
16063 | * - "@@id",
|
16064 | * - "description[@@id]",
|
16065 | * - "meaning|description[@@id]"
|
16066 | * and returns an object with parsed output.
|
16067 | *
|
16068 | * @param meta String that represents i18n meta
|
16069 | * @returns Object with id, meaning and description fields
|
16070 | */
|
16071 | function parseI18nMeta(meta = '') {
|
16072 | let customId;
|
16073 | let meaning;
|
16074 | let description;
|
16075 | meta = meta.trim();
|
16076 | if (meta) {
|
16077 | const idIndex = meta.indexOf(I18N_ID_SEPARATOR);
|
16078 | const descIndex = meta.indexOf(I18N_MEANING_SEPARATOR);
|
16079 | let meaningAndDesc;
|
16080 | [meaningAndDesc, customId] =
|
16081 | (idIndex > -1) ? [meta.slice(0, idIndex), meta.slice(idIndex + 2)] : [meta, ''];
|
16082 | [meaning, description] = (descIndex > -1) ?
|
16083 | [meaningAndDesc.slice(0, descIndex), meaningAndDesc.slice(descIndex + 1)] :
|
16084 | ['', meaningAndDesc];
|
16085 | }
|
16086 | return { customId, meaning, description };
|
16087 | }
|
16088 | // Converts i18n meta information for a message (id, description, meaning)
|
16089 | // to a JsDoc statement formatted as expected by the Closure compiler.
|
16090 | function i18nMetaToJSDoc(meta) {
|
16091 | const tags = [];
|
16092 | if (meta.description) {
|
16093 | tags.push({ tagName: "desc" /* Desc */, text: meta.description });
|
16094 | }
|
16095 | if (meta.meaning) {
|
16096 | tags.push({ tagName: "meaning" /* Meaning */, text: meta.meaning });
|
16097 | }
|
16098 | return tags.length == 0 ? null : jsDocComment(tags);
|
16099 | }
|
16100 |
|
16101 | /** Closure uses `goog.getMsg(message)` to lookup translations */
|
16102 | const GOOG_GET_MSG = 'goog.getMsg';
|
16103 | function createGoogleGetMsgStatements(variable$1, message, closureVar, params) {
|
16104 | const messageString = serializeI18nMessageForGetMsg(message);
|
16105 | const args = [literal(messageString)];
|
16106 | if (Object.keys(params).length) {
|
16107 | args.push(mapLiteral(params, true));
|
16108 | }
|
16109 | // /**
|
16110 | // * @desc description of message
|
16111 | // * @meaning meaning of message
|
16112 | // */
|
16113 | // const MSG_... = goog.getMsg(..);
|
16114 | // I18N_X = MSG_...;
|
16115 | const googGetMsgStmt = closureVar.set(variable(GOOG_GET_MSG).callFn(args)).toConstDecl();
|
16116 | const metaComment = i18nMetaToJSDoc(message);
|
16117 | if (metaComment !== null) {
|
16118 | googGetMsgStmt.addLeadingComment(metaComment);
|
16119 | }
|
16120 | const i18nAssignmentStmt = new ExpressionStatement(variable$1.set(closureVar));
|
16121 | return [googGetMsgStmt, i18nAssignmentStmt];
|
16122 | }
|
16123 | /**
|
16124 | * This visitor walks over i18n tree and generates its string representation, including ICUs and
|
16125 | * placeholders in `{$placeholder}` (for plain messages) or `{PLACEHOLDER}` (inside ICUs) format.
|
16126 | */
|
16127 | class GetMsgSerializerVisitor {
|
16128 | formatPh(value) {
|
16129 | return `{$${formatI18nPlaceholderName(value)}}`;
|
16130 | }
|
16131 | visitText(text) {
|
16132 | return text.value;
|
16133 | }
|
16134 | visitContainer(container) {
|
16135 | return container.children.map(child => child.visit(this)).join('');
|
16136 | }
|
16137 | visitIcu(icu) {
|
16138 | return serializeIcuNode(icu);
|
16139 | }
|
16140 | visitTagPlaceholder(ph) {
|
16141 | return ph.isVoid ?
|
16142 | this.formatPh(ph.startName) :
|
16143 | `${this.formatPh(ph.startName)}${ph.children.map(child => child.visit(this)).join('')}${this.formatPh(ph.closeName)}`;
|
16144 | }
|
16145 | visitPlaceholder(ph) {
|
16146 | return this.formatPh(ph.name);
|
16147 | }
|
16148 | visitIcuPlaceholder(ph, context) {
|
16149 | return this.formatPh(ph.name);
|
16150 | }
|
16151 | }
|
16152 | const serializerVisitor$1 = new GetMsgSerializerVisitor();
|
16153 | function serializeI18nMessageForGetMsg(message) {
|
16154 | return message.nodes.map(node => node.visit(serializerVisitor$1, null)).join('');
|
16155 | }
|
16156 |
|
16157 | function createLocalizeStatements(variable, message, params) {
|
16158 | const { messageParts, placeHolders } = serializeI18nMessageForLocalize(message);
|
16159 | const sourceSpan = getSourceSpan(message);
|
16160 | const expressions = placeHolders.map(ph => params[ph.text]);
|
16161 | const localizedString$1 = localizedString(message, messageParts, placeHolders, expressions, sourceSpan);
|
16162 | const variableInitialization = variable.set(localizedString$1);
|
16163 | return [new ExpressionStatement(variableInitialization)];
|
16164 | }
|
16165 | /**
|
16166 | * This visitor walks over an i18n tree, capturing literal strings and placeholders.
|
16167 | *
|
16168 | * The result can be used for generating the `$localize` tagged template literals.
|
16169 | */
|
16170 | class LocalizeSerializerVisitor {
|
16171 | visitText(text, context) {
|
16172 | if (context[context.length - 1] instanceof LiteralPiece) {
|
16173 | // Two literal pieces in a row means that there was some comment node in-between.
|
16174 | context[context.length - 1].text += text.value;
|
16175 | }
|
16176 | else {
|
16177 | context.push(new LiteralPiece(text.value, text.sourceSpan));
|
16178 | }
|
16179 | }
|
16180 | visitContainer(container, context) {
|
16181 | container.children.forEach(child => child.visit(this, context));
|
16182 | }
|
16183 | visitIcu(icu, context) {
|
16184 | context.push(new LiteralPiece(serializeIcuNode(icu), icu.sourceSpan));
|
16185 | }
|
16186 | visitTagPlaceholder(ph, context) {
|
16187 | var _a, _b;
|
16188 | context.push(this.createPlaceholderPiece(ph.startName, (_a = ph.startSourceSpan) !== null && _a !== void 0 ? _a : ph.sourceSpan));
|
16189 | if (!ph.isVoid) {
|
16190 | ph.children.forEach(child => child.visit(this, context));
|
16191 | context.push(this.createPlaceholderPiece(ph.closeName, (_b = ph.endSourceSpan) !== null && _b !== void 0 ? _b : ph.sourceSpan));
|
16192 | }
|
16193 | }
|
16194 | visitPlaceholder(ph, context) {
|
16195 | context.push(this.createPlaceholderPiece(ph.name, ph.sourceSpan));
|
16196 | }
|
16197 | visitIcuPlaceholder(ph, context) {
|
16198 | context.push(this.createPlaceholderPiece(ph.name, ph.sourceSpan));
|
16199 | }
|
16200 | createPlaceholderPiece(name, sourceSpan) {
|
16201 | return new PlaceholderPiece(formatI18nPlaceholderName(name, /* useCamelCase */ false), sourceSpan);
|
16202 | }
|
16203 | }
|
16204 | const serializerVisitor$2 = new LocalizeSerializerVisitor();
|
16205 | /**
|
16206 | * Serialize an i18n message into two arrays: messageParts and placeholders.
|
16207 | *
|
16208 | * These arrays will be used to generate `$localize` tagged template literals.
|
16209 | *
|
16210 | * @param message The message to be serialized.
|
16211 | * @returns an object containing the messageParts and placeholders.
|
16212 | */
|
16213 | function serializeI18nMessageForLocalize(message) {
|
16214 | const pieces = [];
|
16215 | message.nodes.forEach(node => node.visit(serializerVisitor$2, pieces));
|
16216 | return processMessagePieces(pieces);
|
16217 | }
|
16218 | function getSourceSpan(message) {
|
16219 | const startNode = message.nodes[0];
|
16220 | const endNode = message.nodes[message.nodes.length - 1];
|
16221 | return new ParseSourceSpan(startNode.sourceSpan.start, endNode.sourceSpan.end, startNode.sourceSpan.fullStart, startNode.sourceSpan.details);
|
16222 | }
|
16223 | /**
|
16224 | * Convert the list of serialized MessagePieces into two arrays.
|
16225 | *
|
16226 | * One contains the literal string pieces and the other the placeholders that will be replaced by
|
16227 | * expressions when rendering `$localize` tagged template literals.
|
16228 | *
|
16229 | * @param pieces The pieces to process.
|
16230 | * @returns an object containing the messageParts and placeholders.
|
16231 | */
|
16232 | function processMessagePieces(pieces) {
|
16233 | const messageParts = [];
|
16234 | const placeHolders = [];
|
16235 | if (pieces[0] instanceof PlaceholderPiece) {
|
16236 | // The first piece was a placeholder so we need to add an initial empty message part.
|
16237 | messageParts.push(createEmptyMessagePart(pieces[0].sourceSpan.start));
|
16238 | }
|
16239 | for (let i = 0; i < pieces.length; i++) {
|
16240 | const part = pieces[i];
|
16241 | if (part instanceof LiteralPiece) {
|
16242 | messageParts.push(part);
|
16243 | }
|
16244 | else {
|
16245 | placeHolders.push(part);
|
16246 | if (pieces[i - 1] instanceof PlaceholderPiece) {
|
16247 | // There were two placeholders in a row, so we need to add an empty message part.
|
16248 | messageParts.push(createEmptyMessagePart(pieces[i - 1].sourceSpan.end));
|
16249 | }
|
16250 | }
|
16251 | }
|
16252 | if (pieces[pieces.length - 1] instanceof PlaceholderPiece) {
|
16253 | // The last piece was a placeholder so we need to add a final empty message part.
|
16254 | messageParts.push(createEmptyMessagePart(pieces[pieces.length - 1].sourceSpan.end));
|
16255 | }
|
16256 | return { messageParts, placeHolders };
|
16257 | }
|
16258 | function createEmptyMessagePart(location) {
|
16259 | return new LiteralPiece('', new ParseSourceSpan(location, location));
|
16260 | }
|
16261 |
|
16262 | /**
|
16263 | * @license
|
16264 | * Copyright Google LLC All Rights Reserved.
|
16265 | *
|
16266 | * Use of this source code is governed by an MIT-style license that can be
|
16267 | * found in the LICENSE file at https://angular.io/license
|
16268 | */
|
16269 | // Selector attribute name of `<ng-content>`
|
16270 | const NG_CONTENT_SELECT_ATTR$1 = 'select';
|
16271 | // Attribute name of `ngProjectAs`.
|
16272 | const NG_PROJECT_AS_ATTR_NAME = 'ngProjectAs';
|
16273 | // Global symbols available only inside event bindings.
|
16274 | const EVENT_BINDING_SCOPE_GLOBALS = new Set(['$event']);
|
16275 | // List of supported global targets for event listeners
|
16276 | const GLOBAL_TARGET_RESOLVERS = new Map([['window', Identifiers$1.resolveWindow], ['document', Identifiers$1.resolveDocument], ['body', Identifiers$1.resolveBody]]);
|
16277 | const LEADING_TRIVIA_CHARS = [' ', '\n', '\r', '\t'];
|
16278 | // if (rf & flags) { .. }
|
16279 | function renderFlagCheckIfStmt(flags, statements) {
|
16280 | return ifStmt(variable(RENDER_FLAGS).bitwiseAnd(literal(flags), null, false), statements);
|
16281 | }
|
16282 | function prepareEventListenerParameters(eventAst, handlerName = null, scope = null) {
|
16283 | const { type, name, target, phase, handler } = eventAst;
|
16284 | if (target && !GLOBAL_TARGET_RESOLVERS.has(target)) {
|
16285 | throw new Error(`Unexpected global target '${target}' defined for '${name}' event.
|
16286 | Supported list of global targets: ${Array.from(GLOBAL_TARGET_RESOLVERS.keys())}.`);
|
16287 | }
|
16288 | const eventArgumentName = '$event';
|
16289 | const implicitReceiverAccesses = new Set();
|
16290 | const implicitReceiverExpr = (scope === null || scope.bindingLevel === 0) ?
|
16291 | variable(CONTEXT_NAME) :
|
16292 | scope.getOrCreateSharedContextVar(0);
|
16293 | const bindingExpr = convertActionBinding(scope, implicitReceiverExpr, handler, 'b', () => error('Unexpected interpolation'), eventAst.handlerSpan, implicitReceiverAccesses, EVENT_BINDING_SCOPE_GLOBALS);
|
16294 | const statements = [];
|
16295 | if (scope) {
|
16296 | statements.push(...scope.restoreViewStatement());
|
16297 | statements.push(...scope.variableDeclarations());
|
16298 | }
|
16299 | statements.push(...bindingExpr.render3Stmts);
|
16300 | const eventName = type === 1 /* Animation */ ? prepareSyntheticListenerName(name, phase) : name;
|
16301 | const fnName = handlerName && sanitizeIdentifier(handlerName);
|
16302 | const fnArgs = [];
|
16303 | if (implicitReceiverAccesses.has(eventArgumentName)) {
|
16304 | fnArgs.push(new FnParam(eventArgumentName, DYNAMIC_TYPE));
|
16305 | }
|
16306 | const handlerFn = fn(fnArgs, statements, INFERRED_TYPE, null, fnName);
|
16307 | const params = [literal(eventName), handlerFn];
|
16308 | if (target) {
|
16309 | params.push(literal(false), // `useCapture` flag, defaults to `false`
|
16310 | importExpr(GLOBAL_TARGET_RESOLVERS.get(target)));
|
16311 | }
|
16312 | return params;
|
16313 | }
|
16314 | function createComponentDefConsts() {
|
16315 | return {
|
16316 | prepareStatements: [],
|
16317 | constExpressions: [],
|
16318 | i18nVarRefsCache: new Map(),
|
16319 | };
|
16320 | }
|
16321 | class TemplateDefinitionBuilder {
|
16322 | constructor(constantPool, parentBindingScope, level = 0, contextName, i18nContext, templateIndex, templateName, directiveMatcher, directives, pipeTypeByName, pipes, _namespace, relativeContextFilePath, i18nUseExternalIds, _constants = createComponentDefConsts()) {
|
16323 | this.constantPool = constantPool;
|
16324 | this.level = level;
|
16325 | this.contextName = contextName;
|
16326 | this.i18nContext = i18nContext;
|
16327 | this.templateIndex = templateIndex;
|
16328 | this.templateName = templateName;
|
16329 | this.directiveMatcher = directiveMatcher;
|
16330 | this.directives = directives;
|
16331 | this.pipeTypeByName = pipeTypeByName;
|
16332 | this.pipes = pipes;
|
16333 | this._namespace = _namespace;
|
16334 | this.i18nUseExternalIds = i18nUseExternalIds;
|
16335 | this._constants = _constants;
|
16336 | this._dataIndex = 0;
|
16337 | this._bindingContext = 0;
|
16338 | this._prefixCode = [];
|
16339 | /**
|
16340 | * List of callbacks to generate creation mode instructions. We store them here as we process
|
16341 | * the template so bindings in listeners are resolved only once all nodes have been visited.
|
16342 | * This ensures all local refs and context variables are available for matching.
|
16343 | */
|
16344 | this._creationCodeFns = [];
|
16345 | /**
|
16346 | * List of callbacks to generate update mode instructions. We store them here as we process
|
16347 | * the template so bindings are resolved only once all nodes have been visited. This ensures
|
16348 | * all local refs and context variables are available for matching.
|
16349 | */
|
16350 | this._updateCodeFns = [];
|
16351 | /** Index of the currently-selected node. */
|
16352 | this._currentIndex = 0;
|
16353 | /** Temporary variable declarations generated from visiting pipes, literals, etc. */
|
16354 | this._tempVariables = [];
|
16355 | /**
|
16356 | * List of callbacks to build nested templates. Nested templates must not be visited until
|
16357 | * after the parent template has finished visiting all of its nodes. This ensures that all
|
16358 | * local ref bindings in nested templates are able to find local ref values if the refs
|
16359 | * are defined after the template declaration.
|
16360 | */
|
16361 | this._nestedTemplateFns = [];
|
16362 | this._unsupported = unsupported;
|
16363 | // i18n context local to this template
|
16364 | this.i18n = null;
|
16365 | // Number of slots to reserve for pureFunctions
|
16366 | this._pureFunctionSlots = 0;
|
16367 | // Number of binding slots
|
16368 | this._bindingSlots = 0;
|
16369 | // Projection slots found in the template. Projection slots can distribute projected
|
16370 | // nodes based on a selector, or can just use the wildcard selector to match
|
16371 | // all nodes which aren't matching any selector.
|
16372 | this._ngContentReservedSlots = [];
|
16373 | // Number of non-default selectors found in all parent templates of this template. We need to
|
16374 | // track it to properly adjust projection slot index in the `projection` instruction.
|
16375 | this._ngContentSelectorsOffset = 0;
|
16376 | // Expression that should be used as implicit receiver when converting template
|
16377 | // expressions to output AST.
|
16378 | this._implicitReceiverExpr = null;
|
16379 | // These should be handled in the template or element directly.
|
16380 | this.visitReference = invalid$1;
|
16381 | this.visitVariable = invalid$1;
|
16382 | this.visitTextAttribute = invalid$1;
|
16383 | this.visitBoundAttribute = invalid$1;
|
16384 | this.visitBoundEvent = invalid$1;
|
16385 | this._bindingScope = parentBindingScope.nestedScope(level);
|
16386 | // Turn the relative context file path into an identifier by replacing non-alphanumeric
|
16387 | // characters with underscores.
|
16388 | this.fileBasedI18nSuffix = relativeContextFilePath.replace(/[^A-Za-z0-9]/g, '_') + '_';
|
16389 | this._valueConverter = new ValueConverter(constantPool, () => this.allocateDataSlot(), (numSlots) => this.allocatePureFunctionSlots(numSlots), (name, localName, slot, value) => {
|
16390 | const pipeType = pipeTypeByName.get(name);
|
16391 | if (pipeType) {
|
16392 | this.pipes.add(pipeType);
|
16393 | }
|
16394 | this._bindingScope.set(this.level, localName, value);
|
16395 | this.creationInstruction(null, Identifiers$1.pipe, [literal(slot), literal(name)]);
|
16396 | });
|
16397 | }
|
16398 | buildTemplateFunction(nodes, variables, ngContentSelectorsOffset = 0, i18n) {
|
16399 | this._ngContentSelectorsOffset = ngContentSelectorsOffset;
|
16400 | if (this._namespace !== Identifiers$1.namespaceHTML) {
|
16401 | this.creationInstruction(null, this._namespace);
|
16402 | }
|
16403 | // Create variable bindings
|
16404 | variables.forEach(v => this.registerContextVariables(v));
|
16405 | // Initiate i18n context in case:
|
16406 | // - this template has parent i18n context
|
16407 | // - or the template has i18n meta associated with it,
|
16408 | // but it's not initiated by the Element (e.g. <ng-template i18n>)
|
16409 | const initI18nContext = this.i18nContext ||
|
16410 | (isI18nRootNode(i18n) && !isSingleI18nIcu(i18n) &&
|
16411 | !(isSingleElementTemplate(nodes) && nodes[0].i18n === i18n));
|
16412 | const selfClosingI18nInstruction = hasTextChildrenOnly(nodes);
|
16413 | if (initI18nContext) {
|
16414 | this.i18nStart(null, i18n, selfClosingI18nInstruction);
|
16415 | }
|
16416 | // This is the initial pass through the nodes of this template. In this pass, we
|
16417 | // queue all creation mode and update mode instructions for generation in the second
|
16418 | // pass. It's necessary to separate the passes to ensure local refs are defined before
|
16419 | // resolving bindings. We also count bindings in this pass as we walk bound expressions.
|
16420 | visitAll(this, nodes);
|
16421 | // Add total binding count to pure function count so pure function instructions are
|
16422 | // generated with the correct slot offset when update instructions are processed.
|
16423 | this._pureFunctionSlots += this._bindingSlots;
|
16424 | // Pipes are walked in the first pass (to enqueue `pipe()` creation instructions and
|
16425 | // `pipeBind` update instructions), so we have to update the slot offsets manually
|
16426 | // to account for bindings.
|
16427 | this._valueConverter.updatePipeSlotOffsets(this._bindingSlots);
|
16428 | // Nested templates must be processed before creation instructions so template()
|
16429 | // instructions can be generated with the correct internal const count.
|
16430 | this._nestedTemplateFns.forEach(buildTemplateFn => buildTemplateFn());
|
16431 | // Output the `projectionDef` instruction when some `<ng-content>` tags are present.
|
16432 | // The `projectionDef` instruction is only emitted for the component template and
|
16433 | // is skipped for nested templates (<ng-template> tags).
|
16434 | if (this.level === 0 && this._ngContentReservedSlots.length) {
|
16435 | const parameters = [];
|
16436 | // By default the `projectionDef` instructions creates one slot for the wildcard
|
16437 | // selector if no parameters are passed. Therefore we only want to allocate a new
|
16438 | // array for the projection slots if the default projection slot is not sufficient.
|
16439 | if (this._ngContentReservedSlots.length > 1 || this._ngContentReservedSlots[0] !== '*') {
|
16440 | const r3ReservedSlots = this._ngContentReservedSlots.map(s => s !== '*' ? parseSelectorToR3Selector(s) : s);
|
16441 | parameters.push(this.constantPool.getConstLiteral(asLiteral(r3ReservedSlots), true));
|
16442 | }
|
16443 | // Since we accumulate ngContent selectors while processing template elements,
|
16444 | // we *prepend* `projectionDef` to creation instructions block, to put it before
|
16445 | // any `projection` instructions
|
16446 | this.creationInstruction(null, Identifiers$1.projectionDef, parameters, /* prepend */ true);
|
16447 | }
|
16448 | if (initI18nContext) {
|
16449 | this.i18nEnd(null, selfClosingI18nInstruction);
|
16450 | }
|
16451 | // Generate all the creation mode instructions (e.g. resolve bindings in listeners)
|
16452 | const creationStatements = this._creationCodeFns.map((fn) => fn());
|
16453 | // Generate all the update mode instructions (e.g. resolve property or text bindings)
|
16454 | const updateStatements = this._updateCodeFns.map((fn) => fn());
|
16455 | // Variable declaration must occur after binding resolution so we can generate context
|
16456 | // instructions that build on each other.
|
16457 | // e.g. const b = nextContext().$implicit(); const b = nextContext();
|
16458 | const creationVariables = this._bindingScope.viewSnapshotStatements();
|
16459 | const updateVariables = this._bindingScope.variableDeclarations().concat(this._tempVariables);
|
16460 | const creationBlock = creationStatements.length > 0 ?
|
16461 | [renderFlagCheckIfStmt(1 /* Create */, creationVariables.concat(creationStatements))] :
|
16462 | [];
|
16463 | const updateBlock = updateStatements.length > 0 ?
|
16464 | [renderFlagCheckIfStmt(2 /* Update */, updateVariables.concat(updateStatements))] :
|
16465 | [];
|
16466 | return fn(
|
16467 | // i.e. (rf: RenderFlags, ctx: any)
|
16468 | [new FnParam(RENDER_FLAGS, NUMBER_TYPE), new FnParam(CONTEXT_NAME, null)], [
|
16469 | // Temporary variable declarations for query refresh (i.e. let _t: any;)
|
16470 | ...this._prefixCode,
|
16471 | // Creating mode (i.e. if (rf & RenderFlags.Create) { ... })
|
16472 | ...creationBlock,
|
16473 | // Binding and refresh mode (i.e. if (rf & RenderFlags.Update) {...})
|
16474 | ...updateBlock,
|
16475 | ], INFERRED_TYPE, null, this.templateName);
|
16476 | }
|
16477 | // LocalResolver
|
16478 | getLocal(name) {
|
16479 | return this._bindingScope.get(name);
|
16480 | }
|
16481 | // LocalResolver
|
16482 | notifyImplicitReceiverUse() {
|
16483 | this._bindingScope.notifyImplicitReceiverUse();
|
16484 | }
|
16485 | i18nTranslate(message, params = {}, ref, transformFn) {
|
16486 | const _ref = ref || this.i18nGenerateMainBlockVar();
|
16487 | // Closure Compiler requires const names to start with `MSG_` but disallows any other const to
|
16488 | // start with `MSG_`. We define a variable starting with `MSG_` just for the `goog.getMsg` call
|
16489 | const closureVar = this.i18nGenerateClosureVar(message.id);
|
16490 | const statements = getTranslationDeclStmts(message, _ref, closureVar, params, transformFn);
|
16491 | this._constants.prepareStatements.push(...statements);
|
16492 | return _ref;
|
16493 | }
|
16494 | registerContextVariables(variable$1) {
|
16495 | const scopedName = this._bindingScope.freshReferenceName();
|
16496 | const retrievalLevel = this.level;
|
16497 | const lhs = variable(variable$1.name + scopedName);
|
16498 | this._bindingScope.set(retrievalLevel, variable$1.name, lhs, 1 /* CONTEXT */, (scope, relativeLevel) => {
|
16499 | let rhs;
|
16500 | if (scope.bindingLevel === retrievalLevel) {
|
16501 | // e.g. ctx
|
16502 | rhs = variable(CONTEXT_NAME);
|
16503 | }
|
16504 | else {
|
16505 | const sharedCtxVar = scope.getSharedContextName(retrievalLevel);
|
16506 | // e.g. ctx_r0 OR x(2);
|
16507 | rhs = sharedCtxVar ? sharedCtxVar : generateNextContextExpr(relativeLevel);
|
16508 | }
|
16509 | // e.g. const $item$ = x(2).$implicit;
|
16510 | return [lhs.set(rhs.prop(variable$1.value || IMPLICIT_REFERENCE)).toConstDecl()];
|
16511 | });
|
16512 | }
|
16513 | i18nAppendBindings(expressions) {
|
16514 | if (expressions.length > 0) {
|
16515 | expressions.forEach(expression => this.i18n.appendBinding(expression));
|
16516 | }
|
16517 | }
|
16518 | i18nBindProps(props) {
|
16519 | const bound = {};
|
16520 | Object.keys(props).forEach(key => {
|
16521 | const prop = props[key];
|
16522 | if (prop instanceof Text) {
|
16523 | bound[key] = literal(prop.value);
|
16524 | }
|
16525 | else {
|
16526 | const value = prop.value.visit(this._valueConverter);
|
16527 | this.allocateBindingSlots(value);
|
16528 | if (value instanceof Interpolation) {
|
16529 | const { strings, expressions } = value;
|
16530 | const { id, bindings } = this.i18n;
|
16531 | const label = assembleI18nBoundString(strings, bindings.size, id);
|
16532 | this.i18nAppendBindings(expressions);
|
16533 | bound[key] = literal(label);
|
16534 | }
|
16535 | }
|
16536 | });
|
16537 | return bound;
|
16538 | }
|
16539 | // Generates top level vars for i18n blocks (i.e. `i18n_N`).
|
16540 | i18nGenerateMainBlockVar() {
|
16541 | return variable(this.constantPool.uniqueName(TRANSLATION_VAR_PREFIX));
|
16542 | }
|
16543 | // Generates vars with Closure-specific names for i18n blocks (i.e. `MSG_XXX`).
|
16544 | i18nGenerateClosureVar(messageId) {
|
16545 | let name;
|
16546 | const suffix = this.fileBasedI18nSuffix.toUpperCase();
|
16547 | if (this.i18nUseExternalIds) {
|
16548 | const prefix = getTranslationConstPrefix(`EXTERNAL_`);
|
16549 | const uniqueSuffix = this.constantPool.uniqueName(suffix);
|
16550 | name = `${prefix}${sanitizeIdentifier(messageId)}$$${uniqueSuffix}`;
|
16551 | }
|
16552 | else {
|
16553 | const prefix = getTranslationConstPrefix(suffix);
|
16554 | name = this.constantPool.uniqueName(prefix);
|
16555 | }
|
16556 | return variable(name);
|
16557 | }
|
16558 | i18nUpdateRef(context) {
|
16559 | const { icus, meta, isRoot, isResolved, isEmitted } = context;
|
16560 | if (isRoot && isResolved && !isEmitted && !isSingleI18nIcu(meta)) {
|
16561 | context.isEmitted = true;
|
16562 | const placeholders = context.getSerializedPlaceholders();
|
16563 | let icuMapping = {};
|
16564 | let params = placeholders.size ? placeholdersToParams(placeholders) : {};
|
16565 | if (icus.size) {
|
16566 | icus.forEach((refs, key) => {
|
16567 | if (refs.length === 1) {
|
16568 | // if we have one ICU defined for a given
|
16569 | // placeholder - just output its reference
|
16570 | params[key] = refs[0];
|
16571 | }
|
16572 | else {
|
16573 | // ... otherwise we need to activate post-processing
|
16574 | // to replace ICU placeholders with proper values
|
16575 | const placeholder = wrapI18nPlaceholder(`${I18N_ICU_MAPPING_PREFIX}${key}`);
|
16576 | params[key] = literal(placeholder);
|
16577 | icuMapping[key] = literalArr(refs);
|
16578 | }
|
16579 | });
|
16580 | }
|
16581 | // translation requires post processing in 2 cases:
|
16582 | // - if we have placeholders with multiple values (ex. `START_DIV`: [�#1�, �#2�, ...])
|
16583 | // - if we have multiple ICUs that refer to the same placeholder name
|
16584 | const needsPostprocessing = Array.from(placeholders.values()).some((value) => value.length > 1) ||
|
16585 | Object.keys(icuMapping).length;
|
16586 | let transformFn;
|
16587 | if (needsPostprocessing) {
|
16588 | transformFn = (raw) => {
|
16589 | const args = [raw];
|
16590 | if (Object.keys(icuMapping).length) {
|
16591 | args.push(mapLiteral(icuMapping, true));
|
16592 | }
|
16593 | return instruction(null, Identifiers$1.i18nPostprocess, args);
|
16594 | };
|
16595 | }
|
16596 | this.i18nTranslate(meta, params, context.ref, transformFn);
|
16597 | }
|
16598 | }
|
16599 | i18nStart(span = null, meta, selfClosing) {
|
16600 | const index = this.allocateDataSlot();
|
16601 | this.i18n = this.i18nContext ?
|
16602 | this.i18nContext.forkChildContext(index, this.templateIndex, meta) :
|
16603 | new I18nContext(index, this.i18nGenerateMainBlockVar(), 0, this.templateIndex, meta);
|
16604 | // generate i18nStart instruction
|
16605 | const { id, ref } = this.i18n;
|
16606 | const params = [literal(index), this.addToConsts(ref)];
|
16607 | if (id > 0) {
|
16608 | // do not push 3rd argument (sub-block id)
|
16609 | // into i18nStart call for top level i18n context
|
16610 | params.push(literal(id));
|
16611 | }
|
16612 | this.creationInstruction(span, selfClosing ? Identifiers$1.i18n : Identifiers$1.i18nStart, params);
|
16613 | }
|
16614 | i18nEnd(span = null, selfClosing) {
|
16615 | if (!this.i18n) {
|
16616 | throw new Error('i18nEnd is executed with no i18n context present');
|
16617 | }
|
16618 | if (this.i18nContext) {
|
16619 | this.i18nContext.reconcileChildContext(this.i18n);
|
16620 | this.i18nUpdateRef(this.i18nContext);
|
16621 | }
|
16622 | else {
|
16623 | this.i18nUpdateRef(this.i18n);
|
16624 | }
|
16625 | // setup accumulated bindings
|
16626 | const { index, bindings } = this.i18n;
|
16627 | if (bindings.size) {
|
16628 | const chainBindings = [];
|
16629 | bindings.forEach(binding => {
|
16630 | chainBindings.push({ sourceSpan: span, value: () => this.convertPropertyBinding(binding) });
|
16631 | });
|
16632 | // for i18n block, advance to the most recent element index (by taking the current number of
|
16633 | // elements and subtracting one) before invoking `i18nExp` instructions, to make sure the
|
16634 | // necessary lifecycle hooks of components/directives are properly flushed.
|
16635 | this.updateInstructionChainWithAdvance(this.getConstCount() - 1, Identifiers$1.i18nExp, chainBindings);
|
16636 | this.updateInstruction(span, Identifiers$1.i18nApply, [literal(index)]);
|
16637 | }
|
16638 | if (!selfClosing) {
|
16639 | this.creationInstruction(span, Identifiers$1.i18nEnd);
|
16640 | }
|
16641 | this.i18n = null; // reset local i18n context
|
16642 | }
|
16643 | i18nAttributesInstruction(nodeIndex, attrs, sourceSpan) {
|
16644 | let hasBindings = false;
|
16645 | const i18nAttrArgs = [];
|
16646 | const bindings = [];
|
16647 | attrs.forEach(attr => {
|
16648 | const message = attr.i18n;
|
16649 | const converted = attr.value.visit(this._valueConverter);
|
16650 | this.allocateBindingSlots(converted);
|
16651 | if (converted instanceof Interpolation) {
|
16652 | const placeholders = assembleBoundTextPlaceholders(message);
|
16653 | const params = placeholdersToParams(placeholders);
|
16654 | i18nAttrArgs.push(literal(attr.name), this.i18nTranslate(message, params));
|
16655 | converted.expressions.forEach(expression => {
|
16656 | hasBindings = true;
|
16657 | bindings.push({
|
16658 | sourceSpan,
|
16659 | value: () => this.convertPropertyBinding(expression),
|
16660 | });
|
16661 | });
|
16662 | }
|
16663 | });
|
16664 | if (bindings.length > 0) {
|
16665 | this.updateInstructionChainWithAdvance(nodeIndex, Identifiers$1.i18nExp, bindings);
|
16666 | }
|
16667 | if (i18nAttrArgs.length > 0) {
|
16668 | const index = literal(this.allocateDataSlot());
|
16669 | const constIndex = this.addToConsts(literalArr(i18nAttrArgs));
|
16670 | this.creationInstruction(sourceSpan, Identifiers$1.i18nAttributes, [index, constIndex]);
|
16671 | if (hasBindings) {
|
16672 | this.updateInstruction(sourceSpan, Identifiers$1.i18nApply, [index]);
|
16673 | }
|
16674 | }
|
16675 | }
|
16676 | getNamespaceInstruction(namespaceKey) {
|
16677 | switch (namespaceKey) {
|
16678 | case 'math':
|
16679 | return Identifiers$1.namespaceMathML;
|
16680 | case 'svg':
|
16681 | return Identifiers$1.namespaceSVG;
|
16682 | default:
|
16683 | return Identifiers$1.namespaceHTML;
|
16684 | }
|
16685 | }
|
16686 | addNamespaceInstruction(nsInstruction, element) {
|
16687 | this._namespace = nsInstruction;
|
16688 | this.creationInstruction(element.startSourceSpan, nsInstruction);
|
16689 | }
|
16690 | /**
|
16691 | * Adds an update instruction for an interpolated property or attribute, such as
|
16692 | * `prop="{{value}}"` or `attr.title="{{value}}"`
|
16693 | */
|
16694 | interpolatedUpdateInstruction(instruction, elementIndex, attrName, input, value, params) {
|
16695 | this.updateInstructionWithAdvance(elementIndex, input.sourceSpan, instruction, () => [literal(attrName), ...this.getUpdateInstructionArguments(value), ...params]);
|
16696 | }
|
16697 | visitContent(ngContent) {
|
16698 | const slot = this.allocateDataSlot();
|
16699 | const projectionSlotIdx = this._ngContentSelectorsOffset + this._ngContentReservedSlots.length;
|
16700 | const parameters = [literal(slot)];
|
16701 | this._ngContentReservedSlots.push(ngContent.selector);
|
16702 | const nonContentSelectAttributes = ngContent.attributes.filter(attr => attr.name.toLowerCase() !== NG_CONTENT_SELECT_ATTR$1);
|
16703 | const attributes = this.getAttributeExpressions(ngContent.name, nonContentSelectAttributes, [], []);
|
16704 | if (attributes.length > 0) {
|
16705 | parameters.push(literal(projectionSlotIdx), literalArr(attributes));
|
16706 | }
|
16707 | else if (projectionSlotIdx !== 0) {
|
16708 | parameters.push(literal(projectionSlotIdx));
|
16709 | }
|
16710 | this.creationInstruction(ngContent.sourceSpan, Identifiers$1.projection, parameters);
|
16711 | if (this.i18n) {
|
16712 | this.i18n.appendProjection(ngContent.i18n, slot);
|
16713 | }
|
16714 | }
|
16715 | visitElement(element) {
|
16716 | var _a, _b;
|
16717 | const elementIndex = this.allocateDataSlot();
|
16718 | const stylingBuilder = new StylingBuilder(null);
|
16719 | let isNonBindableMode = false;
|
16720 | const isI18nRootElement = isI18nRootNode(element.i18n) && !isSingleI18nIcu(element.i18n);
|
16721 | const outputAttrs = [];
|
16722 | const [namespaceKey, elementName] = splitNsName(element.name);
|
16723 | const isNgContainer$1 = isNgContainer(element.name);
|
16724 | // Handle styling, i18n, ngNonBindable attributes
|
16725 | for (const attr of element.attributes) {
|
16726 | const { name, value } = attr;
|
16727 | if (name === NON_BINDABLE_ATTR) {
|
16728 | isNonBindableMode = true;
|
16729 | }
|
16730 | else if (name === 'style') {
|
16731 | stylingBuilder.registerStyleAttr(value);
|
16732 | }
|
16733 | else if (name === 'class') {
|
16734 | stylingBuilder.registerClassAttr(value);
|
16735 | }
|
16736 | else {
|
16737 | outputAttrs.push(attr);
|
16738 | }
|
16739 | }
|
16740 | // Match directives on non i18n attributes
|
16741 | this.matchDirectives(element.name, element);
|
16742 | // Regular element or ng-container creation mode
|
16743 | const parameters = [literal(elementIndex)];
|
16744 | if (!isNgContainer$1) {
|
16745 | parameters.push(literal(elementName));
|
16746 | }
|
16747 | // Add the attributes
|
16748 | const allOtherInputs = [];
|
16749 | const boundI18nAttrs = [];
|
16750 | element.inputs.forEach(input => {
|
16751 | const stylingInputWasSet = stylingBuilder.registerBoundInput(input);
|
16752 | if (!stylingInputWasSet) {
|
16753 | if (input.type === 0 /* Property */ && input.i18n) {
|
16754 | boundI18nAttrs.push(input);
|
16755 | }
|
16756 | else {
|
16757 | allOtherInputs.push(input);
|
16758 | }
|
16759 | }
|
16760 | });
|
16761 | // add attributes for directive and projection matching purposes
|
16762 | const attributes = this.getAttributeExpressions(element.name, outputAttrs, allOtherInputs, element.outputs, stylingBuilder, [], boundI18nAttrs);
|
16763 | parameters.push(this.addAttrsToConsts(attributes));
|
16764 | // local refs (ex.: <div #foo #bar="baz">)
|
16765 | const refs = this.prepareRefsArray(element.references);
|
16766 | parameters.push(this.addToConsts(refs));
|
16767 | const wasInNamespace = this._namespace;
|
16768 | const currentNamespace = this.getNamespaceInstruction(namespaceKey);
|
16769 | // If the namespace is changing now, include an instruction to change it
|
16770 | // during element creation.
|
16771 | if (currentNamespace !== wasInNamespace) {
|
16772 | this.addNamespaceInstruction(currentNamespace, element);
|
16773 | }
|
16774 | if (this.i18n) {
|
16775 | this.i18n.appendElement(element.i18n, elementIndex);
|
16776 | }
|
16777 | // Note that we do not append text node instructions and ICUs inside i18n section,
|
16778 | // so we exclude them while calculating whether current element has children
|
16779 | const hasChildren = (!isI18nRootElement && this.i18n) ? !hasTextChildrenOnly(element.children) :
|
16780 | element.children.length > 0;
|
16781 | const createSelfClosingInstruction = !stylingBuilder.hasBindingsWithPipes &&
|
16782 | element.outputs.length === 0 && boundI18nAttrs.length === 0 && !hasChildren;
|
16783 | const createSelfClosingI18nInstruction = !createSelfClosingInstruction && hasTextChildrenOnly(element.children);
|
16784 | if (createSelfClosingInstruction) {
|
16785 | this.creationInstruction(element.sourceSpan, isNgContainer$1 ? Identifiers$1.elementContainer : Identifiers$1.element, trimTrailingNulls(parameters));
|
16786 | }
|
16787 | else {
|
16788 | this.creationInstruction(element.startSourceSpan, isNgContainer$1 ? Identifiers$1.elementContainerStart : Identifiers$1.elementStart, trimTrailingNulls(parameters));
|
16789 | if (isNonBindableMode) {
|
16790 | this.creationInstruction(element.startSourceSpan, Identifiers$1.disableBindings);
|
16791 | }
|
16792 | if (boundI18nAttrs.length > 0) {
|
16793 | this.i18nAttributesInstruction(elementIndex, boundI18nAttrs, (_a = element.startSourceSpan) !== null && _a !== void 0 ? _a : element.sourceSpan);
|
16794 | }
|
16795 | // Generate Listeners (outputs)
|
16796 | if (element.outputs.length > 0) {
|
16797 | const listeners = element.outputs.map((outputAst) => ({
|
16798 | sourceSpan: outputAst.sourceSpan,
|
16799 | params: this.prepareListenerParameter(element.name, outputAst, elementIndex)
|
16800 | }));
|
16801 | this.creationInstructionChain(Identifiers$1.listener, listeners);
|
16802 | }
|
16803 | // Note: it's important to keep i18n/i18nStart instructions after i18nAttributes and
|
16804 | // listeners, to make sure i18nAttributes instruction targets current element at runtime.
|
16805 | if (isI18nRootElement) {
|
16806 | this.i18nStart(element.startSourceSpan, element.i18n, createSelfClosingI18nInstruction);
|
16807 | }
|
16808 | }
|
16809 | // the code here will collect all update-level styling instructions and add them to the
|
16810 | // update block of the template function AOT code. Instructions like `styleProp`,
|
16811 | // `styleMap`, `classMap`, `classProp`
|
16812 | // are all generated and assigned in the code below.
|
16813 | const stylingInstructions = stylingBuilder.buildUpdateLevelInstructions(this._valueConverter);
|
16814 | const limit = stylingInstructions.length - 1;
|
16815 | for (let i = 0; i <= limit; i++) {
|
16816 | const instruction = stylingInstructions[i];
|
16817 | this._bindingSlots += this.processStylingUpdateInstruction(elementIndex, instruction);
|
16818 | }
|
16819 | // the reason why `undefined` is used is because the renderer understands this as a
|
16820 | // special value to symbolize that there is no RHS to this binding
|
16821 | // TODO (matsko): revisit this once FW-959 is approached
|
16822 | const emptyValueBindInstruction = literal(undefined);
|
16823 | const propertyBindings = [];
|
16824 | const attributeBindings = [];
|
16825 | // Generate element input bindings
|
16826 | allOtherInputs.forEach(input => {
|
16827 | const inputType = input.type;
|
16828 | if (inputType === 4 /* Animation */) {
|
16829 | const value = input.value.visit(this._valueConverter);
|
16830 | // animation bindings can be presented in the following formats:
|
16831 | // 1. [@binding]="fooExp"
|
16832 | // 2. [@binding]="{value:fooExp, params:{...}}"
|
16833 | // 3. [@binding]
|
16834 | // 4. @binding
|
16835 | // All formats will be valid for when a synthetic binding is created.
|
16836 | // The reasoning for this is because the renderer should get each
|
16837 | // synthetic binding value in the order of the array that they are
|
16838 | // defined in...
|
16839 | const hasValue = value instanceof LiteralPrimitive ? !!value.value : true;
|
16840 | this.allocateBindingSlots(value);
|
16841 | propertyBindings.push({
|
16842 | name: prepareSyntheticPropertyName(input.name),
|
16843 | sourceSpan: input.sourceSpan,
|
16844 | value: () => hasValue ? this.convertPropertyBinding(value) : emptyValueBindInstruction
|
16845 | });
|
16846 | }
|
16847 | else {
|
16848 | // we must skip attributes with associated i18n context, since these attributes are handled
|
16849 | // separately and corresponding `i18nExp` and `i18nApply` instructions will be generated
|
16850 | if (input.i18n)
|
16851 | return;
|
16852 | const value = input.value.visit(this._valueConverter);
|
16853 | if (value !== undefined) {
|
16854 | const params = [];
|
16855 | const [attrNamespace, attrName] = splitNsName(input.name);
|
16856 | const isAttributeBinding = inputType === 1 /* Attribute */;
|
16857 | const sanitizationRef = resolveSanitizationFn(input.securityContext, isAttributeBinding);
|
16858 | if (sanitizationRef)
|
16859 | params.push(sanitizationRef);
|
16860 | if (attrNamespace) {
|
16861 | const namespaceLiteral = literal(attrNamespace);
|
16862 | if (sanitizationRef) {
|
16863 | params.push(namespaceLiteral);
|
16864 | }
|
16865 | else {
|
16866 | // If there wasn't a sanitization ref, we need to add
|
16867 | // an extra param so that we can pass in the namespace.
|
16868 | params.push(literal(null), namespaceLiteral);
|
16869 | }
|
16870 | }
|
16871 | this.allocateBindingSlots(value);
|
16872 | if (inputType === 0 /* Property */) {
|
16873 | if (value instanceof Interpolation) {
|
16874 | // prop="{{value}}" and friends
|
16875 | this.interpolatedUpdateInstruction(getPropertyInterpolationExpression(value), elementIndex, attrName, input, value, params);
|
16876 | }
|
16877 | else {
|
16878 | // [prop]="value"
|
16879 | // Collect all the properties so that we can chain into a single function at the end.
|
16880 | propertyBindings.push({
|
16881 | name: attrName,
|
16882 | sourceSpan: input.sourceSpan,
|
16883 | value: () => this.convertPropertyBinding(value),
|
16884 | params
|
16885 | });
|
16886 | }
|
16887 | }
|
16888 | else if (inputType === 1 /* Attribute */) {
|
16889 | if (value instanceof Interpolation && getInterpolationArgsLength(value) > 1) {
|
16890 | // attr.name="text{{value}}" and friends
|
16891 | this.interpolatedUpdateInstruction(getAttributeInterpolationExpression(value), elementIndex, attrName, input, value, params);
|
16892 | }
|
16893 | else {
|
16894 | const boundValue = value instanceof Interpolation ? value.expressions[0] : value;
|
16895 | // [attr.name]="value" or attr.name="{{value}}"
|
16896 | // Collect the attribute bindings so that they can be chained at the end.
|
16897 | attributeBindings.push({
|
16898 | name: attrName,
|
16899 | sourceSpan: input.sourceSpan,
|
16900 | value: () => this.convertPropertyBinding(boundValue),
|
16901 | params
|
16902 | });
|
16903 | }
|
16904 | }
|
16905 | else {
|
16906 | // class prop
|
16907 | this.updateInstructionWithAdvance(elementIndex, input.sourceSpan, Identifiers$1.classProp, () => {
|
16908 | return [
|
16909 | literal(elementIndex), literal(attrName), this.convertPropertyBinding(value),
|
16910 | ...params
|
16911 | ];
|
16912 | });
|
16913 | }
|
16914 | }
|
16915 | }
|
16916 | });
|
16917 | if (propertyBindings.length > 0) {
|
16918 | this.updateInstructionChainWithAdvance(elementIndex, Identifiers$1.property, propertyBindings);
|
16919 | }
|
16920 | if (attributeBindings.length > 0) {
|
16921 | this.updateInstructionChainWithAdvance(elementIndex, Identifiers$1.attribute, attributeBindings);
|
16922 | }
|
16923 | // Traverse element child nodes
|
16924 | visitAll(this, element.children);
|
16925 | if (!isI18nRootElement && this.i18n) {
|
16926 | this.i18n.appendElement(element.i18n, elementIndex, true);
|
16927 | }
|
16928 | if (!createSelfClosingInstruction) {
|
16929 | // Finish element construction mode.
|
16930 | const span = (_b = element.endSourceSpan) !== null && _b !== void 0 ? _b : element.sourceSpan;
|
16931 | if (isI18nRootElement) {
|
16932 | this.i18nEnd(span, createSelfClosingI18nInstruction);
|
16933 | }
|
16934 | if (isNonBindableMode) {
|
16935 | this.creationInstruction(span, Identifiers$1.enableBindings);
|
16936 | }
|
16937 | this.creationInstruction(span, isNgContainer$1 ? Identifiers$1.elementContainerEnd : Identifiers$1.elementEnd);
|
16938 | }
|
16939 | }
|
16940 | visitTemplate(template) {
|
16941 | var _a;
|
16942 | const NG_TEMPLATE_TAG_NAME = 'ng-template';
|
16943 | const templateIndex = this.allocateDataSlot();
|
16944 | if (this.i18n) {
|
16945 | this.i18n.appendTemplate(template.i18n, templateIndex);
|
16946 | }
|
16947 | const tagName = sanitizeIdentifier(template.tagName || '');
|
16948 | const contextName = `${this.contextName}${tagName ? '_' + tagName : ''}_${templateIndex}`;
|
16949 | const templateName = `${contextName}_Template`;
|
16950 | const parameters = [
|
16951 | literal(templateIndex),
|
16952 | variable(templateName),
|
16953 | // We don't care about the tag's namespace here, because we infer
|
16954 | // it based on the parent nodes inside the template instruction.
|
16955 | literal(template.tagName ? splitNsName(template.tagName)[1] : template.tagName),
|
16956 | ];
|
16957 | // find directives matching on a given <ng-template> node
|
16958 | this.matchDirectives(NG_TEMPLATE_TAG_NAME, template);
|
16959 | // prepare attributes parameter (including attributes used for directive matching)
|
16960 | const attrsExprs = this.getAttributeExpressions(NG_TEMPLATE_TAG_NAME, template.attributes, template.inputs, template.outputs, undefined /* styles */, template.templateAttrs);
|
16961 | parameters.push(this.addAttrsToConsts(attrsExprs));
|
16962 | // local refs (ex.: <ng-template #foo>)
|
16963 | if (template.references && template.references.length) {
|
16964 | const refs = this.prepareRefsArray(template.references);
|
16965 | parameters.push(this.addToConsts(refs));
|
16966 | parameters.push(importExpr(Identifiers$1.templateRefExtractor));
|
16967 | }
|
16968 | // Create the template function
|
16969 | 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);
|
16970 | // Nested templates must not be visited until after their parent templates have completed
|
16971 | // processing, so they are queued here until after the initial pass. Otherwise, we wouldn't
|
16972 | // be able to support bindings in nested templates to local refs that occur after the
|
16973 | // template definition. e.g. <div *ngIf="showing">{{ foo }}</div> <div #foo></div>
|
16974 | this._nestedTemplateFns.push(() => {
|
16975 | const templateFunctionExpr = templateVisitor.buildTemplateFunction(template.children, template.variables, this._ngContentReservedSlots.length + this._ngContentSelectorsOffset, template.i18n);
|
16976 | this.constantPool.statements.push(templateFunctionExpr.toDeclStmt(templateName));
|
16977 | if (templateVisitor._ngContentReservedSlots.length) {
|
16978 | this._ngContentReservedSlots.push(...templateVisitor._ngContentReservedSlots);
|
16979 | }
|
16980 | });
|
16981 | // e.g. template(1, MyComp_Template_1)
|
16982 | this.creationInstruction(template.sourceSpan, Identifiers$1.templateCreate, () => {
|
16983 | parameters.splice(2, 0, literal(templateVisitor.getConstCount()), literal(templateVisitor.getVarCount()));
|
16984 | return trimTrailingNulls(parameters);
|
16985 | });
|
16986 | // handle property bindings e.g. ɵɵproperty('ngForOf', ctx.items), et al;
|
16987 | this.templatePropertyBindings(templateIndex, template.templateAttrs);
|
16988 | // Only add normal input/output binding instructions on explicit <ng-template> elements.
|
16989 | if (template.tagName === NG_TEMPLATE_TAG_NAME) {
|
16990 | const [i18nInputs, inputs] = partitionArray(template.inputs, hasI18nMeta);
|
16991 | // Add i18n attributes that may act as inputs to directives. If such attributes are present,
|
16992 | // generate `i18nAttributes` instruction. Note: we generate it only for explicit <ng-template>
|
16993 | // elements, in case of inline templates, corresponding instructions will be generated in the
|
16994 | // nested template function.
|
16995 | if (i18nInputs.length > 0) {
|
16996 | this.i18nAttributesInstruction(templateIndex, i18nInputs, (_a = template.startSourceSpan) !== null && _a !== void 0 ? _a : template.sourceSpan);
|
16997 | }
|
16998 | // Add the input bindings
|
16999 | if (inputs.length > 0) {
|
17000 | this.templatePropertyBindings(templateIndex, inputs);
|
17001 | }
|
17002 | // Generate listeners for directive output
|
17003 | if (template.outputs.length > 0) {
|
17004 | const listeners = template.outputs.map((outputAst) => ({
|
17005 | sourceSpan: outputAst.sourceSpan,
|
17006 | params: this.prepareListenerParameter('ng_template', outputAst, templateIndex)
|
17007 | }));
|
17008 | this.creationInstructionChain(Identifiers$1.listener, listeners);
|
17009 | }
|
17010 | }
|
17011 | }
|
17012 | visitBoundText(text) {
|
17013 | if (this.i18n) {
|
17014 | const value = text.value.visit(this._valueConverter);
|
17015 | this.allocateBindingSlots(value);
|
17016 | if (value instanceof Interpolation) {
|
17017 | this.i18n.appendBoundText(text.i18n);
|
17018 | this.i18nAppendBindings(value.expressions);
|
17019 | }
|
17020 | return;
|
17021 | }
|
17022 | const nodeIndex = this.allocateDataSlot();
|
17023 | this.creationInstruction(text.sourceSpan, Identifiers$1.text, [literal(nodeIndex)]);
|
17024 | const value = text.value.visit(this._valueConverter);
|
17025 | this.allocateBindingSlots(value);
|
17026 | if (value instanceof Interpolation) {
|
17027 | this.updateInstructionWithAdvance(nodeIndex, text.sourceSpan, getTextInterpolationExpression(value), () => this.getUpdateInstructionArguments(value));
|
17028 | }
|
17029 | else {
|
17030 | error('Text nodes should be interpolated and never bound directly.');
|
17031 | }
|
17032 | }
|
17033 | visitText(text) {
|
17034 | // when a text element is located within a translatable
|
17035 | // block, we exclude this text element from instructions set,
|
17036 | // since it will be captured in i18n content and processed at runtime
|
17037 | if (!this.i18n) {
|
17038 | this.creationInstruction(text.sourceSpan, Identifiers$1.text, [literal(this.allocateDataSlot()), literal(text.value)]);
|
17039 | }
|
17040 | }
|
17041 | visitIcu(icu) {
|
17042 | let initWasInvoked = false;
|
17043 | // if an ICU was created outside of i18n block, we still treat
|
17044 | // it as a translatable entity and invoke i18nStart and i18nEnd
|
17045 | // to generate i18n context and the necessary instructions
|
17046 | if (!this.i18n) {
|
17047 | initWasInvoked = true;
|
17048 | this.i18nStart(null, icu.i18n, true);
|
17049 | }
|
17050 | const i18n = this.i18n;
|
17051 | const vars = this.i18nBindProps(icu.vars);
|
17052 | const placeholders = this.i18nBindProps(icu.placeholders);
|
17053 | // output ICU directly and keep ICU reference in context
|
17054 | const message = icu.i18n;
|
17055 | // we always need post-processing function for ICUs, to make sure that:
|
17056 | // - all placeholders in a form of {PLACEHOLDER} are replaced with actual values (note:
|
17057 | // `goog.getMsg` does not process ICUs and uses the `{PLACEHOLDER}` format for placeholders
|
17058 | // inside ICUs)
|
17059 | // - all ICU vars (such as `VAR_SELECT` or `VAR_PLURAL`) are replaced with correct values
|
17060 | const transformFn = (raw) => {
|
17061 | const params = Object.assign(Object.assign({}, vars), placeholders);
|
17062 | const formatted = i18nFormatPlaceholderNames(params, /* useCamelCase */ false);
|
17063 | return instruction(null, Identifiers$1.i18nPostprocess, [raw, mapLiteral(formatted, true)]);
|
17064 | };
|
17065 | // in case the whole i18n message is a single ICU - we do not need to
|
17066 | // create a separate top-level translation, we can use the root ref instead
|
17067 | // and make this ICU a top-level translation
|
17068 | // note: ICU placeholders are replaced with actual values in `i18nPostprocess` function
|
17069 | // separately, so we do not pass placeholders into `i18nTranslate` function.
|
17070 | if (isSingleI18nIcu(i18n.meta)) {
|
17071 | this.i18nTranslate(message, /* placeholders */ {}, i18n.ref, transformFn);
|
17072 | }
|
17073 | else {
|
17074 | // output ICU directly and keep ICU reference in context
|
17075 | const ref = this.i18nTranslate(message, /* placeholders */ {}, /* ref */ undefined, transformFn);
|
17076 | i18n.appendIcu(icuFromI18nMessage(message).name, ref);
|
17077 | }
|
17078 | if (initWasInvoked) {
|
17079 | this.i18nEnd(null, true);
|
17080 | }
|
17081 | return null;
|
17082 | }
|
17083 | allocateDataSlot() {
|
17084 | return this._dataIndex++;
|
17085 | }
|
17086 | getConstCount() {
|
17087 | return this._dataIndex;
|
17088 | }
|
17089 | getVarCount() {
|
17090 | return this._pureFunctionSlots;
|
17091 | }
|
17092 | getConsts() {
|
17093 | return this._constants;
|
17094 | }
|
17095 | getNgContentSelectors() {
|
17096 | return this._ngContentReservedSlots.length ?
|
17097 | this.constantPool.getConstLiteral(asLiteral(this._ngContentReservedSlots), true) :
|
17098 | null;
|
17099 | }
|
17100 | bindingContext() {
|
17101 | return `${this._bindingContext++}`;
|
17102 | }
|
17103 | templatePropertyBindings(templateIndex, attrs) {
|
17104 | const propertyBindings = [];
|
17105 | attrs.forEach(input => {
|
17106 | if (input instanceof BoundAttribute) {
|
17107 | const value = input.value.visit(this._valueConverter);
|
17108 | if (value !== undefined) {
|
17109 | this.allocateBindingSlots(value);
|
17110 | if (value instanceof Interpolation) {
|
17111 | // Params typically contain attribute namespace and value sanitizer, which is applicable
|
17112 | // for regular HTML elements, but not applicable for <ng-template> (since props act as
|
17113 | // inputs to directives), so keep params array empty.
|
17114 | const params = [];
|
17115 | // prop="{{value}}" case
|
17116 | this.interpolatedUpdateInstruction(getPropertyInterpolationExpression(value), templateIndex, input.name, input, value, params);
|
17117 | }
|
17118 | else {
|
17119 | // [prop]="value" case
|
17120 | propertyBindings.push({
|
17121 | name: input.name,
|
17122 | sourceSpan: input.sourceSpan,
|
17123 | value: () => this.convertPropertyBinding(value)
|
17124 | });
|
17125 | }
|
17126 | }
|
17127 | }
|
17128 | });
|
17129 | if (propertyBindings.length > 0) {
|
17130 | this.updateInstructionChainWithAdvance(templateIndex, Identifiers$1.property, propertyBindings);
|
17131 | }
|
17132 | }
|
17133 | // Bindings must only be resolved after all local refs have been visited, so all
|
17134 | // instructions are queued in callbacks that execute once the initial pass has completed.
|
17135 | // Otherwise, we wouldn't be able to support local refs that are defined after their
|
17136 | // bindings. e.g. {{ foo }} <div #foo></div>
|
17137 | instructionFn(fns, span, reference, paramsOrFn, prepend = false) {
|
17138 | fns[prepend ? 'unshift' : 'push'](() => {
|
17139 | const params = Array.isArray(paramsOrFn) ? paramsOrFn : paramsOrFn();
|
17140 | return instruction(span, reference, params).toStmt();
|
17141 | });
|
17142 | }
|
17143 | processStylingUpdateInstruction(elementIndex, instruction) {
|
17144 | let allocateBindingSlots = 0;
|
17145 | if (instruction) {
|
17146 | const calls = [];
|
17147 | instruction.calls.forEach(call => {
|
17148 | allocateBindingSlots += call.allocateBindingSlots;
|
17149 | calls.push({
|
17150 | sourceSpan: call.sourceSpan,
|
17151 | value: () => {
|
17152 | return call.params(value => (call.supportsInterpolation && value instanceof Interpolation) ?
|
17153 | this.getUpdateInstructionArguments(value) :
|
17154 | this.convertPropertyBinding(value));
|
17155 | }
|
17156 | });
|
17157 | });
|
17158 | this.updateInstructionChainWithAdvance(elementIndex, instruction.reference, calls);
|
17159 | }
|
17160 | return allocateBindingSlots;
|
17161 | }
|
17162 | creationInstruction(span, reference, paramsOrFn, prepend) {
|
17163 | this.instructionFn(this._creationCodeFns, span, reference, paramsOrFn || [], prepend);
|
17164 | }
|
17165 | creationInstructionChain(reference, calls) {
|
17166 | const span = calls.length ? calls[0].sourceSpan : null;
|
17167 | this._creationCodeFns.push(() => {
|
17168 | return chainedInstruction(reference, calls.map(call => call.params()), span).toStmt();
|
17169 | });
|
17170 | }
|
17171 | updateInstructionWithAdvance(nodeIndex, span, reference, paramsOrFn) {
|
17172 | this.addAdvanceInstructionIfNecessary(nodeIndex, span);
|
17173 | this.updateInstruction(span, reference, paramsOrFn);
|
17174 | }
|
17175 | updateInstruction(span, reference, paramsOrFn) {
|
17176 | this.instructionFn(this._updateCodeFns, span, reference, paramsOrFn || []);
|
17177 | }
|
17178 | updateInstructionChain(reference, bindings) {
|
17179 | const span = bindings.length ? bindings[0].sourceSpan : null;
|
17180 | this._updateCodeFns.push(() => {
|
17181 | const calls = bindings.map(property => {
|
17182 | const value = property.value();
|
17183 | const fnParams = Array.isArray(value) ? value : [value];
|
17184 | if (property.params) {
|
17185 | fnParams.push(...property.params);
|
17186 | }
|
17187 | if (property.name) {
|
17188 | // We want the property name to always be the first function parameter.
|
17189 | fnParams.unshift(literal(property.name));
|
17190 | }
|
17191 | return fnParams;
|
17192 | });
|
17193 | return chainedInstruction(reference, calls, span).toStmt();
|
17194 | });
|
17195 | }
|
17196 | updateInstructionChainWithAdvance(nodeIndex, reference, bindings) {
|
17197 | this.addAdvanceInstructionIfNecessary(nodeIndex, bindings.length ? bindings[0].sourceSpan : null);
|
17198 | this.updateInstructionChain(reference, bindings);
|
17199 | }
|
17200 | addAdvanceInstructionIfNecessary(nodeIndex, span) {
|
17201 | if (nodeIndex !== this._currentIndex) {
|
17202 | const delta = nodeIndex - this._currentIndex;
|
17203 | if (delta < 1) {
|
17204 | throw new Error('advance instruction can only go forwards');
|
17205 | }
|
17206 | this.instructionFn(this._updateCodeFns, span, Identifiers$1.advance, [literal(delta)]);
|
17207 | this._currentIndex = nodeIndex;
|
17208 | }
|
17209 | }
|
17210 | allocatePureFunctionSlots(numSlots) {
|
17211 | const originalSlots = this._pureFunctionSlots;
|
17212 | this._pureFunctionSlots += numSlots;
|
17213 | return originalSlots;
|
17214 | }
|
17215 | allocateBindingSlots(value) {
|
17216 | this._bindingSlots += value instanceof Interpolation ? value.expressions.length : 1;
|
17217 | }
|
17218 | /**
|
17219 | * Gets an expression that refers to the implicit receiver. The implicit
|
17220 | * receiver is always the root level context.
|
17221 | */
|
17222 | getImplicitReceiverExpr() {
|
17223 | if (this._implicitReceiverExpr) {
|
17224 | return this._implicitReceiverExpr;
|
17225 | }
|
17226 | return this._implicitReceiverExpr = this.level === 0 ?
|
17227 | variable(CONTEXT_NAME) :
|
17228 | this._bindingScope.getOrCreateSharedContextVar(0);
|
17229 | }
|
17230 | convertPropertyBinding(value) {
|
17231 | const convertedPropertyBinding = convertPropertyBinding(this, this.getImplicitReceiverExpr(), value, this.bindingContext(), BindingForm.Expression, () => error('Unexpected interpolation'));
|
17232 | const valExpr = convertedPropertyBinding.currValExpr;
|
17233 | this._tempVariables.push(...convertedPropertyBinding.stmts);
|
17234 | return valExpr;
|
17235 | }
|
17236 | /**
|
17237 | * Gets a list of argument expressions to pass to an update instruction expression. Also updates
|
17238 | * the temp variables state with temp variables that were identified as needing to be created
|
17239 | * while visiting the arguments.
|
17240 | * @param value The original expression we will be resolving an arguments list from.
|
17241 | */
|
17242 | getUpdateInstructionArguments(value) {
|
17243 | const { args, stmts } = convertUpdateArguments(this, this.getImplicitReceiverExpr(), value, this.bindingContext());
|
17244 | this._tempVariables.push(...stmts);
|
17245 | return args;
|
17246 | }
|
17247 | matchDirectives(elementName, elOrTpl) {
|
17248 | if (this.directiveMatcher) {
|
17249 | const selector = createCssSelector(elementName, getAttrsForDirectiveMatching(elOrTpl));
|
17250 | this.directiveMatcher.match(selector, (cssSelector, staticType) => {
|
17251 | this.directives.add(staticType);
|
17252 | });
|
17253 | }
|
17254 | }
|
17255 | /**
|
17256 | * Prepares all attribute expression values for the `TAttributes` array.
|
17257 | *
|
17258 | * The purpose of this function is to properly construct an attributes array that
|
17259 | * is passed into the `elementStart` (or just `element`) functions. Because there
|
17260 | * are many different types of attributes, the array needs to be constructed in a
|
17261 | * special way so that `elementStart` can properly evaluate them.
|
17262 | *
|
17263 | * The format looks like this:
|
17264 | *
|
17265 | * ```
|
17266 | * attrs = [prop, value, prop2, value2,
|
17267 | * PROJECT_AS, selector,
|
17268 | * CLASSES, class1, class2,
|
17269 | * STYLES, style1, value1, style2, value2,
|
17270 | * BINDINGS, name1, name2, name3,
|
17271 | * TEMPLATE, name4, name5, name6,
|
17272 | * I18N, name7, name8, ...]
|
17273 | * ```
|
17274 | *
|
17275 | * Note that this function will fully ignore all synthetic (@foo) attribute values
|
17276 | * because those values are intended to always be generated as property instructions.
|
17277 | */
|
17278 | getAttributeExpressions(elementName, renderAttributes, inputs, outputs, styles, templateAttrs = [], boundI18nAttrs = []) {
|
17279 | const alreadySeen = new Set();
|
17280 | const attrExprs = [];
|
17281 | let ngProjectAsAttr;
|
17282 | for (const attr of renderAttributes) {
|
17283 | if (attr.name === NG_PROJECT_AS_ATTR_NAME) {
|
17284 | ngProjectAsAttr = attr;
|
17285 | }
|
17286 | // Note that static i18n attributes aren't in the i18n array,
|
17287 | // because they're treated in the same way as regular attributes.
|
17288 | if (attr.i18n) {
|
17289 | // When i18n attributes are present on elements with structural directives
|
17290 | // (e.g. `<div *ngIf title="Hello" i18n-title>`), we want to avoid generating
|
17291 | // duplicate i18n translation blocks for `ɵɵtemplate` and `ɵɵelement` instruction
|
17292 | // attributes. So we do a cache lookup to see if suitable i18n translation block
|
17293 | // already exists.
|
17294 | const { i18nVarRefsCache } = this._constants;
|
17295 | let i18nVarRef;
|
17296 | if (i18nVarRefsCache.has(attr.i18n)) {
|
17297 | i18nVarRef = i18nVarRefsCache.get(attr.i18n);
|
17298 | }
|
17299 | else {
|
17300 | i18nVarRef = this.i18nTranslate(attr.i18n);
|
17301 | i18nVarRefsCache.set(attr.i18n, i18nVarRef);
|
17302 | }
|
17303 | attrExprs.push(literal(attr.name), i18nVarRef);
|
17304 | }
|
17305 | else {
|
17306 | attrExprs.push(...getAttributeNameLiterals(attr.name), trustedConstAttribute(elementName, attr));
|
17307 | }
|
17308 | }
|
17309 | // Keep ngProjectAs next to the other name, value pairs so we can verify that we match
|
17310 | // ngProjectAs marker in the attribute name slot.
|
17311 | if (ngProjectAsAttr) {
|
17312 | attrExprs.push(...getNgProjectAsLiteral(ngProjectAsAttr));
|
17313 | }
|
17314 | function addAttrExpr(key, value) {
|
17315 | if (typeof key === 'string') {
|
17316 | if (!alreadySeen.has(key)) {
|
17317 | attrExprs.push(...getAttributeNameLiterals(key));
|
17318 | value !== undefined && attrExprs.push(value);
|
17319 | alreadySeen.add(key);
|
17320 | }
|
17321 | }
|
17322 | else {
|
17323 | attrExprs.push(literal(key));
|
17324 | }
|
17325 | }
|
17326 | // it's important that this occurs before BINDINGS and TEMPLATE because once `elementStart`
|
17327 | // comes across the BINDINGS or TEMPLATE markers then it will continue reading each value as
|
17328 | // as single property value cell by cell.
|
17329 | if (styles) {
|
17330 | styles.populateInitialStylingAttrs(attrExprs);
|
17331 | }
|
17332 | if (inputs.length || outputs.length) {
|
17333 | const attrsLengthBeforeInputs = attrExprs.length;
|
17334 | for (let i = 0; i < inputs.length; i++) {
|
17335 | const input = inputs[i];
|
17336 | // We don't want the animation and attribute bindings in the
|
17337 | // attributes array since they aren't used for directive matching.
|
17338 | if (input.type !== 4 /* Animation */ && input.type !== 1 /* Attribute */) {
|
17339 | addAttrExpr(input.name);
|
17340 | }
|
17341 | }
|
17342 | for (let i = 0; i < outputs.length; i++) {
|
17343 | const output = outputs[i];
|
17344 | if (output.type !== 1 /* Animation */) {
|
17345 | addAttrExpr(output.name);
|
17346 | }
|
17347 | }
|
17348 | // this is a cheap way of adding the marker only after all the input/output
|
17349 | // values have been filtered (by not including the animation ones) and added
|
17350 | // to the expressions. The marker is important because it tells the runtime
|
17351 | // code that this is where attributes without values start...
|
17352 | if (attrExprs.length !== attrsLengthBeforeInputs) {
|
17353 | attrExprs.splice(attrsLengthBeforeInputs, 0, literal(3 /* Bindings */));
|
17354 | }
|
17355 | }
|
17356 | if (templateAttrs.length) {
|
17357 | attrExprs.push(literal(4 /* Template */));
|
17358 | templateAttrs.forEach(attr => addAttrExpr(attr.name));
|
17359 | }
|
17360 | if (boundI18nAttrs.length) {
|
17361 | attrExprs.push(literal(6 /* I18n */));
|
17362 | boundI18nAttrs.forEach(attr => addAttrExpr(attr.name));
|
17363 | }
|
17364 | return attrExprs;
|
17365 | }
|
17366 | addToConsts(expression) {
|
17367 | if (isNull(expression)) {
|
17368 | return TYPED_NULL_EXPR;
|
17369 | }
|
17370 | const consts = this._constants.constExpressions;
|
17371 | // Try to reuse a literal that's already in the array, if possible.
|
17372 | for (let i = 0; i < consts.length; i++) {
|
17373 | if (consts[i].isEquivalent(expression)) {
|
17374 | return literal(i);
|
17375 | }
|
17376 | }
|
17377 | return literal(consts.push(expression) - 1);
|
17378 | }
|
17379 | addAttrsToConsts(attrs) {
|
17380 | return attrs.length > 0 ? this.addToConsts(literalArr(attrs)) : TYPED_NULL_EXPR;
|
17381 | }
|
17382 | prepareRefsArray(references) {
|
17383 | if (!references || references.length === 0) {
|
17384 | return TYPED_NULL_EXPR;
|
17385 | }
|
17386 | const refsParam = flatten(references.map(reference => {
|
17387 | const slot = this.allocateDataSlot();
|
17388 | // Generate the update temporary.
|
17389 | const variableName = this._bindingScope.freshReferenceName();
|
17390 | const retrievalLevel = this.level;
|
17391 | const lhs = variable(variableName);
|
17392 | this._bindingScope.set(retrievalLevel, reference.name, lhs, 0 /* DEFAULT */, (scope, relativeLevel) => {
|
17393 | // e.g. nextContext(2);
|
17394 | const nextContextStmt = relativeLevel > 0 ? [generateNextContextExpr(relativeLevel).toStmt()] : [];
|
17395 | // e.g. const $foo$ = reference(1);
|
17396 | const refExpr = lhs.set(importExpr(Identifiers$1.reference).callFn([literal(slot)]));
|
17397 | return nextContextStmt.concat(refExpr.toConstDecl());
|
17398 | }, true);
|
17399 | return [reference.name, reference.value];
|
17400 | }));
|
17401 | return asLiteral(refsParam);
|
17402 | }
|
17403 | prepareListenerParameter(tagName, outputAst, index) {
|
17404 | return () => {
|
17405 | const eventName = outputAst.name;
|
17406 | const bindingFnName = outputAst.type === 1 /* Animation */ ?
|
17407 | // synthetic @listener.foo values are treated the exact same as are standard listeners
|
17408 | prepareSyntheticListenerFunctionName(eventName, outputAst.phase) :
|
17409 | sanitizeIdentifier(eventName);
|
17410 | const handlerName = `${this.templateName}_${tagName}_${bindingFnName}_${index}_listener`;
|
17411 | const scope = this._bindingScope.nestedScope(this._bindingScope.bindingLevel, EVENT_BINDING_SCOPE_GLOBALS);
|
17412 | return prepareEventListenerParameters(outputAst, handlerName, scope);
|
17413 | };
|
17414 | }
|
17415 | }
|
17416 | class ValueConverter extends AstMemoryEfficientTransformer {
|
17417 | constructor(constantPool, allocateSlot, allocatePureFunctionSlots, definePipe) {
|
17418 | super();
|
17419 | this.constantPool = constantPool;
|
17420 | this.allocateSlot = allocateSlot;
|
17421 | this.allocatePureFunctionSlots = allocatePureFunctionSlots;
|
17422 | this.definePipe = definePipe;
|
17423 | this._pipeBindExprs = [];
|
17424 | }
|
17425 | // AstMemoryEfficientTransformer
|
17426 | visitPipe(pipe, context) {
|
17427 | // Allocate a slot to create the pipe
|
17428 | const slot = this.allocateSlot();
|
17429 | const slotPseudoLocal = `PIPE:${slot}`;
|
17430 | // Allocate one slot for the result plus one slot per pipe argument
|
17431 | const pureFunctionSlot = this.allocatePureFunctionSlots(2 + pipe.args.length);
|
17432 | const target = new PropertyRead(pipe.span, pipe.sourceSpan, pipe.nameSpan, new ImplicitReceiver(pipe.span, pipe.sourceSpan), slotPseudoLocal);
|
17433 | const { identifier, isVarLength } = pipeBindingCallInfo(pipe.args);
|
17434 | this.definePipe(pipe.name, slotPseudoLocal, slot, importExpr(identifier));
|
17435 | const args = [pipe.exp, ...pipe.args];
|
17436 | const convertedArgs = isVarLength ?
|
17437 | this.visitAll([new LiteralArray(pipe.span, pipe.sourceSpan, args)]) :
|
17438 | this.visitAll(args);
|
17439 | const pipeBindExpr = new FunctionCall(pipe.span, pipe.sourceSpan, target, [
|
17440 | new LiteralPrimitive(pipe.span, pipe.sourceSpan, slot),
|
17441 | new LiteralPrimitive(pipe.span, pipe.sourceSpan, pureFunctionSlot),
|
17442 | ...convertedArgs,
|
17443 | ]);
|
17444 | this._pipeBindExprs.push(pipeBindExpr);
|
17445 | return pipeBindExpr;
|
17446 | }
|
17447 | updatePipeSlotOffsets(bindingSlots) {
|
17448 | this._pipeBindExprs.forEach((pipe) => {
|
17449 | // update the slot offset arg (index 1) to account for binding slots
|
17450 | const slotOffset = pipe.args[1];
|
17451 | slotOffset.value += bindingSlots;
|
17452 | });
|
17453 | }
|
17454 | visitLiteralArray(array, context) {
|
17455 | return new BuiltinFunctionCall(array.span, array.sourceSpan, this.visitAll(array.expressions), values => {
|
17456 | // If the literal has calculated (non-literal) elements transform it into
|
17457 | // calls to literal factories that compose the literal and will cache intermediate
|
17458 | // values.
|
17459 | const literal = literalArr(values);
|
17460 | return getLiteralFactory(this.constantPool, literal, this.allocatePureFunctionSlots);
|
17461 | });
|
17462 | }
|
17463 | visitLiteralMap(map, context) {
|
17464 | return new BuiltinFunctionCall(map.span, map.sourceSpan, this.visitAll(map.values), values => {
|
17465 | // If the literal has calculated (non-literal) elements transform it into
|
17466 | // calls to literal factories that compose the literal and will cache intermediate
|
17467 | // values.
|
17468 | const literal = literalMap(values.map((value, index) => ({ key: map.keys[index].key, value, quoted: map.keys[index].quoted })));
|
17469 | return getLiteralFactory(this.constantPool, literal, this.allocatePureFunctionSlots);
|
17470 | });
|
17471 | }
|
17472 | }
|
17473 | // Pipes always have at least one parameter, the value they operate on
|
17474 | const pipeBindingIdentifiers = [Identifiers$1.pipeBind1, Identifiers$1.pipeBind2, Identifiers$1.pipeBind3, Identifiers$1.pipeBind4];
|
17475 | function pipeBindingCallInfo(args) {
|
17476 | const identifier = pipeBindingIdentifiers[args.length];
|
17477 | return {
|
17478 | identifier: identifier || Identifiers$1.pipeBindV,
|
17479 | isVarLength: !identifier,
|
17480 | };
|
17481 | }
|
17482 | const pureFunctionIdentifiers = [
|
17483 | Identifiers$1.pureFunction0, Identifiers$1.pureFunction1, Identifiers$1.pureFunction2, Identifiers$1.pureFunction3, Identifiers$1.pureFunction4,
|
17484 | Identifiers$1.pureFunction5, Identifiers$1.pureFunction6, Identifiers$1.pureFunction7, Identifiers$1.pureFunction8
|
17485 | ];
|
17486 | function pureFunctionCallInfo(args) {
|
17487 | const identifier = pureFunctionIdentifiers[args.length];
|
17488 | return {
|
17489 | identifier: identifier || Identifiers$1.pureFunctionV,
|
17490 | isVarLength: !identifier,
|
17491 | };
|
17492 | }
|
17493 | function instruction(span, reference, params) {
|
17494 | return importExpr(reference, null, span).callFn(params, span);
|
17495 | }
|
17496 | // e.g. x(2);
|
17497 | function generateNextContextExpr(relativeLevelDiff) {
|
17498 | return importExpr(Identifiers$1.nextContext)
|
17499 | .callFn(relativeLevelDiff > 1 ? [literal(relativeLevelDiff)] : []);
|
17500 | }
|
17501 | function getLiteralFactory(constantPool, literal$1, allocateSlots) {
|
17502 | const { literalFactory, literalFactoryArguments } = constantPool.getLiteralFactory(literal$1);
|
17503 | // Allocate 1 slot for the result plus 1 per argument
|
17504 | const startSlot = allocateSlots(1 + literalFactoryArguments.length);
|
17505 | const { identifier, isVarLength } = pureFunctionCallInfo(literalFactoryArguments);
|
17506 | // Literal factories are pure functions that only need to be re-invoked when the parameters
|
17507 | // change.
|
17508 | const args = [literal(startSlot), literalFactory];
|
17509 | if (isVarLength) {
|
17510 | args.push(literalArr(literalFactoryArguments));
|
17511 | }
|
17512 | else {
|
17513 | args.push(...literalFactoryArguments);
|
17514 | }
|
17515 | return importExpr(identifier).callFn(args);
|
17516 | }
|
17517 | /**
|
17518 | * Gets an array of literals that can be added to an expression
|
17519 | * to represent the name and namespace of an attribute. E.g.
|
17520 | * `:xlink:href` turns into `[AttributeMarker.NamespaceURI, 'xlink', 'href']`.
|
17521 | *
|
17522 | * @param name Name of the attribute, including the namespace.
|
17523 | */
|
17524 | function getAttributeNameLiterals(name) {
|
17525 | const [attributeNamespace, attributeName] = splitNsName(name);
|
17526 | const nameLiteral = literal(attributeName);
|
17527 | if (attributeNamespace) {
|
17528 | return [
|
17529 | literal(0 /* NamespaceURI */), literal(attributeNamespace), nameLiteral
|
17530 | ];
|
17531 | }
|
17532 | return [nameLiteral];
|
17533 | }
|
17534 | /** The prefix used to get a shared context in BindingScope's map. */
|
17535 | const SHARED_CONTEXT_KEY = '$$shared_ctx$$';
|
17536 | class BindingScope {
|
17537 | constructor(bindingLevel = 0, parent = null, globals) {
|
17538 | this.bindingLevel = bindingLevel;
|
17539 | this.parent = parent;
|
17540 | this.globals = globals;
|
17541 | /** Keeps a map from local variables to their BindingData. */
|
17542 | this.map = new Map();
|
17543 | this.referenceNameIndex = 0;
|
17544 | this.restoreViewVariable = null;
|
17545 | if (globals !== undefined) {
|
17546 | for (const name of globals) {
|
17547 | this.set(0, name, variable(name));
|
17548 | }
|
17549 | }
|
17550 | }
|
17551 | static createRootScope() {
|
17552 | return new BindingScope();
|
17553 | }
|
17554 | get(name) {
|
17555 | let current = this;
|
17556 | while (current) {
|
17557 | let value = current.map.get(name);
|
17558 | if (value != null) {
|
17559 | if (current !== this) {
|
17560 | // make a local copy and reset the `declare` state
|
17561 | value = {
|
17562 | retrievalLevel: value.retrievalLevel,
|
17563 | lhs: value.lhs,
|
17564 | declareLocalCallback: value.declareLocalCallback,
|
17565 | declare: false,
|
17566 | priority: value.priority,
|
17567 | localRef: value.localRef
|
17568 | };
|
17569 | // Cache the value locally.
|
17570 | this.map.set(name, value);
|
17571 | // Possibly generate a shared context var
|
17572 | this.maybeGenerateSharedContextVar(value);
|
17573 | this.maybeRestoreView(value.retrievalLevel, value.localRef);
|
17574 | }
|
17575 | if (value.declareLocalCallback && !value.declare) {
|
17576 | value.declare = true;
|
17577 | }
|
17578 | return value.lhs;
|
17579 | }
|
17580 | current = current.parent;
|
17581 | }
|
17582 | // If we get to this point, we are looking for a property on the top level component
|
17583 | // - If level === 0, we are on the top and don't need to re-declare `ctx`.
|
17584 | // - If level > 0, we are in an embedded view. We need to retrieve the name of the
|
17585 | // local var we used to store the component context, e.g. const $comp$ = x();
|
17586 | return this.bindingLevel === 0 ? null : this.getComponentProperty(name);
|
17587 | }
|
17588 | /**
|
17589 | * Create a local variable for later reference.
|
17590 | *
|
17591 | * @param retrievalLevel The level from which this value can be retrieved
|
17592 | * @param name Name of the variable.
|
17593 | * @param lhs AST representing the left hand side of the `let lhs = rhs;`.
|
17594 | * @param priority The sorting priority of this var
|
17595 | * @param declareLocalCallback The callback to invoke when declaring this local var
|
17596 | * @param localRef Whether or not this is a local ref
|
17597 | */
|
17598 | set(retrievalLevel, name, lhs, priority = 0 /* DEFAULT */, declareLocalCallback, localRef) {
|
17599 | if (this.map.has(name)) {
|
17600 | if (localRef) {
|
17601 | // Do not throw an error if it's a local ref and do not update existing value,
|
17602 | // so the first defined ref is always returned.
|
17603 | return this;
|
17604 | }
|
17605 | error(`The name ${name} is already defined in scope to be ${this.map.get(name)}`);
|
17606 | }
|
17607 | this.map.set(name, {
|
17608 | retrievalLevel: retrievalLevel,
|
17609 | lhs: lhs,
|
17610 | declare: false,
|
17611 | declareLocalCallback: declareLocalCallback,
|
17612 | priority: priority,
|
17613 | localRef: localRef || false
|
17614 | });
|
17615 | return this;
|
17616 | }
|
17617 | // Implemented as part of LocalResolver.
|
17618 | getLocal(name) {
|
17619 | return this.get(name);
|
17620 | }
|
17621 | // Implemented as part of LocalResolver.
|
17622 | notifyImplicitReceiverUse() {
|
17623 | if (this.bindingLevel !== 0) {
|
17624 | // Since the implicit receiver is accessed in an embedded view, we need to
|
17625 | // ensure that we declare a shared context variable for the current template
|
17626 | // in the update variables.
|
17627 | this.map.get(SHARED_CONTEXT_KEY + 0).declare = true;
|
17628 | }
|
17629 | }
|
17630 | nestedScope(level, globals) {
|
17631 | const newScope = new BindingScope(level, this, globals);
|
17632 | if (level > 0)
|
17633 | newScope.generateSharedContextVar(0);
|
17634 | return newScope;
|
17635 | }
|
17636 | /**
|
17637 | * Gets or creates a shared context variable and returns its expression. Note that
|
17638 | * this does not mean that the shared variable will be declared. Variables in the
|
17639 | * binding scope will be only declared if they are used.
|
17640 | */
|
17641 | getOrCreateSharedContextVar(retrievalLevel) {
|
17642 | const bindingKey = SHARED_CONTEXT_KEY + retrievalLevel;
|
17643 | if (!this.map.has(bindingKey)) {
|
17644 | this.generateSharedContextVar(retrievalLevel);
|
17645 | }
|
17646 | // Shared context variables are always generated as "ReadVarExpr".
|
17647 | return this.map.get(bindingKey).lhs;
|
17648 | }
|
17649 | getSharedContextName(retrievalLevel) {
|
17650 | const sharedCtxObj = this.map.get(SHARED_CONTEXT_KEY + retrievalLevel);
|
17651 | // Shared context variables are always generated as "ReadVarExpr".
|
17652 | return sharedCtxObj && sharedCtxObj.declare ? sharedCtxObj.lhs : null;
|
17653 | }
|
17654 | maybeGenerateSharedContextVar(value) {
|
17655 | if (value.priority === 1 /* CONTEXT */ &&
|
17656 | value.retrievalLevel < this.bindingLevel) {
|
17657 | const sharedCtxObj = this.map.get(SHARED_CONTEXT_KEY + value.retrievalLevel);
|
17658 | if (sharedCtxObj) {
|
17659 | sharedCtxObj.declare = true;
|
17660 | }
|
17661 | else {
|
17662 | this.generateSharedContextVar(value.retrievalLevel);
|
17663 | }
|
17664 | }
|
17665 | }
|
17666 | generateSharedContextVar(retrievalLevel) {
|
17667 | const lhs = variable(CONTEXT_NAME + this.freshReferenceName());
|
17668 | this.map.set(SHARED_CONTEXT_KEY + retrievalLevel, {
|
17669 | retrievalLevel: retrievalLevel,
|
17670 | lhs: lhs,
|
17671 | declareLocalCallback: (scope, relativeLevel) => {
|
17672 | // const ctx_r0 = nextContext(2);
|
17673 | return [lhs.set(generateNextContextExpr(relativeLevel)).toConstDecl()];
|
17674 | },
|
17675 | declare: false,
|
17676 | priority: 2 /* SHARED_CONTEXT */,
|
17677 | localRef: false
|
17678 | });
|
17679 | }
|
17680 | getComponentProperty(name) {
|
17681 | const componentValue = this.map.get(SHARED_CONTEXT_KEY + 0);
|
17682 | componentValue.declare = true;
|
17683 | this.maybeRestoreView(0, false);
|
17684 | return componentValue.lhs.prop(name);
|
17685 | }
|
17686 | maybeRestoreView(retrievalLevel, localRefLookup) {
|
17687 | // We want to restore the current view in listener fns if:
|
17688 | // 1 - we are accessing a value in a parent view, which requires walking the view tree rather
|
17689 | // than using the ctx arg. In this case, the retrieval and binding level will be different.
|
17690 | // 2 - we are looking up a local ref, which requires restoring the view where the local
|
17691 | // ref is stored
|
17692 | if (this.isListenerScope() && (retrievalLevel < this.bindingLevel || localRefLookup)) {
|
17693 | if (!this.parent.restoreViewVariable) {
|
17694 | // parent saves variable to generate a shared `const $s$ = getCurrentView();` instruction
|
17695 | this.parent.restoreViewVariable = variable(this.parent.freshReferenceName());
|
17696 | }
|
17697 | this.restoreViewVariable = this.parent.restoreViewVariable;
|
17698 | }
|
17699 | }
|
17700 | restoreViewStatement() {
|
17701 | // restoreView($state$);
|
17702 | return this.restoreViewVariable ?
|
17703 | [instruction(null, Identifiers$1.restoreView, [this.restoreViewVariable]).toStmt()] :
|
17704 | [];
|
17705 | }
|
17706 | viewSnapshotStatements() {
|
17707 | // const $state$ = getCurrentView();
|
17708 | const getCurrentViewInstruction = instruction(null, Identifiers$1.getCurrentView, []);
|
17709 | return this.restoreViewVariable ?
|
17710 | [this.restoreViewVariable.set(getCurrentViewInstruction).toConstDecl()] :
|
17711 | [];
|
17712 | }
|
17713 | isListenerScope() {
|
17714 | return this.parent && this.parent.bindingLevel === this.bindingLevel;
|
17715 | }
|
17716 | variableDeclarations() {
|
17717 | let currentContextLevel = 0;
|
17718 | return Array.from(this.map.values())
|
17719 | .filter(value => value.declare)
|
17720 | .sort((a, b) => b.retrievalLevel - a.retrievalLevel || b.priority - a.priority)
|
17721 | .reduce((stmts, value) => {
|
17722 | const levelDiff = this.bindingLevel - value.retrievalLevel;
|
17723 | const currStmts = value.declareLocalCallback(this, levelDiff - currentContextLevel);
|
17724 | currentContextLevel = levelDiff;
|
17725 | return stmts.concat(currStmts);
|
17726 | }, []);
|
17727 | }
|
17728 | freshReferenceName() {
|
17729 | let current = this;
|
17730 | // Find the top scope as it maintains the global reference count
|
17731 | while (current.parent)
|
17732 | current = current.parent;
|
17733 | const ref = `${REFERENCE_PREFIX}${current.referenceNameIndex++}`;
|
17734 | return ref;
|
17735 | }
|
17736 | }
|
17737 | /**
|
17738 | * Creates a `CssSelector` given a tag name and a map of attributes
|
17739 | */
|
17740 | function createCssSelector(elementName, attributes) {
|
17741 | const cssSelector = new CssSelector();
|
17742 | const elementNameNoNs = splitNsName(elementName)[1];
|
17743 | cssSelector.setElement(elementNameNoNs);
|
17744 | Object.getOwnPropertyNames(attributes).forEach((name) => {
|
17745 | const nameNoNs = splitNsName(name)[1];
|
17746 | const value = attributes[name];
|
17747 | cssSelector.addAttribute(nameNoNs, value);
|
17748 | if (name.toLowerCase() === 'class') {
|
17749 | const classes = value.trim().split(/\s+/);
|
17750 | classes.forEach(className => cssSelector.addClassName(className));
|
17751 | }
|
17752 | });
|
17753 | return cssSelector;
|
17754 | }
|
17755 | /**
|
17756 | * Creates an array of expressions out of an `ngProjectAs` attributes
|
17757 | * which can be added to the instruction parameters.
|
17758 | */
|
17759 | function getNgProjectAsLiteral(attribute) {
|
17760 | // Parse the attribute value into a CssSelectorList. Note that we only take the
|
17761 | // first selector, because we don't support multiple selectors in ngProjectAs.
|
17762 | const parsedR3Selector = parseSelectorToR3Selector(attribute.value)[0];
|
17763 | return [literal(5 /* ProjectAs */), asLiteral(parsedR3Selector)];
|
17764 | }
|
17765 | /**
|
17766 | * Gets the instruction to generate for an interpolated property
|
17767 | * @param interpolation An Interpolation AST
|
17768 | */
|
17769 | function getPropertyInterpolationExpression(interpolation) {
|
17770 | switch (getInterpolationArgsLength(interpolation)) {
|
17771 | case 1:
|
17772 | return Identifiers$1.propertyInterpolate;
|
17773 | case 3:
|
17774 | return Identifiers$1.propertyInterpolate1;
|
17775 | case 5:
|
17776 | return Identifiers$1.propertyInterpolate2;
|
17777 | case 7:
|
17778 | return Identifiers$1.propertyInterpolate3;
|
17779 | case 9:
|
17780 | return Identifiers$1.propertyInterpolate4;
|
17781 | case 11:
|
17782 | return Identifiers$1.propertyInterpolate5;
|
17783 | case 13:
|
17784 | return Identifiers$1.propertyInterpolate6;
|
17785 | case 15:
|
17786 | return Identifiers$1.propertyInterpolate7;
|
17787 | case 17:
|
17788 | return Identifiers$1.propertyInterpolate8;
|
17789 | default:
|
17790 | return Identifiers$1.propertyInterpolateV;
|
17791 | }
|
17792 | }
|
17793 | /**
|
17794 | * Gets the instruction to generate for an interpolated attribute
|
17795 | * @param interpolation An Interpolation AST
|
17796 | */
|
17797 | function getAttributeInterpolationExpression(interpolation) {
|
17798 | switch (getInterpolationArgsLength(interpolation)) {
|
17799 | case 3:
|
17800 | return Identifiers$1.attributeInterpolate1;
|
17801 | case 5:
|
17802 | return Identifiers$1.attributeInterpolate2;
|
17803 | case 7:
|
17804 | return Identifiers$1.attributeInterpolate3;
|
17805 | case 9:
|
17806 | return Identifiers$1.attributeInterpolate4;
|
17807 | case 11:
|
17808 | return Identifiers$1.attributeInterpolate5;
|
17809 | case 13:
|
17810 | return Identifiers$1.attributeInterpolate6;
|
17811 | case 15:
|
17812 | return Identifiers$1.attributeInterpolate7;
|
17813 | case 17:
|
17814 | return Identifiers$1.attributeInterpolate8;
|
17815 | default:
|
17816 | return Identifiers$1.attributeInterpolateV;
|
17817 | }
|
17818 | }
|
17819 | /**
|
17820 | * Gets the instruction to generate for interpolated text.
|
17821 | * @param interpolation An Interpolation AST
|
17822 | */
|
17823 | function getTextInterpolationExpression(interpolation) {
|
17824 | switch (getInterpolationArgsLength(interpolation)) {
|
17825 | case 1:
|
17826 | return Identifiers$1.textInterpolate;
|
17827 | case 3:
|
17828 | return Identifiers$1.textInterpolate1;
|
17829 | case 5:
|
17830 | return Identifiers$1.textInterpolate2;
|
17831 | case 7:
|
17832 | return Identifiers$1.textInterpolate3;
|
17833 | case 9:
|
17834 | return Identifiers$1.textInterpolate4;
|
17835 | case 11:
|
17836 | return Identifiers$1.textInterpolate5;
|
17837 | case 13:
|
17838 | return Identifiers$1.textInterpolate6;
|
17839 | case 15:
|
17840 | return Identifiers$1.textInterpolate7;
|
17841 | case 17:
|
17842 | return Identifiers$1.textInterpolate8;
|
17843 | default:
|
17844 | return Identifiers$1.textInterpolateV;
|
17845 | }
|
17846 | }
|
17847 | /**
|
17848 | * Parse a template into render3 `Node`s and additional metadata, with no other dependencies.
|
17849 | *
|
17850 | * @param template text of the template to parse
|
17851 | * @param templateUrl URL to use for source mapping of the parsed template
|
17852 | * @param options options to modify how the template is parsed
|
17853 | */
|
17854 | function parseTemplate(template, templateUrl, options = {}) {
|
17855 | var _a;
|
17856 | const { interpolationConfig, preserveWhitespaces, enableI18nLegacyMessageIdFormat } = options;
|
17857 | const isInline = (_a = options.isInline) !== null && _a !== void 0 ? _a : false;
|
17858 | const bindingParser = makeBindingParser(interpolationConfig);
|
17859 | const htmlParser = new HtmlParser();
|
17860 | const parseResult = htmlParser.parse(template, templateUrl, Object.assign(Object.assign({ leadingTriviaChars: LEADING_TRIVIA_CHARS }, options), { tokenizeExpansionForms: true }));
|
17861 | if (parseResult.errors && parseResult.errors.length > 0) {
|
17862 | // TODO(ayazhafiz): we may not always want to bail out at this point (e.g. in
|
17863 | // the context of a language service).
|
17864 | return {
|
17865 | interpolationConfig,
|
17866 | preserveWhitespaces,
|
17867 | template,
|
17868 | templateUrl,
|
17869 | isInline,
|
17870 | errors: parseResult.errors,
|
17871 | nodes: [],
|
17872 | styleUrls: [],
|
17873 | styles: [],
|
17874 | ngContentSelectors: []
|
17875 | };
|
17876 | }
|
17877 | let rootNodes = parseResult.rootNodes;
|
17878 | // process i18n meta information (scan attributes, generate ids)
|
17879 | // before we run whitespace removal process, because existing i18n
|
17880 | // extraction process (ng extract-i18n) relies on a raw content to generate
|
17881 | // message ids
|
17882 | const i18nMetaVisitor = new I18nMetaVisitor(interpolationConfig, /* keepI18nAttrs */ !preserveWhitespaces, enableI18nLegacyMessageIdFormat);
|
17883 | const i18nMetaResult = i18nMetaVisitor.visitAllWithErrors(rootNodes);
|
17884 | if (i18nMetaResult.errors && i18nMetaResult.errors.length > 0) {
|
17885 | return {
|
17886 | interpolationConfig,
|
17887 | preserveWhitespaces,
|
17888 | template,
|
17889 | templateUrl,
|
17890 | isInline,
|
17891 | errors: i18nMetaResult.errors,
|
17892 | nodes: [],
|
17893 | styleUrls: [],
|
17894 | styles: [],
|
17895 | ngContentSelectors: []
|
17896 | };
|
17897 | }
|
17898 | rootNodes = i18nMetaResult.rootNodes;
|
17899 | if (!preserveWhitespaces) {
|
17900 | rootNodes = visitAll$1(new WhitespaceVisitor(), rootNodes);
|
17901 | // run i18n meta visitor again in case whitespaces are removed (because that might affect
|
17902 | // generated i18n message content) and first pass indicated that i18n content is present in a
|
17903 | // template. During this pass i18n IDs generated at the first pass will be preserved, so we can
|
17904 | // mimic existing extraction process (ng extract-i18n)
|
17905 | if (i18nMetaVisitor.hasI18nMeta) {
|
17906 | rootNodes = visitAll$1(new I18nMetaVisitor(interpolationConfig, /* keepI18nAttrs */ false), rootNodes);
|
17907 | }
|
17908 | }
|
17909 | const { nodes, errors, styleUrls, styles, ngContentSelectors } = htmlAstToRender3Ast(rootNodes, bindingParser);
|
17910 | return {
|
17911 | interpolationConfig,
|
17912 | preserveWhitespaces,
|
17913 | errors: errors.length > 0 ? errors : null,
|
17914 | template,
|
17915 | templateUrl,
|
17916 | isInline,
|
17917 | nodes,
|
17918 | styleUrls,
|
17919 | styles,
|
17920 | ngContentSelectors
|
17921 | };
|
17922 | }
|
17923 | const elementRegistry = new DomElementSchemaRegistry();
|
17924 | /**
|
17925 | * Construct a `BindingParser` with a default configuration.
|
17926 | */
|
17927 | function makeBindingParser(interpolationConfig = DEFAULT_INTERPOLATION_CONFIG) {
|
17928 | return new BindingParser(new IvyParser(new Lexer()), interpolationConfig, elementRegistry, null, []);
|
17929 | }
|
17930 | function resolveSanitizationFn(context, isAttribute) {
|
17931 | switch (context) {
|
17932 | case SecurityContext.HTML:
|
17933 | return importExpr(Identifiers$1.sanitizeHtml);
|
17934 | case SecurityContext.SCRIPT:
|
17935 | return importExpr(Identifiers$1.sanitizeScript);
|
17936 | case SecurityContext.STYLE:
|
17937 | // the compiler does not fill in an instruction for [style.prop?] binding
|
17938 | // values because the style algorithm knows internally what props are subject
|
17939 | // to sanitization (only [attr.style] values are explicitly sanitized)
|
17940 | return isAttribute ? importExpr(Identifiers$1.sanitizeStyle) : null;
|
17941 | case SecurityContext.URL:
|
17942 | return importExpr(Identifiers$1.sanitizeUrl);
|
17943 | case SecurityContext.RESOURCE_URL:
|
17944 | return importExpr(Identifiers$1.sanitizeResourceUrl);
|
17945 | default:
|
17946 | return null;
|
17947 | }
|
17948 | }
|
17949 | function trustedConstAttribute(tagName, attr) {
|
17950 | const value = asLiteral(attr.value);
|
17951 | if (isTrustedTypesSink(tagName, attr.name)) {
|
17952 | switch (elementRegistry.securityContext(tagName, attr.name, /* isAttribute */ true)) {
|
17953 | case SecurityContext.HTML:
|
17954 | return taggedTemplate(importExpr(Identifiers$1.trustConstantHtml), new TemplateLiteral([new TemplateLiteralElement(attr.value)], []), undefined, attr.valueSpan);
|
17955 | // NB: no SecurityContext.SCRIPT here, as the corresponding tags are stripped by the compiler.
|
17956 | case SecurityContext.RESOURCE_URL:
|
17957 | return taggedTemplate(importExpr(Identifiers$1.trustConstantResourceUrl), new TemplateLiteral([new TemplateLiteralElement(attr.value)], []), undefined, attr.valueSpan);
|
17958 | default:
|
17959 | return value;
|
17960 | }
|
17961 | }
|
17962 | else {
|
17963 | return value;
|
17964 | }
|
17965 | }
|
17966 | function isSingleElementTemplate(children) {
|
17967 | return children.length === 1 && children[0] instanceof Element;
|
17968 | }
|
17969 | function isTextNode(node) {
|
17970 | return node instanceof Text || node instanceof BoundText || node instanceof Icu;
|
17971 | }
|
17972 | function hasTextChildrenOnly(children) {
|
17973 | return children.every(isTextNode);
|
17974 | }
|
17975 | /** Name of the global variable that is used to determine if we use Closure translations or not */
|
17976 | const NG_I18N_CLOSURE_MODE = 'ngI18nClosureMode';
|
17977 | /**
|
17978 | * Generate statements that define a given translation message.
|
17979 | *
|
17980 | * ```
|
17981 | * var I18N_1;
|
17982 | * if (typeof ngI18nClosureMode !== undefined && ngI18nClosureMode) {
|
17983 | * var MSG_EXTERNAL_XXX = goog.getMsg(
|
17984 | * "Some message with {$interpolation}!",
|
17985 | * { "interpolation": "\uFFFD0\uFFFD" }
|
17986 | * );
|
17987 | * I18N_1 = MSG_EXTERNAL_XXX;
|
17988 | * }
|
17989 | * else {
|
17990 | * I18N_1 = $localize`Some message with ${'\uFFFD0\uFFFD'}!`;
|
17991 | * }
|
17992 | * ```
|
17993 | *
|
17994 | * @param message The original i18n AST message node
|
17995 | * @param variable The variable that will be assigned the translation, e.g. `I18N_1`.
|
17996 | * @param closureVar The variable for Closure `goog.getMsg` calls, e.g. `MSG_EXTERNAL_XXX`.
|
17997 | * @param params Object mapping placeholder names to their values (e.g.
|
17998 | * `{ "interpolation": "\uFFFD0\uFFFD" }`).
|
17999 | * @param transformFn Optional transformation function that will be applied to the translation (e.g.
|
18000 | * post-processing).
|
18001 | * @returns An array of statements that defined a given translation.
|
18002 | */
|
18003 | function getTranslationDeclStmts(message, variable, closureVar, params = {}, transformFn) {
|
18004 | const statements = [
|
18005 | declareI18nVariable(variable),
|
18006 | ifStmt(createClosureModeGuard(), createGoogleGetMsgStatements(variable, message, closureVar, i18nFormatPlaceholderNames(params, /* useCamelCase */ true)), createLocalizeStatements(variable, message, i18nFormatPlaceholderNames(params, /* useCamelCase */ false))),
|
18007 | ];
|
18008 | if (transformFn) {
|
18009 | statements.push(new ExpressionStatement(variable.set(transformFn(variable))));
|
18010 | }
|
18011 | return statements;
|
18012 | }
|
18013 | /**
|
18014 | * Create the expression that will be used to guard the closure mode block
|
18015 | * It is equivalent to:
|
18016 | *
|
18017 | * ```
|
18018 | * typeof ngI18nClosureMode !== undefined && ngI18nClosureMode
|
18019 | * ```
|
18020 | */
|
18021 | function createClosureModeGuard() {
|
18022 | return typeofExpr(variable(NG_I18N_CLOSURE_MODE))
|
18023 | .notIdentical(literal('undefined', STRING_TYPE))
|
18024 | .and(variable(NG_I18N_CLOSURE_MODE));
|
18025 | }
|
18026 |
|
18027 | /**
|
18028 | * @license
|
18029 | * Copyright Google LLC All Rights Reserved.
|
18030 | *
|
18031 | * Use of this source code is governed by an MIT-style license that can be
|
18032 | * found in the LICENSE file at https://angular.io/license
|
18033 | */
|
18034 | // This regex matches any binding names that contain the "attr." prefix, e.g. "attr.required"
|
18035 | // If there is a match, the first matching group will contain the attribute name to bind.
|
18036 | const ATTR_REGEX = /attr\.([^\]]+)/;
|
18037 | function baseDirectiveFields(meta, constantPool, bindingParser) {
|
18038 | const definitionMap = new DefinitionMap();
|
18039 | const selectors = parseSelectorToR3Selector(meta.selector);
|
18040 | // e.g. `type: MyDirective`
|
18041 | definitionMap.set('type', meta.internalType);
|
18042 | // e.g. `selectors: [['', 'someDir', '']]`
|
18043 | if (selectors.length > 0) {
|
18044 | definitionMap.set('selectors', asLiteral(selectors));
|
18045 | }
|
18046 | if (meta.queries.length > 0) {
|
18047 | // e.g. `contentQueries: (rf, ctx, dirIndex) => { ... }
|
18048 | definitionMap.set('contentQueries', createContentQueriesFunction(meta.queries, constantPool, meta.name));
|
18049 | }
|
18050 | if (meta.viewQueries.length) {
|
18051 | definitionMap.set('viewQuery', createViewQueriesFunction(meta.viewQueries, constantPool, meta.name));
|
18052 | }
|
18053 | // e.g. `hostBindings: (rf, ctx) => { ... }
|
18054 | definitionMap.set('hostBindings', createHostBindingsFunction(meta.host, meta.typeSourceSpan, bindingParser, constantPool, meta.selector || '', meta.name, definitionMap));
|
18055 | // e.g 'inputs: {a: 'a'}`
|
18056 | definitionMap.set('inputs', conditionallyCreateMapObjectLiteral(meta.inputs, true));
|
18057 | // e.g 'outputs: {a: 'a'}`
|
18058 | definitionMap.set('outputs', conditionallyCreateMapObjectLiteral(meta.outputs));
|
18059 | if (meta.exportAs !== null) {
|
18060 | definitionMap.set('exportAs', literalArr(meta.exportAs.map(e => literal(e))));
|
18061 | }
|
18062 | return definitionMap;
|
18063 | }
|
18064 | /**
|
18065 | * Add features to the definition map.
|
18066 | */
|
18067 | function addFeatures(definitionMap, meta) {
|
18068 | // e.g. `features: [NgOnChangesFeature]`
|
18069 | const features = [];
|
18070 | const providers = meta.providers;
|
18071 | const viewProviders = meta.viewProviders;
|
18072 | if (providers || viewProviders) {
|
18073 | const args = [providers || new LiteralArrayExpr([])];
|
18074 | if (viewProviders) {
|
18075 | args.push(viewProviders);
|
18076 | }
|
18077 | features.push(importExpr(Identifiers$1.ProvidersFeature).callFn(args));
|
18078 | }
|
18079 | if (meta.usesInheritance) {
|
18080 | features.push(importExpr(Identifiers$1.InheritDefinitionFeature));
|
18081 | }
|
18082 | if (meta.fullInheritance) {
|
18083 | features.push(importExpr(Identifiers$1.CopyDefinitionFeature));
|
18084 | }
|
18085 | if (meta.lifecycle.usesOnChanges) {
|
18086 | features.push(importExpr(Identifiers$1.NgOnChangesFeature));
|
18087 | }
|
18088 | if (features.length) {
|
18089 | definitionMap.set('features', literalArr(features));
|
18090 | }
|
18091 | }
|
18092 | /**
|
18093 | * Compile a directive for the render3 runtime as defined by the `R3DirectiveMetadata`.
|
18094 | */
|
18095 | function compileDirectiveFromMetadata(meta, constantPool, bindingParser) {
|
18096 | const definitionMap = baseDirectiveFields(meta, constantPool, bindingParser);
|
18097 | addFeatures(definitionMap, meta);
|
18098 | const expression = importExpr(Identifiers$1.defineDirective).callFn([definitionMap.toLiteralMap()]);
|
18099 | const type = createDirectiveType(meta);
|
18100 | return { expression, type };
|
18101 | }
|
18102 | /**
|
18103 | * Compile a component for the render3 runtime as defined by the `R3ComponentMetadata`.
|
18104 | */
|
18105 | function compileComponentFromMetadata(meta, constantPool, bindingParser) {
|
18106 | const definitionMap = baseDirectiveFields(meta, constantPool, bindingParser);
|
18107 | addFeatures(definitionMap, meta);
|
18108 | const selector = meta.selector && CssSelector.parse(meta.selector);
|
18109 | const firstSelector = selector && selector[0];
|
18110 | // e.g. `attr: ["class", ".my.app"]`
|
18111 | // This is optional an only included if the first selector of a component specifies attributes.
|
18112 | if (firstSelector) {
|
18113 | const selectorAttributes = firstSelector.getAttrs();
|
18114 | if (selectorAttributes.length) {
|
18115 | definitionMap.set('attrs', constantPool.getConstLiteral(literalArr(selectorAttributes.map(value => value != null ? literal(value) : literal(undefined))),
|
18116 | /* forceShared */ true));
|
18117 | }
|
18118 | }
|
18119 | // Generate the CSS matcher that recognize directive
|
18120 | let directiveMatcher = null;
|
18121 | if (meta.directives.length > 0) {
|
18122 | const matcher = new SelectorMatcher();
|
18123 | for (const { selector, type } of meta.directives) {
|
18124 | matcher.addSelectables(CssSelector.parse(selector), type);
|
18125 | }
|
18126 | directiveMatcher = matcher;
|
18127 | }
|
18128 | // e.g. `template: function MyComponent_Template(_ctx, _cm) {...}`
|
18129 | const templateTypeName = meta.name;
|
18130 | const templateName = templateTypeName ? `${templateTypeName}_Template` : null;
|
18131 | const directivesUsed = new Set();
|
18132 | const pipesUsed = new Set();
|
18133 | const changeDetection = meta.changeDetection;
|
18134 | const template = meta.template;
|
18135 | const templateBuilder = new TemplateDefinitionBuilder(constantPool, BindingScope.createRootScope(), 0, templateTypeName, null, null, templateName, directiveMatcher, directivesUsed, meta.pipes, pipesUsed, Identifiers$1.namespaceHTML, meta.relativeContextFilePath, meta.i18nUseExternalIds);
|
18136 | const templateFunctionExpression = templateBuilder.buildTemplateFunction(template.nodes, []);
|
18137 | // We need to provide this so that dynamically generated components know what
|
18138 | // projected content blocks to pass through to the component when it is instantiated.
|
18139 | const ngContentSelectors = templateBuilder.getNgContentSelectors();
|
18140 | if (ngContentSelectors) {
|
18141 | definitionMap.set('ngContentSelectors', ngContentSelectors);
|
18142 | }
|
18143 | // e.g. `decls: 2`
|
18144 | definitionMap.set('decls', literal(templateBuilder.getConstCount()));
|
18145 | // e.g. `vars: 2`
|
18146 | definitionMap.set('vars', literal(templateBuilder.getVarCount()));
|
18147 | // Generate `consts` section of ComponentDef:
|
18148 | // - either as an array:
|
18149 | // `consts: [['one', 'two'], ['three', 'four']]`
|
18150 | // - or as a factory function in case additional statements are present (to support i18n):
|
18151 | // `consts: function() { var i18n_0; if (ngI18nClosureMode) {...} else {...} return [i18n_0]; }`
|
18152 | const { constExpressions, prepareStatements } = templateBuilder.getConsts();
|
18153 | if (constExpressions.length > 0) {
|
18154 | let constsExpr = literalArr(constExpressions);
|
18155 | // Prepare statements are present - turn `consts` into a function.
|
18156 | if (prepareStatements.length > 0) {
|
18157 | constsExpr = fn([], [...prepareStatements, new ReturnStatement(constsExpr)]);
|
18158 | }
|
18159 | definitionMap.set('consts', constsExpr);
|
18160 | }
|
18161 | definitionMap.set('template', templateFunctionExpression);
|
18162 | // e.g. `directives: [MyDirective]`
|
18163 | if (directivesUsed.size) {
|
18164 | const directivesList = literalArr(Array.from(directivesUsed));
|
18165 | const directivesExpr = compileDeclarationList(directivesList, meta.declarationListEmitMode);
|
18166 | definitionMap.set('directives', directivesExpr);
|
18167 | }
|
18168 | // e.g. `pipes: [MyPipe]`
|
18169 | if (pipesUsed.size) {
|
18170 | const pipesList = literalArr(Array.from(pipesUsed));
|
18171 | const pipesExpr = compileDeclarationList(pipesList, meta.declarationListEmitMode);
|
18172 | definitionMap.set('pipes', pipesExpr);
|
18173 | }
|
18174 | if (meta.encapsulation === null) {
|
18175 | meta.encapsulation = ViewEncapsulation.Emulated;
|
18176 | }
|
18177 | // e.g. `styles: [str1, str2]`
|
18178 | if (meta.styles && meta.styles.length) {
|
18179 | const styleValues = meta.encapsulation == ViewEncapsulation.Emulated ?
|
18180 | compileStyles(meta.styles, CONTENT_ATTR, HOST_ATTR) :
|
18181 | meta.styles;
|
18182 | const strings = styleValues.map(str => constantPool.getConstLiteral(literal(str)));
|
18183 | definitionMap.set('styles', literalArr(strings));
|
18184 | }
|
18185 | else if (meta.encapsulation === ViewEncapsulation.Emulated) {
|
18186 | // If there is no style, don't generate css selectors on elements
|
18187 | meta.encapsulation = ViewEncapsulation.None;
|
18188 | }
|
18189 | // Only set view encapsulation if it's not the default value
|
18190 | if (meta.encapsulation !== ViewEncapsulation.Emulated) {
|
18191 | definitionMap.set('encapsulation', literal(meta.encapsulation));
|
18192 | }
|
18193 | // e.g. `animation: [trigger('123', [])]`
|
18194 | if (meta.animations !== null) {
|
18195 | definitionMap.set('data', literalMap([{ key: 'animation', value: meta.animations, quoted: false }]));
|
18196 | }
|
18197 | // Only set the change detection flag if it's defined and it's not the default.
|
18198 | if (changeDetection != null && changeDetection !== ChangeDetectionStrategy.Default) {
|
18199 | definitionMap.set('changeDetection', literal(changeDetection));
|
18200 | }
|
18201 | const expression = importExpr(Identifiers$1.defineComponent).callFn([definitionMap.toLiteralMap()]);
|
18202 | const type = createComponentType(meta);
|
18203 | return { expression, type };
|
18204 | }
|
18205 | /**
|
18206 | * Creates the type specification from the component meta. This type is inserted into .d.ts files
|
18207 | * to be consumed by upstream compilations.
|
18208 | */
|
18209 | function createComponentType(meta) {
|
18210 | const typeParams = createDirectiveTypeParams(meta);
|
18211 | typeParams.push(stringArrayAsType(meta.template.ngContentSelectors));
|
18212 | return expressionType(importExpr(Identifiers$1.ComponentDefWithMeta, typeParams));
|
18213 | }
|
18214 | /**
|
18215 | * Compiles the array literal of declarations into an expression according to the provided emit
|
18216 | * mode.
|
18217 | */
|
18218 | function compileDeclarationList(list, mode) {
|
18219 | switch (mode) {
|
18220 | case 0 /* Direct */:
|
18221 | // directives: [MyDir],
|
18222 | return list;
|
18223 | case 1 /* Closure */:
|
18224 | // directives: function () { return [MyDir]; }
|
18225 | return fn([], [new ReturnStatement(list)]);
|
18226 | case 2 /* ClosureResolved */:
|
18227 | // directives: function () { return [MyDir].map(ng.resolveForwardRef); }
|
18228 | const resolvedList = list.callMethod('map', [importExpr(Identifiers$1.resolveForwardRef)]);
|
18229 | return fn([], [new ReturnStatement(resolvedList)]);
|
18230 | }
|
18231 | }
|
18232 | function prepareQueryParams(query, constantPool) {
|
18233 | const parameters = [getQueryPredicate(query, constantPool), literal(toQueryFlags(query))];
|
18234 | if (query.read) {
|
18235 | parameters.push(query.read);
|
18236 | }
|
18237 | return parameters;
|
18238 | }
|
18239 | /**
|
18240 | * Translates query flags into `TQueryFlags` type in packages/core/src/render3/interfaces/query.ts
|
18241 | * @param query
|
18242 | */
|
18243 | function toQueryFlags(query) {
|
18244 | return (query.descendants ? 1 /* descendants */ : 0 /* none */) |
|
18245 | (query.static ? 2 /* isStatic */ : 0 /* none */) |
|
18246 | (query.emitDistinctChangesOnly ? 4 /* emitDistinctChangesOnly */ : 0 /* none */);
|
18247 | }
|
18248 | function convertAttributesToExpressions(attributes) {
|
18249 | const values = [];
|
18250 | for (let key of Object.getOwnPropertyNames(attributes)) {
|
18251 | const value = attributes[key];
|
18252 | values.push(literal(key), value);
|
18253 | }
|
18254 | return values;
|
18255 | }
|
18256 | // Define and update any content queries
|
18257 | function createContentQueriesFunction(queries, constantPool, name) {
|
18258 | const createStatements = [];
|
18259 | const updateStatements = [];
|
18260 | const tempAllocator = temporaryAllocator(updateStatements, TEMPORARY_NAME);
|
18261 | for (const query of queries) {
|
18262 | // creation, e.g. r3.contentQuery(dirIndex, somePredicate, true, null);
|
18263 | createStatements.push(importExpr(Identifiers$1.contentQuery)
|
18264 | .callFn([variable('dirIndex'), ...prepareQueryParams(query, constantPool)])
|
18265 | .toStmt());
|
18266 | // update, e.g. (r3.queryRefresh(tmp = r3.loadQuery()) && (ctx.someDir = tmp));
|
18267 | const temporary = tempAllocator();
|
18268 | const getQueryList = importExpr(Identifiers$1.loadQuery).callFn([]);
|
18269 | const refresh = importExpr(Identifiers$1.queryRefresh).callFn([temporary.set(getQueryList)]);
|
18270 | const updateDirective = variable(CONTEXT_NAME)
|
18271 | .prop(query.propertyName)
|
18272 | .set(query.first ? temporary.prop('first') : temporary);
|
18273 | updateStatements.push(refresh.and(updateDirective).toStmt());
|
18274 | }
|
18275 | const contentQueriesFnName = name ? `${name}_ContentQueries` : null;
|
18276 | return fn([
|
18277 | new FnParam(RENDER_FLAGS, NUMBER_TYPE), new FnParam(CONTEXT_NAME, null),
|
18278 | new FnParam('dirIndex', null)
|
18279 | ], [
|
18280 | renderFlagCheckIfStmt(1 /* Create */, createStatements),
|
18281 | renderFlagCheckIfStmt(2 /* Update */, updateStatements)
|
18282 | ], INFERRED_TYPE, null, contentQueriesFnName);
|
18283 | }
|
18284 | function stringAsType(str) {
|
18285 | return expressionType(literal(str));
|
18286 | }
|
18287 | function stringMapAsType(map) {
|
18288 | const mapValues = Object.keys(map).map(key => {
|
18289 | const value = Array.isArray(map[key]) ? map[key][0] : map[key];
|
18290 | return {
|
18291 | key,
|
18292 | value: literal(value),
|
18293 | quoted: true,
|
18294 | };
|
18295 | });
|
18296 | return expressionType(literalMap(mapValues));
|
18297 | }
|
18298 | function stringArrayAsType(arr) {
|
18299 | return arr.length > 0 ? expressionType(literalArr(arr.map(value => literal(value)))) :
|
18300 | NONE_TYPE;
|
18301 | }
|
18302 | function createDirectiveTypeParams(meta) {
|
18303 | // On the type side, remove newlines from the selector as it will need to fit into a TypeScript
|
18304 | // string literal, which must be on one line.
|
18305 | const selectorForType = meta.selector !== null ? meta.selector.replace(/\n/g, '') : null;
|
18306 | return [
|
18307 | typeWithParameters(meta.type.type, meta.typeArgumentCount),
|
18308 | selectorForType !== null ? stringAsType(selectorForType) : NONE_TYPE,
|
18309 | meta.exportAs !== null ? stringArrayAsType(meta.exportAs) : NONE_TYPE,
|
18310 | stringMapAsType(meta.inputs),
|
18311 | stringMapAsType(meta.outputs),
|
18312 | stringArrayAsType(meta.queries.map(q => q.propertyName)),
|
18313 | ];
|
18314 | }
|
18315 | /**
|
18316 | * Creates the type specification from the directive meta. This type is inserted into .d.ts files
|
18317 | * to be consumed by upstream compilations.
|
18318 | */
|
18319 | function createDirectiveType(meta) {
|
18320 | const typeParams = createDirectiveTypeParams(meta);
|
18321 | return expressionType(importExpr(Identifiers$1.DirectiveDefWithMeta, typeParams));
|
18322 | }
|
18323 | // Define and update any view queries
|
18324 | function createViewQueriesFunction(viewQueries, constantPool, name) {
|
18325 | const createStatements = [];
|
18326 | const updateStatements = [];
|
18327 | const tempAllocator = temporaryAllocator(updateStatements, TEMPORARY_NAME);
|
18328 | viewQueries.forEach((query) => {
|
18329 | // creation, e.g. r3.viewQuery(somePredicate, true);
|
18330 | const queryDefinition = importExpr(Identifiers$1.viewQuery).callFn(prepareQueryParams(query, constantPool));
|
18331 | createStatements.push(queryDefinition.toStmt());
|
18332 | // update, e.g. (r3.queryRefresh(tmp = r3.loadQuery()) && (ctx.someDir = tmp));
|
18333 | const temporary = tempAllocator();
|
18334 | const getQueryList = importExpr(Identifiers$1.loadQuery).callFn([]);
|
18335 | const refresh = importExpr(Identifiers$1.queryRefresh).callFn([temporary.set(getQueryList)]);
|
18336 | const updateDirective = variable(CONTEXT_NAME)
|
18337 | .prop(query.propertyName)
|
18338 | .set(query.first ? temporary.prop('first') : temporary);
|
18339 | updateStatements.push(refresh.and(updateDirective).toStmt());
|
18340 | });
|
18341 | const viewQueryFnName = name ? `${name}_Query` : null;
|
18342 | return fn([new FnParam(RENDER_FLAGS, NUMBER_TYPE), new FnParam(CONTEXT_NAME, null)], [
|
18343 | renderFlagCheckIfStmt(1 /* Create */, createStatements),
|
18344 | renderFlagCheckIfStmt(2 /* Update */, updateStatements)
|
18345 | ], INFERRED_TYPE, null, viewQueryFnName);
|
18346 | }
|
18347 | // Return a host binding function or null if one is not necessary.
|
18348 | function createHostBindingsFunction(hostBindingsMetadata, typeSourceSpan, bindingParser, constantPool, selector, name, definitionMap) {
|
18349 | const bindingContext = variable(CONTEXT_NAME);
|
18350 | const styleBuilder = new StylingBuilder(bindingContext);
|
18351 | const { styleAttr, classAttr } = hostBindingsMetadata.specialAttributes;
|
18352 | if (styleAttr !== undefined) {
|
18353 | styleBuilder.registerStyleAttr(styleAttr);
|
18354 | }
|
18355 | if (classAttr !== undefined) {
|
18356 | styleBuilder.registerClassAttr(classAttr);
|
18357 | }
|
18358 | const createStatements = [];
|
18359 | const updateStatements = [];
|
18360 | const hostBindingSourceSpan = typeSourceSpan;
|
18361 | const directiveSummary = metadataAsSummary(hostBindingsMetadata);
|
18362 | // Calculate host event bindings
|
18363 | const eventBindings = bindingParser.createDirectiveHostEventAsts(directiveSummary, hostBindingSourceSpan);
|
18364 | if (eventBindings && eventBindings.length) {
|
18365 | const listeners = createHostListeners(eventBindings, name);
|
18366 | createStatements.push(...listeners);
|
18367 | }
|
18368 | // Calculate the host property bindings
|
18369 | const bindings = bindingParser.createBoundHostProperties(directiveSummary, hostBindingSourceSpan);
|
18370 | const allOtherBindings = [];
|
18371 | // We need to calculate the total amount of binding slots required by
|
18372 | // all the instructions together before any value conversions happen.
|
18373 | // Value conversions may require additional slots for interpolation and
|
18374 | // bindings with pipes. These calculates happen after this block.
|
18375 | let totalHostVarsCount = 0;
|
18376 | bindings && bindings.forEach((binding) => {
|
18377 | const stylingInputWasSet = styleBuilder.registerInputBasedOnName(binding.name, binding.expression, hostBindingSourceSpan);
|
18378 | if (stylingInputWasSet) {
|
18379 | totalHostVarsCount += MIN_STYLING_BINDING_SLOTS_REQUIRED;
|
18380 | }
|
18381 | else {
|
18382 | allOtherBindings.push(binding);
|
18383 | totalHostVarsCount++;
|
18384 | }
|
18385 | });
|
18386 | let valueConverter;
|
18387 | const getValueConverter = () => {
|
18388 | if (!valueConverter) {
|
18389 | const hostVarsCountFn = (numSlots) => {
|
18390 | const originalVarsCount = totalHostVarsCount;
|
18391 | totalHostVarsCount += numSlots;
|
18392 | return originalVarsCount;
|
18393 | };
|
18394 | valueConverter = new ValueConverter(constantPool, () => error('Unexpected node'), // new nodes are illegal here
|
18395 | hostVarsCountFn, () => error('Unexpected pipe')); // pipes are illegal here
|
18396 | }
|
18397 | return valueConverter;
|
18398 | };
|
18399 | const propertyBindings = [];
|
18400 | const attributeBindings = [];
|
18401 | const syntheticHostBindings = [];
|
18402 | allOtherBindings.forEach((binding) => {
|
18403 | // resolve literal arrays and literal objects
|
18404 | const value = binding.expression.visit(getValueConverter());
|
18405 | const bindingExpr = bindingFn(bindingContext, value);
|
18406 | const { bindingName, instruction, isAttribute } = getBindingNameAndInstruction(binding);
|
18407 | const securityContexts = bindingParser.calcPossibleSecurityContexts(selector, bindingName, isAttribute)
|
18408 | .filter(context => context !== SecurityContext.NONE);
|
18409 | let sanitizerFn = null;
|
18410 | if (securityContexts.length) {
|
18411 | if (securityContexts.length === 2 &&
|
18412 | securityContexts.indexOf(SecurityContext.URL) > -1 &&
|
18413 | securityContexts.indexOf(SecurityContext.RESOURCE_URL) > -1) {
|
18414 | // Special case for some URL attributes (such as "src" and "href") that may be a part
|
18415 | // of different security contexts. In this case we use special santitization function and
|
18416 | // select the actual sanitizer at runtime based on a tag name that is provided while
|
18417 | // invoking sanitization function.
|
18418 | sanitizerFn = importExpr(Identifiers$1.sanitizeUrlOrResourceUrl);
|
18419 | }
|
18420 | else {
|
18421 | sanitizerFn = resolveSanitizationFn(securityContexts[0], isAttribute);
|
18422 | }
|
18423 | }
|
18424 | const instructionParams = [literal(bindingName), bindingExpr.currValExpr];
|
18425 | if (sanitizerFn) {
|
18426 | instructionParams.push(sanitizerFn);
|
18427 | }
|
18428 | updateStatements.push(...bindingExpr.stmts);
|
18429 | if (instruction === Identifiers$1.hostProperty) {
|
18430 | propertyBindings.push(instructionParams);
|
18431 | }
|
18432 | else if (instruction === Identifiers$1.attribute) {
|
18433 | attributeBindings.push(instructionParams);
|
18434 | }
|
18435 | else if (instruction === Identifiers$1.syntheticHostProperty) {
|
18436 | syntheticHostBindings.push(instructionParams);
|
18437 | }
|
18438 | else {
|
18439 | updateStatements.push(importExpr(instruction).callFn(instructionParams).toStmt());
|
18440 | }
|
18441 | });
|
18442 | if (propertyBindings.length > 0) {
|
18443 | updateStatements.push(chainedInstruction(Identifiers$1.hostProperty, propertyBindings).toStmt());
|
18444 | }
|
18445 | if (attributeBindings.length > 0) {
|
18446 | updateStatements.push(chainedInstruction(Identifiers$1.attribute, attributeBindings).toStmt());
|
18447 | }
|
18448 | if (syntheticHostBindings.length > 0) {
|
18449 | updateStatements.push(chainedInstruction(Identifiers$1.syntheticHostProperty, syntheticHostBindings).toStmt());
|
18450 | }
|
18451 | // since we're dealing with directives/components and both have hostBinding
|
18452 | // functions, we need to generate a special hostAttrs instruction that deals
|
18453 | // with both the assignment of styling as well as static attributes to the host
|
18454 | // element. The instruction below will instruct all initial styling (styling
|
18455 | // that is inside of a host binding within a directive/component) to be attached
|
18456 | // to the host element alongside any of the provided host attributes that were
|
18457 | // collected earlier.
|
18458 | const hostAttrs = convertAttributesToExpressions(hostBindingsMetadata.attributes);
|
18459 | styleBuilder.assignHostAttrs(hostAttrs, definitionMap);
|
18460 | if (styleBuilder.hasBindings) {
|
18461 | // finally each binding that was registered in the statement above will need to be added to
|
18462 | // the update block of a component/directive templateFn/hostBindingsFn so that the bindings
|
18463 | // are evaluated and updated for the element.
|
18464 | styleBuilder.buildUpdateLevelInstructions(getValueConverter()).forEach(instruction => {
|
18465 | if (instruction.calls.length > 0) {
|
18466 | const calls = [];
|
18467 | instruction.calls.forEach(call => {
|
18468 | // we subtract a value of `1` here because the binding slot was already allocated
|
18469 | // at the top of this method when all the input bindings were counted.
|
18470 | totalHostVarsCount +=
|
18471 | Math.max(call.allocateBindingSlots - MIN_STYLING_BINDING_SLOTS_REQUIRED, 0);
|
18472 | calls.push(convertStylingCall(call, bindingContext, bindingFn));
|
18473 | });
|
18474 | updateStatements.push(chainedInstruction(instruction.reference, calls).toStmt());
|
18475 | }
|
18476 | });
|
18477 | }
|
18478 | if (totalHostVarsCount) {
|
18479 | definitionMap.set('hostVars', literal(totalHostVarsCount));
|
18480 | }
|
18481 | if (createStatements.length > 0 || updateStatements.length > 0) {
|
18482 | const hostBindingsFnName = name ? `${name}_HostBindings` : null;
|
18483 | const statements = [];
|
18484 | if (createStatements.length > 0) {
|
18485 | statements.push(renderFlagCheckIfStmt(1 /* Create */, createStatements));
|
18486 | }
|
18487 | if (updateStatements.length > 0) {
|
18488 | statements.push(renderFlagCheckIfStmt(2 /* Update */, updateStatements));
|
18489 | }
|
18490 | return fn([new FnParam(RENDER_FLAGS, NUMBER_TYPE), new FnParam(CONTEXT_NAME, null)], statements, INFERRED_TYPE, null, hostBindingsFnName);
|
18491 | }
|
18492 | return null;
|
18493 | }
|
18494 | function bindingFn(implicit, value) {
|
18495 | return convertPropertyBinding(null, implicit, value, 'b', BindingForm.Expression, () => error('Unexpected interpolation'));
|
18496 | }
|
18497 | function convertStylingCall(call, bindingContext, bindingFn) {
|
18498 | return call.params(value => bindingFn(bindingContext, value).currValExpr);
|
18499 | }
|
18500 | function getBindingNameAndInstruction(binding) {
|
18501 | let bindingName = binding.name;
|
18502 | let instruction;
|
18503 | // Check to see if this is an attr binding or a property binding
|
18504 | const attrMatches = bindingName.match(ATTR_REGEX);
|
18505 | if (attrMatches) {
|
18506 | bindingName = attrMatches[1];
|
18507 | instruction = Identifiers$1.attribute;
|
18508 | }
|
18509 | else {
|
18510 | if (binding.isAnimation) {
|
18511 | bindingName = prepareSyntheticPropertyName(bindingName);
|
18512 | // host bindings that have a synthetic property (e.g. @foo) should always be rendered
|
18513 | // in the context of the component and not the parent. Therefore there is a special
|
18514 | // compatibility instruction available for this purpose.
|
18515 | instruction = Identifiers$1.syntheticHostProperty;
|
18516 | }
|
18517 | else {
|
18518 | instruction = Identifiers$1.hostProperty;
|
18519 | }
|
18520 | }
|
18521 | return { bindingName, instruction, isAttribute: !!attrMatches };
|
18522 | }
|
18523 | function createHostListeners(eventBindings, name) {
|
18524 | const listeners = [];
|
18525 | const syntheticListeners = [];
|
18526 | const instructions = [];
|
18527 | eventBindings.forEach(binding => {
|
18528 | let bindingName = binding.name && sanitizeIdentifier(binding.name);
|
18529 | const bindingFnName = binding.type === 1 /* Animation */ ?
|
18530 | prepareSyntheticListenerFunctionName(bindingName, binding.targetOrPhase) :
|
18531 | bindingName;
|
18532 | const handlerName = name && bindingName ? `${name}_${bindingFnName}_HostBindingHandler` : null;
|
18533 | const params = prepareEventListenerParameters(BoundEvent.fromParsedEvent(binding), handlerName);
|
18534 | if (binding.type == 1 /* Animation */) {
|
18535 | syntheticListeners.push(params);
|
18536 | }
|
18537 | else {
|
18538 | listeners.push(params);
|
18539 | }
|
18540 | });
|
18541 | if (syntheticListeners.length > 0) {
|
18542 | instructions.push(chainedInstruction(Identifiers$1.syntheticHostListener, syntheticListeners).toStmt());
|
18543 | }
|
18544 | if (listeners.length > 0) {
|
18545 | instructions.push(chainedInstruction(Identifiers$1.listener, listeners).toStmt());
|
18546 | }
|
18547 | return instructions;
|
18548 | }
|
18549 | function metadataAsSummary(meta) {
|
18550 | // clang-format off
|
18551 | return {
|
18552 | // This is used by the BindingParser, which only deals with listeners and properties. There's no
|
18553 | // need to pass attributes to it.
|
18554 | hostAttributes: {},
|
18555 | hostListeners: meta.listeners,
|
18556 | hostProperties: meta.properties,
|
18557 | };
|
18558 | // clang-format on
|
18559 | }
|
18560 | const HOST_REG_EXP$1 = /^(?:\[([^\]]+)\])|(?:\(([^\)]+)\))$/;
|
18561 | function parseHostBindings(host) {
|
18562 | const attributes = {};
|
18563 | const listeners = {};
|
18564 | const properties = {};
|
18565 | const specialAttributes = {};
|
18566 | for (const key of Object.keys(host)) {
|
18567 | const value = host[key];
|
18568 | const matches = key.match(HOST_REG_EXP$1);
|
18569 | if (matches === null) {
|
18570 | switch (key) {
|
18571 | case 'class':
|
18572 | if (typeof value !== 'string') {
|
18573 | // TODO(alxhub): make this a diagnostic.
|
18574 | throw new Error(`Class binding must be string`);
|
18575 | }
|
18576 | specialAttributes.classAttr = value;
|
18577 | break;
|
18578 | case 'style':
|
18579 | if (typeof value !== 'string') {
|
18580 | // TODO(alxhub): make this a diagnostic.
|
18581 | throw new Error(`Style binding must be string`);
|
18582 | }
|
18583 | specialAttributes.styleAttr = value;
|
18584 | break;
|
18585 | default:
|
18586 | if (typeof value === 'string') {
|
18587 | attributes[key] = literal(value);
|
18588 | }
|
18589 | else {
|
18590 | attributes[key] = value;
|
18591 | }
|
18592 | }
|
18593 | }
|
18594 | else if (matches[1 /* Binding */] != null) {
|
18595 | if (typeof value !== 'string') {
|
18596 | // TODO(alxhub): make this a diagnostic.
|
18597 | throw new Error(`Property binding must be string`);
|
18598 | }
|
18599 | // synthetic properties (the ones that have a `@` as a prefix)
|
18600 | // are still treated the same as regular properties. Therefore
|
18601 | // there is no point in storing them in a separate map.
|
18602 | properties[matches[1 /* Binding */]] = value;
|
18603 | }
|
18604 | else if (matches[2 /* Event */] != null) {
|
18605 | if (typeof value !== 'string') {
|
18606 | // TODO(alxhub): make this a diagnostic.
|
18607 | throw new Error(`Event binding must be string`);
|
18608 | }
|
18609 | listeners[matches[2 /* Event */]] = value;
|
18610 | }
|
18611 | }
|
18612 | return { attributes, listeners, properties, specialAttributes };
|
18613 | }
|
18614 | /**
|
18615 | * Verifies host bindings and returns the list of errors (if any). Empty array indicates that a
|
18616 | * given set of host bindings has no errors.
|
18617 | *
|
18618 | * @param bindings set of host bindings to verify.
|
18619 | * @param sourceSpan source span where host bindings were defined.
|
18620 | * @returns array of errors associated with a given set of host bindings.
|
18621 | */
|
18622 | function verifyHostBindings(bindings, sourceSpan) {
|
18623 | const summary = metadataAsSummary(bindings);
|
18624 | // TODO: abstract out host bindings verification logic and use it instead of
|
18625 | // creating events and properties ASTs to detect errors (FW-996)
|
18626 | const bindingParser = makeBindingParser();
|
18627 | bindingParser.createDirectiveHostEventAsts(summary, sourceSpan);
|
18628 | bindingParser.createBoundHostProperties(summary, sourceSpan);
|
18629 | return bindingParser.errors;
|
18630 | }
|
18631 | function compileStyles(styles, selector, hostSelector) {
|
18632 | const shadowCss = new ShadowCss();
|
18633 | return styles.map(style => {
|
18634 | return shadowCss.shimCssText(style, selector, hostSelector);
|
18635 | });
|
18636 | }
|
18637 |
|
18638 | /**
|
18639 | * @license
|
18640 | * Copyright Google LLC All Rights Reserved.
|
18641 | *
|
18642 | * Use of this source code is governed by an MIT-style license that can be
|
18643 | * found in the LICENSE file at https://angular.io/license
|
18644 | */
|
18645 | /**
|
18646 | * An interface for retrieving documents by URL that the compiler uses
|
18647 | * to load templates.
|
18648 | */
|
18649 | class ResourceLoader {
|
18650 | get(url) {
|
18651 | return '';
|
18652 | }
|
18653 | }
|
18654 |
|
18655 | /**
|
18656 | * @license
|
18657 | * Copyright Google LLC All Rights Reserved.
|
18658 | *
|
18659 | * Use of this source code is governed by an MIT-style license that can be
|
18660 | * found in the LICENSE file at https://angular.io/license
|
18661 | */
|
18662 | class CompilerFacadeImpl {
|
18663 | constructor(jitEvaluator = new JitEvaluator()) {
|
18664 | this.jitEvaluator = jitEvaluator;
|
18665 | this.R3ResolvedDependencyType = R3ResolvedDependencyType;
|
18666 | this.R3FactoryTarget = R3FactoryTarget;
|
18667 | this.ResourceLoader = ResourceLoader;
|
18668 | this.elementSchemaRegistry = new DomElementSchemaRegistry();
|
18669 | }
|
18670 | compilePipe(angularCoreEnv, sourceMapUrl, facade) {
|
18671 | const metadata = {
|
18672 | name: facade.name,
|
18673 | type: wrapReference(facade.type),
|
18674 | internalType: new WrappedNodeExpr(facade.type),
|
18675 | typeArgumentCount: facade.typeArgumentCount,
|
18676 | deps: convertR3DependencyMetadataArray(facade.deps),
|
18677 | pipeName: facade.pipeName,
|
18678 | pure: facade.pure,
|
18679 | };
|
18680 | const res = compilePipeFromMetadata(metadata);
|
18681 | return this.jitExpression(res.expression, angularCoreEnv, sourceMapUrl, []);
|
18682 | }
|
18683 | compileInjectable(angularCoreEnv, sourceMapUrl, facade) {
|
18684 | const { expression, statements } = compileInjectable({
|
18685 | name: facade.name,
|
18686 | type: wrapReference(facade.type),
|
18687 | internalType: new WrappedNodeExpr(facade.type),
|
18688 | typeArgumentCount: facade.typeArgumentCount,
|
18689 | providedIn: computeProvidedIn(facade.providedIn),
|
18690 | useClass: wrapExpression(facade, USE_CLASS),
|
18691 | useFactory: wrapExpression(facade, USE_FACTORY),
|
18692 | useValue: wrapExpression(facade, USE_VALUE),
|
18693 | useExisting: wrapExpression(facade, USE_EXISTING),
|
18694 | userDeps: convertR3DependencyMetadataArray(facade.userDeps) || undefined,
|
18695 | });
|
18696 | return this.jitExpression(expression, angularCoreEnv, sourceMapUrl, statements);
|
18697 | }
|
18698 | compileInjector(angularCoreEnv, sourceMapUrl, facade) {
|
18699 | const meta = {
|
18700 | name: facade.name,
|
18701 | type: wrapReference(facade.type),
|
18702 | internalType: new WrappedNodeExpr(facade.type),
|
18703 | deps: convertR3DependencyMetadataArray(facade.deps),
|
18704 | providers: new WrappedNodeExpr(facade.providers),
|
18705 | imports: facade.imports.map(i => new WrappedNodeExpr(i)),
|
18706 | };
|
18707 | const res = compileInjector(meta);
|
18708 | return this.jitExpression(res.expression, angularCoreEnv, sourceMapUrl, res.statements);
|
18709 | }
|
18710 | compileNgModule(angularCoreEnv, sourceMapUrl, facade) {
|
18711 | const meta = {
|
18712 | type: wrapReference(facade.type),
|
18713 | internalType: new WrappedNodeExpr(facade.type),
|
18714 | adjacentType: new WrappedNodeExpr(facade.type),
|
18715 | bootstrap: facade.bootstrap.map(wrapReference),
|
18716 | declarations: facade.declarations.map(wrapReference),
|
18717 | imports: facade.imports.map(wrapReference),
|
18718 | exports: facade.exports.map(wrapReference),
|
18719 | emitInline: true,
|
18720 | containsForwardDecls: false,
|
18721 | schemas: facade.schemas ? facade.schemas.map(wrapReference) : null,
|
18722 | id: facade.id ? new WrappedNodeExpr(facade.id) : null,
|
18723 | };
|
18724 | const res = compileNgModule(meta);
|
18725 | return this.jitExpression(res.expression, angularCoreEnv, sourceMapUrl, []);
|
18726 | }
|
18727 | compileDirective(angularCoreEnv, sourceMapUrl, facade) {
|
18728 | const meta = convertDirectiveFacadeToMetadata(facade);
|
18729 | return this.compileDirectiveFromMeta(angularCoreEnv, sourceMapUrl, meta);
|
18730 | }
|
18731 | compileDirectiveDeclaration(angularCoreEnv, sourceMapUrl, declaration) {
|
18732 | const typeSourceSpan = this.createParseSourceSpan('Directive', declaration.type.name, sourceMapUrl);
|
18733 | const meta = convertDeclareDirectiveFacadeToMetadata(declaration, typeSourceSpan);
|
18734 | return this.compileDirectiveFromMeta(angularCoreEnv, sourceMapUrl, meta);
|
18735 | }
|
18736 | compileDirectiveFromMeta(angularCoreEnv, sourceMapUrl, meta) {
|
18737 | const constantPool = new ConstantPool();
|
18738 | const bindingParser = makeBindingParser();
|
18739 | const res = compileDirectiveFromMetadata(meta, constantPool, bindingParser);
|
18740 | return this.jitExpression(res.expression, angularCoreEnv, sourceMapUrl, constantPool.statements);
|
18741 | }
|
18742 | compileComponent(angularCoreEnv, sourceMapUrl, facade) {
|
18743 | // Parse the template and check for errors.
|
18744 | const { template, interpolation } = parseJitTemplate(facade.template, facade.name, sourceMapUrl, facade.preserveWhitespaces, facade.interpolation);
|
18745 | // Compile the component metadata, including template, into an expression.
|
18746 | 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) :
|
18747 | null, relativeContextFilePath: '', i18nUseExternalIds: true });
|
18748 | const jitExpressionSourceMap = `ng:///${facade.name}.js`;
|
18749 | return this.compileComponentFromMeta(angularCoreEnv, jitExpressionSourceMap, meta);
|
18750 | }
|
18751 | compileComponentDeclaration(angularCoreEnv, sourceMapUrl, declaration) {
|
18752 | const typeSourceSpan = this.createParseSourceSpan('Component', declaration.type.name, sourceMapUrl);
|
18753 | const meta = convertDeclareComponentFacadeToMetadata(declaration, typeSourceSpan, sourceMapUrl);
|
18754 | return this.compileComponentFromMeta(angularCoreEnv, sourceMapUrl, meta);
|
18755 | }
|
18756 | compileComponentFromMeta(angularCoreEnv, sourceMapUrl, meta) {
|
18757 | const constantPool = new ConstantPool();
|
18758 | const bindingParser = makeBindingParser(meta.interpolation);
|
18759 | const res = compileComponentFromMetadata(meta, constantPool, bindingParser);
|
18760 | return this.jitExpression(res.expression, angularCoreEnv, sourceMapUrl, constantPool.statements);
|
18761 | }
|
18762 | compileFactory(angularCoreEnv, sourceMapUrl, meta) {
|
18763 | const factoryRes = compileFactoryFunction({
|
18764 | name: meta.name,
|
18765 | type: wrapReference(meta.type),
|
18766 | internalType: new WrappedNodeExpr(meta.type),
|
18767 | typeArgumentCount: meta.typeArgumentCount,
|
18768 | deps: convertR3DependencyMetadataArray(meta.deps),
|
18769 | injectFn: meta.injectFn === 'directiveInject' ? Identifiers.directiveInject :
|
18770 | Identifiers.inject,
|
18771 | target: meta.target,
|
18772 | });
|
18773 | return this.jitExpression(factoryRes.factory, angularCoreEnv, sourceMapUrl, factoryRes.statements);
|
18774 | }
|
18775 | createParseSourceSpan(kind, typeName, sourceUrl) {
|
18776 | return r3JitTypeSourceSpan(kind, typeName, sourceUrl);
|
18777 | }
|
18778 | /**
|
18779 | * JIT compiles an expression and returns the result of executing that expression.
|
18780 | *
|
18781 | * @param def the definition which will be compiled and executed to get the value to patch
|
18782 | * @param context an object map of @angular/core symbol names to symbols which will be available
|
18783 | * in the context of the compiled expression
|
18784 | * @param sourceUrl a URL to use for the source map of the compiled expression
|
18785 | * @param preStatements a collection of statements that should be evaluated before the expression.
|
18786 | */
|
18787 | jitExpression(def, context, sourceUrl, preStatements) {
|
18788 | // The ConstantPool may contain Statements which declare variables used in the final expression.
|
18789 | // Therefore, its statements need to precede the actual JIT operation. The final statement is a
|
18790 | // declaration of $def which is set to the expression being compiled.
|
18791 | const statements = [
|
18792 | ...preStatements,
|
18793 | new DeclareVarStmt('$def', def, undefined, [StmtModifier.Exported]),
|
18794 | ];
|
18795 | const res = this.jitEvaluator.evaluateStatements(sourceUrl, statements, new R3JitReflector(context), /* enableSourceMaps */ true);
|
18796 | return res['$def'];
|
18797 | }
|
18798 | }
|
18799 | const USE_CLASS = Object.keys({ useClass: null })[0];
|
18800 | const USE_FACTORY = Object.keys({ useFactory: null })[0];
|
18801 | const USE_VALUE = Object.keys({ useValue: null })[0];
|
18802 | const USE_EXISTING = Object.keys({ useExisting: null })[0];
|
18803 | const wrapReference = function (value) {
|
18804 | const wrapped = new WrappedNodeExpr(value);
|
18805 | return { value: wrapped, type: wrapped };
|
18806 | };
|
18807 | function convertToR3QueryMetadata(facade) {
|
18808 | return Object.assign(Object.assign({}, facade), { predicate: Array.isArray(facade.predicate) ? facade.predicate :
|
18809 | new WrappedNodeExpr(facade.predicate), read: facade.read ? new WrappedNodeExpr(facade.read) : null, static: facade.static, emitDistinctChangesOnly: facade.emitDistinctChangesOnly });
|
18810 | }
|
18811 | function convertQueryDeclarationToMetadata(declaration) {
|
18812 | var _a, _b, _c, _d;
|
18813 | return {
|
18814 | propertyName: declaration.propertyName,
|
18815 | first: (_a = declaration.first) !== null && _a !== void 0 ? _a : false,
|
18816 | predicate: Array.isArray(declaration.predicate) ? declaration.predicate :
|
18817 | new WrappedNodeExpr(declaration.predicate),
|
18818 | descendants: (_b = declaration.descendants) !== null && _b !== void 0 ? _b : false,
|
18819 | read: declaration.read ? new WrappedNodeExpr(declaration.read) : null,
|
18820 | static: (_c = declaration.static) !== null && _c !== void 0 ? _c : false,
|
18821 | emitDistinctChangesOnly: (_d = declaration.emitDistinctChangesOnly) !== null && _d !== void 0 ? _d : true,
|
18822 | };
|
18823 | }
|
18824 | function convertDirectiveFacadeToMetadata(facade) {
|
18825 | const inputsFromMetadata = parseInputOutputs(facade.inputs || []);
|
18826 | const outputsFromMetadata = parseInputOutputs(facade.outputs || []);
|
18827 | const propMetadata = facade.propMetadata;
|
18828 | const inputsFromType = {};
|
18829 | const outputsFromType = {};
|
18830 | for (const field in propMetadata) {
|
18831 | if (propMetadata.hasOwnProperty(field)) {
|
18832 | propMetadata[field].forEach(ann => {
|
18833 | if (isInput(ann)) {
|
18834 | inputsFromType[field] =
|
18835 | ann.bindingPropertyName ? [ann.bindingPropertyName, field] : field;
|
18836 | }
|
18837 | else if (isOutput(ann)) {
|
18838 | outputsFromType[field] = ann.bindingPropertyName || field;
|
18839 | }
|
18840 | });
|
18841 | }
|
18842 | }
|
18843 | 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 });
|
18844 | }
|
18845 | function convertDeclareDirectiveFacadeToMetadata(declaration, typeSourceSpan) {
|
18846 | var _a, _b, _c, _d, _e, _f, _g, _h;
|
18847 | return {
|
18848 | name: declaration.type.name,
|
18849 | type: wrapReference(declaration.type),
|
18850 | typeSourceSpan,
|
18851 | internalType: new WrappedNodeExpr(declaration.type),
|
18852 | selector: (_a = declaration.selector) !== null && _a !== void 0 ? _a : null,
|
18853 | inputs: (_b = declaration.inputs) !== null && _b !== void 0 ? _b : {},
|
18854 | outputs: (_c = declaration.outputs) !== null && _c !== void 0 ? _c : {},
|
18855 | host: convertHostDeclarationToMetadata(declaration.host),
|
18856 | queries: ((_d = declaration.queries) !== null && _d !== void 0 ? _d : []).map(convertQueryDeclarationToMetadata),
|
18857 | viewQueries: ((_e = declaration.viewQueries) !== null && _e !== void 0 ? _e : []).map(convertQueryDeclarationToMetadata),
|
18858 | providers: declaration.providers !== undefined ? new WrappedNodeExpr(declaration.providers) :
|
18859 | null,
|
18860 | exportAs: (_f = declaration.exportAs) !== null && _f !== void 0 ? _f : null,
|
18861 | usesInheritance: (_g = declaration.usesInheritance) !== null && _g !== void 0 ? _g : false,
|
18862 | lifecycle: { usesOnChanges: (_h = declaration.usesOnChanges) !== null && _h !== void 0 ? _h : false },
|
18863 | deps: null,
|
18864 | typeArgumentCount: 0,
|
18865 | fullInheritance: false,
|
18866 | };
|
18867 | }
|
18868 | function convertHostDeclarationToMetadata(host = {}) {
|
18869 | var _a, _b, _c;
|
18870 | return {
|
18871 | attributes: convertOpaqueValuesToExpressions((_a = host.attributes) !== null && _a !== void 0 ? _a : {}),
|
18872 | listeners: (_b = host.listeners) !== null && _b !== void 0 ? _b : {},
|
18873 | properties: (_c = host.properties) !== null && _c !== void 0 ? _c : {},
|
18874 | specialAttributes: {
|
18875 | classAttr: host.classAttribute,
|
18876 | styleAttr: host.styleAttribute,
|
18877 | },
|
18878 | };
|
18879 | }
|
18880 | function convertOpaqueValuesToExpressions(obj) {
|
18881 | const result = {};
|
18882 | for (const key of Object.keys(obj)) {
|
18883 | result[key] = new WrappedNodeExpr(obj[key]);
|
18884 | }
|
18885 | return result;
|
18886 | }
|
18887 | function convertDeclareComponentFacadeToMetadata(declaration, typeSourceSpan, sourceMapUrl) {
|
18888 | var _a, _b, _c, _d, _e;
|
18889 | const { template, interpolation } = parseJitTemplate(declaration.template, declaration.type.name, sourceMapUrl, (_a = declaration.preserveWhitespaces) !== null && _a !== void 0 ? _a : false, declaration.interpolation);
|
18890 | 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 ?
|
18891 | new WrappedNodeExpr(declaration.viewProviders) :
|
18892 | null, animations: declaration.animations !== undefined ? new WrappedNodeExpr(declaration.animations) :
|
18893 | 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 });
|
18894 | }
|
18895 | function convertUsedDirectiveDeclarationToMetadata(declaration) {
|
18896 | var _a, _b, _c;
|
18897 | return {
|
18898 | selector: declaration.selector,
|
18899 | type: new WrappedNodeExpr(declaration.type),
|
18900 | inputs: (_a = declaration.inputs) !== null && _a !== void 0 ? _a : [],
|
18901 | outputs: (_b = declaration.outputs) !== null && _b !== void 0 ? _b : [],
|
18902 | exportAs: (_c = declaration.exportAs) !== null && _c !== void 0 ? _c : null,
|
18903 | };
|
18904 | }
|
18905 | function convertUsedPipesToMetadata(declaredPipes) {
|
18906 | const pipes = new Map();
|
18907 | if (declaredPipes === undefined) {
|
18908 | return pipes;
|
18909 | }
|
18910 | for (const pipeName of Object.keys(declaredPipes)) {
|
18911 | const pipeType = declaredPipes[pipeName];
|
18912 | pipes.set(pipeName, new WrappedNodeExpr(pipeType));
|
18913 | }
|
18914 | return pipes;
|
18915 | }
|
18916 | function parseJitTemplate(template, typeName, sourceMapUrl, preserveWhitespaces, interpolation) {
|
18917 | const interpolationConfig = interpolation ? InterpolationConfig.fromArray(interpolation) : DEFAULT_INTERPOLATION_CONFIG;
|
18918 | // Parse the template and check for errors.
|
18919 | const parsed = parseTemplate(template, sourceMapUrl, { preserveWhitespaces: preserveWhitespaces, interpolationConfig });
|
18920 | if (parsed.errors !== null) {
|
18921 | const errors = parsed.errors.map(err => err.toString()).join(', ');
|
18922 | throw new Error(`Errors during JIT compilation of template for ${typeName}: ${errors}`);
|
18923 | }
|
18924 | return { template: parsed, interpolation: interpolationConfig };
|
18925 | }
|
18926 | function wrapExpression(obj, property) {
|
18927 | if (obj.hasOwnProperty(property)) {
|
18928 | return new WrappedNodeExpr(obj[property]);
|
18929 | }
|
18930 | else {
|
18931 | return undefined;
|
18932 | }
|
18933 | }
|
18934 | function computeProvidedIn(providedIn) {
|
18935 | if (providedIn == null || typeof providedIn === 'string') {
|
18936 | return new LiteralExpr(providedIn);
|
18937 | }
|
18938 | else {
|
18939 | return new WrappedNodeExpr(providedIn);
|
18940 | }
|
18941 | }
|
18942 | function convertR3DependencyMetadata(facade) {
|
18943 | let tokenExpr;
|
18944 | if (facade.token === null) {
|
18945 | tokenExpr = new LiteralExpr(null);
|
18946 | }
|
18947 | else if (facade.resolved === R3ResolvedDependencyType.Attribute) {
|
18948 | tokenExpr = new LiteralExpr(facade.token);
|
18949 | }
|
18950 | else {
|
18951 | tokenExpr = new WrappedNodeExpr(facade.token);
|
18952 | }
|
18953 | return {
|
18954 | token: tokenExpr,
|
18955 | attribute: null,
|
18956 | resolved: facade.resolved,
|
18957 | host: facade.host,
|
18958 | optional: facade.optional,
|
18959 | self: facade.self,
|
18960 | skipSelf: facade.skipSelf,
|
18961 | };
|
18962 | }
|
18963 | function convertR3DependencyMetadataArray(facades) {
|
18964 | return facades == null ? null : facades.map(convertR3DependencyMetadata);
|
18965 | }
|
18966 | function extractHostBindings(propMetadata, sourceSpan, host) {
|
18967 | // First parse the declarations from the metadata.
|
18968 | const bindings = parseHostBindings(host || {});
|
18969 | // After that check host bindings for errors
|
18970 | const errors = verifyHostBindings(bindings, sourceSpan);
|
18971 | if (errors.length) {
|
18972 | throw new Error(errors.map((error) => error.msg).join('\n'));
|
18973 | }
|
18974 | // Next, loop over the properties of the object, looking for @HostBinding and @HostListener.
|
18975 | for (const field in propMetadata) {
|
18976 | if (propMetadata.hasOwnProperty(field)) {
|
18977 | propMetadata[field].forEach(ann => {
|
18978 | if (isHostBinding(ann)) {
|
18979 | // Since this is a decorator, we know that the value is a class member. Always access it
|
18980 | // through `this` so that further down the line it can't be confused for a literal value
|
18981 | // (e.g. if there's a property called `true`).
|
18982 | bindings.properties[ann.hostPropertyName || field] =
|
18983 | getSafePropertyAccessString('this', field);
|
18984 | }
|
18985 | else if (isHostListener(ann)) {
|
18986 | bindings.listeners[ann.eventName || field] = `${field}(${(ann.args || []).join(',')})`;
|
18987 | }
|
18988 | });
|
18989 | }
|
18990 | }
|
18991 | return bindings;
|
18992 | }
|
18993 | function isHostBinding(value) {
|
18994 | return value.ngMetadataName === 'HostBinding';
|
18995 | }
|
18996 | function isHostListener(value) {
|
18997 | return value.ngMetadataName === 'HostListener';
|
18998 | }
|
18999 | function isInput(value) {
|
19000 | return value.ngMetadataName === 'Input';
|
19001 | }
|
19002 | function isOutput(value) {
|
19003 | return value.ngMetadataName === 'Output';
|
19004 | }
|
19005 | function parseInputOutputs(values) {
|
19006 | return values.reduce((map, value) => {
|
19007 | const [field, property] = value.split(',').map(piece => piece.trim());
|
19008 | map[field] = property || field;
|
19009 | return map;
|
19010 | }, {});
|
19011 | }
|
19012 | function publishFacade(global) {
|
19013 | const ng = global.ng || (global.ng = {});
|
19014 | ng.ɵcompilerFacade = new CompilerFacadeImpl();
|
19015 | }
|
19016 |
|
19017 | /**
|
19018 | * @license
|
19019 | * Copyright Google LLC All Rights Reserved.
|
19020 | *
|
19021 | * Use of this source code is governed by an MIT-style license that can be
|
19022 | * found in the LICENSE file at https://angular.io/license
|
19023 | */
|
19024 | const VERSION$1 = new Version('11.2.0');
|
19025 |
|
19026 | /**
|
19027 | * @license
|
19028 | * Copyright Google LLC All Rights Reserved.
|
19029 | *
|
19030 | * Use of this source code is governed by an MIT-style license that can be
|
19031 | * found in the LICENSE file at https://angular.io/license
|
19032 | */
|
19033 | class CompilerConfig {
|
19034 | constructor({ defaultEncapsulation = ViewEncapsulation.Emulated, useJit = true, jitDevMode = false, missingTranslation = null, preserveWhitespaces, strictInjectionParameters } = {}) {
|
19035 | this.defaultEncapsulation = defaultEncapsulation;
|
19036 | this.useJit = !!useJit;
|
19037 | this.jitDevMode = !!jitDevMode;
|
19038 | this.missingTranslation = missingTranslation;
|
19039 | this.preserveWhitespaces = preserveWhitespacesDefault(noUndefined(preserveWhitespaces));
|
19040 | this.strictInjectionParameters = strictInjectionParameters === true;
|
19041 | }
|
19042 | }
|
19043 | function preserveWhitespacesDefault(preserveWhitespacesOption, defaultSetting = false) {
|
19044 | return preserveWhitespacesOption === null ? defaultSetting : preserveWhitespacesOption;
|
19045 | }
|
19046 |
|
19047 | /**
|
19048 | * @license
|
19049 | * Copyright Google LLC All Rights Reserved.
|
19050 | *
|
19051 | * Use of this source code is governed by an MIT-style license that can be
|
19052 | * found in the LICENSE file at https://angular.io/license
|
19053 | */
|
19054 | class DirectiveNormalizer {
|
19055 | constructor(_resourceLoader, _urlResolver, _htmlParser, _config) {
|
19056 | this._resourceLoader = _resourceLoader;
|
19057 | this._urlResolver = _urlResolver;
|
19058 | this._htmlParser = _htmlParser;
|
19059 | this._config = _config;
|
19060 | this._resourceLoaderCache = new Map();
|
19061 | }
|
19062 | clearCache() {
|
19063 | this._resourceLoaderCache.clear();
|
19064 | }
|
19065 | clearCacheFor(normalizedDirective) {
|
19066 | if (!normalizedDirective.isComponent) {
|
19067 | return;
|
19068 | }
|
19069 | const template = normalizedDirective.template;
|
19070 | this._resourceLoaderCache.delete(template.templateUrl);
|
19071 | template.externalStylesheets.forEach((stylesheet) => {
|
19072 | this._resourceLoaderCache.delete(stylesheet.moduleUrl);
|
19073 | });
|
19074 | }
|
19075 | _fetch(url) {
|
19076 | let result = this._resourceLoaderCache.get(url);
|
19077 | if (!result) {
|
19078 | result = this._resourceLoader.get(url);
|
19079 | this._resourceLoaderCache.set(url, result);
|
19080 | }
|
19081 | return result;
|
19082 | }
|
19083 | normalizeTemplate(prenormData) {
|
19084 | if (isDefined(prenormData.template)) {
|
19085 | if (isDefined(prenormData.templateUrl)) {
|
19086 | throw syntaxError(`'${stringify(prenormData
|
19087 | .componentType)}' component cannot define both template and templateUrl`);
|
19088 | }
|
19089 | if (typeof prenormData.template !== 'string') {
|
19090 | throw syntaxError(`The template specified for component ${stringify(prenormData.componentType)} is not a string`);
|
19091 | }
|
19092 | }
|
19093 | else if (isDefined(prenormData.templateUrl)) {
|
19094 | if (typeof prenormData.templateUrl !== 'string') {
|
19095 | throw syntaxError(`The templateUrl specified for component ${stringify(prenormData.componentType)} is not a string`);
|
19096 | }
|
19097 | }
|
19098 | else {
|
19099 | throw syntaxError(`No template specified for component ${stringify(prenormData.componentType)}`);
|
19100 | }
|
19101 | if (isDefined(prenormData.preserveWhitespaces) &&
|
19102 | typeof prenormData.preserveWhitespaces !== 'boolean') {
|
19103 | throw syntaxError(`The preserveWhitespaces option for component ${stringify(prenormData.componentType)} must be a boolean`);
|
19104 | }
|
19105 | return SyncAsync.then(this._preParseTemplate(prenormData), (preparsedTemplate) => this._normalizeTemplateMetadata(prenormData, preparsedTemplate));
|
19106 | }
|
19107 | _preParseTemplate(prenomData) {
|
19108 | let template;
|
19109 | let templateUrl;
|
19110 | if (prenomData.template != null) {
|
19111 | template = prenomData.template;
|
19112 | templateUrl = prenomData.moduleUrl;
|
19113 | }
|
19114 | else {
|
19115 | templateUrl = this._urlResolver.resolve(prenomData.moduleUrl, prenomData.templateUrl);
|
19116 | template = this._fetch(templateUrl);
|
19117 | }
|
19118 | return SyncAsync.then(template, (template) => this._preparseLoadedTemplate(prenomData, template, templateUrl));
|
19119 | }
|
19120 | _preparseLoadedTemplate(prenormData, template, templateAbsUrl) {
|
19121 | const isInline = !!prenormData.template;
|
19122 | const interpolationConfig = InterpolationConfig.fromArray(prenormData.interpolation);
|
19123 | const templateUrl = templateSourceUrl({ reference: prenormData.ngModuleType }, { type: { reference: prenormData.componentType } }, { isInline, templateUrl: templateAbsUrl });
|
19124 | const rootNodesAndErrors = this._htmlParser.parse(template, templateUrl, { tokenizeExpansionForms: true, interpolationConfig });
|
19125 | if (rootNodesAndErrors.errors.length > 0) {
|
19126 | const errorString = rootNodesAndErrors.errors.join('\n');
|
19127 | throw syntaxError(`Template parse errors:\n${errorString}`);
|
19128 | }
|
19129 | const templateMetadataStyles = this._normalizeStylesheet(new CompileStylesheetMetadata({ styles: prenormData.styles, moduleUrl: prenormData.moduleUrl }));
|
19130 | const visitor = new TemplatePreparseVisitor();
|
19131 | visitAll$1(visitor, rootNodesAndErrors.rootNodes);
|
19132 | const templateStyles = this._normalizeStylesheet(new CompileStylesheetMetadata({ styles: visitor.styles, styleUrls: visitor.styleUrls, moduleUrl: templateAbsUrl }));
|
19133 | const styles = templateMetadataStyles.styles.concat(templateStyles.styles);
|
19134 | const inlineStyleUrls = templateMetadataStyles.styleUrls.concat(templateStyles.styleUrls);
|
19135 | const styleUrls = this
|
19136 | ._normalizeStylesheet(new CompileStylesheetMetadata({ styleUrls: prenormData.styleUrls, moduleUrl: prenormData.moduleUrl }))
|
19137 | .styleUrls;
|
19138 | return {
|
19139 | template,
|
19140 | templateUrl: templateAbsUrl,
|
19141 | isInline,
|
19142 | htmlAst: rootNodesAndErrors,
|
19143 | styles,
|
19144 | inlineStyleUrls,
|
19145 | styleUrls,
|
19146 | ngContentSelectors: visitor.ngContentSelectors,
|
19147 | };
|
19148 | }
|
19149 | _normalizeTemplateMetadata(prenormData, preparsedTemplate) {
|
19150 | return SyncAsync.then(this._loadMissingExternalStylesheets(preparsedTemplate.styleUrls.concat(preparsedTemplate.inlineStyleUrls)), (externalStylesheets) => this._normalizeLoadedTemplateMetadata(prenormData, preparsedTemplate, externalStylesheets));
|
19151 | }
|
19152 | _normalizeLoadedTemplateMetadata(prenormData, preparsedTemplate, stylesheets) {
|
19153 | // Algorithm:
|
19154 | // - produce exactly 1 entry per original styleUrl in
|
19155 | // CompileTemplateMetadata.externalStylesheets with all styles inlined
|
19156 | // - inline all styles that are referenced by the template into CompileTemplateMetadata.styles.
|
19157 | // Reason: be able to determine how many stylesheets there are even without loading
|
19158 | // the template nor the stylesheets, so we can create a stub for TypeScript always synchronously
|
19159 | // (as resource loading may be async)
|
19160 | const styles = [...preparsedTemplate.styles];
|
19161 | this._inlineStyles(preparsedTemplate.inlineStyleUrls, stylesheets, styles);
|
19162 | const styleUrls = preparsedTemplate.styleUrls;
|
19163 | const externalStylesheets = styleUrls.map(styleUrl => {
|
19164 | const stylesheet = stylesheets.get(styleUrl);
|
19165 | const styles = [...stylesheet.styles];
|
19166 | this._inlineStyles(stylesheet.styleUrls, stylesheets, styles);
|
19167 | return new CompileStylesheetMetadata({ moduleUrl: styleUrl, styles: styles });
|
19168 | });
|
19169 | let encapsulation = prenormData.encapsulation;
|
19170 | if (encapsulation == null) {
|
19171 | encapsulation = this._config.defaultEncapsulation;
|
19172 | }
|
19173 | if (encapsulation === ViewEncapsulation.Emulated && styles.length === 0 &&
|
19174 | styleUrls.length === 0) {
|
19175 | encapsulation = ViewEncapsulation.None;
|
19176 | }
|
19177 | return new CompileTemplateMetadata({
|
19178 | encapsulation,
|
19179 | template: preparsedTemplate.template,
|
19180 | templateUrl: preparsedTemplate.templateUrl,
|
19181 | htmlAst: preparsedTemplate.htmlAst,
|
19182 | styles,
|
19183 | styleUrls,
|
19184 | ngContentSelectors: preparsedTemplate.ngContentSelectors,
|
19185 | animations: prenormData.animations,
|
19186 | interpolation: prenormData.interpolation,
|
19187 | isInline: preparsedTemplate.isInline,
|
19188 | externalStylesheets,
|
19189 | preserveWhitespaces: preserveWhitespacesDefault(prenormData.preserveWhitespaces, this._config.preserveWhitespaces),
|
19190 | });
|
19191 | }
|
19192 | _inlineStyles(styleUrls, stylesheets, targetStyles) {
|
19193 | styleUrls.forEach(styleUrl => {
|
19194 | const stylesheet = stylesheets.get(styleUrl);
|
19195 | stylesheet.styles.forEach(style => targetStyles.push(style));
|
19196 | this._inlineStyles(stylesheet.styleUrls, stylesheets, targetStyles);
|
19197 | });
|
19198 | }
|
19199 | _loadMissingExternalStylesheets(styleUrls, loadedStylesheets = new Map()) {
|
19200 | return SyncAsync.then(SyncAsync.all(styleUrls.filter((styleUrl) => !loadedStylesheets.has(styleUrl))
|
19201 | .map(styleUrl => SyncAsync.then(this._fetch(styleUrl), (loadedStyle) => {
|
19202 | const stylesheet = this._normalizeStylesheet(new CompileStylesheetMetadata({ styles: [loadedStyle], moduleUrl: styleUrl }));
|
19203 | loadedStylesheets.set(styleUrl, stylesheet);
|
19204 | return this._loadMissingExternalStylesheets(stylesheet.styleUrls, loadedStylesheets);
|
19205 | }))), (_) => loadedStylesheets);
|
19206 | }
|
19207 | _normalizeStylesheet(stylesheet) {
|
19208 | const moduleUrl = stylesheet.moduleUrl;
|
19209 | const allStyleUrls = stylesheet.styleUrls.filter(isStyleUrlResolvable)
|
19210 | .map(url => this._urlResolver.resolve(moduleUrl, url));
|
19211 | const allStyles = stylesheet.styles.map(style => {
|
19212 | const styleWithImports = extractStyleUrls(this._urlResolver, moduleUrl, style);
|
19213 | allStyleUrls.push(...styleWithImports.styleUrls);
|
19214 | return styleWithImports.style;
|
19215 | });
|
19216 | return new CompileStylesheetMetadata({ styles: allStyles, styleUrls: allStyleUrls, moduleUrl: moduleUrl });
|
19217 | }
|
19218 | }
|
19219 | class TemplatePreparseVisitor {
|
19220 | constructor() {
|
19221 | this.ngContentSelectors = [];
|
19222 | this.styles = [];
|
19223 | this.styleUrls = [];
|
19224 | this.ngNonBindableStackCount = 0;
|
19225 | }
|
19226 | visitElement(ast, context) {
|
19227 | const preparsedElement = preparseElement(ast);
|
19228 | switch (preparsedElement.type) {
|
19229 | case PreparsedElementType.NG_CONTENT:
|
19230 | if (this.ngNonBindableStackCount === 0) {
|
19231 | this.ngContentSelectors.push(preparsedElement.selectAttr);
|
19232 | }
|
19233 | break;
|
19234 | case PreparsedElementType.STYLE:
|
19235 | let textContent = '';
|
19236 | ast.children.forEach(child => {
|
19237 | if (child instanceof Text$2) {
|
19238 | textContent += child.value;
|
19239 | }
|
19240 | });
|
19241 | this.styles.push(textContent);
|
19242 | break;
|
19243 | case PreparsedElementType.STYLESHEET:
|
19244 | this.styleUrls.push(preparsedElement.hrefAttr);
|
19245 | break;
|
19246 | }
|
19247 | if (preparsedElement.nonBindable) {
|
19248 | this.ngNonBindableStackCount++;
|
19249 | }
|
19250 | visitAll$1(this, ast.children);
|
19251 | if (preparsedElement.nonBindable) {
|
19252 | this.ngNonBindableStackCount--;
|
19253 | }
|
19254 | return null;
|
19255 | }
|
19256 | visitExpansion(ast, context) {
|
19257 | visitAll$1(this, ast.cases);
|
19258 | }
|
19259 | visitExpansionCase(ast, context) {
|
19260 | visitAll$1(this, ast.expression);
|
19261 | }
|
19262 | visitComment(ast, context) {
|
19263 | return null;
|
19264 | }
|
19265 | visitAttribute(ast, context) {
|
19266 | return null;
|
19267 | }
|
19268 | visitText(ast, context) {
|
19269 | return null;
|
19270 | }
|
19271 | }
|
19272 |
|
19273 | /**
|
19274 | * @license
|
19275 | * Copyright Google LLC All Rights Reserved.
|
19276 | *
|
19277 | * Use of this source code is governed by an MIT-style license that can be
|
19278 | * found in the LICENSE file at https://angular.io/license
|
19279 | */
|
19280 | const QUERY_METADATA_IDENTIFIERS = [
|
19281 | createViewChild,
|
19282 | createViewChildren,
|
19283 | createContentChild,
|
19284 | createContentChildren,
|
19285 | ];
|
19286 | /*
|
19287 | * Resolve a `Type` for {@link Directive}.
|
19288 | *
|
19289 | * This interface can be overridden by the application developer to create custom behavior.
|
19290 | *
|
19291 | * See {@link Compiler}
|
19292 | */
|
19293 | class DirectiveResolver {
|
19294 | constructor(_reflector) {
|
19295 | this._reflector = _reflector;
|
19296 | }
|
19297 | isDirective(type) {
|
19298 | const typeMetadata = this._reflector.annotations(resolveForwardRef(type));
|
19299 | return typeMetadata && typeMetadata.some(isDirectiveMetadata);
|
19300 | }
|
19301 | resolve(type, throwIfNotFound = true) {
|
19302 | const typeMetadata = this._reflector.annotations(resolveForwardRef(type));
|
19303 | if (typeMetadata) {
|
19304 | const metadata = findLast(typeMetadata, isDirectiveMetadata);
|
19305 | if (metadata) {
|
19306 | const propertyMetadata = this._reflector.propMetadata(type);
|
19307 | const guards = this._reflector.guards(type);
|
19308 | return this._mergeWithPropertyMetadata(metadata, propertyMetadata, guards, type);
|
19309 | }
|
19310 | }
|
19311 | if (throwIfNotFound) {
|
19312 | throw new Error(`No Directive annotation found on ${stringify(type)}`);
|
19313 | }
|
19314 | return null;
|
19315 | }
|
19316 | _mergeWithPropertyMetadata(dm, propertyMetadata, guards, directiveType) {
|
19317 | const inputs = [];
|
19318 | const outputs = [];
|
19319 | const host = {};
|
19320 | const queries = {};
|
19321 | Object.keys(propertyMetadata).forEach((propName) => {
|
19322 | const input = findLast(propertyMetadata[propName], (a) => createInput.isTypeOf(a));
|
19323 | if (input) {
|
19324 | if (input.bindingPropertyName) {
|
19325 | inputs.push(`${propName}: ${input.bindingPropertyName}`);
|
19326 | }
|
19327 | else {
|
19328 | inputs.push(propName);
|
19329 | }
|
19330 | }
|
19331 | const output = findLast(propertyMetadata[propName], (a) => createOutput.isTypeOf(a));
|
19332 | if (output) {
|
19333 | if (output.bindingPropertyName) {
|
19334 | outputs.push(`${propName}: ${output.bindingPropertyName}`);
|
19335 | }
|
19336 | else {
|
19337 | outputs.push(propName);
|
19338 | }
|
19339 | }
|
19340 | const hostBindings = propertyMetadata[propName].filter(a => createHostBinding.isTypeOf(a));
|
19341 | hostBindings.forEach(hostBinding => {
|
19342 | if (hostBinding.hostPropertyName) {
|
19343 | const startWith = hostBinding.hostPropertyName[0];
|
19344 | if (startWith === '(') {
|
19345 | throw new Error(`@HostBinding can not bind to events. Use @HostListener instead.`);
|
19346 | }
|
19347 | else if (startWith === '[') {
|
19348 | throw new Error(`@HostBinding parameter should be a property name, 'class.<name>', or 'attr.<name>'.`);
|
19349 | }
|
19350 | host[`[${hostBinding.hostPropertyName}]`] = propName;
|
19351 | }
|
19352 | else {
|
19353 | host[`[${propName}]`] = propName;
|
19354 | }
|
19355 | });
|
19356 | const hostListeners = propertyMetadata[propName].filter(a => createHostListener.isTypeOf(a));
|
19357 | hostListeners.forEach(hostListener => {
|
19358 | const args = hostListener.args || [];
|
19359 | host[`(${hostListener.eventName})`] = `${propName}(${args.join(',')})`;
|
19360 | });
|
19361 | const query = findLast(propertyMetadata[propName], (a) => QUERY_METADATA_IDENTIFIERS.some(i => i.isTypeOf(a)));
|
19362 | if (query) {
|
19363 | queries[propName] = query;
|
19364 | }
|
19365 | });
|
19366 | return this._merge(dm, inputs, outputs, host, queries, guards, directiveType);
|
19367 | }
|
19368 | _extractPublicName(def) {
|
19369 | return splitAtColon(def, [null, def])[1].trim();
|
19370 | }
|
19371 | _dedupeBindings(bindings) {
|
19372 | const names = new Set();
|
19373 | const publicNames = new Set();
|
19374 | const reversedResult = [];
|
19375 | // go last to first to allow later entries to overwrite previous entries
|
19376 | for (let i = bindings.length - 1; i >= 0; i--) {
|
19377 | const binding = bindings[i];
|
19378 | const name = this._extractPublicName(binding);
|
19379 | publicNames.add(name);
|
19380 | if (!names.has(name)) {
|
19381 | names.add(name);
|
19382 | reversedResult.push(binding);
|
19383 | }
|
19384 | }
|
19385 | return reversedResult.reverse();
|
19386 | }
|
19387 | _merge(directive, inputs, outputs, host, queries, guards, directiveType) {
|
19388 | const mergedInputs = this._dedupeBindings(directive.inputs ? directive.inputs.concat(inputs) : inputs);
|
19389 | const mergedOutputs = this._dedupeBindings(directive.outputs ? directive.outputs.concat(outputs) : outputs);
|
19390 | const mergedHost = directive.host ? Object.assign(Object.assign({}, directive.host), host) : host;
|
19391 | const mergedQueries = directive.queries ? Object.assign(Object.assign({}, directive.queries), queries) : queries;
|
19392 | if (createComponent.isTypeOf(directive)) {
|
19393 | const comp = directive;
|
19394 | return createComponent({
|
19395 | selector: comp.selector,
|
19396 | inputs: mergedInputs,
|
19397 | outputs: mergedOutputs,
|
19398 | host: mergedHost,
|
19399 | exportAs: comp.exportAs,
|
19400 | moduleId: comp.moduleId,
|
19401 | queries: mergedQueries,
|
19402 | changeDetection: comp.changeDetection,
|
19403 | providers: comp.providers,
|
19404 | viewProviders: comp.viewProviders,
|
19405 | entryComponents: comp.entryComponents,
|
19406 | template: comp.template,
|
19407 | templateUrl: comp.templateUrl,
|
19408 | styles: comp.styles,
|
19409 | styleUrls: comp.styleUrls,
|
19410 | encapsulation: comp.encapsulation,
|
19411 | animations: comp.animations,
|
19412 | interpolation: comp.interpolation,
|
19413 | preserveWhitespaces: directive.preserveWhitespaces,
|
19414 | });
|
19415 | }
|
19416 | else {
|
19417 | return createDirective({
|
19418 | selector: directive.selector,
|
19419 | inputs: mergedInputs,
|
19420 | outputs: mergedOutputs,
|
19421 | host: mergedHost,
|
19422 | exportAs: directive.exportAs,
|
19423 | queries: mergedQueries,
|
19424 | providers: directive.providers,
|
19425 | guards
|
19426 | });
|
19427 | }
|
19428 | }
|
19429 | }
|
19430 | function isDirectiveMetadata(type) {
|
19431 | return createDirective.isTypeOf(type) || createComponent.isTypeOf(type);
|
19432 | }
|
19433 | function findLast(arr, condition) {
|
19434 | for (let i = arr.length - 1; i >= 0; i--) {
|
19435 | if (condition(arr[i])) {
|
19436 | return arr[i];
|
19437 | }
|
19438 | }
|
19439 | return null;
|
19440 | }
|
19441 |
|
19442 | /**
|
19443 | * @license
|
19444 | * Copyright Google LLC All Rights Reserved.
|
19445 | *
|
19446 | * Use of this source code is governed by an MIT-style license that can be
|
19447 | * found in the LICENSE file at https://angular.io/license
|
19448 | */
|
19449 | var _VisitorMode;
|
19450 | (function (_VisitorMode) {
|
19451 | _VisitorMode[_VisitorMode["Extract"] = 0] = "Extract";
|
19452 | _VisitorMode[_VisitorMode["Merge"] = 1] = "Merge";
|
19453 | })(_VisitorMode || (_VisitorMode = {}));
|
19454 |
|
19455 | /**
|
19456 | * @license
|
19457 | * Copyright Google LLC All Rights Reserved.
|
19458 | *
|
19459 | * Use of this source code is governed by an MIT-style license that can be
|
19460 | * found in the LICENSE file at https://angular.io/license
|
19461 | */
|
19462 | const STRIP_SRC_FILE_SUFFIXES = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/;
|
19463 | const GENERATED_FILE = /\.ngfactory\.|\.ngsummary\./;
|
19464 | const JIT_SUMMARY_FILE = /\.ngsummary\./;
|
19465 | const JIT_SUMMARY_NAME = /NgSummary$/;
|
19466 | function ngfactoryFilePath(filePath, forceSourceFile = false) {
|
19467 | const urlWithSuffix = splitTypescriptSuffix(filePath, forceSourceFile);
|
19468 | return `${urlWithSuffix[0]}.ngfactory${normalizeGenFileSuffix(urlWithSuffix[1])}`;
|
19469 | }
|
19470 | function stripGeneratedFileSuffix(filePath) {
|
19471 | return filePath.replace(GENERATED_FILE, '.');
|
19472 | }
|
19473 | function isGeneratedFile(filePath) {
|
19474 | return GENERATED_FILE.test(filePath);
|
19475 | }
|
19476 | function splitTypescriptSuffix(path, forceSourceFile = false) {
|
19477 | if (path.endsWith('.d.ts')) {
|
19478 | return [path.slice(0, -5), forceSourceFile ? '.ts' : '.d.ts'];
|
19479 | }
|
19480 | const lastDot = path.lastIndexOf('.');
|
19481 | if (lastDot !== -1) {
|
19482 | return [path.substring(0, lastDot), path.substring(lastDot)];
|
19483 | }
|
19484 | return [path, ''];
|
19485 | }
|
19486 | function normalizeGenFileSuffix(srcFileSuffix) {
|
19487 | return srcFileSuffix === '.tsx' ? '.ts' : srcFileSuffix;
|
19488 | }
|
19489 | function summaryFileName(fileName) {
|
19490 | const fileNameWithoutSuffix = fileName.replace(STRIP_SRC_FILE_SUFFIXES, '');
|
19491 | return `${fileNameWithoutSuffix}.ngsummary.json`;
|
19492 | }
|
19493 | function summaryForJitFileName(fileName, forceSourceFile = false) {
|
19494 | const urlWithSuffix = splitTypescriptSuffix(stripGeneratedFileSuffix(fileName), forceSourceFile);
|
19495 | return `${urlWithSuffix[0]}.ngsummary${urlWithSuffix[1]}`;
|
19496 | }
|
19497 | function stripSummaryForJitFileSuffix(filePath) {
|
19498 | return filePath.replace(JIT_SUMMARY_FILE, '.');
|
19499 | }
|
19500 | function summaryForJitName(symbolName) {
|
19501 | return `${symbolName}NgSummary`;
|
19502 | }
|
19503 | function stripSummaryForJitNameSuffix(symbolName) {
|
19504 | return symbolName.replace(JIT_SUMMARY_NAME, '');
|
19505 | }
|
19506 |
|
19507 | /**
|
19508 | * @license
|
19509 | * Copyright Google LLC All Rights Reserved.
|
19510 | *
|
19511 | * Use of this source code is governed by an MIT-style license that can be
|
19512 | * found in the LICENSE file at https://angular.io/license
|
19513 | */
|
19514 | var LifecycleHooks;
|
19515 | (function (LifecycleHooks) {
|
19516 | LifecycleHooks[LifecycleHooks["OnInit"] = 0] = "OnInit";
|
19517 | LifecycleHooks[LifecycleHooks["OnDestroy"] = 1] = "OnDestroy";
|
19518 | LifecycleHooks[LifecycleHooks["DoCheck"] = 2] = "DoCheck";
|
19519 | LifecycleHooks[LifecycleHooks["OnChanges"] = 3] = "OnChanges";
|
19520 | LifecycleHooks[LifecycleHooks["AfterContentInit"] = 4] = "AfterContentInit";
|
19521 | LifecycleHooks[LifecycleHooks["AfterContentChecked"] = 5] = "AfterContentChecked";
|
19522 | LifecycleHooks[LifecycleHooks["AfterViewInit"] = 6] = "AfterViewInit";
|
19523 | LifecycleHooks[LifecycleHooks["AfterViewChecked"] = 7] = "AfterViewChecked";
|
19524 | })(LifecycleHooks || (LifecycleHooks = {}));
|
19525 | const LIFECYCLE_HOOKS_VALUES = [
|
19526 | LifecycleHooks.OnInit, LifecycleHooks.OnDestroy, LifecycleHooks.DoCheck, LifecycleHooks.OnChanges,
|
19527 | LifecycleHooks.AfterContentInit, LifecycleHooks.AfterContentChecked, LifecycleHooks.AfterViewInit,
|
19528 | LifecycleHooks.AfterViewChecked
|
19529 | ];
|
19530 | function hasLifecycleHook(reflector, hook, token) {
|
19531 | return reflector.hasLifecycleHook(token, getHookName(hook));
|
19532 | }
|
19533 | function getAllLifecycleHooks(reflector, token) {
|
19534 | return LIFECYCLE_HOOKS_VALUES.filter(hook => hasLifecycleHook(reflector, hook, token));
|
19535 | }
|
19536 | function getHookName(hook) {
|
19537 | switch (hook) {
|
19538 | case LifecycleHooks.OnInit:
|
19539 | return 'ngOnInit';
|
19540 | case LifecycleHooks.OnDestroy:
|
19541 | return 'ngOnDestroy';
|
19542 | case LifecycleHooks.DoCheck:
|
19543 | return 'ngDoCheck';
|
19544 | case LifecycleHooks.OnChanges:
|
19545 | return 'ngOnChanges';
|
19546 | case LifecycleHooks.AfterContentInit:
|
19547 | return 'ngAfterContentInit';
|
19548 | case LifecycleHooks.AfterContentChecked:
|
19549 | return 'ngAfterContentChecked';
|
19550 | case LifecycleHooks.AfterViewInit:
|
19551 | return 'ngAfterViewInit';
|
19552 | case LifecycleHooks.AfterViewChecked:
|
19553 | return 'ngAfterViewChecked';
|
19554 | default:
|
19555 | // This default case is not needed by TypeScript compiler, as the switch is exhaustive.
|
19556 | // However Closure Compiler does not understand that and reports an error in typed mode.
|
19557 | // The `throw new Error` below works around the problem, and the unexpected: never variable
|
19558 | // makes sure tsc still checks this code is unreachable.
|
19559 | const unexpected = hook;
|
19560 | throw new Error(`unexpected ${unexpected}`);
|
19561 | }
|
19562 | }
|
19563 |
|
19564 | /**
|
19565 | * @license
|
19566 | * Copyright Google LLC All Rights Reserved.
|
19567 | *
|
19568 | * Use of this source code is governed by an MIT-style license that can be
|
19569 | * found in the LICENSE file at https://angular.io/license
|
19570 | */
|
19571 | const ERROR_COMPONENT_TYPE = 'ngComponentType';
|
19572 | // Design notes:
|
19573 | // - don't lazily create metadata:
|
19574 | // For some metadata, we need to do async work sometimes,
|
19575 | // so the user has to kick off this loading.
|
19576 | // But we want to report errors even when the async work is
|
19577 | // not required to check that the user would have been able
|
19578 | // to wait correctly.
|
19579 | class CompileMetadataResolver {
|
19580 | constructor(_config, _htmlParser, _ngModuleResolver, _directiveResolver, _pipeResolver, _summaryResolver, _schemaRegistry, _directiveNormalizer, _console, _staticSymbolCache, _reflector, _errorCollector) {
|
19581 | this._config = _config;
|
19582 | this._htmlParser = _htmlParser;
|
19583 | this._ngModuleResolver = _ngModuleResolver;
|
19584 | this._directiveResolver = _directiveResolver;
|
19585 | this._pipeResolver = _pipeResolver;
|
19586 | this._summaryResolver = _summaryResolver;
|
19587 | this._schemaRegistry = _schemaRegistry;
|
19588 | this._directiveNormalizer = _directiveNormalizer;
|
19589 | this._console = _console;
|
19590 | this._staticSymbolCache = _staticSymbolCache;
|
19591 | this._reflector = _reflector;
|
19592 | this._errorCollector = _errorCollector;
|
19593 | this._nonNormalizedDirectiveCache = new Map();
|
19594 | this._directiveCache = new Map();
|
19595 | this._summaryCache = new Map();
|
19596 | this._pipeCache = new Map();
|
19597 | this._ngModuleCache = new Map();
|
19598 | this._ngModuleOfTypes = new Map();
|
19599 | this._shallowModuleCache = new Map();
|
19600 | }
|
19601 | getReflector() {
|
19602 | return this._reflector;
|
19603 | }
|
19604 | clearCacheFor(type) {
|
19605 | const dirMeta = this._directiveCache.get(type);
|
19606 | this._directiveCache.delete(type);
|
19607 | this._nonNormalizedDirectiveCache.delete(type);
|
19608 | this._summaryCache.delete(type);
|
19609 | this._pipeCache.delete(type);
|
19610 | this._ngModuleOfTypes.delete(type);
|
19611 | // Clear all of the NgModule as they contain transitive information!
|
19612 | this._ngModuleCache.clear();
|
19613 | if (dirMeta) {
|
19614 | this._directiveNormalizer.clearCacheFor(dirMeta);
|
19615 | }
|
19616 | }
|
19617 | clearCache() {
|
19618 | this._directiveCache.clear();
|
19619 | this._nonNormalizedDirectiveCache.clear();
|
19620 | this._summaryCache.clear();
|
19621 | this._pipeCache.clear();
|
19622 | this._ngModuleCache.clear();
|
19623 | this._ngModuleOfTypes.clear();
|
19624 | this._directiveNormalizer.clearCache();
|
19625 | }
|
19626 | _createProxyClass(baseType, name) {
|
19627 | let delegate = null;
|
19628 | const proxyClass = function () {
|
19629 | if (!delegate) {
|
19630 | throw new Error(`Illegal state: Class ${name} for type ${stringify(baseType)} is not compiled yet!`);
|
19631 | }
|
19632 | return delegate.apply(this, arguments);
|
19633 | };
|
19634 | proxyClass.setDelegate = (d) => {
|
19635 | delegate = d;
|
19636 | proxyClass.prototype = d.prototype;
|
19637 | };
|
19638 | // Make stringify work correctly
|
19639 | proxyClass.overriddenName = name;
|
19640 | return proxyClass;
|
19641 | }
|
19642 | getGeneratedClass(dirType, name) {
|
19643 | if (dirType instanceof StaticSymbol) {
|
19644 | return this._staticSymbolCache.get(ngfactoryFilePath(dirType.filePath), name);
|
19645 | }
|
19646 | else {
|
19647 | return this._createProxyClass(dirType, name);
|
19648 | }
|
19649 | }
|
19650 | getComponentViewClass(dirType) {
|
19651 | return this.getGeneratedClass(dirType, viewClassName(dirType, 0));
|
19652 | }
|
19653 | getHostComponentViewClass(dirType) {
|
19654 | return this.getGeneratedClass(dirType, hostViewClassName(dirType));
|
19655 | }
|
19656 | getHostComponentType(dirType) {
|
19657 | const name = `${identifierName({ reference: dirType })}_Host`;
|
19658 | if (dirType instanceof StaticSymbol) {
|
19659 | return this._staticSymbolCache.get(dirType.filePath, name);
|
19660 | }
|
19661 | return this._createProxyClass(dirType, name);
|
19662 | }
|
19663 | getRendererType(dirType) {
|
19664 | if (dirType instanceof StaticSymbol) {
|
19665 | return this._staticSymbolCache.get(ngfactoryFilePath(dirType.filePath), rendererTypeName(dirType));
|
19666 | }
|
19667 | else {
|
19668 | // returning an object as proxy,
|
19669 | // that we fill later during runtime compilation.
|
19670 | return {};
|
19671 | }
|
19672 | }
|
19673 | getComponentFactory(selector, dirType, inputs, outputs) {
|
19674 | if (dirType instanceof StaticSymbol) {
|
19675 | return this._staticSymbolCache.get(ngfactoryFilePath(dirType.filePath), componentFactoryName(dirType));
|
19676 | }
|
19677 | else {
|
19678 | const hostView = this.getHostComponentViewClass(dirType);
|
19679 | // Note: ngContentSelectors will be filled later once the template is
|
19680 | // loaded.
|
19681 | const createComponentFactory = this._reflector.resolveExternalReference(Identifiers.createComponentFactory);
|
19682 | return createComponentFactory(selector, dirType, hostView, inputs, outputs, []);
|
19683 | }
|
19684 | }
|
19685 | initComponentFactory(factory, ngContentSelectors) {
|
19686 | if (!(factory instanceof StaticSymbol)) {
|
19687 | factory.ngContentSelectors.push(...ngContentSelectors);
|
19688 | }
|
19689 | }
|
19690 | _loadSummary(type, kind) {
|
19691 | let typeSummary = this._summaryCache.get(type);
|
19692 | if (!typeSummary) {
|
19693 | const summary = this._summaryResolver.resolveSummary(type);
|
19694 | typeSummary = summary ? summary.type : null;
|
19695 | this._summaryCache.set(type, typeSummary || null);
|
19696 | }
|
19697 | return typeSummary && typeSummary.summaryKind === kind ? typeSummary : null;
|
19698 | }
|
19699 | getHostComponentMetadata(compMeta, hostViewType) {
|
19700 | const hostType = this.getHostComponentType(compMeta.type.reference);
|
19701 | if (!hostViewType) {
|
19702 | hostViewType = this.getHostComponentViewClass(hostType);
|
19703 | }
|
19704 | // Note: ! is ok here as this method should only be called with normalized directive
|
19705 | // metadata, which always fills in the selector.
|
19706 | const template = CssSelector.parse(compMeta.selector)[0].getMatchingElementTemplate();
|
19707 | const templateUrl = '';
|
19708 | const htmlAst = this._htmlParser.parse(template, templateUrl);
|
19709 | return CompileDirectiveMetadata.create({
|
19710 | isHost: true,
|
19711 | type: { reference: hostType, diDeps: [], lifecycleHooks: [] },
|
19712 | template: new CompileTemplateMetadata({
|
19713 | encapsulation: ViewEncapsulation.None,
|
19714 | template,
|
19715 | templateUrl,
|
19716 | htmlAst,
|
19717 | styles: [],
|
19718 | styleUrls: [],
|
19719 | ngContentSelectors: [],
|
19720 | animations: [],
|
19721 | isInline: true,
|
19722 | externalStylesheets: [],
|
19723 | interpolation: null,
|
19724 | preserveWhitespaces: false,
|
19725 | }),
|
19726 | exportAs: null,
|
19727 | changeDetection: ChangeDetectionStrategy.Default,
|
19728 | inputs: [],
|
19729 | outputs: [],
|
19730 | host: {},
|
19731 | isComponent: true,
|
19732 | selector: '*',
|
19733 | providers: [],
|
19734 | viewProviders: [],
|
19735 | queries: [],
|
19736 | guards: {},
|
19737 | viewQueries: [],
|
19738 | componentViewType: hostViewType,
|
19739 | rendererType: { id: '__Host__', encapsulation: ViewEncapsulation.None, styles: [], data: {} },
|
19740 | entryComponents: [],
|
19741 | componentFactory: null
|
19742 | });
|
19743 | }
|
19744 | loadDirectiveMetadata(ngModuleType, directiveType, isSync) {
|
19745 | if (this._directiveCache.has(directiveType)) {
|
19746 | return null;
|
19747 | }
|
19748 | directiveType = resolveForwardRef(directiveType);
|
19749 | const { annotation, metadata } = this.getNonNormalizedDirectiveMetadata(directiveType);
|
19750 | const createDirectiveMetadata = (templateMetadata) => {
|
19751 | const normalizedDirMeta = new CompileDirectiveMetadata({
|
19752 | isHost: false,
|
19753 | type: metadata.type,
|
19754 | isComponent: metadata.isComponent,
|
19755 | selector: metadata.selector,
|
19756 | exportAs: metadata.exportAs,
|
19757 | changeDetection: metadata.changeDetection,
|
19758 | inputs: metadata.inputs,
|
19759 | outputs: metadata.outputs,
|
19760 | hostListeners: metadata.hostListeners,
|
19761 | hostProperties: metadata.hostProperties,
|
19762 | hostAttributes: metadata.hostAttributes,
|
19763 | providers: metadata.providers,
|
19764 | viewProviders: metadata.viewProviders,
|
19765 | queries: metadata.queries,
|
19766 | guards: metadata.guards,
|
19767 | viewQueries: metadata.viewQueries,
|
19768 | entryComponents: metadata.entryComponents,
|
19769 | componentViewType: metadata.componentViewType,
|
19770 | rendererType: metadata.rendererType,
|
19771 | componentFactory: metadata.componentFactory,
|
19772 | template: templateMetadata
|
19773 | });
|
19774 | if (templateMetadata) {
|
19775 | this.initComponentFactory(metadata.componentFactory, templateMetadata.ngContentSelectors);
|
19776 | }
|
19777 | this._directiveCache.set(directiveType, normalizedDirMeta);
|
19778 | this._summaryCache.set(directiveType, normalizedDirMeta.toSummary());
|
19779 | return null;
|
19780 | };
|
19781 | if (metadata.isComponent) {
|
19782 | const template = metadata.template;
|
19783 | const templateMeta = this._directiveNormalizer.normalizeTemplate({
|
19784 | ngModuleType,
|
19785 | componentType: directiveType,
|
19786 | moduleUrl: this._reflector.componentModuleUrl(directiveType, annotation),
|
19787 | encapsulation: template.encapsulation,
|
19788 | template: template.template,
|
19789 | templateUrl: template.templateUrl,
|
19790 | styles: template.styles,
|
19791 | styleUrls: template.styleUrls,
|
19792 | animations: template.animations,
|
19793 | interpolation: template.interpolation,
|
19794 | preserveWhitespaces: template.preserveWhitespaces
|
19795 | });
|
19796 | if (isPromise(templateMeta) && isSync) {
|
19797 | this._reportError(componentStillLoadingError(directiveType), directiveType);
|
19798 | return null;
|
19799 | }
|
19800 | return SyncAsync.then(templateMeta, createDirectiveMetadata);
|
19801 | }
|
19802 | else {
|
19803 | // directive
|
19804 | createDirectiveMetadata(null);
|
19805 | return null;
|
19806 | }
|
19807 | }
|
19808 | getNonNormalizedDirectiveMetadata(directiveType) {
|
19809 | directiveType = resolveForwardRef(directiveType);
|
19810 | if (!directiveType) {
|
19811 | return null;
|
19812 | }
|
19813 | let cacheEntry = this._nonNormalizedDirectiveCache.get(directiveType);
|
19814 | if (cacheEntry) {
|
19815 | return cacheEntry;
|
19816 | }
|
19817 | const dirMeta = this._directiveResolver.resolve(directiveType, false);
|
19818 | if (!dirMeta) {
|
19819 | return null;
|
19820 | }
|
19821 | let nonNormalizedTemplateMetadata = undefined;
|
19822 | if (createComponent.isTypeOf(dirMeta)) {
|
19823 | // component
|
19824 | const compMeta = dirMeta;
|
19825 | assertArrayOfStrings('styles', compMeta.styles);
|
19826 | assertArrayOfStrings('styleUrls', compMeta.styleUrls);
|
19827 | assertInterpolationSymbols('interpolation', compMeta.interpolation);
|
19828 | const animations = compMeta.animations;
|
19829 | nonNormalizedTemplateMetadata = new CompileTemplateMetadata({
|
19830 | encapsulation: noUndefined(compMeta.encapsulation),
|
19831 | template: noUndefined(compMeta.template),
|
19832 | templateUrl: noUndefined(compMeta.templateUrl),
|
19833 | htmlAst: null,
|
19834 | styles: compMeta.styles || [],
|
19835 | styleUrls: compMeta.styleUrls || [],
|
19836 | animations: animations || [],
|
19837 | interpolation: noUndefined(compMeta.interpolation),
|
19838 | isInline: !!compMeta.template,
|
19839 | externalStylesheets: [],
|
19840 | ngContentSelectors: [],
|
19841 | preserveWhitespaces: noUndefined(dirMeta.preserveWhitespaces),
|
19842 | });
|
19843 | }
|
19844 | let changeDetectionStrategy = null;
|
19845 | let viewProviders = [];
|
19846 | let entryComponentMetadata = [];
|
19847 | let selector = dirMeta.selector;
|
19848 | if (createComponent.isTypeOf(dirMeta)) {
|
19849 | // Component
|
19850 | const compMeta = dirMeta;
|
19851 | changeDetectionStrategy = compMeta.changeDetection;
|
19852 | if (compMeta.viewProviders) {
|
19853 | viewProviders = this._getProvidersMetadata(compMeta.viewProviders, entryComponentMetadata, `viewProviders for "${stringifyType(directiveType)}"`, [], directiveType);
|
19854 | }
|
19855 | if (compMeta.entryComponents) {
|
19856 | entryComponentMetadata = flattenAndDedupeArray(compMeta.entryComponents)
|
19857 | .map((type) => this._getEntryComponentMetadata(type))
|
19858 | .concat(entryComponentMetadata);
|
19859 | }
|
19860 | if (!selector) {
|
19861 | selector = this._schemaRegistry.getDefaultComponentElementName();
|
19862 | }
|
19863 | }
|
19864 | else {
|
19865 | // Directive
|
19866 | if (!selector) {
|
19867 | selector = null;
|
19868 | }
|
19869 | }
|
19870 | let providers = [];
|
19871 | if (dirMeta.providers != null) {
|
19872 | providers = this._getProvidersMetadata(dirMeta.providers, entryComponentMetadata, `providers for "${stringifyType(directiveType)}"`, [], directiveType);
|
19873 | }
|
19874 | let queries = [];
|
19875 | let viewQueries = [];
|
19876 | if (dirMeta.queries != null) {
|
19877 | queries = this._getQueriesMetadata(dirMeta.queries, false, directiveType);
|
19878 | viewQueries = this._getQueriesMetadata(dirMeta.queries, true, directiveType);
|
19879 | }
|
19880 | const metadata = CompileDirectiveMetadata.create({
|
19881 | isHost: false,
|
19882 | selector: selector,
|
19883 | exportAs: noUndefined(dirMeta.exportAs),
|
19884 | isComponent: !!nonNormalizedTemplateMetadata,
|
19885 | type: this._getTypeMetadata(directiveType),
|
19886 | template: nonNormalizedTemplateMetadata,
|
19887 | changeDetection: changeDetectionStrategy,
|
19888 | inputs: dirMeta.inputs || [],
|
19889 | outputs: dirMeta.outputs || [],
|
19890 | host: dirMeta.host || {},
|
19891 | providers: providers || [],
|
19892 | viewProviders: viewProviders || [],
|
19893 | queries: queries || [],
|
19894 | guards: dirMeta.guards || {},
|
19895 | viewQueries: viewQueries || [],
|
19896 | entryComponents: entryComponentMetadata,
|
19897 | componentViewType: nonNormalizedTemplateMetadata ? this.getComponentViewClass(directiveType) :
|
19898 | null,
|
19899 | rendererType: nonNormalizedTemplateMetadata ? this.getRendererType(directiveType) : null,
|
19900 | componentFactory: null
|
19901 | });
|
19902 | if (nonNormalizedTemplateMetadata) {
|
19903 | metadata.componentFactory =
|
19904 | this.getComponentFactory(selector, directiveType, metadata.inputs, metadata.outputs);
|
19905 | }
|
19906 | cacheEntry = { metadata, annotation: dirMeta };
|
19907 | this._nonNormalizedDirectiveCache.set(directiveType, cacheEntry);
|
19908 | return cacheEntry;
|
19909 | }
|
19910 | /**
|
19911 | * Gets the metadata for the given directive.
|
19912 | * This assumes `loadNgModuleDirectiveAndPipeMetadata` has been called first.
|
19913 | */
|
19914 | getDirectiveMetadata(directiveType) {
|
19915 | const dirMeta = this._directiveCache.get(directiveType);
|
19916 | if (!dirMeta) {
|
19917 | this._reportError(syntaxError(`Illegal state: getDirectiveMetadata can only be called after loadNgModuleDirectiveAndPipeMetadata for a module that declares it. Directive ${stringifyType(directiveType)}.`), directiveType);
|
19918 | }
|
19919 | return dirMeta;
|
19920 | }
|
19921 | getDirectiveSummary(dirType) {
|
19922 | const dirSummary = this._loadSummary(dirType, CompileSummaryKind.Directive);
|
19923 | if (!dirSummary) {
|
19924 | this._reportError(syntaxError(`Illegal state: Could not load the summary for directive ${stringifyType(dirType)}.`), dirType);
|
19925 | }
|
19926 | return dirSummary;
|
19927 | }
|
19928 | isDirective(type) {
|
19929 | return !!this._loadSummary(type, CompileSummaryKind.Directive) ||
|
19930 | this._directiveResolver.isDirective(type);
|
19931 | }
|
19932 | isAbstractDirective(type) {
|
19933 | const summary = this._loadSummary(type, CompileSummaryKind.Directive);
|
19934 | if (summary && !summary.isComponent) {
|
19935 | return !summary.selector;
|
19936 | }
|
19937 | const meta = this._directiveResolver.resolve(type, false);
|
19938 | if (meta && !createComponent.isTypeOf(meta)) {
|
19939 | return !meta.selector;
|
19940 | }
|
19941 | return false;
|
19942 | }
|
19943 | isPipe(type) {
|
19944 | return !!this._loadSummary(type, CompileSummaryKind.Pipe) ||
|
19945 | this._pipeResolver.isPipe(type);
|
19946 | }
|
19947 | isNgModule(type) {
|
19948 | return !!this._loadSummary(type, CompileSummaryKind.NgModule) ||
|
19949 | this._ngModuleResolver.isNgModule(type);
|
19950 | }
|
19951 | getNgModuleSummary(moduleType, alreadyCollecting = null) {
|
19952 | let moduleSummary = this._loadSummary(moduleType, CompileSummaryKind.NgModule);
|
19953 | if (!moduleSummary) {
|
19954 | const moduleMeta = this.getNgModuleMetadata(moduleType, false, alreadyCollecting);
|
19955 | moduleSummary = moduleMeta ? moduleMeta.toSummary() : null;
|
19956 | if (moduleSummary) {
|
19957 | this._summaryCache.set(moduleType, moduleSummary);
|
19958 | }
|
19959 | }
|
19960 | return moduleSummary;
|
19961 | }
|
19962 | /**
|
19963 | * Loads the declared directives and pipes of an NgModule.
|
19964 | */
|
19965 | loadNgModuleDirectiveAndPipeMetadata(moduleType, isSync, throwIfNotFound = true) {
|
19966 | const ngModule = this.getNgModuleMetadata(moduleType, throwIfNotFound);
|
19967 | const loading = [];
|
19968 | if (ngModule) {
|
19969 | ngModule.declaredDirectives.forEach((id) => {
|
19970 | const promise = this.loadDirectiveMetadata(moduleType, id.reference, isSync);
|
19971 | if (promise) {
|
19972 | loading.push(promise);
|
19973 | }
|
19974 | });
|
19975 | ngModule.declaredPipes.forEach((id) => this._loadPipeMetadata(id.reference));
|
19976 | }
|
19977 | return Promise.all(loading);
|
19978 | }
|
19979 | getShallowModuleMetadata(moduleType) {
|
19980 | let compileMeta = this._shallowModuleCache.get(moduleType);
|
19981 | if (compileMeta) {
|
19982 | return compileMeta;
|
19983 | }
|
19984 | const ngModuleMeta = findLast(this._reflector.shallowAnnotations(moduleType), createNgModule.isTypeOf);
|
19985 | compileMeta = {
|
19986 | type: this._getTypeMetadata(moduleType),
|
19987 | rawExports: ngModuleMeta.exports,
|
19988 | rawImports: ngModuleMeta.imports,
|
19989 | rawProviders: ngModuleMeta.providers,
|
19990 | };
|
19991 | this._shallowModuleCache.set(moduleType, compileMeta);
|
19992 | return compileMeta;
|
19993 | }
|
19994 | getNgModuleMetadata(moduleType, throwIfNotFound = true, alreadyCollecting = null) {
|
19995 | moduleType = resolveForwardRef(moduleType);
|
19996 | let compileMeta = this._ngModuleCache.get(moduleType);
|
19997 | if (compileMeta) {
|
19998 | return compileMeta;
|
19999 | }
|
20000 | const meta = this._ngModuleResolver.resolve(moduleType, throwIfNotFound);
|
20001 | if (!meta) {
|
20002 | return null;
|
20003 | }
|
20004 | const declaredDirectives = [];
|
20005 | const exportedNonModuleIdentifiers = [];
|
20006 | const declaredPipes = [];
|
20007 | const importedModules = [];
|
20008 | const exportedModules = [];
|
20009 | const providers = [];
|
20010 | const entryComponents = [];
|
20011 | const bootstrapComponents = [];
|
20012 | const schemas = [];
|
20013 | if (meta.imports) {
|
20014 | flattenAndDedupeArray(meta.imports).forEach((importedType) => {
|
20015 | let importedModuleType = undefined;
|
20016 | if (isValidType(importedType)) {
|
20017 | importedModuleType = importedType;
|
20018 | }
|
20019 | else if (importedType && importedType.ngModule) {
|
20020 | const moduleWithProviders = importedType;
|
20021 | importedModuleType = moduleWithProviders.ngModule;
|
20022 | if (moduleWithProviders.providers) {
|
20023 | providers.push(...this._getProvidersMetadata(moduleWithProviders.providers, entryComponents, `provider for the NgModule '${stringifyType(importedModuleType)}'`, [], importedType));
|
20024 | }
|
20025 | }
|
20026 | if (importedModuleType) {
|
20027 | if (this._checkSelfImport(moduleType, importedModuleType))
|
20028 | return;
|
20029 | if (!alreadyCollecting)
|
20030 | alreadyCollecting = new Set();
|
20031 | if (alreadyCollecting.has(importedModuleType)) {
|
20032 | this._reportError(syntaxError(`${this._getTypeDescriptor(importedModuleType)} '${stringifyType(importedType)}' is imported recursively by the module '${stringifyType(moduleType)}'.`), moduleType);
|
20033 | return;
|
20034 | }
|
20035 | alreadyCollecting.add(importedModuleType);
|
20036 | const importedModuleSummary = this.getNgModuleSummary(importedModuleType, alreadyCollecting);
|
20037 | alreadyCollecting.delete(importedModuleType);
|
20038 | if (!importedModuleSummary) {
|
20039 | this._reportError(syntaxError(`Unexpected ${this._getTypeDescriptor(importedType)} '${stringifyType(importedType)}' imported by the module '${stringifyType(moduleType)}'. Please add a @NgModule annotation.`), moduleType);
|
20040 | return;
|
20041 | }
|
20042 | importedModules.push(importedModuleSummary);
|
20043 | }
|
20044 | else {
|
20045 | this._reportError(syntaxError(`Unexpected value '${stringifyType(importedType)}' imported by the module '${stringifyType(moduleType)}'`), moduleType);
|
20046 | return;
|
20047 | }
|
20048 | });
|
20049 | }
|
20050 | if (meta.exports) {
|
20051 | flattenAndDedupeArray(meta.exports).forEach((exportedType) => {
|
20052 | if (!isValidType(exportedType)) {
|
20053 | this._reportError(syntaxError(`Unexpected value '${stringifyType(exportedType)}' exported by the module '${stringifyType(moduleType)}'`), moduleType);
|
20054 | return;
|
20055 | }
|
20056 | if (!alreadyCollecting)
|
20057 | alreadyCollecting = new Set();
|
20058 | if (alreadyCollecting.has(exportedType)) {
|
20059 | this._reportError(syntaxError(`${this._getTypeDescriptor(exportedType)} '${stringify(exportedType)}' is exported recursively by the module '${stringifyType(moduleType)}'`), moduleType);
|
20060 | return;
|
20061 | }
|
20062 | alreadyCollecting.add(exportedType);
|
20063 | const exportedModuleSummary = this.getNgModuleSummary(exportedType, alreadyCollecting);
|
20064 | alreadyCollecting.delete(exportedType);
|
20065 | if (exportedModuleSummary) {
|
20066 | exportedModules.push(exportedModuleSummary);
|
20067 | }
|
20068 | else {
|
20069 | exportedNonModuleIdentifiers.push(this._getIdentifierMetadata(exportedType));
|
20070 | }
|
20071 | });
|
20072 | }
|
20073 | // Note: This will be modified later, so we rely on
|
20074 | // getting a new instance every time!
|
20075 | const transitiveModule = this._getTransitiveNgModuleMetadata(importedModules, exportedModules);
|
20076 | if (meta.declarations) {
|
20077 | flattenAndDedupeArray(meta.declarations).forEach((declaredType) => {
|
20078 | if (!isValidType(declaredType)) {
|
20079 | this._reportError(syntaxError(`Unexpected value '${stringifyType(declaredType)}' declared by the module '${stringifyType(moduleType)}'`), moduleType);
|
20080 | return;
|
20081 | }
|
20082 | const declaredIdentifier = this._getIdentifierMetadata(declaredType);
|
20083 | if (this.isDirective(declaredType)) {
|
20084 | if (this.isAbstractDirective(declaredType)) {
|
20085 | this._reportError(syntaxError(`Directive ${stringifyType(declaredType)} has no selector, please add it!`), declaredType);
|
20086 | }
|
20087 | transitiveModule.addDirective(declaredIdentifier);
|
20088 | declaredDirectives.push(declaredIdentifier);
|
20089 | this._addTypeToModule(declaredType, moduleType);
|
20090 | }
|
20091 | else if (this.isPipe(declaredType)) {
|
20092 | transitiveModule.addPipe(declaredIdentifier);
|
20093 | transitiveModule.pipes.push(declaredIdentifier);
|
20094 | declaredPipes.push(declaredIdentifier);
|
20095 | this._addTypeToModule(declaredType, moduleType);
|
20096 | }
|
20097 | else {
|
20098 | this._reportError(syntaxError(`Unexpected ${this._getTypeDescriptor(declaredType)} '${stringifyType(declaredType)}' declared by the module '${stringifyType(moduleType)}'. Please add a @Pipe/@Directive/@Component annotation.`), moduleType);
|
20099 | return;
|
20100 | }
|
20101 | });
|
20102 | }
|
20103 | const exportedDirectives = [];
|
20104 | const exportedPipes = [];
|
20105 | exportedNonModuleIdentifiers.forEach((exportedId) => {
|
20106 | if (transitiveModule.directivesSet.has(exportedId.reference)) {
|
20107 | exportedDirectives.push(exportedId);
|
20108 | transitiveModule.addExportedDirective(exportedId);
|
20109 | }
|
20110 | else if (transitiveModule.pipesSet.has(exportedId.reference)) {
|
20111 | exportedPipes.push(exportedId);
|
20112 | transitiveModule.addExportedPipe(exportedId);
|
20113 | }
|
20114 | else {
|
20115 | this._reportError(syntaxError(`Can't export ${this._getTypeDescriptor(exportedId.reference)} ${stringifyType(exportedId.reference)} from ${stringifyType(moduleType)} as it was neither declared nor imported!`), moduleType);
|
20116 | return;
|
20117 | }
|
20118 | });
|
20119 | // The providers of the module have to go last
|
20120 | // so that they overwrite any other provider we already added.
|
20121 | if (meta.providers) {
|
20122 | providers.push(...this._getProvidersMetadata(meta.providers, entryComponents, `provider for the NgModule '${stringifyType(moduleType)}'`, [], moduleType));
|
20123 | }
|
20124 | if (meta.entryComponents) {
|
20125 | entryComponents.push(...flattenAndDedupeArray(meta.entryComponents)
|
20126 | .map(type => this._getEntryComponentMetadata(type)));
|
20127 | }
|
20128 | if (meta.bootstrap) {
|
20129 | flattenAndDedupeArray(meta.bootstrap).forEach(type => {
|
20130 | if (!isValidType(type)) {
|
20131 | this._reportError(syntaxError(`Unexpected value '${stringifyType(type)}' used in the bootstrap property of module '${stringifyType(moduleType)}'`), moduleType);
|
20132 | return;
|
20133 | }
|
20134 | bootstrapComponents.push(this._getIdentifierMetadata(type));
|
20135 | });
|
20136 | }
|
20137 | entryComponents.push(...bootstrapComponents.map(type => this._getEntryComponentMetadata(type.reference)));
|
20138 | if (meta.schemas) {
|
20139 | schemas.push(...flattenAndDedupeArray(meta.schemas));
|
20140 | }
|
20141 | compileMeta = new CompileNgModuleMetadata({
|
20142 | type: this._getTypeMetadata(moduleType),
|
20143 | providers,
|
20144 | entryComponents,
|
20145 | bootstrapComponents,
|
20146 | schemas,
|
20147 | declaredDirectives,
|
20148 | exportedDirectives,
|
20149 | declaredPipes,
|
20150 | exportedPipes,
|
20151 | importedModules,
|
20152 | exportedModules,
|
20153 | transitiveModule,
|
20154 | id: meta.id || null,
|
20155 | });
|
20156 | entryComponents.forEach((id) => transitiveModule.addEntryComponent(id));
|
20157 | providers.forEach((provider) => transitiveModule.addProvider(provider, compileMeta.type));
|
20158 | transitiveModule.addModule(compileMeta.type);
|
20159 | this._ngModuleCache.set(moduleType, compileMeta);
|
20160 | return compileMeta;
|
20161 | }
|
20162 | _checkSelfImport(moduleType, importedModuleType) {
|
20163 | if (moduleType === importedModuleType) {
|
20164 | this._reportError(syntaxError(`'${stringifyType(moduleType)}' module can't import itself`), moduleType);
|
20165 | return true;
|
20166 | }
|
20167 | return false;
|
20168 | }
|
20169 | _getTypeDescriptor(type) {
|
20170 | if (isValidType(type)) {
|
20171 | if (this.isDirective(type)) {
|
20172 | return 'directive';
|
20173 | }
|
20174 | if (this.isPipe(type)) {
|
20175 | return 'pipe';
|
20176 | }
|
20177 | if (this.isNgModule(type)) {
|
20178 | return 'module';
|
20179 | }
|
20180 | }
|
20181 | if (type.provide) {
|
20182 | return 'provider';
|
20183 | }
|
20184 | return 'value';
|
20185 | }
|
20186 | _addTypeToModule(type, moduleType) {
|
20187 | const oldModule = this._ngModuleOfTypes.get(type);
|
20188 | if (oldModule && oldModule !== moduleType) {
|
20189 | this._reportError(syntaxError(`Type ${stringifyType(type)} is part of the declarations of 2 modules: ${stringifyType(oldModule)} and ${stringifyType(moduleType)}! ` +
|
20190 | `Please consider moving ${stringifyType(type)} to a higher module that imports ${stringifyType(oldModule)} and ${stringifyType(moduleType)}. ` +
|
20191 | `You can also create a new NgModule that exports and includes ${stringifyType(type)} then import that NgModule in ${stringifyType(oldModule)} and ${stringifyType(moduleType)}.`), moduleType);
|
20192 | return;
|
20193 | }
|
20194 | this._ngModuleOfTypes.set(type, moduleType);
|
20195 | }
|
20196 | _getTransitiveNgModuleMetadata(importedModules, exportedModules) {
|
20197 | // collect `providers` / `entryComponents` from all imported and all exported modules
|
20198 | const result = new TransitiveCompileNgModuleMetadata();
|
20199 | const modulesByToken = new Map();
|
20200 | importedModules.concat(exportedModules).forEach((modSummary) => {
|
20201 | modSummary.modules.forEach((mod) => result.addModule(mod));
|
20202 | modSummary.entryComponents.forEach((comp) => result.addEntryComponent(comp));
|
20203 | const addedTokens = new Set();
|
20204 | modSummary.providers.forEach((entry) => {
|
20205 | const tokenRef = tokenReference(entry.provider.token);
|
20206 | let prevModules = modulesByToken.get(tokenRef);
|
20207 | if (!prevModules) {
|
20208 | prevModules = new Set();
|
20209 | modulesByToken.set(tokenRef, prevModules);
|
20210 | }
|
20211 | const moduleRef = entry.module.reference;
|
20212 | // Note: the providers of one module may still contain multiple providers
|
20213 | // per token (e.g. for multi providers), and we need to preserve these.
|
20214 | if (addedTokens.has(tokenRef) || !prevModules.has(moduleRef)) {
|
20215 | prevModules.add(moduleRef);
|
20216 | addedTokens.add(tokenRef);
|
20217 | result.addProvider(entry.provider, entry.module);
|
20218 | }
|
20219 | });
|
20220 | });
|
20221 | exportedModules.forEach((modSummary) => {
|
20222 | modSummary.exportedDirectives.forEach((id) => result.addExportedDirective(id));
|
20223 | modSummary.exportedPipes.forEach((id) => result.addExportedPipe(id));
|
20224 | });
|
20225 | importedModules.forEach((modSummary) => {
|
20226 | modSummary.exportedDirectives.forEach((id) => result.addDirective(id));
|
20227 | modSummary.exportedPipes.forEach((id) => result.addPipe(id));
|
20228 | });
|
20229 | return result;
|
20230 | }
|
20231 | _getIdentifierMetadata(type) {
|
20232 | type = resolveForwardRef(type);
|
20233 | return { reference: type };
|
20234 | }
|
20235 | isInjectable(type) {
|
20236 | const annotations = this._reflector.tryAnnotations(type);
|
20237 | return annotations.some(ann => createInjectable.isTypeOf(ann));
|
20238 | }
|
20239 | getInjectableSummary(type) {
|
20240 | return {
|
20241 | summaryKind: CompileSummaryKind.Injectable,
|
20242 | type: this._getTypeMetadata(type, null, false)
|
20243 | };
|
20244 | }
|
20245 | getInjectableMetadata(type, dependencies = null, throwOnUnknownDeps = true) {
|
20246 | const typeSummary = this._loadSummary(type, CompileSummaryKind.Injectable);
|
20247 | const typeMetadata = typeSummary ?
|
20248 | typeSummary.type :
|
20249 | this._getTypeMetadata(type, dependencies, throwOnUnknownDeps);
|
20250 | const annotations = this._reflector.annotations(type).filter(ann => createInjectable.isTypeOf(ann));
|
20251 | if (annotations.length === 0) {
|
20252 | return null;
|
20253 | }
|
20254 | const meta = annotations[annotations.length - 1];
|
20255 | return {
|
20256 | symbol: type,
|
20257 | type: typeMetadata,
|
20258 | providedIn: meta.providedIn,
|
20259 | useValue: meta.useValue,
|
20260 | useClass: meta.useClass,
|
20261 | useExisting: meta.useExisting,
|
20262 | useFactory: meta.useFactory,
|
20263 | deps: meta.deps,
|
20264 | };
|
20265 | }
|
20266 | _getTypeMetadata(type, dependencies = null, throwOnUnknownDeps = true) {
|
20267 | const identifier = this._getIdentifierMetadata(type);
|
20268 | return {
|
20269 | reference: identifier.reference,
|
20270 | diDeps: this._getDependenciesMetadata(identifier.reference, dependencies, throwOnUnknownDeps),
|
20271 | lifecycleHooks: getAllLifecycleHooks(this._reflector, identifier.reference),
|
20272 | };
|
20273 | }
|
20274 | _getFactoryMetadata(factory, dependencies = null) {
|
20275 | factory = resolveForwardRef(factory);
|
20276 | return { reference: factory, diDeps: this._getDependenciesMetadata(factory, dependencies) };
|
20277 | }
|
20278 | /**
|
20279 | * Gets the metadata for the given pipe.
|
20280 | * This assumes `loadNgModuleDirectiveAndPipeMetadata` has been called first.
|
20281 | */
|
20282 | getPipeMetadata(pipeType) {
|
20283 | const pipeMeta = this._pipeCache.get(pipeType);
|
20284 | if (!pipeMeta) {
|
20285 | this._reportError(syntaxError(`Illegal state: getPipeMetadata can only be called after loadNgModuleDirectiveAndPipeMetadata for a module that declares it. Pipe ${stringifyType(pipeType)}.`), pipeType);
|
20286 | }
|
20287 | return pipeMeta || null;
|
20288 | }
|
20289 | getPipeSummary(pipeType) {
|
20290 | const pipeSummary = this._loadSummary(pipeType, CompileSummaryKind.Pipe);
|
20291 | if (!pipeSummary) {
|
20292 | this._reportError(syntaxError(`Illegal state: Could not load the summary for pipe ${stringifyType(pipeType)}.`), pipeType);
|
20293 | }
|
20294 | return pipeSummary;
|
20295 | }
|
20296 | getOrLoadPipeMetadata(pipeType) {
|
20297 | let pipeMeta = this._pipeCache.get(pipeType);
|
20298 | if (!pipeMeta) {
|
20299 | pipeMeta = this._loadPipeMetadata(pipeType);
|
20300 | }
|
20301 | return pipeMeta;
|
20302 | }
|
20303 | _loadPipeMetadata(pipeType) {
|
20304 | pipeType = resolveForwardRef(pipeType);
|
20305 | const pipeAnnotation = this._pipeResolver.resolve(pipeType);
|
20306 | const pipeMeta = new CompilePipeMetadata({
|
20307 | type: this._getTypeMetadata(pipeType),
|
20308 | name: pipeAnnotation.name,
|
20309 | pure: !!pipeAnnotation.pure
|
20310 | });
|
20311 | this._pipeCache.set(pipeType, pipeMeta);
|
20312 | this._summaryCache.set(pipeType, pipeMeta.toSummary());
|
20313 | return pipeMeta;
|
20314 | }
|
20315 | _getDependenciesMetadata(typeOrFunc, dependencies, throwOnUnknownDeps = true) {
|
20316 | let hasUnknownDeps = false;
|
20317 | const params = dependencies || this._reflector.parameters(typeOrFunc) || [];
|
20318 | const dependenciesMetadata = params.map((param) => {
|
20319 | let isAttribute = false;
|
20320 | let isHost = false;
|
20321 | let isSelf = false;
|
20322 | let isSkipSelf = false;
|
20323 | let isOptional = false;
|
20324 | let token = null;
|
20325 | if (Array.isArray(param)) {
|
20326 | param.forEach((paramEntry) => {
|
20327 | if (createHost.isTypeOf(paramEntry)) {
|
20328 | isHost = true;
|
20329 | }
|
20330 | else if (createSelf.isTypeOf(paramEntry)) {
|
20331 | isSelf = true;
|
20332 | }
|
20333 | else if (createSkipSelf.isTypeOf(paramEntry)) {
|
20334 | isSkipSelf = true;
|
20335 | }
|
20336 | else if (createOptional.isTypeOf(paramEntry)) {
|
20337 | isOptional = true;
|
20338 | }
|
20339 | else if (createAttribute.isTypeOf(paramEntry)) {
|
20340 | isAttribute = true;
|
20341 | token = paramEntry.attributeName;
|
20342 | }
|
20343 | else if (createInject.isTypeOf(paramEntry)) {
|
20344 | token = paramEntry.token;
|
20345 | }
|
20346 | else if (createInjectionToken.isTypeOf(paramEntry) ||
|
20347 | paramEntry instanceof StaticSymbol) {
|
20348 | token = paramEntry;
|
20349 | }
|
20350 | else if (isValidType(paramEntry) && token == null) {
|
20351 | token = paramEntry;
|
20352 | }
|
20353 | });
|
20354 | }
|
20355 | else {
|
20356 | token = param;
|
20357 | }
|
20358 | if (token == null) {
|
20359 | hasUnknownDeps = true;
|
20360 | return {};
|
20361 | }
|
20362 | return {
|
20363 | isAttribute,
|
20364 | isHost,
|
20365 | isSelf,
|
20366 | isSkipSelf,
|
20367 | isOptional,
|
20368 | token: this._getTokenMetadata(token)
|
20369 | };
|
20370 | });
|
20371 | if (hasUnknownDeps) {
|
20372 | const depsTokens = dependenciesMetadata.map((dep) => dep.token ? stringifyType(dep.token) : '?').join(', ');
|
20373 | const message = `Can't resolve all parameters for ${stringifyType(typeOrFunc)}: (${depsTokens}).`;
|
20374 | if (throwOnUnknownDeps || this._config.strictInjectionParameters) {
|
20375 | this._reportError(syntaxError(message), typeOrFunc);
|
20376 | }
|
20377 | }
|
20378 | return dependenciesMetadata;
|
20379 | }
|
20380 | _getTokenMetadata(token) {
|
20381 | token = resolveForwardRef(token);
|
20382 | let compileToken;
|
20383 | if (typeof token === 'string') {
|
20384 | compileToken = { value: token };
|
20385 | }
|
20386 | else {
|
20387 | compileToken = { identifier: { reference: token } };
|
20388 | }
|
20389 | return compileToken;
|
20390 | }
|
20391 | _getProvidersMetadata(providers, targetEntryComponents, debugInfo, compileProviders = [], type) {
|
20392 | providers.forEach((provider, providerIdx) => {
|
20393 | if (Array.isArray(provider)) {
|
20394 | this._getProvidersMetadata(provider, targetEntryComponents, debugInfo, compileProviders);
|
20395 | }
|
20396 | else {
|
20397 | provider = resolveForwardRef(provider);
|
20398 | let providerMeta = undefined;
|
20399 | if (provider && typeof provider === 'object' && provider.hasOwnProperty('provide')) {
|
20400 | this._validateProvider(provider);
|
20401 | providerMeta = new ProviderMeta(provider.provide, provider);
|
20402 | }
|
20403 | else if (isValidType(provider)) {
|
20404 | providerMeta = new ProviderMeta(provider, { useClass: provider });
|
20405 | }
|
20406 | else if (provider === void 0) {
|
20407 | this._reportError(syntaxError(`Encountered undefined provider! Usually this means you have a circular dependencies. This might be caused by using 'barrel' index.ts files.`));
|
20408 | return;
|
20409 | }
|
20410 | else {
|
20411 | const providersInfo = providers
|
20412 | .reduce((soFar, seenProvider, seenProviderIdx) => {
|
20413 | if (seenProviderIdx < providerIdx) {
|
20414 | soFar.push(`${stringifyType(seenProvider)}`);
|
20415 | }
|
20416 | else if (seenProviderIdx == providerIdx) {
|
20417 | soFar.push(`?${stringifyType(seenProvider)}?`);
|
20418 | }
|
20419 | else if (seenProviderIdx == providerIdx + 1) {
|
20420 | soFar.push('...');
|
20421 | }
|
20422 | return soFar;
|
20423 | }, [])
|
20424 | .join(', ');
|
20425 | this._reportError(syntaxError(`Invalid ${debugInfo ?
|
20426 | debugInfo :
|
20427 | 'provider'} - only instances of Provider and Type are allowed, got: [${providersInfo}]`), type);
|
20428 | return;
|
20429 | }
|
20430 | if (providerMeta.token ===
|
20431 | this._reflector.resolveExternalReference(Identifiers.ANALYZE_FOR_ENTRY_COMPONENTS)) {
|
20432 | targetEntryComponents.push(...this._getEntryComponentsFromProvider(providerMeta, type));
|
20433 | }
|
20434 | else {
|
20435 | compileProviders.push(this.getProviderMetadata(providerMeta));
|
20436 | }
|
20437 | }
|
20438 | });
|
20439 | return compileProviders;
|
20440 | }
|
20441 | _validateProvider(provider) {
|
20442 | if (provider.hasOwnProperty('useClass') && provider.useClass == null) {
|
20443 | this._reportError(syntaxError(`Invalid provider for ${stringifyType(provider.provide)}. useClass cannot be ${provider.useClass}.
|
20444 | Usually it happens when:
|
20445 | 1. There's a circular dependency (might be caused by using index.ts (barrel) files).
|
20446 | 2. Class was used before it was declared. Use forwardRef in this case.`));
|
20447 | }
|
20448 | }
|
20449 | _getEntryComponentsFromProvider(provider, type) {
|
20450 | const components = [];
|
20451 | const collectedIdentifiers = [];
|
20452 | if (provider.useFactory || provider.useExisting || provider.useClass) {
|
20453 | this._reportError(syntaxError(`The ANALYZE_FOR_ENTRY_COMPONENTS token only supports useValue!`), type);
|
20454 | return [];
|
20455 | }
|
20456 | if (!provider.multi) {
|
20457 | this._reportError(syntaxError(`The ANALYZE_FOR_ENTRY_COMPONENTS token only supports 'multi = true'!`), type);
|
20458 | return [];
|
20459 | }
|
20460 | extractIdentifiers(provider.useValue, collectedIdentifiers);
|
20461 | collectedIdentifiers.forEach((identifier) => {
|
20462 | const entry = this._getEntryComponentMetadata(identifier.reference, false);
|
20463 | if (entry) {
|
20464 | components.push(entry);
|
20465 | }
|
20466 | });
|
20467 | return components;
|
20468 | }
|
20469 | _getEntryComponentMetadata(dirType, throwIfNotFound = true) {
|
20470 | const dirMeta = this.getNonNormalizedDirectiveMetadata(dirType);
|
20471 | if (dirMeta && dirMeta.metadata.isComponent) {
|
20472 | return { componentType: dirType, componentFactory: dirMeta.metadata.componentFactory };
|
20473 | }
|
20474 | const dirSummary = this._loadSummary(dirType, CompileSummaryKind.Directive);
|
20475 | if (dirSummary && dirSummary.isComponent) {
|
20476 | return { componentType: dirType, componentFactory: dirSummary.componentFactory };
|
20477 | }
|
20478 | if (throwIfNotFound) {
|
20479 | throw syntaxError(`${dirType.name} cannot be used as an entry component.`);
|
20480 | }
|
20481 | return null;
|
20482 | }
|
20483 | _getInjectableTypeMetadata(type, dependencies = null) {
|
20484 | const typeSummary = this._loadSummary(type, CompileSummaryKind.Injectable);
|
20485 | if (typeSummary) {
|
20486 | return typeSummary.type;
|
20487 | }
|
20488 | return this._getTypeMetadata(type, dependencies);
|
20489 | }
|
20490 | getProviderMetadata(provider) {
|
20491 | let compileDeps = undefined;
|
20492 | let compileTypeMetadata = null;
|
20493 | let compileFactoryMetadata = null;
|
20494 | let token = this._getTokenMetadata(provider.token);
|
20495 | if (provider.useClass) {
|
20496 | compileTypeMetadata =
|
20497 | this._getInjectableTypeMetadata(provider.useClass, provider.dependencies);
|
20498 | compileDeps = compileTypeMetadata.diDeps;
|
20499 | if (provider.token === provider.useClass) {
|
20500 | // use the compileTypeMetadata as it contains information about lifecycleHooks...
|
20501 | token = { identifier: compileTypeMetadata };
|
20502 | }
|
20503 | }
|
20504 | else if (provider.useFactory) {
|
20505 | compileFactoryMetadata = this._getFactoryMetadata(provider.useFactory, provider.dependencies);
|
20506 | compileDeps = compileFactoryMetadata.diDeps;
|
20507 | }
|
20508 | return {
|
20509 | token: token,
|
20510 | useClass: compileTypeMetadata,
|
20511 | useValue: provider.useValue,
|
20512 | useFactory: compileFactoryMetadata,
|
20513 | useExisting: provider.useExisting ? this._getTokenMetadata(provider.useExisting) : undefined,
|
20514 | deps: compileDeps,
|
20515 | multi: provider.multi
|
20516 | };
|
20517 | }
|
20518 | _getQueriesMetadata(queries, isViewQuery, directiveType) {
|
20519 | const res = [];
|
20520 | Object.keys(queries).forEach((propertyName) => {
|
20521 | const query = queries[propertyName];
|
20522 | if (query.isViewQuery === isViewQuery) {
|
20523 | res.push(this._getQueryMetadata(query, propertyName, directiveType));
|
20524 | }
|
20525 | });
|
20526 | return res;
|
20527 | }
|
20528 | _queryVarBindings(selector) {
|
20529 | return selector.split(/\s*,\s*/);
|
20530 | }
|
20531 | _getQueryMetadata(q, propertyName, typeOrFunc) {
|
20532 | let selectors;
|
20533 | if (typeof q.selector === 'string') {
|
20534 | selectors =
|
20535 | this._queryVarBindings(q.selector).map(varName => this._getTokenMetadata(varName));
|
20536 | }
|
20537 | else {
|
20538 | if (!q.selector) {
|
20539 | this._reportError(syntaxError(`Can't construct a query for the property "${propertyName}" of "${stringifyType(typeOrFunc)}" since the query selector wasn't defined.`), typeOrFunc);
|
20540 | selectors = [];
|
20541 | }
|
20542 | else {
|
20543 | selectors = [this._getTokenMetadata(q.selector)];
|
20544 | }
|
20545 | }
|
20546 | return {
|
20547 | selectors,
|
20548 | first: q.first,
|
20549 | descendants: q.descendants,
|
20550 | emitDistinctChangesOnly: q.emitDistinctChangesOnly,
|
20551 | propertyName,
|
20552 | read: q.read ? this._getTokenMetadata(q.read) : null,
|
20553 | static: q.static
|
20554 | };
|
20555 | }
|
20556 | _reportError(error, type, otherType) {
|
20557 | if (this._errorCollector) {
|
20558 | this._errorCollector(error, type);
|
20559 | if (otherType) {
|
20560 | this._errorCollector(error, otherType);
|
20561 | }
|
20562 | }
|
20563 | else {
|
20564 | throw error;
|
20565 | }
|
20566 | }
|
20567 | }
|
20568 | function flattenArray(tree, out = []) {
|
20569 | if (tree) {
|
20570 | for (let i = 0; i < tree.length; i++) {
|
20571 | const item = resolveForwardRef(tree[i]);
|
20572 | if (Array.isArray(item)) {
|
20573 | flattenArray(item, out);
|
20574 | }
|
20575 | else {
|
20576 | out.push(item);
|
20577 | }
|
20578 | }
|
20579 | }
|
20580 | return out;
|
20581 | }
|
20582 | function dedupeArray(array) {
|
20583 | if (array) {
|
20584 | return Array.from(new Set(array));
|
20585 | }
|
20586 | return [];
|
20587 | }
|
20588 | function flattenAndDedupeArray(tree) {
|
20589 | return dedupeArray(flattenArray(tree));
|
20590 | }
|
20591 | function isValidType(value) {
|
20592 | return (value instanceof StaticSymbol) || (value instanceof Type);
|
20593 | }
|
20594 | function extractIdentifiers(value, targetIdentifiers) {
|
20595 | visitValue(value, new _CompileValueConverter(), targetIdentifiers);
|
20596 | }
|
20597 | class _CompileValueConverter extends ValueTransformer {
|
20598 | visitOther(value, targetIdentifiers) {
|
20599 | targetIdentifiers.push({ reference: value });
|
20600 | }
|
20601 | }
|
20602 | function stringifyType(type) {
|
20603 | if (type instanceof StaticSymbol) {
|
20604 | return `${type.name} in ${type.filePath}`;
|
20605 | }
|
20606 | else {
|
20607 | return stringify(type);
|
20608 | }
|
20609 | }
|
20610 | /**
|
20611 | * Indicates that a component is still being loaded in a synchronous compile.
|
20612 | */
|
20613 | function componentStillLoadingError(compType) {
|
20614 | const error = Error(`Can't compile synchronously as ${stringify(compType)} is still being loaded!`);
|
20615 | error[ERROR_COMPONENT_TYPE] = compType;
|
20616 | return error;
|
20617 | }
|
20618 |
|
20619 | /**
|
20620 | * @license
|
20621 | * Copyright Google LLC All Rights Reserved.
|
20622 | *
|
20623 | * Use of this source code is governed by an MIT-style license that can be
|
20624 | * found in the LICENSE file at https://angular.io/license
|
20625 | */
|
20626 | const LOG_VAR = variable('_l');
|
20627 |
|
20628 | /**
|
20629 | * @license
|
20630 | * Copyright Google LLC All Rights Reserved.
|
20631 | *
|
20632 | * Use of this source code is governed by an MIT-style license that can be
|
20633 | * found in the LICENSE file at https://angular.io/license
|
20634 | */
|
20635 | /**
|
20636 | * Resolves types to {@link NgModule}.
|
20637 | */
|
20638 | class NgModuleResolver {
|
20639 | constructor(_reflector) {
|
20640 | this._reflector = _reflector;
|
20641 | }
|
20642 | isNgModule(type) {
|
20643 | return this._reflector.annotations(type).some(createNgModule.isTypeOf);
|
20644 | }
|
20645 | resolve(type, throwIfNotFound = true) {
|
20646 | const ngModuleMeta = findLast(this._reflector.annotations(type), createNgModule.isTypeOf);
|
20647 | if (ngModuleMeta) {
|
20648 | return ngModuleMeta;
|
20649 | }
|
20650 | else {
|
20651 | if (throwIfNotFound) {
|
20652 | throw new Error(`No NgModule metadata found for '${stringify(type)}'.`);
|
20653 | }
|
20654 | return null;
|
20655 | }
|
20656 | }
|
20657 | }
|
20658 |
|
20659 | /**
|
20660 | * @license
|
20661 | * Copyright Google LLC All Rights Reserved.
|
20662 | *
|
20663 | * Use of this source code is governed by an MIT-style license that can be
|
20664 | * found in the LICENSE file at https://angular.io/license
|
20665 | */
|
20666 | /**
|
20667 | * Resolve a `Type` for {@link Pipe}.
|
20668 | *
|
20669 | * This interface can be overridden by the application developer to create custom behavior.
|
20670 | *
|
20671 | * See {@link Compiler}
|
20672 | */
|
20673 | class PipeResolver {
|
20674 | constructor(_reflector) {
|
20675 | this._reflector = _reflector;
|
20676 | }
|
20677 | isPipe(type) {
|
20678 | const typeMetadata = this._reflector.annotations(resolveForwardRef(type));
|
20679 | return typeMetadata && typeMetadata.some(createPipe.isTypeOf);
|
20680 | }
|
20681 | /**
|
20682 | * Return {@link Pipe} for a given `Type`.
|
20683 | */
|
20684 | resolve(type, throwIfNotFound = true) {
|
20685 | const metas = this._reflector.annotations(resolveForwardRef(type));
|
20686 | if (metas) {
|
20687 | const annotation = findLast(metas, createPipe.isTypeOf);
|
20688 | if (annotation) {
|
20689 | return annotation;
|
20690 | }
|
20691 | }
|
20692 | if (throwIfNotFound) {
|
20693 | throw new Error(`No Pipe decorator found on ${stringify(type)}`);
|
20694 | }
|
20695 | return null;
|
20696 | }
|
20697 | }
|
20698 |
|
20699 | /**
|
20700 | * @license
|
20701 | * Copyright Google LLC All Rights Reserved.
|
20702 | *
|
20703 | * Use of this source code is governed by an MIT-style license that can be
|
20704 | * found in the LICENSE file at https://angular.io/license
|
20705 | */
|
20706 | const LOG_VAR$1 = variable('_l');
|
20707 | const VIEW_VAR = variable('_v');
|
20708 | const CHECK_VAR = variable('_ck');
|
20709 | const COMP_VAR = variable('_co');
|
20710 | const EVENT_NAME_VAR = variable('en');
|
20711 | const ALLOW_DEFAULT_VAR = variable(`ad`);
|
20712 |
|
20713 | /**
|
20714 | * @license
|
20715 | * Copyright Google LLC All Rights Reserved.
|
20716 | *
|
20717 | * Use of this source code is governed by an MIT-style license that can be
|
20718 | * found in the LICENSE file at https://angular.io/license
|
20719 | */
|
20720 | const TS = /^(?!.*\.d\.ts$).*\.ts$/;
|
20721 | class ResolvedStaticSymbol {
|
20722 | constructor(symbol, metadata) {
|
20723 | this.symbol = symbol;
|
20724 | this.metadata = metadata;
|
20725 | }
|
20726 | }
|
20727 | const SUPPORTED_SCHEMA_VERSION = 4;
|
20728 | /**
|
20729 | * This class is responsible for loading metadata per symbol,
|
20730 | * and normalizing references between symbols.
|
20731 | *
|
20732 | * Internally, it only uses symbols without members,
|
20733 | * and deduces the values for symbols with members based
|
20734 | * on these symbols.
|
20735 | */
|
20736 | class StaticSymbolResolver {
|
20737 | constructor(host, staticSymbolCache, summaryResolver, errorRecorder) {
|
20738 | this.host = host;
|
20739 | this.staticSymbolCache = staticSymbolCache;
|
20740 | this.summaryResolver = summaryResolver;
|
20741 | this.errorRecorder = errorRecorder;
|
20742 | this.metadataCache = new Map();
|
20743 | // Note: this will only contain StaticSymbols without members!
|
20744 | this.resolvedSymbols = new Map();
|
20745 | // Note: this will only contain StaticSymbols without members!
|
20746 | this.importAs = new Map();
|
20747 | this.symbolResourcePaths = new Map();
|
20748 | this.symbolFromFile = new Map();
|
20749 | this.knownFileNameToModuleNames = new Map();
|
20750 | }
|
20751 | resolveSymbol(staticSymbol) {
|
20752 | if (staticSymbol.members.length > 0) {
|
20753 | return this._resolveSymbolMembers(staticSymbol);
|
20754 | }
|
20755 | // Note: always ask for a summary first,
|
20756 | // as we might have read shallow metadata via a .d.ts file
|
20757 | // for the symbol.
|
20758 | const resultFromSummary = this._resolveSymbolFromSummary(staticSymbol);
|
20759 | if (resultFromSummary) {
|
20760 | return resultFromSummary;
|
20761 | }
|
20762 | const resultFromCache = this.resolvedSymbols.get(staticSymbol);
|
20763 | if (resultFromCache) {
|
20764 | return resultFromCache;
|
20765 | }
|
20766 | // Note: Some users use libraries that were not compiled with ngc, i.e. they don't
|
20767 | // have summaries, only .d.ts files. So we always need to check both, the summary
|
20768 | // and metadata.
|
20769 | this._createSymbolsOf(staticSymbol.filePath);
|
20770 | return this.resolvedSymbols.get(staticSymbol);
|
20771 | }
|
20772 | /**
|
20773 | * getImportAs produces a symbol that can be used to import the given symbol.
|
20774 | * The import might be different than the symbol if the symbol is exported from
|
20775 | * a library with a summary; in which case we want to import the symbol from the
|
20776 | * ngfactory re-export instead of directly to avoid introducing a direct dependency
|
20777 | * on an otherwise indirect dependency.
|
20778 | *
|
20779 | * @param staticSymbol the symbol for which to generate a import symbol
|
20780 | */
|
20781 | getImportAs(staticSymbol, useSummaries = true) {
|
20782 | if (staticSymbol.members.length) {
|
20783 | const baseSymbol = this.getStaticSymbol(staticSymbol.filePath, staticSymbol.name);
|
20784 | const baseImportAs = this.getImportAs(baseSymbol, useSummaries);
|
20785 | return baseImportAs ?
|
20786 | this.getStaticSymbol(baseImportAs.filePath, baseImportAs.name, staticSymbol.members) :
|
20787 | null;
|
20788 | }
|
20789 | const summarizedFileName = stripSummaryForJitFileSuffix(staticSymbol.filePath);
|
20790 | if (summarizedFileName !== staticSymbol.filePath) {
|
20791 | const summarizedName = stripSummaryForJitNameSuffix(staticSymbol.name);
|
20792 | const baseSymbol = this.getStaticSymbol(summarizedFileName, summarizedName, staticSymbol.members);
|
20793 | const baseImportAs = this.getImportAs(baseSymbol, useSummaries);
|
20794 | return baseImportAs ? this.getStaticSymbol(summaryForJitFileName(baseImportAs.filePath), summaryForJitName(baseImportAs.name), baseSymbol.members) :
|
20795 | null;
|
20796 | }
|
20797 | let result = (useSummaries && this.summaryResolver.getImportAs(staticSymbol)) || null;
|
20798 | if (!result) {
|
20799 | result = this.importAs.get(staticSymbol);
|
20800 | }
|
20801 | return result;
|
20802 | }
|
20803 | /**
|
20804 | * getResourcePath produces the path to the original location of the symbol and should
|
20805 | * be used to determine the relative location of resource references recorded in
|
20806 | * symbol metadata.
|
20807 | */
|
20808 | getResourcePath(staticSymbol) {
|
20809 | return this.symbolResourcePaths.get(staticSymbol) || staticSymbol.filePath;
|
20810 | }
|
20811 | /**
|
20812 | * getTypeArity returns the number of generic type parameters the given symbol
|
20813 | * has. If the symbol is not a type the result is null.
|
20814 | */
|
20815 | getTypeArity(staticSymbol) {
|
20816 | // If the file is a factory/ngsummary file, don't resolve the symbol as doing so would
|
20817 | // cause the metadata for an factory/ngsummary file to be loaded which doesn't exist.
|
20818 | // All references to generated classes must include the correct arity whenever
|
20819 | // generating code.
|
20820 | if (isGeneratedFile(staticSymbol.filePath)) {
|
20821 | return null;
|
20822 | }
|
20823 | let resolvedSymbol = unwrapResolvedMetadata(this.resolveSymbol(staticSymbol));
|
20824 | while (resolvedSymbol && resolvedSymbol.metadata instanceof StaticSymbol) {
|
20825 | resolvedSymbol = unwrapResolvedMetadata(this.resolveSymbol(resolvedSymbol.metadata));
|
20826 | }
|
20827 | return (resolvedSymbol && resolvedSymbol.metadata && resolvedSymbol.metadata.arity) || null;
|
20828 | }
|
20829 | getKnownModuleName(filePath) {
|
20830 | return this.knownFileNameToModuleNames.get(filePath) || null;
|
20831 | }
|
20832 | recordImportAs(sourceSymbol, targetSymbol) {
|
20833 | sourceSymbol.assertNoMembers();
|
20834 | targetSymbol.assertNoMembers();
|
20835 | this.importAs.set(sourceSymbol, targetSymbol);
|
20836 | }
|
20837 | recordModuleNameForFileName(fileName, moduleName) {
|
20838 | this.knownFileNameToModuleNames.set(fileName, moduleName);
|
20839 | }
|
20840 | /**
|
20841 | * Invalidate all information derived from the given file and return the
|
20842 | * static symbols contained in the file.
|
20843 | *
|
20844 | * @param fileName the file to invalidate
|
20845 | */
|
20846 | invalidateFile(fileName) {
|
20847 | this.metadataCache.delete(fileName);
|
20848 | const symbols = this.symbolFromFile.get(fileName);
|
20849 | if (!symbols) {
|
20850 | return [];
|
20851 | }
|
20852 | this.symbolFromFile.delete(fileName);
|
20853 | for (const symbol of symbols) {
|
20854 | this.resolvedSymbols.delete(symbol);
|
20855 | this.importAs.delete(symbol);
|
20856 | this.symbolResourcePaths.delete(symbol);
|
20857 | }
|
20858 | return symbols;
|
20859 | }
|
20860 | /** @internal */
|
20861 | ignoreErrorsFor(cb) {
|
20862 | const recorder = this.errorRecorder;
|
20863 | this.errorRecorder = () => { };
|
20864 | try {
|
20865 | return cb();
|
20866 | }
|
20867 | finally {
|
20868 | this.errorRecorder = recorder;
|
20869 | }
|
20870 | }
|
20871 | _resolveSymbolMembers(staticSymbol) {
|
20872 | const members = staticSymbol.members;
|
20873 | const baseResolvedSymbol = this.resolveSymbol(this.getStaticSymbol(staticSymbol.filePath, staticSymbol.name));
|
20874 | if (!baseResolvedSymbol) {
|
20875 | return null;
|
20876 | }
|
20877 | let baseMetadata = unwrapResolvedMetadata(baseResolvedSymbol.metadata);
|
20878 | if (baseMetadata instanceof StaticSymbol) {
|
20879 | return new ResolvedStaticSymbol(staticSymbol, this.getStaticSymbol(baseMetadata.filePath, baseMetadata.name, members));
|
20880 | }
|
20881 | else if (baseMetadata && baseMetadata.__symbolic === 'class') {
|
20882 | if (baseMetadata.statics && members.length === 1) {
|
20883 | return new ResolvedStaticSymbol(staticSymbol, baseMetadata.statics[members[0]]);
|
20884 | }
|
20885 | }
|
20886 | else {
|
20887 | let value = baseMetadata;
|
20888 | for (let i = 0; i < members.length && value; i++) {
|
20889 | value = value[members[i]];
|
20890 | }
|
20891 | return new ResolvedStaticSymbol(staticSymbol, value);
|
20892 | }
|
20893 | return null;
|
20894 | }
|
20895 | _resolveSymbolFromSummary(staticSymbol) {
|
20896 | const summary = this.summaryResolver.resolveSummary(staticSymbol);
|
20897 | return summary ? new ResolvedStaticSymbol(staticSymbol, summary.metadata) : null;
|
20898 | }
|
20899 | /**
|
20900 | * getStaticSymbol produces a Type whose metadata is known but whose implementation is not loaded.
|
20901 | * All types passed to the StaticResolver should be pseudo-types returned by this method.
|
20902 | *
|
20903 | * @param declarationFile the absolute path of the file where the symbol is declared
|
20904 | * @param name the name of the type.
|
20905 | * @param members a symbol for a static member of the named type
|
20906 | */
|
20907 | getStaticSymbol(declarationFile, name, members) {
|
20908 | return this.staticSymbolCache.get(declarationFile, name, members);
|
20909 | }
|
20910 | /**
|
20911 | * hasDecorators checks a file's metadata for the presence of decorators without evaluating the
|
20912 | * metadata.
|
20913 | *
|
20914 | * @param filePath the absolute path to examine for decorators.
|
20915 | * @returns true if any class in the file has a decorator.
|
20916 | */
|
20917 | hasDecorators(filePath) {
|
20918 | const metadata = this.getModuleMetadata(filePath);
|
20919 | if (metadata['metadata']) {
|
20920 | return Object.keys(metadata['metadata']).some((metadataKey) => {
|
20921 | const entry = metadata['metadata'][metadataKey];
|
20922 | return entry && entry.__symbolic === 'class' && entry.decorators;
|
20923 | });
|
20924 | }
|
20925 | return false;
|
20926 | }
|
20927 | getSymbolsOf(filePath) {
|
20928 | const summarySymbols = this.summaryResolver.getSymbolsOf(filePath);
|
20929 | if (summarySymbols) {
|
20930 | return summarySymbols;
|
20931 | }
|
20932 | // Note: Some users use libraries that were not compiled with ngc, i.e. they don't
|
20933 | // have summaries, only .d.ts files, but `summaryResolver.isLibraryFile` returns true.
|
20934 | this._createSymbolsOf(filePath);
|
20935 | return this.symbolFromFile.get(filePath) || [];
|
20936 | }
|
20937 | _createSymbolsOf(filePath) {
|
20938 | if (this.symbolFromFile.has(filePath)) {
|
20939 | return;
|
20940 | }
|
20941 | const resolvedSymbols = [];
|
20942 | const metadata = this.getModuleMetadata(filePath);
|
20943 | if (metadata['importAs']) {
|
20944 | // Index bundle indices should use the importAs module name defined
|
20945 | // in the bundle.
|
20946 | this.knownFileNameToModuleNames.set(filePath, metadata['importAs']);
|
20947 | }
|
20948 | // handle the symbols in one of the re-export location
|
20949 | if (metadata['exports']) {
|
20950 | for (const moduleExport of metadata['exports']) {
|
20951 | // handle the symbols in the list of explicitly re-exported symbols.
|
20952 | if (moduleExport.export) {
|
20953 | moduleExport.export.forEach((exportSymbol) => {
|
20954 | let symbolName;
|
20955 | if (typeof exportSymbol === 'string') {
|
20956 | symbolName = exportSymbol;
|
20957 | }
|
20958 | else {
|
20959 | symbolName = exportSymbol.as;
|
20960 | }
|
20961 | symbolName = unescapeIdentifier(symbolName);
|
20962 | let symName = symbolName;
|
20963 | if (typeof exportSymbol !== 'string') {
|
20964 | symName = unescapeIdentifier(exportSymbol.name);
|
20965 | }
|
20966 | const resolvedModule = this.resolveModule(moduleExport.from, filePath);
|
20967 | if (resolvedModule) {
|
20968 | const targetSymbol = this.getStaticSymbol(resolvedModule, symName);
|
20969 | const sourceSymbol = this.getStaticSymbol(filePath, symbolName);
|
20970 | resolvedSymbols.push(this.createExport(sourceSymbol, targetSymbol));
|
20971 | }
|
20972 | });
|
20973 | }
|
20974 | else {
|
20975 | // Handle the symbols loaded by 'export *' directives.
|
20976 | const resolvedModule = this.resolveModule(moduleExport.from, filePath);
|
20977 | if (resolvedModule && resolvedModule !== filePath) {
|
20978 | const nestedExports = this.getSymbolsOf(resolvedModule);
|
20979 | nestedExports.forEach((targetSymbol) => {
|
20980 | const sourceSymbol = this.getStaticSymbol(filePath, targetSymbol.name);
|
20981 | resolvedSymbols.push(this.createExport(sourceSymbol, targetSymbol));
|
20982 | });
|
20983 | }
|
20984 | }
|
20985 | }
|
20986 | }
|
20987 | // handle the actual metadata. Has to be after the exports
|
20988 | // as there might be collisions in the names, and we want the symbols
|
20989 | // of the current module to win ofter reexports.
|
20990 | if (metadata['metadata']) {
|
20991 | // handle direct declarations of the symbol
|
20992 | const topLevelSymbolNames = new Set(Object.keys(metadata['metadata']).map(unescapeIdentifier));
|
20993 | const origins = metadata['origins'] || {};
|
20994 | Object.keys(metadata['metadata']).forEach((metadataKey) => {
|
20995 | const symbolMeta = metadata['metadata'][metadataKey];
|
20996 | const name = unescapeIdentifier(metadataKey);
|
20997 | const symbol = this.getStaticSymbol(filePath, name);
|
20998 | const origin = origins.hasOwnProperty(metadataKey) && origins[metadataKey];
|
20999 | if (origin) {
|
21000 | // If the symbol is from a bundled index, use the declaration location of the
|
21001 | // symbol so relative references (such as './my.html') will be calculated
|
21002 | // correctly.
|
21003 | const originFilePath = this.resolveModule(origin, filePath);
|
21004 | if (!originFilePath) {
|
21005 | this.reportError(new Error(`Couldn't resolve original symbol for ${origin} from ${this.host.getOutputName(filePath)}`));
|
21006 | }
|
21007 | else {
|
21008 | this.symbolResourcePaths.set(symbol, originFilePath);
|
21009 | }
|
21010 | }
|
21011 | resolvedSymbols.push(this.createResolvedSymbol(symbol, filePath, topLevelSymbolNames, symbolMeta));
|
21012 | });
|
21013 | }
|
21014 | const uniqueSymbols = new Set();
|
21015 | for (const resolvedSymbol of resolvedSymbols) {
|
21016 | this.resolvedSymbols.set(resolvedSymbol.symbol, resolvedSymbol);
|
21017 | uniqueSymbols.add(resolvedSymbol.symbol);
|
21018 | }
|
21019 | this.symbolFromFile.set(filePath, Array.from(uniqueSymbols));
|
21020 | }
|
21021 | createResolvedSymbol(sourceSymbol, topLevelPath, topLevelSymbolNames, metadata) {
|
21022 | // For classes that don't have Angular summaries / metadata,
|
21023 | // we only keep their arity, but nothing else
|
21024 | // (e.g. their constructor parameters).
|
21025 | // We do this to prevent introducing deep imports
|
21026 | // as we didn't generate .ngfactory.ts files with proper reexports.
|
21027 | const isTsFile = TS.test(sourceSymbol.filePath);
|
21028 | if (this.summaryResolver.isLibraryFile(sourceSymbol.filePath) && !isTsFile && metadata &&
|
21029 | metadata['__symbolic'] === 'class') {
|
21030 | const transformedMeta = { __symbolic: 'class', arity: metadata.arity };
|
21031 | return new ResolvedStaticSymbol(sourceSymbol, transformedMeta);
|
21032 | }
|
21033 | let _originalFileMemo;
|
21034 | const getOriginalName = () => {
|
21035 | if (!_originalFileMemo) {
|
21036 | // Guess what the original file name is from the reference. If it has a `.d.ts` extension
|
21037 | // replace it with `.ts`. If it already has `.ts` just leave it in place. If it doesn't have
|
21038 | // .ts or .d.ts, append `.ts'. Also, if it is in `node_modules`, trim the `node_module`
|
21039 | // location as it is not important to finding the file.
|
21040 | _originalFileMemo =
|
21041 | this.host.getOutputName(topLevelPath.replace(/((\.ts)|(\.d\.ts)|)$/, '.ts')
|
21042 | .replace(/^.*node_modules[/\\]/, ''));
|
21043 | }
|
21044 | return _originalFileMemo;
|
21045 | };
|
21046 | const self = this;
|
21047 | class ReferenceTransformer extends ValueTransformer {
|
21048 | visitStringMap(map, functionParams) {
|
21049 | const symbolic = map['__symbolic'];
|
21050 | if (symbolic === 'function') {
|
21051 | const oldLen = functionParams.length;
|
21052 | functionParams.push(...(map['parameters'] || []));
|
21053 | const result = super.visitStringMap(map, functionParams);
|
21054 | functionParams.length = oldLen;
|
21055 | return result;
|
21056 | }
|
21057 | else if (symbolic === 'reference') {
|
21058 | const module = map['module'];
|
21059 | const name = map['name'] ? unescapeIdentifier(map['name']) : map['name'];
|
21060 | if (!name) {
|
21061 | return null;
|
21062 | }
|
21063 | let filePath;
|
21064 | if (module) {
|
21065 | filePath = self.resolveModule(module, sourceSymbol.filePath);
|
21066 | if (!filePath) {
|
21067 | return {
|
21068 | __symbolic: 'error',
|
21069 | message: `Could not resolve ${module} relative to ${self.host.getMetadataFor(sourceSymbol.filePath)}.`,
|
21070 | line: map['line'],
|
21071 | character: map['character'],
|
21072 | fileName: getOriginalName()
|
21073 | };
|
21074 | }
|
21075 | return {
|
21076 | __symbolic: 'resolved',
|
21077 | symbol: self.getStaticSymbol(filePath, name),
|
21078 | line: map['line'],
|
21079 | character: map['character'],
|
21080 | fileName: getOriginalName()
|
21081 | };
|
21082 | }
|
21083 | else if (functionParams.indexOf(name) >= 0) {
|
21084 | // reference to a function parameter
|
21085 | return { __symbolic: 'reference', name: name };
|
21086 | }
|
21087 | else {
|
21088 | if (topLevelSymbolNames.has(name)) {
|
21089 | return self.getStaticSymbol(topLevelPath, name);
|
21090 | }
|
21091 | }
|
21092 | }
|
21093 | else if (symbolic === 'error') {
|
21094 | return Object.assign(Object.assign({}, map), { fileName: getOriginalName() });
|
21095 | }
|
21096 | else {
|
21097 | return super.visitStringMap(map, functionParams);
|
21098 | }
|
21099 | }
|
21100 | }
|
21101 | const transformedMeta = visitValue(metadata, new ReferenceTransformer(), []);
|
21102 | let unwrappedTransformedMeta = unwrapResolvedMetadata(transformedMeta);
|
21103 | if (unwrappedTransformedMeta instanceof StaticSymbol) {
|
21104 | return this.createExport(sourceSymbol, unwrappedTransformedMeta);
|
21105 | }
|
21106 | return new ResolvedStaticSymbol(sourceSymbol, transformedMeta);
|
21107 | }
|
21108 | createExport(sourceSymbol, targetSymbol) {
|
21109 | sourceSymbol.assertNoMembers();
|
21110 | targetSymbol.assertNoMembers();
|
21111 | if (this.summaryResolver.isLibraryFile(sourceSymbol.filePath) &&
|
21112 | this.summaryResolver.isLibraryFile(targetSymbol.filePath)) {
|
21113 | // This case is for an ng library importing symbols from a plain ts library
|
21114 | // transitively.
|
21115 | // Note: We rely on the fact that we discover symbols in the direction
|
21116 | // from source files to library files
|
21117 | this.importAs.set(targetSymbol, this.getImportAs(sourceSymbol) || sourceSymbol);
|
21118 | }
|
21119 | return new ResolvedStaticSymbol(sourceSymbol, targetSymbol);
|
21120 | }
|
21121 | reportError(error, context, path) {
|
21122 | if (this.errorRecorder) {
|
21123 | this.errorRecorder(error, (context && context.filePath) || path);
|
21124 | }
|
21125 | else {
|
21126 | throw error;
|
21127 | }
|
21128 | }
|
21129 | /**
|
21130 | * @param module an absolute path to a module file.
|
21131 | */
|
21132 | getModuleMetadata(module) {
|
21133 | let moduleMetadata = this.metadataCache.get(module);
|
21134 | if (!moduleMetadata) {
|
21135 | const moduleMetadatas = this.host.getMetadataFor(module);
|
21136 | if (moduleMetadatas) {
|
21137 | let maxVersion = -1;
|
21138 | moduleMetadatas.forEach((md) => {
|
21139 | if (md && md['version'] > maxVersion) {
|
21140 | maxVersion = md['version'];
|
21141 | moduleMetadata = md;
|
21142 | }
|
21143 | });
|
21144 | }
|
21145 | if (!moduleMetadata) {
|
21146 | moduleMetadata =
|
21147 | { __symbolic: 'module', version: SUPPORTED_SCHEMA_VERSION, module: module, metadata: {} };
|
21148 | }
|
21149 | if (moduleMetadata['version'] != SUPPORTED_SCHEMA_VERSION) {
|
21150 | const errorMessage = moduleMetadata['version'] == 2 ?
|
21151 | `Unsupported metadata version ${moduleMetadata['version']} for module ${module}. This module should be compiled with a newer version of ngc` :
|
21152 | `Metadata version mismatch for module ${this.host.getOutputName(module)}, found version ${moduleMetadata['version']}, expected ${SUPPORTED_SCHEMA_VERSION}`;
|
21153 | this.reportError(new Error(errorMessage));
|
21154 | }
|
21155 | this.metadataCache.set(module, moduleMetadata);
|
21156 | }
|
21157 | return moduleMetadata;
|
21158 | }
|
21159 | getSymbolByModule(module, symbolName, containingFile) {
|
21160 | const filePath = this.resolveModule(module, containingFile);
|
21161 | if (!filePath) {
|
21162 | this.reportError(new Error(`Could not resolve module ${module}${containingFile ? ' relative to ' + this.host.getOutputName(containingFile) : ''}`));
|
21163 | return this.getStaticSymbol(`ERROR:${module}`, symbolName);
|
21164 | }
|
21165 | return this.getStaticSymbol(filePath, symbolName);
|
21166 | }
|
21167 | resolveModule(module, containingFile) {
|
21168 | try {
|
21169 | return this.host.moduleNameToFileName(module, containingFile);
|
21170 | }
|
21171 | catch (e) {
|
21172 | console.error(`Could not resolve module '${module}' relative to file ${containingFile}`);
|
21173 | this.reportError(e, undefined, containingFile);
|
21174 | }
|
21175 | return null;
|
21176 | }
|
21177 | }
|
21178 | // Remove extra underscore from escaped identifier.
|
21179 | // See https://github.com/Microsoft/TypeScript/blob/master/src/compiler/utilities.ts
|
21180 | function unescapeIdentifier(identifier) {
|
21181 | return identifier.startsWith('___') ? identifier.substr(1) : identifier;
|
21182 | }
|
21183 | function unwrapResolvedMetadata(metadata) {
|
21184 | if (metadata && metadata.__symbolic === 'resolved') {
|
21185 | return metadata.symbol;
|
21186 | }
|
21187 | return metadata;
|
21188 | }
|
21189 |
|
21190 | /**
|
21191 | * @license
|
21192 | * Copyright Google LLC All Rights Reserved.
|
21193 | *
|
21194 | * Use of this source code is governed by an MIT-style license that can be
|
21195 | * found in the LICENSE file at https://angular.io/license
|
21196 | */
|
21197 | function deserializeSummaries(symbolCache, summaryResolver, libraryFileName, json) {
|
21198 | const deserializer = new FromJsonDeserializer(symbolCache, summaryResolver);
|
21199 | return deserializer.deserialize(libraryFileName, json);
|
21200 | }
|
21201 | class FromJsonDeserializer extends ValueTransformer {
|
21202 | constructor(symbolCache, summaryResolver) {
|
21203 | super();
|
21204 | this.symbolCache = symbolCache;
|
21205 | this.summaryResolver = summaryResolver;
|
21206 | }
|
21207 | deserialize(libraryFileName, json) {
|
21208 | const data = JSON.parse(json);
|
21209 | const allImportAs = [];
|
21210 | this.symbols = data.symbols.map((serializedSymbol) => this.symbolCache.get(this.summaryResolver.fromSummaryFileName(serializedSymbol.filePath, libraryFileName), serializedSymbol.name));
|
21211 | data.symbols.forEach((serializedSymbol, index) => {
|
21212 | const symbol = this.symbols[index];
|
21213 | const importAs = serializedSymbol.importAs;
|
21214 | if (typeof importAs === 'number') {
|
21215 | allImportAs.push({ symbol, importAs: this.symbols[importAs] });
|
21216 | }
|
21217 | else if (typeof importAs === 'string') {
|
21218 | allImportAs.push({ symbol, importAs: this.symbolCache.get(ngfactoryFilePath(libraryFileName), importAs) });
|
21219 | }
|
21220 | });
|
21221 | const summaries = visitValue(data.summaries, this, null);
|
21222 | return { moduleName: data.moduleName, summaries, importAs: allImportAs };
|
21223 | }
|
21224 | visitStringMap(map, context) {
|
21225 | if ('__symbol' in map) {
|
21226 | const baseSymbol = this.symbols[map['__symbol']];
|
21227 | const members = map['members'];
|
21228 | return members.length ? this.symbolCache.get(baseSymbol.filePath, baseSymbol.name, members) :
|
21229 | baseSymbol;
|
21230 | }
|
21231 | else {
|
21232 | return super.visitStringMap(map, context);
|
21233 | }
|
21234 | }
|
21235 | }
|
21236 |
|
21237 | /**
|
21238 | * @license
|
21239 | * Copyright Google LLC All Rights Reserved.
|
21240 | *
|
21241 | * Use of this source code is governed by an MIT-style license that can be
|
21242 | * found in the LICENSE file at https://angular.io/license
|
21243 | */
|
21244 | function analyzeNgModules(fileNames, host, staticSymbolResolver, metadataResolver) {
|
21245 | const files = _analyzeFilesIncludingNonProgramFiles(fileNames, host, staticSymbolResolver, metadataResolver);
|
21246 | return mergeAnalyzedFiles(files);
|
21247 | }
|
21248 | // Analyzes all of the program files,
|
21249 | // including files that are not part of the program
|
21250 | // but are referenced by an NgModule.
|
21251 | function _analyzeFilesIncludingNonProgramFiles(fileNames, host, staticSymbolResolver, metadataResolver) {
|
21252 | const seenFiles = new Set();
|
21253 | const files = [];
|
21254 | const visitFile = (fileName) => {
|
21255 | if (seenFiles.has(fileName) || !host.isSourceFile(fileName)) {
|
21256 | return false;
|
21257 | }
|
21258 | seenFiles.add(fileName);
|
21259 | const analyzedFile = analyzeFile(host, staticSymbolResolver, metadataResolver, fileName);
|
21260 | files.push(analyzedFile);
|
21261 | analyzedFile.ngModules.forEach(ngModule => {
|
21262 | ngModule.transitiveModule.modules.forEach(modMeta => visitFile(modMeta.reference.filePath));
|
21263 | });
|
21264 | };
|
21265 | fileNames.forEach((fileName) => visitFile(fileName));
|
21266 | return files;
|
21267 | }
|
21268 | function analyzeFile(host, staticSymbolResolver, metadataResolver, fileName) {
|
21269 | const abstractDirectives = [];
|
21270 | const directives = [];
|
21271 | const pipes = [];
|
21272 | const injectables = [];
|
21273 | const ngModules = [];
|
21274 | const hasDecorators = staticSymbolResolver.hasDecorators(fileName);
|
21275 | let exportsNonSourceFiles = false;
|
21276 | const isDeclarationFile = fileName.endsWith('.d.ts');
|
21277 | // Don't analyze .d.ts files that have no decorators as a shortcut
|
21278 | // to speed up the analysis. This prevents us from
|
21279 | // resolving the references in these files.
|
21280 | // Note: exportsNonSourceFiles is only needed when compiling with summaries,
|
21281 | // which is not the case when .d.ts files are treated as input files.
|
21282 | if (!isDeclarationFile || hasDecorators) {
|
21283 | staticSymbolResolver.getSymbolsOf(fileName).forEach((symbol) => {
|
21284 | const resolvedSymbol = staticSymbolResolver.resolveSymbol(symbol);
|
21285 | const symbolMeta = resolvedSymbol.metadata;
|
21286 | if (!symbolMeta || symbolMeta.__symbolic === 'error') {
|
21287 | return;
|
21288 | }
|
21289 | let isNgSymbol = false;
|
21290 | if (symbolMeta.__symbolic === 'class') {
|
21291 | if (metadataResolver.isDirective(symbol)) {
|
21292 | isNgSymbol = true;
|
21293 | // This directive either has a selector or doesn't. Selector-less directives get tracked
|
21294 | // in abstractDirectives, not directives. The compiler doesn't deal with selector-less
|
21295 | // directives at all, really, other than to persist their metadata. This is done so that
|
21296 | // apps will have an easier time migrating to Ivy, which requires the selector-less
|
21297 | // annotations to be applied.
|
21298 | if (!metadataResolver.isAbstractDirective(symbol)) {
|
21299 | // The directive is an ordinary directive.
|
21300 | directives.push(symbol);
|
21301 | }
|
21302 | else {
|
21303 | // The directive has no selector and is an "abstract" directive, so track it
|
21304 | // accordingly.
|
21305 | abstractDirectives.push(symbol);
|
21306 | }
|
21307 | }
|
21308 | else if (metadataResolver.isPipe(symbol)) {
|
21309 | isNgSymbol = true;
|
21310 | pipes.push(symbol);
|
21311 | }
|
21312 | else if (metadataResolver.isNgModule(symbol)) {
|
21313 | const ngModule = metadataResolver.getNgModuleMetadata(symbol, false);
|
21314 | if (ngModule) {
|
21315 | isNgSymbol = true;
|
21316 | ngModules.push(ngModule);
|
21317 | }
|
21318 | }
|
21319 | else if (metadataResolver.isInjectable(symbol)) {
|
21320 | isNgSymbol = true;
|
21321 | const injectable = metadataResolver.getInjectableMetadata(symbol, null, false);
|
21322 | if (injectable) {
|
21323 | injectables.push(injectable);
|
21324 | }
|
21325 | }
|
21326 | }
|
21327 | if (!isNgSymbol) {
|
21328 | exportsNonSourceFiles =
|
21329 | exportsNonSourceFiles || isValueExportingNonSourceFile(host, symbolMeta);
|
21330 | }
|
21331 | });
|
21332 | }
|
21333 | return {
|
21334 | fileName,
|
21335 | directives,
|
21336 | abstractDirectives,
|
21337 | pipes,
|
21338 | ngModules,
|
21339 | injectables,
|
21340 | exportsNonSourceFiles,
|
21341 | };
|
21342 | }
|
21343 | function isValueExportingNonSourceFile(host, metadata) {
|
21344 | let exportsNonSourceFiles = false;
|
21345 | class Visitor {
|
21346 | visitArray(arr, context) {
|
21347 | arr.forEach(v => visitValue(v, this, context));
|
21348 | }
|
21349 | visitStringMap(map, context) {
|
21350 | Object.keys(map).forEach((key) => visitValue(map[key], this, context));
|
21351 | }
|
21352 | visitPrimitive(value, context) { }
|
21353 | visitOther(value, context) {
|
21354 | if (value instanceof StaticSymbol && !host.isSourceFile(value.filePath)) {
|
21355 | exportsNonSourceFiles = true;
|
21356 | }
|
21357 | }
|
21358 | }
|
21359 | visitValue(metadata, new Visitor(), null);
|
21360 | return exportsNonSourceFiles;
|
21361 | }
|
21362 | function mergeAnalyzedFiles(analyzedFiles) {
|
21363 | const allNgModules = [];
|
21364 | const ngModuleByPipeOrDirective = new Map();
|
21365 | const allPipesAndDirectives = new Set();
|
21366 | analyzedFiles.forEach(af => {
|
21367 | af.ngModules.forEach(ngModule => {
|
21368 | allNgModules.push(ngModule);
|
21369 | ngModule.declaredDirectives.forEach(d => ngModuleByPipeOrDirective.set(d.reference, ngModule));
|
21370 | ngModule.declaredPipes.forEach(p => ngModuleByPipeOrDirective.set(p.reference, ngModule));
|
21371 | });
|
21372 | af.directives.forEach(d => allPipesAndDirectives.add(d));
|
21373 | af.pipes.forEach(p => allPipesAndDirectives.add(p));
|
21374 | });
|
21375 | const symbolsMissingModule = [];
|
21376 | allPipesAndDirectives.forEach(ref => {
|
21377 | if (!ngModuleByPipeOrDirective.has(ref)) {
|
21378 | symbolsMissingModule.push(ref);
|
21379 | }
|
21380 | });
|
21381 | return {
|
21382 | ngModules: allNgModules,
|
21383 | ngModuleByPipeOrDirective,
|
21384 | symbolsMissingModule,
|
21385 | files: analyzedFiles
|
21386 | };
|
21387 | }
|
21388 |
|
21389 | /**
|
21390 | * @license
|
21391 | * Copyright Google LLC All Rights Reserved.
|
21392 | *
|
21393 | * Use of this source code is governed by an MIT-style license that can be
|
21394 | * found in the LICENSE file at https://angular.io/license
|
21395 | */
|
21396 | const FORMATTED_MESSAGE = 'ngFormattedMessage';
|
21397 | function indentStr(level) {
|
21398 | if (level <= 0)
|
21399 | return '';
|
21400 | if (level < 6)
|
21401 | return ['', ' ', ' ', ' ', ' ', ' '][level];
|
21402 | const half = indentStr(Math.floor(level / 2));
|
21403 | return half + half + (level % 2 === 1 ? ' ' : '');
|
21404 | }
|
21405 | function formatChain(chain, indent = 0) {
|
21406 | if (!chain)
|
21407 | return '';
|
21408 | const position = chain.position ?
|
21409 | `${chain.position.fileName}(${chain.position.line + 1},${chain.position.column + 1})` :
|
21410 | '';
|
21411 | const prefix = position && indent === 0 ? `${position}: ` : '';
|
21412 | const postfix = position && indent !== 0 ? ` at ${position}` : '';
|
21413 | let message = `${prefix}${chain.message}${postfix}`;
|
21414 | if (chain.next) {
|
21415 | for (const kid of chain.next) {
|
21416 | message += '\n' + formatChain(kid, indent + 2);
|
21417 | }
|
21418 | }
|
21419 | return `${indentStr(indent)}${message}`;
|
21420 | }
|
21421 | function formattedError(chain) {
|
21422 | const message = formatChain(chain) + '.';
|
21423 | const error = syntaxError(message);
|
21424 | error[FORMATTED_MESSAGE] = true;
|
21425 | error.chain = chain;
|
21426 | error.position = chain.position;
|
21427 | return error;
|
21428 | }
|
21429 | function isFormattedError(error) {
|
21430 | return !!error[FORMATTED_MESSAGE];
|
21431 | }
|
21432 |
|
21433 | /**
|
21434 | * @license
|
21435 | * Copyright Google LLC All Rights Reserved.
|
21436 | *
|
21437 | * Use of this source code is governed by an MIT-style license that can be
|
21438 | * found in the LICENSE file at https://angular.io/license
|
21439 | */
|
21440 | const ANGULAR_CORE = '@angular/core';
|
21441 | const ANGULAR_ROUTER = '@angular/router';
|
21442 | const HIDDEN_KEY = /^\$.*\$$/;
|
21443 | const IGNORE = {
|
21444 | __symbolic: 'ignore'
|
21445 | };
|
21446 | const USE_VALUE$1 = 'useValue';
|
21447 | const PROVIDE = 'provide';
|
21448 | const REFERENCE_SET = new Set([USE_VALUE$1, 'useFactory', 'data', 'id', 'loadChildren']);
|
21449 | const TYPEGUARD_POSTFIX = 'TypeGuard';
|
21450 | const USE_IF = 'UseIf';
|
21451 | function shouldIgnore(value) {
|
21452 | return value && value.__symbolic == 'ignore';
|
21453 | }
|
21454 | /**
|
21455 | * A static reflector implements enough of the Reflector API that is necessary to compile
|
21456 | * templates statically.
|
21457 | */
|
21458 | class StaticReflector {
|
21459 | constructor(summaryResolver, symbolResolver, knownMetadataClasses = [], knownMetadataFunctions = [], errorRecorder) {
|
21460 | this.summaryResolver = summaryResolver;
|
21461 | this.symbolResolver = symbolResolver;
|
21462 | this.errorRecorder = errorRecorder;
|
21463 | this.annotationCache = new Map();
|
21464 | this.shallowAnnotationCache = new Map();
|
21465 | this.propertyCache = new Map();
|
21466 | this.parameterCache = new Map();
|
21467 | this.methodCache = new Map();
|
21468 | this.staticCache = new Map();
|
21469 | this.conversionMap = new Map();
|
21470 | this.resolvedExternalReferences = new Map();
|
21471 | this.annotationForParentClassWithSummaryKind = new Map();
|
21472 | this.initializeConversionMap();
|
21473 | knownMetadataClasses.forEach((kc) => this._registerDecoratorOrConstructor(this.getStaticSymbol(kc.filePath, kc.name), kc.ctor));
|
21474 | knownMetadataFunctions.forEach((kf) => this._registerFunction(this.getStaticSymbol(kf.filePath, kf.name), kf.fn));
|
21475 | this.annotationForParentClassWithSummaryKind.set(CompileSummaryKind.Directive, [createDirective, createComponent]);
|
21476 | this.annotationForParentClassWithSummaryKind.set(CompileSummaryKind.Pipe, [createPipe]);
|
21477 | this.annotationForParentClassWithSummaryKind.set(CompileSummaryKind.NgModule, [createNgModule]);
|
21478 | this.annotationForParentClassWithSummaryKind.set(CompileSummaryKind.Injectable, [createInjectable, createPipe, createDirective, createComponent, createNgModule]);
|
21479 | }
|
21480 | componentModuleUrl(typeOrFunc) {
|
21481 | const staticSymbol = this.findSymbolDeclaration(typeOrFunc);
|
21482 | return this.symbolResolver.getResourcePath(staticSymbol);
|
21483 | }
|
21484 | /**
|
21485 | * Invalidate the specified `symbols` on program change.
|
21486 | * @param symbols
|
21487 | */
|
21488 | invalidateSymbols(symbols) {
|
21489 | for (const symbol of symbols) {
|
21490 | this.annotationCache.delete(symbol);
|
21491 | this.shallowAnnotationCache.delete(symbol);
|
21492 | this.propertyCache.delete(symbol);
|
21493 | this.parameterCache.delete(symbol);
|
21494 | this.methodCache.delete(symbol);
|
21495 | this.staticCache.delete(symbol);
|
21496 | this.conversionMap.delete(symbol);
|
21497 | }
|
21498 | }
|
21499 | resolveExternalReference(ref, containingFile) {
|
21500 | let key = undefined;
|
21501 | if (!containingFile) {
|
21502 | key = `${ref.moduleName}:${ref.name}`;
|
21503 | const declarationSymbol = this.resolvedExternalReferences.get(key);
|
21504 | if (declarationSymbol)
|
21505 | return declarationSymbol;
|
21506 | }
|
21507 | const refSymbol = this.symbolResolver.getSymbolByModule(ref.moduleName, ref.name, containingFile);
|
21508 | const declarationSymbol = this.findSymbolDeclaration(refSymbol);
|
21509 | if (!containingFile) {
|
21510 | this.symbolResolver.recordModuleNameForFileName(refSymbol.filePath, ref.moduleName);
|
21511 | this.symbolResolver.recordImportAs(declarationSymbol, refSymbol);
|
21512 | }
|
21513 | if (key) {
|
21514 | this.resolvedExternalReferences.set(key, declarationSymbol);
|
21515 | }
|
21516 | return declarationSymbol;
|
21517 | }
|
21518 | findDeclaration(moduleUrl, name, containingFile) {
|
21519 | return this.findSymbolDeclaration(this.symbolResolver.getSymbolByModule(moduleUrl, name, containingFile));
|
21520 | }
|
21521 | tryFindDeclaration(moduleUrl, name, containingFile) {
|
21522 | return this.symbolResolver.ignoreErrorsFor(() => this.findDeclaration(moduleUrl, name, containingFile));
|
21523 | }
|
21524 | findSymbolDeclaration(symbol) {
|
21525 | const resolvedSymbol = this.symbolResolver.resolveSymbol(symbol);
|
21526 | if (resolvedSymbol) {
|
21527 | let resolvedMetadata = resolvedSymbol.metadata;
|
21528 | if (resolvedMetadata && resolvedMetadata.__symbolic === 'resolved') {
|
21529 | resolvedMetadata = resolvedMetadata.symbol;
|
21530 | }
|
21531 | if (resolvedMetadata instanceof StaticSymbol) {
|
21532 | return this.findSymbolDeclaration(resolvedSymbol.metadata);
|
21533 | }
|
21534 | }
|
21535 | return symbol;
|
21536 | }
|
21537 | tryAnnotations(type) {
|
21538 | const originalRecorder = this.errorRecorder;
|
21539 | this.errorRecorder = (error, fileName) => { };
|
21540 | try {
|
21541 | return this.annotations(type);
|
21542 | }
|
21543 | finally {
|
21544 | this.errorRecorder = originalRecorder;
|
21545 | }
|
21546 | }
|
21547 | annotations(type) {
|
21548 | return this._annotations(type, (type, decorators) => this.simplify(type, decorators), this.annotationCache);
|
21549 | }
|
21550 | shallowAnnotations(type) {
|
21551 | return this._annotations(type, (type, decorators) => this.simplify(type, decorators, true), this.shallowAnnotationCache);
|
21552 | }
|
21553 | _annotations(type, simplify, annotationCache) {
|
21554 | let annotations = annotationCache.get(type);
|
21555 | if (!annotations) {
|
21556 | annotations = [];
|
21557 | const classMetadata = this.getTypeMetadata(type);
|
21558 | const parentType = this.findParentType(type, classMetadata);
|
21559 | if (parentType) {
|
21560 | const parentAnnotations = this.annotations(parentType);
|
21561 | annotations.push(...parentAnnotations);
|
21562 | }
|
21563 | let ownAnnotations = [];
|
21564 | if (classMetadata['decorators']) {
|
21565 | ownAnnotations = simplify(type, classMetadata['decorators']);
|
21566 | if (ownAnnotations) {
|
21567 | annotations.push(...ownAnnotations);
|
21568 | }
|
21569 | }
|
21570 | if (parentType && !this.summaryResolver.isLibraryFile(type.filePath) &&
|
21571 | this.summaryResolver.isLibraryFile(parentType.filePath)) {
|
21572 | const summary = this.summaryResolver.resolveSummary(parentType);
|
21573 | if (summary && summary.type) {
|
21574 | const requiredAnnotationTypes = this.annotationForParentClassWithSummaryKind.get(summary.type.summaryKind);
|
21575 | const typeHasRequiredAnnotation = requiredAnnotationTypes.some((requiredType) => ownAnnotations.some(ann => requiredType.isTypeOf(ann)));
|
21576 | if (!typeHasRequiredAnnotation) {
|
21577 | 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`,
|
21578 | /* summary */ undefined, `Please add a ${requiredAnnotationTypes.map((type) => type.ngMetadataName)
|
21579 | .join(' or ')} decorator to the class`), type), type);
|
21580 | }
|
21581 | }
|
21582 | }
|
21583 | annotationCache.set(type, annotations.filter(ann => !!ann));
|
21584 | }
|
21585 | return annotations;
|
21586 | }
|
21587 | propMetadata(type) {
|
21588 | let propMetadata = this.propertyCache.get(type);
|
21589 | if (!propMetadata) {
|
21590 | const classMetadata = this.getTypeMetadata(type);
|
21591 | propMetadata = {};
|
21592 | const parentType = this.findParentType(type, classMetadata);
|
21593 | if (parentType) {
|
21594 | const parentPropMetadata = this.propMetadata(parentType);
|
21595 | Object.keys(parentPropMetadata).forEach((parentProp) => {
|
21596 | propMetadata[parentProp] = parentPropMetadata[parentProp];
|
21597 | });
|
21598 | }
|
21599 | const members = classMetadata['members'] || {};
|
21600 | Object.keys(members).forEach((propName) => {
|
21601 | const propData = members[propName];
|
21602 | const prop = propData
|
21603 | .find(a => a['__symbolic'] == 'property' || a['__symbolic'] == 'method');
|
21604 | const decorators = [];
|
21605 | // hasOwnProperty() is used here to make sure we do not look up methods
|
21606 | // on `Object.prototype`.
|
21607 | if (propMetadata === null || propMetadata === void 0 ? void 0 : propMetadata.hasOwnProperty(propName)) {
|
21608 | decorators.push(...propMetadata[propName]);
|
21609 | }
|
21610 | propMetadata[propName] = decorators;
|
21611 | if (prop && prop['decorators']) {
|
21612 | decorators.push(...this.simplify(type, prop['decorators']));
|
21613 | }
|
21614 | });
|
21615 | this.propertyCache.set(type, propMetadata);
|
21616 | }
|
21617 | return propMetadata;
|
21618 | }
|
21619 | parameters(type) {
|
21620 | if (!(type instanceof StaticSymbol)) {
|
21621 | this.reportError(new Error(`parameters received ${JSON.stringify(type)} which is not a StaticSymbol`), type);
|
21622 | return [];
|
21623 | }
|
21624 | try {
|
21625 | let parameters = this.parameterCache.get(type);
|
21626 | if (!parameters) {
|
21627 | const classMetadata = this.getTypeMetadata(type);
|
21628 | const parentType = this.findParentType(type, classMetadata);
|
21629 | const members = classMetadata ? classMetadata['members'] : null;
|
21630 | const ctorData = members ? members['__ctor__'] : null;
|
21631 | if (ctorData) {
|
21632 | const ctor = ctorData.find(a => a['__symbolic'] == 'constructor');
|
21633 | const rawParameterTypes = ctor['parameters'] || [];
|
21634 | const parameterDecorators = this.simplify(type, ctor['parameterDecorators'] || []);
|
21635 | parameters = [];
|
21636 | rawParameterTypes.forEach((rawParamType, index) => {
|
21637 | const nestedResult = [];
|
21638 | const paramType = this.trySimplify(type, rawParamType);
|
21639 | if (paramType)
|
21640 | nestedResult.push(paramType);
|
21641 | const decorators = parameterDecorators ? parameterDecorators[index] : null;
|
21642 | if (decorators) {
|
21643 | nestedResult.push(...decorators);
|
21644 | }
|
21645 | parameters.push(nestedResult);
|
21646 | });
|
21647 | }
|
21648 | else if (parentType) {
|
21649 | parameters = this.parameters(parentType);
|
21650 | }
|
21651 | if (!parameters) {
|
21652 | parameters = [];
|
21653 | }
|
21654 | this.parameterCache.set(type, parameters);
|
21655 | }
|
21656 | return parameters;
|
21657 | }
|
21658 | catch (e) {
|
21659 | console.error(`Failed on type ${JSON.stringify(type)} with error ${e}`);
|
21660 | throw e;
|
21661 | }
|
21662 | }
|
21663 | _methodNames(type) {
|
21664 | let methodNames = this.methodCache.get(type);
|
21665 | if (!methodNames) {
|
21666 | const classMetadata = this.getTypeMetadata(type);
|
21667 | methodNames = {};
|
21668 | const parentType = this.findParentType(type, classMetadata);
|
21669 | if (parentType) {
|
21670 | const parentMethodNames = this._methodNames(parentType);
|
21671 | Object.keys(parentMethodNames).forEach((parentProp) => {
|
21672 | methodNames[parentProp] = parentMethodNames[parentProp];
|
21673 | });
|
21674 | }
|
21675 | const members = classMetadata['members'] || {};
|
21676 | Object.keys(members).forEach((propName) => {
|
21677 | const propData = members[propName];
|
21678 | const isMethod = propData.some(a => a['__symbolic'] == 'method');
|
21679 | methodNames[propName] = methodNames[propName] || isMethod;
|
21680 | });
|
21681 | this.methodCache.set(type, methodNames);
|
21682 | }
|
21683 | return methodNames;
|
21684 | }
|
21685 | _staticMembers(type) {
|
21686 | let staticMembers = this.staticCache.get(type);
|
21687 | if (!staticMembers) {
|
21688 | const classMetadata = this.getTypeMetadata(type);
|
21689 | const staticMemberData = classMetadata['statics'] || {};
|
21690 | staticMembers = Object.keys(staticMemberData);
|
21691 | this.staticCache.set(type, staticMembers);
|
21692 | }
|
21693 | return staticMembers;
|
21694 | }
|
21695 | findParentType(type, classMetadata) {
|
21696 | const parentType = this.trySimplify(type, classMetadata['extends']);
|
21697 | if (parentType instanceof StaticSymbol) {
|
21698 | return parentType;
|
21699 | }
|
21700 | }
|
21701 | hasLifecycleHook(type, lcProperty) {
|
21702 | if (!(type instanceof StaticSymbol)) {
|
21703 | this.reportError(new Error(`hasLifecycleHook received ${JSON.stringify(type)} which is not a StaticSymbol`), type);
|
21704 | }
|
21705 | try {
|
21706 | return !!this._methodNames(type)[lcProperty];
|
21707 | }
|
21708 | catch (e) {
|
21709 | console.error(`Failed on type ${JSON.stringify(type)} with error ${e}`);
|
21710 | throw e;
|
21711 | }
|
21712 | }
|
21713 | guards(type) {
|
21714 | if (!(type instanceof StaticSymbol)) {
|
21715 | this.reportError(new Error(`guards received ${JSON.stringify(type)} which is not a StaticSymbol`), type);
|
21716 | return {};
|
21717 | }
|
21718 | const staticMembers = this._staticMembers(type);
|
21719 | const result = {};
|
21720 | for (let name of staticMembers) {
|
21721 | if (name.endsWith(TYPEGUARD_POSTFIX)) {
|
21722 | let property = name.substr(0, name.length - TYPEGUARD_POSTFIX.length);
|
21723 | let value;
|
21724 | if (property.endsWith(USE_IF)) {
|
21725 | property = name.substr(0, property.length - USE_IF.length);
|
21726 | value = USE_IF;
|
21727 | }
|
21728 | else {
|
21729 | value = this.getStaticSymbol(type.filePath, type.name, [name]);
|
21730 | }
|
21731 | result[property] = value;
|
21732 | }
|
21733 | }
|
21734 | return result;
|
21735 | }
|
21736 | _registerDecoratorOrConstructor(type, ctor) {
|
21737 | this.conversionMap.set(type, (context, args) => new ctor(...args));
|
21738 | }
|
21739 | _registerFunction(type, fn) {
|
21740 | this.conversionMap.set(type, (context, args) => fn.apply(undefined, args));
|
21741 | }
|
21742 | initializeConversionMap() {
|
21743 | this._registerDecoratorOrConstructor(this.findDeclaration(ANGULAR_CORE, 'Injectable'), createInjectable);
|
21744 | this.injectionToken = this.findDeclaration(ANGULAR_CORE, 'InjectionToken');
|
21745 | this.opaqueToken = this.findDeclaration(ANGULAR_CORE, 'OpaqueToken');
|
21746 | this.ROUTES = this.tryFindDeclaration(ANGULAR_ROUTER, 'ROUTES');
|
21747 | this.ANALYZE_FOR_ENTRY_COMPONENTS =
|
21748 | this.findDeclaration(ANGULAR_CORE, 'ANALYZE_FOR_ENTRY_COMPONENTS');
|
21749 | this._registerDecoratorOrConstructor(this.findDeclaration(ANGULAR_CORE, 'Host'), createHost);
|
21750 | this._registerDecoratorOrConstructor(this.findDeclaration(ANGULAR_CORE, 'Self'), createSelf);
|
21751 | this._registerDecoratorOrConstructor(this.findDeclaration(ANGULAR_CORE, 'SkipSelf'), createSkipSelf);
|
21752 | this._registerDecoratorOrConstructor(this.findDeclaration(ANGULAR_CORE, 'Inject'), createInject);
|
21753 | this._registerDecoratorOrConstructor(this.findDeclaration(ANGULAR_CORE, 'Optional'), createOptional);
|
21754 | this._registerDecoratorOrConstructor(this.findDeclaration(ANGULAR_CORE, 'Attribute'), createAttribute);
|
21755 | this._registerDecoratorOrConstructor(this.findDeclaration(ANGULAR_CORE, 'ContentChild'), createContentChild);
|
21756 | this._registerDecoratorOrConstructor(this.findDeclaration(ANGULAR_CORE, 'ContentChildren'), createContentChildren);
|
21757 | this._registerDecoratorOrConstructor(this.findDeclaration(ANGULAR_CORE, 'ViewChild'), createViewChild);
|
21758 | this._registerDecoratorOrConstructor(this.findDeclaration(ANGULAR_CORE, 'ViewChildren'), createViewChildren);
|
21759 | this._registerDecoratorOrConstructor(this.findDeclaration(ANGULAR_CORE, 'Input'), createInput);
|
21760 | this._registerDecoratorOrConstructor(this.findDeclaration(ANGULAR_CORE, 'Output'), createOutput);
|
21761 | this._registerDecoratorOrConstructor(this.findDeclaration(ANGULAR_CORE, 'Pipe'), createPipe);
|
21762 | this._registerDecoratorOrConstructor(this.findDeclaration(ANGULAR_CORE, 'HostBinding'), createHostBinding);
|
21763 | this._registerDecoratorOrConstructor(this.findDeclaration(ANGULAR_CORE, 'HostListener'), createHostListener);
|
21764 | this._registerDecoratorOrConstructor(this.findDeclaration(ANGULAR_CORE, 'Directive'), createDirective);
|
21765 | this._registerDecoratorOrConstructor(this.findDeclaration(ANGULAR_CORE, 'Component'), createComponent);
|
21766 | this._registerDecoratorOrConstructor(this.findDeclaration(ANGULAR_CORE, 'NgModule'), createNgModule);
|
21767 | // Note: Some metadata classes can be used directly with Provider.deps.
|
21768 | this._registerDecoratorOrConstructor(this.findDeclaration(ANGULAR_CORE, 'Host'), createHost);
|
21769 | this._registerDecoratorOrConstructor(this.findDeclaration(ANGULAR_CORE, 'Self'), createSelf);
|
21770 | this._registerDecoratorOrConstructor(this.findDeclaration(ANGULAR_CORE, 'SkipSelf'), createSkipSelf);
|
21771 | this._registerDecoratorOrConstructor(this.findDeclaration(ANGULAR_CORE, 'Optional'), createOptional);
|
21772 | }
|
21773 | /**
|
21774 | * getStaticSymbol produces a Type whose metadata is known but whose implementation is not loaded.
|
21775 | * All types passed to the StaticResolver should be pseudo-types returned by this method.
|
21776 | *
|
21777 | * @param declarationFile the absolute path of the file where the symbol is declared
|
21778 | * @param name the name of the type.
|
21779 | */
|
21780 | getStaticSymbol(declarationFile, name, members) {
|
21781 | return this.symbolResolver.getStaticSymbol(declarationFile, name, members);
|
21782 | }
|
21783 | /**
|
21784 | * Simplify but discard any errors
|
21785 | */
|
21786 | trySimplify(context, value) {
|
21787 | const originalRecorder = this.errorRecorder;
|
21788 | this.errorRecorder = (error, fileName) => { };
|
21789 | const result = this.simplify(context, value);
|
21790 | this.errorRecorder = originalRecorder;
|
21791 | return result;
|
21792 | }
|
21793 | /** @internal */
|
21794 | simplify(context, value, lazy = false) {
|
21795 | const self = this;
|
21796 | let scope = BindingScope$1.empty;
|
21797 | const calling = new Map();
|
21798 | function simplifyInContext(context, value, depth, references) {
|
21799 | function resolveReferenceValue(staticSymbol) {
|
21800 | const resolvedSymbol = self.symbolResolver.resolveSymbol(staticSymbol);
|
21801 | return resolvedSymbol ? resolvedSymbol.metadata : null;
|
21802 | }
|
21803 | function simplifyEagerly(value) {
|
21804 | return simplifyInContext(context, value, depth, 0);
|
21805 | }
|
21806 | function simplifyLazily(value) {
|
21807 | return simplifyInContext(context, value, depth, references + 1);
|
21808 | }
|
21809 | function simplifyNested(nestedContext, value) {
|
21810 | if (nestedContext === context) {
|
21811 | // If the context hasn't changed let the exception propagate unmodified.
|
21812 | return simplifyInContext(nestedContext, value, depth + 1, references);
|
21813 | }
|
21814 | try {
|
21815 | return simplifyInContext(nestedContext, value, depth + 1, references);
|
21816 | }
|
21817 | catch (e) {
|
21818 | if (isMetadataError(e)) {
|
21819 | // Propagate the message text up but add a message to the chain that explains how we got
|
21820 | // here.
|
21821 | // e.chain implies e.symbol
|
21822 | const summaryMsg = e.chain ? 'references \'' + e.symbol.name + '\'' : errorSummary(e);
|
21823 | const summary = `'${nestedContext.name}' ${summaryMsg}`;
|
21824 | const chain = { message: summary, position: e.position, next: e.chain };
|
21825 | // TODO(chuckj): retrieve the position information indirectly from the collectors node
|
21826 | // map if the metadata is from a .ts file.
|
21827 | self.error({
|
21828 | message: e.message,
|
21829 | advise: e.advise,
|
21830 | context: e.context,
|
21831 | chain,
|
21832 | symbol: nestedContext
|
21833 | }, context);
|
21834 | }
|
21835 | else {
|
21836 | // It is probably an internal error.
|
21837 | throw e;
|
21838 | }
|
21839 | }
|
21840 | }
|
21841 | function simplifyCall(functionSymbol, targetFunction, args, targetExpression) {
|
21842 | if (targetFunction && targetFunction['__symbolic'] == 'function') {
|
21843 | if (calling.get(functionSymbol)) {
|
21844 | self.error({
|
21845 | message: 'Recursion is not supported',
|
21846 | summary: `called '${functionSymbol.name}' recursively`,
|
21847 | value: targetFunction
|
21848 | }, functionSymbol);
|
21849 | }
|
21850 | try {
|
21851 | const value = targetFunction['value'];
|
21852 | if (value && (depth != 0 || value.__symbolic != 'error')) {
|
21853 | const parameters = targetFunction['parameters'];
|
21854 | const defaults = targetFunction.defaults;
|
21855 | args = args.map(arg => simplifyNested(context, arg))
|
21856 | .map(arg => shouldIgnore(arg) ? undefined : arg);
|
21857 | if (defaults && defaults.length > args.length) {
|
21858 | args.push(...defaults.slice(args.length).map((value) => simplify(value)));
|
21859 | }
|
21860 | calling.set(functionSymbol, true);
|
21861 | const functionScope = BindingScope$1.build();
|
21862 | for (let i = 0; i < parameters.length; i++) {
|
21863 | functionScope.define(parameters[i], args[i]);
|
21864 | }
|
21865 | const oldScope = scope;
|
21866 | let result;
|
21867 | try {
|
21868 | scope = functionScope.done();
|
21869 | result = simplifyNested(functionSymbol, value);
|
21870 | }
|
21871 | finally {
|
21872 | scope = oldScope;
|
21873 | }
|
21874 | return result;
|
21875 | }
|
21876 | }
|
21877 | finally {
|
21878 | calling.delete(functionSymbol);
|
21879 | }
|
21880 | }
|
21881 | if (depth === 0) {
|
21882 | // If depth is 0 we are evaluating the top level expression that is describing element
|
21883 | // decorator. In this case, it is a decorator we don't understand, such as a custom
|
21884 | // non-angular decorator, and we should just ignore it.
|
21885 | return IGNORE;
|
21886 | }
|
21887 | let position = undefined;
|
21888 | if (targetExpression && targetExpression.__symbolic == 'resolved') {
|
21889 | const line = targetExpression.line;
|
21890 | const character = targetExpression.character;
|
21891 | const fileName = targetExpression.fileName;
|
21892 | if (fileName != null && line != null && character != null) {
|
21893 | position = { fileName, line, column: character };
|
21894 | }
|
21895 | }
|
21896 | self.error({
|
21897 | message: FUNCTION_CALL_NOT_SUPPORTED,
|
21898 | context: functionSymbol,
|
21899 | value: targetFunction,
|
21900 | position
|
21901 | }, context);
|
21902 | }
|
21903 | function simplify(expression) {
|
21904 | if (isPrimitive(expression)) {
|
21905 | return expression;
|
21906 | }
|
21907 | if (Array.isArray(expression)) {
|
21908 | const result = [];
|
21909 | for (const item of expression) {
|
21910 | // Check for a spread expression
|
21911 | if (item && item.__symbolic === 'spread') {
|
21912 | // We call with references as 0 because we require the actual value and cannot
|
21913 | // tolerate a reference here.
|
21914 | const spreadArray = simplifyEagerly(item.expression);
|
21915 | if (Array.isArray(spreadArray)) {
|
21916 | for (const spreadItem of spreadArray) {
|
21917 | result.push(spreadItem);
|
21918 | }
|
21919 | continue;
|
21920 | }
|
21921 | }
|
21922 | const value = simplify(item);
|
21923 | if (shouldIgnore(value)) {
|
21924 | continue;
|
21925 | }
|
21926 | result.push(value);
|
21927 | }
|
21928 | return result;
|
21929 | }
|
21930 | if (expression instanceof StaticSymbol) {
|
21931 | // Stop simplification at builtin symbols or if we are in a reference context and
|
21932 | // the symbol doesn't have members.
|
21933 | if (expression === self.injectionToken || self.conversionMap.has(expression) ||
|
21934 | (references > 0 && !expression.members.length)) {
|
21935 | return expression;
|
21936 | }
|
21937 | else {
|
21938 | const staticSymbol = expression;
|
21939 | const declarationValue = resolveReferenceValue(staticSymbol);
|
21940 | if (declarationValue != null) {
|
21941 | return simplifyNested(staticSymbol, declarationValue);
|
21942 | }
|
21943 | else {
|
21944 | return staticSymbol;
|
21945 | }
|
21946 | }
|
21947 | }
|
21948 | if (expression) {
|
21949 | if (expression['__symbolic']) {
|
21950 | let staticSymbol;
|
21951 | switch (expression['__symbolic']) {
|
21952 | case 'binop':
|
21953 | let left = simplify(expression['left']);
|
21954 | if (shouldIgnore(left))
|
21955 | return left;
|
21956 | let right = simplify(expression['right']);
|
21957 | if (shouldIgnore(right))
|
21958 | return right;
|
21959 | switch (expression['operator']) {
|
21960 | case '&&':
|
21961 | return left && right;
|
21962 | case '||':
|
21963 | return left || right;
|
21964 | case '|':
|
21965 | return left | right;
|
21966 | case '^':
|
21967 | return left ^ right;
|
21968 | case '&':
|
21969 | return left & right;
|
21970 | case '==':
|
21971 | return left == right;
|
21972 | case '!=':
|
21973 | return left != right;
|
21974 | case '===':
|
21975 | return left === right;
|
21976 | case '!==':
|
21977 | return left !== right;
|
21978 | case '<':
|
21979 | return left < right;
|
21980 | case '>':
|
21981 | return left > right;
|
21982 | case '<=':
|
21983 | return left <= right;
|
21984 | case '>=':
|
21985 | return left >= right;
|
21986 | case '<<':
|
21987 | return left << right;
|
21988 | case '>>':
|
21989 | return left >> right;
|
21990 | case '+':
|
21991 | return left + right;
|
21992 | case '-':
|
21993 | return left - right;
|
21994 | case '*':
|
21995 | return left * right;
|
21996 | case '/':
|
21997 | return left / right;
|
21998 | case '%':
|
21999 | return left % right;
|
22000 | }
|
22001 | return null;
|
22002 | case 'if':
|
22003 | let condition = simplify(expression['condition']);
|
22004 | return condition ? simplify(expression['thenExpression']) :
|
22005 | simplify(expression['elseExpression']);
|
22006 | case 'pre':
|
22007 | let operand = simplify(expression['operand']);
|
22008 | if (shouldIgnore(operand))
|
22009 | return operand;
|
22010 | switch (expression['operator']) {
|
22011 | case '+':
|
22012 | return operand;
|
22013 | case '-':
|
22014 | return -operand;
|
22015 | case '!':
|
22016 | return !operand;
|
22017 | case '~':
|
22018 | return ~operand;
|
22019 | }
|
22020 | return null;
|
22021 | case 'index':
|
22022 | let indexTarget = simplifyEagerly(expression['expression']);
|
22023 | let index = simplifyEagerly(expression['index']);
|
22024 | if (indexTarget && isPrimitive(index))
|
22025 | return indexTarget[index];
|
22026 | return null;
|
22027 | case 'select':
|
22028 | const member = expression['member'];
|
22029 | let selectContext = context;
|
22030 | let selectTarget = simplify(expression['expression']);
|
22031 | if (selectTarget instanceof StaticSymbol) {
|
22032 | const members = selectTarget.members.concat(member);
|
22033 | selectContext =
|
22034 | self.getStaticSymbol(selectTarget.filePath, selectTarget.name, members);
|
22035 | const declarationValue = resolveReferenceValue(selectContext);
|
22036 | if (declarationValue != null) {
|
22037 | return simplifyNested(selectContext, declarationValue);
|
22038 | }
|
22039 | else {
|
22040 | return selectContext;
|
22041 | }
|
22042 | }
|
22043 | if (selectTarget && isPrimitive(member))
|
22044 | return simplifyNested(selectContext, selectTarget[member]);
|
22045 | return null;
|
22046 | case 'reference':
|
22047 | // Note: This only has to deal with variable references, as symbol references have
|
22048 | // been converted into 'resolved'
|
22049 | // in the StaticSymbolResolver.
|
22050 | const name = expression['name'];
|
22051 | const localValue = scope.resolve(name);
|
22052 | if (localValue != BindingScope$1.missing) {
|
22053 | return localValue;
|
22054 | }
|
22055 | break;
|
22056 | case 'resolved':
|
22057 | try {
|
22058 | return simplify(expression.symbol);
|
22059 | }
|
22060 | catch (e) {
|
22061 | // If an error is reported evaluating the symbol record the position of the
|
22062 | // reference in the error so it can
|
22063 | // be reported in the error message generated from the exception.
|
22064 | if (isMetadataError(e) && expression.fileName != null &&
|
22065 | expression.line != null && expression.character != null) {
|
22066 | e.position = {
|
22067 | fileName: expression.fileName,
|
22068 | line: expression.line,
|
22069 | column: expression.character
|
22070 | };
|
22071 | }
|
22072 | throw e;
|
22073 | }
|
22074 | case 'class':
|
22075 | return context;
|
22076 | case 'function':
|
22077 | return context;
|
22078 | case 'new':
|
22079 | case 'call':
|
22080 | // Determine if the function is a built-in conversion
|
22081 | staticSymbol = simplifyInContext(context, expression['expression'], depth + 1, /* references */ 0);
|
22082 | if (staticSymbol instanceof StaticSymbol) {
|
22083 | if (staticSymbol === self.injectionToken || staticSymbol === self.opaqueToken) {
|
22084 | // if somebody calls new InjectionToken, don't create an InjectionToken,
|
22085 | // but rather return the symbol to which the InjectionToken is assigned to.
|
22086 | // OpaqueToken is supported too as it is required by the language service to
|
22087 | // support v4 and prior versions of Angular.
|
22088 | return context;
|
22089 | }
|
22090 | const argExpressions = expression['arguments'] || [];
|
22091 | let converter = self.conversionMap.get(staticSymbol);
|
22092 | if (converter) {
|
22093 | const args = argExpressions.map(arg => simplifyNested(context, arg))
|
22094 | .map(arg => shouldIgnore(arg) ? undefined : arg);
|
22095 | return converter(context, args);
|
22096 | }
|
22097 | else {
|
22098 | // Determine if the function is one we can simplify.
|
22099 | const targetFunction = resolveReferenceValue(staticSymbol);
|
22100 | return simplifyCall(staticSymbol, targetFunction, argExpressions, expression['expression']);
|
22101 | }
|
22102 | }
|
22103 | return IGNORE;
|
22104 | case 'error':
|
22105 | let message = expression.message;
|
22106 | if (expression['line'] != null) {
|
22107 | self.error({
|
22108 | message,
|
22109 | context: expression.context,
|
22110 | value: expression,
|
22111 | position: {
|
22112 | fileName: expression['fileName'],
|
22113 | line: expression['line'],
|
22114 | column: expression['character']
|
22115 | }
|
22116 | }, context);
|
22117 | }
|
22118 | else {
|
22119 | self.error({ message, context: expression.context }, context);
|
22120 | }
|
22121 | return IGNORE;
|
22122 | case 'ignore':
|
22123 | return expression;
|
22124 | }
|
22125 | return null;
|
22126 | }
|
22127 | return mapStringMap(expression, (value, name) => {
|
22128 | if (REFERENCE_SET.has(name)) {
|
22129 | if (name === USE_VALUE$1 && PROVIDE in expression) {
|
22130 | // If this is a provider expression, check for special tokens that need the value
|
22131 | // during analysis.
|
22132 | const provide = simplify(expression.provide);
|
22133 | if (provide === self.ROUTES || provide == self.ANALYZE_FOR_ENTRY_COMPONENTS) {
|
22134 | return simplify(value);
|
22135 | }
|
22136 | }
|
22137 | return simplifyLazily(value);
|
22138 | }
|
22139 | return simplify(value);
|
22140 | });
|
22141 | }
|
22142 | return IGNORE;
|
22143 | }
|
22144 | return simplify(value);
|
22145 | }
|
22146 | let result;
|
22147 | try {
|
22148 | result = simplifyInContext(context, value, 0, lazy ? 1 : 0);
|
22149 | }
|
22150 | catch (e) {
|
22151 | if (this.errorRecorder) {
|
22152 | this.reportError(e, context);
|
22153 | }
|
22154 | else {
|
22155 | throw formatMetadataError(e, context);
|
22156 | }
|
22157 | }
|
22158 | if (shouldIgnore(result)) {
|
22159 | return undefined;
|
22160 | }
|
22161 | return result;
|
22162 | }
|
22163 | getTypeMetadata(type) {
|
22164 | const resolvedSymbol = this.symbolResolver.resolveSymbol(type);
|
22165 | return resolvedSymbol && resolvedSymbol.metadata ? resolvedSymbol.metadata :
|
22166 | { __symbolic: 'class' };
|
22167 | }
|
22168 | reportError(error, context, path) {
|
22169 | if (this.errorRecorder) {
|
22170 | this.errorRecorder(formatMetadataError(error, context), (context && context.filePath) || path);
|
22171 | }
|
22172 | else {
|
22173 | throw error;
|
22174 | }
|
22175 | }
|
22176 | error({ message, summary, advise, position, context, value, symbol, chain }, reportingContext) {
|
22177 | this.reportError(metadataError(message, summary, advise, position, symbol, context, chain), reportingContext);
|
22178 | }
|
22179 | }
|
22180 | const METADATA_ERROR = 'ngMetadataError';
|
22181 | function metadataError(message, summary, advise, position, symbol, context, chain) {
|
22182 | const error = syntaxError(message);
|
22183 | error[METADATA_ERROR] = true;
|
22184 | if (advise)
|
22185 | error.advise = advise;
|
22186 | if (position)
|
22187 | error.position = position;
|
22188 | if (summary)
|
22189 | error.summary = summary;
|
22190 | if (context)
|
22191 | error.context = context;
|
22192 | if (chain)
|
22193 | error.chain = chain;
|
22194 | if (symbol)
|
22195 | error.symbol = symbol;
|
22196 | return error;
|
22197 | }
|
22198 | function isMetadataError(error) {
|
22199 | return !!error[METADATA_ERROR];
|
22200 | }
|
22201 | const REFERENCE_TO_NONEXPORTED_CLASS = 'Reference to non-exported class';
|
22202 | const VARIABLE_NOT_INITIALIZED = 'Variable not initialized';
|
22203 | const DESTRUCTURE_NOT_SUPPORTED = 'Destructuring not supported';
|
22204 | const COULD_NOT_RESOLVE_TYPE = 'Could not resolve type';
|
22205 | const FUNCTION_CALL_NOT_SUPPORTED = 'Function call not supported';
|
22206 | const REFERENCE_TO_LOCAL_SYMBOL = 'Reference to a local symbol';
|
22207 | const LAMBDA_NOT_SUPPORTED = 'Lambda not supported';
|
22208 | function expandedMessage(message, context) {
|
22209 | switch (message) {
|
22210 | case REFERENCE_TO_NONEXPORTED_CLASS:
|
22211 | if (context && context.className) {
|
22212 | return `References to a non-exported class are not supported in decorators but ${context.className} was referenced.`;
|
22213 | }
|
22214 | break;
|
22215 | case VARIABLE_NOT_INITIALIZED:
|
22216 | return 'Only initialized variables and constants can be referenced in decorators because the value of this variable is needed by the template compiler';
|
22217 | case DESTRUCTURE_NOT_SUPPORTED:
|
22218 | return 'Referencing an exported destructured variable or constant is not supported in decorators and this value is needed by the template compiler';
|
22219 | case COULD_NOT_RESOLVE_TYPE:
|
22220 | if (context && context.typeName) {
|
22221 | return `Could not resolve type ${context.typeName}`;
|
22222 | }
|
22223 | break;
|
22224 | case FUNCTION_CALL_NOT_SUPPORTED:
|
22225 | if (context && context.name) {
|
22226 | return `Function calls are not supported in decorators but '${context.name}' was called`;
|
22227 | }
|
22228 | return 'Function calls are not supported in decorators';
|
22229 | case REFERENCE_TO_LOCAL_SYMBOL:
|
22230 | if (context && context.name) {
|
22231 | return `Reference to a local (non-exported) symbols are not supported in decorators but '${context.name}' was referenced`;
|
22232 | }
|
22233 | break;
|
22234 | case LAMBDA_NOT_SUPPORTED:
|
22235 | return `Function expressions are not supported in decorators`;
|
22236 | }
|
22237 | return message;
|
22238 | }
|
22239 | function messageAdvise(message, context) {
|
22240 | switch (message) {
|
22241 | case REFERENCE_TO_NONEXPORTED_CLASS:
|
22242 | if (context && context.className) {
|
22243 | return `Consider exporting '${context.className}'`;
|
22244 | }
|
22245 | break;
|
22246 | case DESTRUCTURE_NOT_SUPPORTED:
|
22247 | return 'Consider simplifying to avoid destructuring';
|
22248 | case REFERENCE_TO_LOCAL_SYMBOL:
|
22249 | if (context && context.name) {
|
22250 | return `Consider exporting '${context.name}'`;
|
22251 | }
|
22252 | break;
|
22253 | case LAMBDA_NOT_SUPPORTED:
|
22254 | return `Consider changing the function expression into an exported function`;
|
22255 | }
|
22256 | return undefined;
|
22257 | }
|
22258 | function errorSummary(error) {
|
22259 | if (error.summary) {
|
22260 | return error.summary;
|
22261 | }
|
22262 | switch (error.message) {
|
22263 | case REFERENCE_TO_NONEXPORTED_CLASS:
|
22264 | if (error.context && error.context.className) {
|
22265 | return `references non-exported class ${error.context.className}`;
|
22266 | }
|
22267 | break;
|
22268 | case VARIABLE_NOT_INITIALIZED:
|
22269 | return 'is not initialized';
|
22270 | case DESTRUCTURE_NOT_SUPPORTED:
|
22271 | return 'is a destructured variable';
|
22272 | case COULD_NOT_RESOLVE_TYPE:
|
22273 | return 'could not be resolved';
|
22274 | case FUNCTION_CALL_NOT_SUPPORTED:
|
22275 | if (error.context && error.context.name) {
|
22276 | return `calls '${error.context.name}'`;
|
22277 | }
|
22278 | return `calls a function`;
|
22279 | case REFERENCE_TO_LOCAL_SYMBOL:
|
22280 | if (error.context && error.context.name) {
|
22281 | return `references local variable ${error.context.name}`;
|
22282 | }
|
22283 | return `references a local variable`;
|
22284 | }
|
22285 | return 'contains the error';
|
22286 | }
|
22287 | function mapStringMap(input, transform) {
|
22288 | if (!input)
|
22289 | return {};
|
22290 | const result = {};
|
22291 | Object.keys(input).forEach((key) => {
|
22292 | const value = transform(input[key], key);
|
22293 | if (!shouldIgnore(value)) {
|
22294 | if (HIDDEN_KEY.test(key)) {
|
22295 | Object.defineProperty(result, key, { enumerable: false, configurable: true, value: value });
|
22296 | }
|
22297 | else {
|
22298 | result[key] = value;
|
22299 | }
|
22300 | }
|
22301 | });
|
22302 | return result;
|
22303 | }
|
22304 | function isPrimitive(o) {
|
22305 | return o === null || (typeof o !== 'function' && typeof o !== 'object');
|
22306 | }
|
22307 | class BindingScope$1 {
|
22308 | static build() {
|
22309 | const current = new Map();
|
22310 | return {
|
22311 | define: function (name, value) {
|
22312 | current.set(name, value);
|
22313 | return this;
|
22314 | },
|
22315 | done: function () {
|
22316 | return current.size > 0 ? new PopulatedScope(current) : BindingScope$1.empty;
|
22317 | }
|
22318 | };
|
22319 | }
|
22320 | }
|
22321 | BindingScope$1.missing = {};
|
22322 | BindingScope$1.empty = { resolve: name => BindingScope$1.missing };
|
22323 | class PopulatedScope extends BindingScope$1 {
|
22324 | constructor(bindings) {
|
22325 | super();
|
22326 | this.bindings = bindings;
|
22327 | }
|
22328 | resolve(name) {
|
22329 | return this.bindings.has(name) ? this.bindings.get(name) : BindingScope$1.missing;
|
22330 | }
|
22331 | }
|
22332 | function formatMetadataMessageChain(chain, advise) {
|
22333 | const expanded = expandedMessage(chain.message, chain.context);
|
22334 | const nesting = chain.symbol ? ` in '${chain.symbol.name}'` : '';
|
22335 | const message = `${expanded}${nesting}`;
|
22336 | const position = chain.position;
|
22337 | const next = chain.next ?
|
22338 | formatMetadataMessageChain(chain.next, advise) :
|
22339 | advise ? { message: advise } : undefined;
|
22340 | return { message, position, next: next ? [next] : undefined };
|
22341 | }
|
22342 | function formatMetadataError(e, context) {
|
22343 | if (isMetadataError(e)) {
|
22344 | // Produce a formatted version of the and leaving enough information in the original error
|
22345 | // to recover the formatting information to eventually produce a diagnostic error message.
|
22346 | const position = e.position;
|
22347 | const chain = {
|
22348 | message: `Error during template compile of '${context.name}'`,
|
22349 | position: position,
|
22350 | next: { message: e.message, next: e.chain, context: e.context, symbol: e.symbol }
|
22351 | };
|
22352 | const advise = e.advise || messageAdvise(e.message, e.context);
|
22353 | return formattedError(formatMetadataMessageChain(chain, advise));
|
22354 | }
|
22355 | return e;
|
22356 | }
|
22357 |
|
22358 | /**
|
22359 | * @license
|
22360 | * Copyright Google LLC All Rights Reserved.
|
22361 | *
|
22362 | * Use of this source code is governed by an MIT-style license that can be
|
22363 | * found in the LICENSE file at https://angular.io/license
|
22364 | */
|
22365 | class AotSummaryResolver {
|
22366 | constructor(host, staticSymbolCache) {
|
22367 | this.host = host;
|
22368 | this.staticSymbolCache = staticSymbolCache;
|
22369 | // Note: this will only contain StaticSymbols without members!
|
22370 | this.summaryCache = new Map();
|
22371 | this.loadedFilePaths = new Map();
|
22372 | // Note: this will only contain StaticSymbols without members!
|
22373 | this.importAs = new Map();
|
22374 | this.knownFileNameToModuleNames = new Map();
|
22375 | }
|
22376 | isLibraryFile(filePath) {
|
22377 | // Note: We need to strip the .ngfactory. file path,
|
22378 | // so this method also works for generated files
|
22379 | // (for which host.isSourceFile will always return false).
|
22380 | return !this.host.isSourceFile(stripGeneratedFileSuffix(filePath));
|
22381 | }
|
22382 | toSummaryFileName(filePath, referringSrcFileName) {
|
22383 | return this.host.toSummaryFileName(filePath, referringSrcFileName);
|
22384 | }
|
22385 | fromSummaryFileName(fileName, referringLibFileName) {
|
22386 | return this.host.fromSummaryFileName(fileName, referringLibFileName);
|
22387 | }
|
22388 | resolveSummary(staticSymbol) {
|
22389 | const rootSymbol = staticSymbol.members.length ?
|
22390 | this.staticSymbolCache.get(staticSymbol.filePath, staticSymbol.name) :
|
22391 | staticSymbol;
|
22392 | let summary = this.summaryCache.get(rootSymbol);
|
22393 | if (!summary) {
|
22394 | this._loadSummaryFile(staticSymbol.filePath);
|
22395 | summary = this.summaryCache.get(staticSymbol);
|
22396 | }
|
22397 | return (rootSymbol === staticSymbol && summary) || null;
|
22398 | }
|
22399 | getSymbolsOf(filePath) {
|
22400 | if (this._loadSummaryFile(filePath)) {
|
22401 | return Array.from(this.summaryCache.keys()).filter((symbol) => symbol.filePath === filePath);
|
22402 | }
|
22403 | return null;
|
22404 | }
|
22405 | getImportAs(staticSymbol) {
|
22406 | staticSymbol.assertNoMembers();
|
22407 | return this.importAs.get(staticSymbol);
|
22408 | }
|
22409 | /**
|
22410 | * Converts a file path to a module name that can be used as an `import`.
|
22411 | */
|
22412 | getKnownModuleName(importedFilePath) {
|
22413 | return this.knownFileNameToModuleNames.get(importedFilePath) || null;
|
22414 | }
|
22415 | addSummary(summary) {
|
22416 | this.summaryCache.set(summary.symbol, summary);
|
22417 | }
|
22418 | _loadSummaryFile(filePath) {
|
22419 | let hasSummary = this.loadedFilePaths.get(filePath);
|
22420 | if (hasSummary != null) {
|
22421 | return hasSummary;
|
22422 | }
|
22423 | let json = null;
|
22424 | if (this.isLibraryFile(filePath)) {
|
22425 | const summaryFilePath = summaryFileName(filePath);
|
22426 | try {
|
22427 | json = this.host.loadSummary(summaryFilePath);
|
22428 | }
|
22429 | catch (e) {
|
22430 | console.error(`Error loading summary file ${summaryFilePath}`);
|
22431 | throw e;
|
22432 | }
|
22433 | }
|
22434 | hasSummary = json != null;
|
22435 | this.loadedFilePaths.set(filePath, hasSummary);
|
22436 | if (json) {
|
22437 | const { moduleName, summaries, importAs } = deserializeSummaries(this.staticSymbolCache, this, filePath, json);
|
22438 | summaries.forEach((summary) => this.summaryCache.set(summary.symbol, summary));
|
22439 | if (moduleName) {
|
22440 | this.knownFileNameToModuleNames.set(filePath, moduleName);
|
22441 | }
|
22442 | importAs.forEach((importAs) => {
|
22443 | this.importAs.set(importAs.symbol, importAs.importAs);
|
22444 | });
|
22445 | }
|
22446 | return hasSummary;
|
22447 | }
|
22448 | }
|
22449 |
|
22450 | class JitSummaryResolver {
|
22451 | constructor() {
|
22452 | this._summaries = new Map();
|
22453 | }
|
22454 | isLibraryFile() {
|
22455 | return false;
|
22456 | }
|
22457 | toSummaryFileName(fileName) {
|
22458 | return fileName;
|
22459 | }
|
22460 | fromSummaryFileName(fileName) {
|
22461 | return fileName;
|
22462 | }
|
22463 | resolveSummary(reference) {
|
22464 | return this._summaries.get(reference) || null;
|
22465 | }
|
22466 | getSymbolsOf() {
|
22467 | return [];
|
22468 | }
|
22469 | getImportAs(reference) {
|
22470 | return reference;
|
22471 | }
|
22472 | getKnownModuleName(fileName) {
|
22473 | return null;
|
22474 | }
|
22475 | addSummary(summary) {
|
22476 | this._summaries.set(summary.symbol, summary);
|
22477 | }
|
22478 | }
|
22479 |
|
22480 | /**
|
22481 | * @license
|
22482 | * Copyright Google LLC All Rights Reserved.
|
22483 | *
|
22484 | * Use of this source code is governed by an MIT-style license that can be
|
22485 | * found in the LICENSE file at https://angular.io/license
|
22486 | */
|
22487 | /**
|
22488 | * The index of each URI component in the return value of goog.uri.utils.split.
|
22489 | * @enum {number}
|
22490 | */
|
22491 | var _ComponentIndex;
|
22492 | (function (_ComponentIndex) {
|
22493 | _ComponentIndex[_ComponentIndex["Scheme"] = 1] = "Scheme";
|
22494 | _ComponentIndex[_ComponentIndex["UserInfo"] = 2] = "UserInfo";
|
22495 | _ComponentIndex[_ComponentIndex["Domain"] = 3] = "Domain";
|
22496 | _ComponentIndex[_ComponentIndex["Port"] = 4] = "Port";
|
22497 | _ComponentIndex[_ComponentIndex["Path"] = 5] = "Path";
|
22498 | _ComponentIndex[_ComponentIndex["QueryData"] = 6] = "QueryData";
|
22499 | _ComponentIndex[_ComponentIndex["Fragment"] = 7] = "Fragment";
|
22500 | })(_ComponentIndex || (_ComponentIndex = {}));
|
22501 |
|
22502 | /**
|
22503 | * @license
|
22504 | * Copyright Google LLC All Rights Reserved.
|
22505 | *
|
22506 | * Use of this source code is governed by an MIT-style license that can be
|
22507 | * found in the LICENSE file at https://angular.io/license
|
22508 | */
|
22509 | // This file only reexports content of the `src` folder. Keep it that way.
|
22510 | // This function call has a global side effects and publishes the compiler into global namespace for
|
22511 | // the late binding of the Compiler to the @angular/core for jit compilation.
|
22512 | publishFacade(_global);
|
22513 |
|
22514 | /**
|
22515 | * @license
|
22516 | * Copyright Google LLC All Rights Reserved.
|
22517 | *
|
22518 | * Use of this source code is governed by an MIT-style license that can be
|
22519 | * found in the LICENSE file at https://angular.io/license
|
22520 | */
|
22521 | /**
|
22522 | * Matches an Angular attribute to a binding type. See `ATTR` for more details.
|
22523 | *
|
22524 | * This is adapted from packages/compiler/src/render3/r3_template_transform.ts
|
22525 | * to allow empty binding names and match template attributes.
|
22526 | */
|
22527 | const BIND_NAME_REGEXP$2 = /^(?:(?:(?:(bind-)|(let-)|(ref-|#)|(on-)|(bindon-)|(@)|(\*))(.*))|\[\(([^\)]*)\)\]|\[([^\]]*)\]|\(([^\)]*)\))$/;
|
22528 | /**
|
22529 | * Represents possible Angular attribute bindings, as indices on a match of `BIND_NAME_REGEXP`.
|
22530 | */
|
22531 | var ATTR;
|
22532 | (function (ATTR) {
|
22533 | /** "bind-" */
|
22534 | ATTR[ATTR["KW_BIND"] = 1] = "KW_BIND";
|
22535 | /** "let-" */
|
22536 | ATTR[ATTR["KW_LET"] = 2] = "KW_LET";
|
22537 | /** "ref-/#" */
|
22538 | ATTR[ATTR["KW_REF"] = 3] = "KW_REF";
|
22539 | /** "on-" */
|
22540 | ATTR[ATTR["KW_ON"] = 4] = "KW_ON";
|
22541 | /** "bindon-" */
|
22542 | ATTR[ATTR["KW_BINDON"] = 5] = "KW_BINDON";
|
22543 | /** "@" */
|
22544 | ATTR[ATTR["KW_AT"] = 6] = "KW_AT";
|
22545 | /**
|
22546 | * "*"
|
22547 | * Microsyntax template starts with '*'. See https://angular.io/api/core/TemplateRef
|
22548 | */
|
22549 | ATTR[ATTR["KW_MICROSYNTAX"] = 7] = "KW_MICROSYNTAX";
|
22550 | /** The identifier after "bind-", "let-", "ref-/#", "on-", "bindon-", "@", or "*" */
|
22551 | ATTR[ATTR["IDENT_KW"] = 8] = "IDENT_KW";
|
22552 | /** Identifier inside [()] */
|
22553 | ATTR[ATTR["IDENT_BANANA_BOX"] = 9] = "IDENT_BANANA_BOX";
|
22554 | /** Identifier inside [] */
|
22555 | ATTR[ATTR["IDENT_PROPERTY"] = 10] = "IDENT_PROPERTY";
|
22556 | /** Identifier inside () */
|
22557 | ATTR[ATTR["IDENT_EVENT"] = 11] = "IDENT_EVENT";
|
22558 | })(ATTR || (ATTR = {}));
|
22559 | /**
|
22560 | * Returns a descriptor for a given Angular attribute, or undefined if the attribute is
|
22561 | * not an Angular attribute.
|
22562 | */
|
22563 | function getBindingDescriptor(attribute) {
|
22564 | const bindParts = attribute.match(BIND_NAME_REGEXP$2);
|
22565 | if (!bindParts)
|
22566 | return;
|
22567 | // The first match element is skipped because it matches the entire attribute text, including the
|
22568 | // binding part.
|
22569 | const kind = bindParts.findIndex((val, i) => i > 0 && val !== undefined);
|
22570 | if (!(kind in ATTR)) {
|
22571 | throw TypeError(`"${kind}" is not a valid Angular binding kind for "${attribute}"`);
|
22572 | }
|
22573 | return {
|
22574 | kind,
|
22575 | name: bindParts[ATTR.IDENT_KW],
|
22576 | };
|
22577 | }
|
22578 |
|
22579 | /**
|
22580 | * @license
|
22581 | * Copyright Google LLC All Rights Reserved.
|
22582 | *
|
22583 | * Use of this source code is governed by an MIT-style license that can be
|
22584 | * found in the LICENSE file at https://angular.io/license
|
22585 | */
|
22586 | const Diagnostic = {
|
22587 | directive_not_in_module: {
|
22588 | message: `%1 '%2' is not included in a module and will not be available inside a template. Consider adding it to a NgModule declaration.`,
|
22589 | kind: 'Suggestion',
|
22590 | },
|
22591 | missing_template_and_templateurl: {
|
22592 | message: `Component '%1' must have a template or templateUrl`,
|
22593 | kind: 'Error',
|
22594 | },
|
22595 | both_template_and_templateurl: {
|
22596 | message: `Component '%1' must not have both template and templateUrl`,
|
22597 | kind: 'Error',
|
22598 | },
|
22599 | invalid_templateurl: {
|
22600 | message: `URL does not point to a valid file`,
|
22601 | kind: 'Error',
|
22602 | },
|
22603 | template_context_missing_member: {
|
22604 | message: `The template context of '%1' does not define %2.\n` +
|
22605 | `If the context type is a base type or 'any', consider refining it to a more specific type.`,
|
22606 | kind: 'Suggestion',
|
22607 | },
|
22608 | callable_expression_expected_method_call: {
|
22609 | message: 'Unexpected callable expression. Expected a method call',
|
22610 | kind: 'Warning',
|
22611 | },
|
22612 | call_target_not_callable: {
|
22613 | message: `Call target '%1' has non-callable type '%2'.`,
|
22614 | kind: 'Error',
|
22615 | },
|
22616 | expression_might_be_null: {
|
22617 | message: 'The expression might be null',
|
22618 | kind: 'Error',
|
22619 | },
|
22620 | expected_a_number_type: {
|
22621 | message: 'Expected a number type',
|
22622 | kind: 'Error',
|
22623 | },
|
22624 | expected_a_string_or_number_type: {
|
22625 | message: 'Expected operands to be a string or number type',
|
22626 | kind: 'Error',
|
22627 | },
|
22628 | expected_operands_of_comparable_types_or_any: {
|
22629 | message: 'Expected operands to be of comparable types or any',
|
22630 | kind: 'Error',
|
22631 | },
|
22632 | unrecognized_operator: {
|
22633 | message: 'Unrecognized operator %1',
|
22634 | kind: 'Error',
|
22635 | },
|
22636 | unrecognized_primitive: {
|
22637 | message: 'Unrecognized primitive %1',
|
22638 | kind: 'Error',
|
22639 | },
|
22640 | no_pipe_found: {
|
22641 | message: 'No pipe of name %1 found',
|
22642 | kind: 'Error',
|
22643 | },
|
22644 | // TODO: Consider a better error message here.
|
22645 | unable_to_resolve_compatible_call_signature: {
|
22646 | message: 'Unable to resolve compatible call signature',
|
22647 | kind: 'Error',
|
22648 | },
|
22649 | unable_to_resolve_signature: {
|
22650 | message: 'Unable to resolve signature for call of %1',
|
22651 | kind: 'Error',
|
22652 | },
|
22653 | could_not_resolve_type: {
|
22654 | message: `Could not resolve the type of '%1'`,
|
22655 | kind: 'Error',
|
22656 | },
|
22657 | identifier_not_callable: {
|
22658 | message: `'%1' is not callable`,
|
22659 | kind: 'Error',
|
22660 | },
|
22661 | identifier_possibly_undefined: {
|
22662 | message: `'%1' is possibly undefined. Consider using the safe navigation operator (%2) or non-null assertion operator (%3).`,
|
22663 | kind: 'Suggestion',
|
22664 | },
|
22665 | identifier_not_defined_in_app_context: {
|
22666 | message: `Identifier '%1' is not defined. The component declaration, template variable declarations, and element references do not contain such a member`,
|
22667 | kind: 'Error',
|
22668 | },
|
22669 | identifier_not_defined_on_receiver: {
|
22670 | message: `Identifier '%1' is not defined. '%2' does not contain such a member`,
|
22671 | kind: 'Error',
|
22672 | },
|
22673 | identifier_is_private: {
|
22674 | message: `Identifier '%1' refers to a private member of %2`,
|
22675 | kind: 'Warning',
|
22676 | },
|
22677 | };
|
22678 | /**
|
22679 | * Creates a language service diagnostic.
|
22680 | * @param span location the diagnostic for
|
22681 | * @param dm diagnostic message
|
22682 | * @param formatArgs run-time arguments to format the diagnostic message with (see the messages in
|
22683 | * the `Diagnostic` object for an example).
|
22684 | * @returns a created diagnostic
|
22685 | */
|
22686 | function createDiagnostic(span, dm, ...formatArgs) {
|
22687 | // Formats "%1 %2" with formatArgs ['a', 'b'] as "a b"
|
22688 | const formattedMessage = dm.message.replace(/%(\d+)/g, (_, index) => formatArgs[+index - 1]);
|
22689 | return {
|
22690 | kind: ts.DiagnosticCategory[dm.kind],
|
22691 | message: formattedMessage,
|
22692 | span,
|
22693 | };
|
22694 | }
|
22695 |
|
22696 | /**
|
22697 | * @license
|
22698 | * Copyright Google LLC All Rights Reserved.
|
22699 | *
|
22700 | * Use of this source code is governed by an MIT-style license that can be
|
22701 | * found in the LICENSE file at https://angular.io/license
|
22702 | */
|
22703 | /**
|
22704 | * An enumeration of basic types.
|
22705 | *
|
22706 | * @publicApi
|
22707 | */
|
22708 | var BuiltinType$1;
|
22709 | (function (BuiltinType) {
|
22710 | /**
|
22711 | * The type is a type that can hold any other type.
|
22712 | */
|
22713 | BuiltinType[BuiltinType["Any"] = -1] = "Any";
|
22714 | /** Unknown types are functionally identical to any. */
|
22715 | BuiltinType[BuiltinType["Unknown"] = -1] = "Unknown";
|
22716 | /**
|
22717 | * The type of a string literal.
|
22718 | */
|
22719 | BuiltinType[BuiltinType["String"] = 1] = "String";
|
22720 | /**
|
22721 | * The type of a numeric literal.
|
22722 | */
|
22723 | BuiltinType[BuiltinType["Number"] = 2] = "Number";
|
22724 | /**
|
22725 | * The type of the `true` and `false` literals.
|
22726 | */
|
22727 | BuiltinType[BuiltinType["Boolean"] = 4] = "Boolean";
|
22728 | /**
|
22729 | * The type of the `undefined` literal.
|
22730 | */
|
22731 | BuiltinType[BuiltinType["Undefined"] = 8] = "Undefined";
|
22732 | /**
|
22733 | * the type of the `null` literal.
|
22734 | */
|
22735 | BuiltinType[BuiltinType["Null"] = 16] = "Null";
|
22736 | /**
|
22737 | * the type is an unbound type parameter.
|
22738 | */
|
22739 | BuiltinType[BuiltinType["Unbound"] = 32] = "Unbound";
|
22740 | /**
|
22741 | * Not a built-in type.
|
22742 | */
|
22743 | BuiltinType[BuiltinType["Other"] = 64] = "Other";
|
22744 | BuiltinType[BuiltinType["Object"] = 128] = "Object";
|
22745 | })(BuiltinType$1 || (BuiltinType$1 = {}));
|
22746 |
|
22747 | /**
|
22748 | * @license
|
22749 | * Copyright Google LLC All Rights Reserved.
|
22750 | *
|
22751 | * Use of this source code is governed by an MIT-style license that can be
|
22752 | * found in the LICENSE file at https://angular.io/license
|
22753 | */
|
22754 | function isParseSourceSpan(value) {
|
22755 | return value && !!value.start;
|
22756 | }
|
22757 | function spanOf(span) {
|
22758 | if (!span)
|
22759 | return undefined;
|
22760 | if (isParseSourceSpan(span)) {
|
22761 | return { start: span.start.offset, end: span.end.offset };
|
22762 | }
|
22763 | else {
|
22764 | if (span.endSourceSpan) {
|
22765 | return { start: span.sourceSpan.start.offset, end: span.endSourceSpan.end.offset };
|
22766 | }
|
22767 | else if (span.children && span.children.length) {
|
22768 | return {
|
22769 | start: span.sourceSpan.start.offset,
|
22770 | end: spanOf(span.children[span.children.length - 1]).end
|
22771 | };
|
22772 | }
|
22773 | return { start: span.sourceSpan.start.offset, end: span.sourceSpan.end.offset };
|
22774 | }
|
22775 | }
|
22776 | function inSpan(position, span, exclusive) {
|
22777 | return span != null &&
|
22778 | (exclusive ? position >= span.start && position < span.end :
|
22779 | position >= span.start && position <= span.end);
|
22780 | }
|
22781 | function offsetSpan(span, amount) {
|
22782 | return { start: span.start + amount, end: span.end + amount };
|
22783 | }
|
22784 | function isNarrower(spanA, spanB) {
|
22785 | return spanA.start >= spanB.start && spanA.end <= spanB.end;
|
22786 | }
|
22787 | function isStructuralDirective(type) {
|
22788 | var _a;
|
22789 | for (const diDep of type.diDeps) {
|
22790 | const diDepName = identifierName((_a = diDep.token) === null || _a === void 0 ? void 0 : _a.identifier);
|
22791 | if (diDepName === Identifiers.TemplateRef.name ||
|
22792 | diDepName === Identifiers.ViewContainerRef.name) {
|
22793 | return true;
|
22794 | }
|
22795 | }
|
22796 | return false;
|
22797 | }
|
22798 | function getSelectors(info) {
|
22799 | const map = new Map();
|
22800 | const results = [];
|
22801 | for (const directive of info.directives) {
|
22802 | const selectors = CssSelector.parse(directive.selector);
|
22803 | for (const selector of selectors) {
|
22804 | results.push(selector);
|
22805 | map.set(selector, directive);
|
22806 | }
|
22807 | }
|
22808 | return { selectors: results, map };
|
22809 | }
|
22810 | function diagnosticInfoFromTemplateInfo(info) {
|
22811 | return {
|
22812 | fileName: info.template.fileName,
|
22813 | offset: info.template.span.start,
|
22814 | query: info.template.query,
|
22815 | members: info.template.members,
|
22816 | htmlAst: info.htmlAst,
|
22817 | templateAst: info.templateAst,
|
22818 | source: info.template.source,
|
22819 | };
|
22820 | }
|
22821 | function findTemplateAstAt(ast, position) {
|
22822 | const path = [];
|
22823 | const visitor = new class extends RecursiveTemplateAstVisitor {
|
22824 | visit(ast) {
|
22825 | let span = spanOf(ast);
|
22826 | if (inSpan(position, span)) {
|
22827 | const len = path.length;
|
22828 | if (!len || isNarrower(span, spanOf(path[len - 1]))) {
|
22829 | path.push(ast);
|
22830 | }
|
22831 | }
|
22832 | else {
|
22833 | // Returning a value here will result in the children being skipped.
|
22834 | return true;
|
22835 | }
|
22836 | }
|
22837 | visitEmbeddedTemplate(ast, context) {
|
22838 | return this.visitChildren(context, visit => {
|
22839 | // Ignore reference, variable and providers
|
22840 | visit(ast.attrs);
|
22841 | visit(ast.directives);
|
22842 | visit(ast.children);
|
22843 | });
|
22844 | }
|
22845 | visitElement(ast, context) {
|
22846 | return this.visitChildren(context, visit => {
|
22847 | // Ingnore providers
|
22848 | visit(ast.attrs);
|
22849 | visit(ast.inputs);
|
22850 | visit(ast.outputs);
|
22851 | visit(ast.references);
|
22852 | visit(ast.directives);
|
22853 | visit(ast.children);
|
22854 | });
|
22855 | }
|
22856 | visitDirective(ast, context) {
|
22857 | // Ignore the host properties of a directive
|
22858 | const result = this.visitChildren(context, visit => {
|
22859 | visit(ast.inputs);
|
22860 | });
|
22861 | // We never care about the diretive itself, just its inputs.
|
22862 | if (path[path.length - 1] === ast) {
|
22863 | path.pop();
|
22864 | }
|
22865 | return result;
|
22866 | }
|
22867 | };
|
22868 | templateVisitAll(visitor, ast);
|
22869 | return new AstPath(path, position);
|
22870 | }
|
22871 | /**
|
22872 | * Find the tightest node at the specified `position` from the AST `nodes`, and
|
22873 | * return the path to the node.
|
22874 | * @param nodes HTML AST nodes
|
22875 | * @param position
|
22876 | */
|
22877 | function getPathToNodeAtPosition(nodes, position) {
|
22878 | const path = [];
|
22879 | const visitor = new class extends RecursiveVisitor {
|
22880 | visit(ast) {
|
22881 | const span = spanOf(ast);
|
22882 | if (inSpan(position, span)) {
|
22883 | path.push(ast);
|
22884 | }
|
22885 | else {
|
22886 | // Returning a truthy value here will skip all children and terminate
|
22887 | // the visit.
|
22888 | return true;
|
22889 | }
|
22890 | }
|
22891 | };
|
22892 | visitAll$1(visitor, nodes);
|
22893 | return new AstPath(path, position);
|
22894 | }
|
22895 | /**
|
22896 | * Inverts an object's key-value pairs.
|
22897 | */
|
22898 | function invertMap(obj) {
|
22899 | const result = {};
|
22900 | for (const name of Object.keys(obj)) {
|
22901 | const v = obj[name];
|
22902 | result[v] = name;
|
22903 | }
|
22904 | return result;
|
22905 | }
|
22906 | /**
|
22907 | * Finds the directive member providing a template output binding, if one exists.
|
22908 | * @param info aggregate template AST information
|
22909 | * @param path narrowing
|
22910 | */
|
22911 | function findOutputBinding(binding, path, query) {
|
22912 | const element = path.first(ElementAst);
|
22913 | if (element) {
|
22914 | for (const directive of element.directives) {
|
22915 | const invertedOutputs = invertMap(directive.directive.outputs);
|
22916 | const fieldName = invertedOutputs[binding.name];
|
22917 | if (fieldName) {
|
22918 | const classSymbol = query.getTypeSymbol(directive.directive.type.reference);
|
22919 | if (classSymbol) {
|
22920 | return classSymbol.members().get(fieldName);
|
22921 | }
|
22922 | }
|
22923 | }
|
22924 | }
|
22925 | }
|
22926 | /**
|
22927 | * Returns an absolute path from the text in `node`. If the text is already
|
22928 | * an absolute path, return it as is, otherwise join the path with the filename
|
22929 | * of the source file.
|
22930 | */
|
22931 | function extractAbsoluteFilePath(node) {
|
22932 | const url = node.text;
|
22933 | return path.isAbsolute(url) ? url : path.join(path.dirname(node.getSourceFile().fileName), url);
|
22934 | }
|
22935 |
|
22936 | /**
|
22937 | * @license
|
22938 | * Copyright Google LLC All Rights Reserved.
|
22939 | *
|
22940 | * Use of this source code is governed by an MIT-style license that can be
|
22941 | * found in the LICENSE file at https://angular.io/license
|
22942 | */
|
22943 | // AstType calculatetype of the ast given AST element.
|
22944 | class AstType {
|
22945 | constructor(scope, query, context, source) {
|
22946 | this.scope = scope;
|
22947 | this.query = query;
|
22948 | this.context = context;
|
22949 | this.source = source;
|
22950 | this.diagnostics = [];
|
22951 | }
|
22952 | getType(ast) {
|
22953 | return ast.visit(this);
|
22954 | }
|
22955 | getDiagnostics(ast) {
|
22956 | const type = ast.visit(this);
|
22957 | if (this.context.inEvent && type.callable) {
|
22958 | this.diagnostics.push(createDiagnostic(refinedSpan(ast), Diagnostic.callable_expression_expected_method_call));
|
22959 | }
|
22960 | return this.diagnostics;
|
22961 | }
|
22962 | visitUnary(ast) {
|
22963 | // Visit the child to produce diagnostics.
|
22964 | ast.expr.visit(this);
|
22965 | // The unary plus and minus operator are always of type number.
|
22966 | // https://github.com/Microsoft/TypeScript/blob/v1.8.10/doc/spec.md#4.18
|
22967 | switch (ast.operator) {
|
22968 | case '-':
|
22969 | case '+':
|
22970 | return this.query.getBuiltinType(BuiltinType$1.Number);
|
22971 | }
|
22972 | this.diagnostics.push(createDiagnostic(refinedSpan(ast), Diagnostic.unrecognized_operator, ast.operator));
|
22973 | return this.anyType;
|
22974 | }
|
22975 | visitBinary(ast) {
|
22976 | const getType = (ast, operation) => {
|
22977 | const type = this.getType(ast);
|
22978 | if (type.nullable) {
|
22979 | switch (operation) {
|
22980 | case '&&':
|
22981 | case '||':
|
22982 | case '==':
|
22983 | case '!=':
|
22984 | case '===':
|
22985 | case '!==':
|
22986 | // Nullable allowed.
|
22987 | break;
|
22988 | default:
|
22989 | this.diagnostics.push(createDiagnostic(refinedSpan(ast), Diagnostic.expression_might_be_null));
|
22990 | break;
|
22991 | }
|
22992 | }
|
22993 | return type;
|
22994 | };
|
22995 | const leftType = getType(ast.left, ast.operation);
|
22996 | const rightType = getType(ast.right, ast.operation);
|
22997 | const leftKind = this.query.getTypeKind(leftType);
|
22998 | const rightKind = this.query.getTypeKind(rightType);
|
22999 | // The following swtich implements operator typing similar to the
|
23000 | // type production tables in the TypeScript specification.
|
23001 | // https://github.com/Microsoft/TypeScript/blob/v1.8.10/doc/spec.md#4.19
|
23002 | const operKind = leftKind << 8 | rightKind;
|
23003 | switch (ast.operation) {
|
23004 | case '*':
|
23005 | case '/':
|
23006 | case '%':
|
23007 | case '-':
|
23008 | case '<<':
|
23009 | case '>>':
|
23010 | case '>>>':
|
23011 | case '&':
|
23012 | case '^':
|
23013 | case '|':
|
23014 | switch (operKind) {
|
23015 | case BuiltinType$1.Any << 8 | BuiltinType$1.Any:
|
23016 | case BuiltinType$1.Number << 8 | BuiltinType$1.Any:
|
23017 | case BuiltinType$1.Any << 8 | BuiltinType$1.Number:
|
23018 | case BuiltinType$1.Number << 8 | BuiltinType$1.Number:
|
23019 | return this.query.getBuiltinType(BuiltinType$1.Number);
|
23020 | default:
|
23021 | let errorAst = ast.left;
|
23022 | switch (leftKind) {
|
23023 | case BuiltinType$1.Any:
|
23024 | case BuiltinType$1.Number:
|
23025 | errorAst = ast.right;
|
23026 | break;
|
23027 | }
|
23028 | this.diagnostics.push(createDiagnostic(errorAst.span, Diagnostic.expected_a_number_type));
|
23029 | return this.anyType;
|
23030 | }
|
23031 | case '+':
|
23032 | switch (operKind) {
|
23033 | case BuiltinType$1.Any << 8 | BuiltinType$1.Any:
|
23034 | case BuiltinType$1.Any << 8 | BuiltinType$1.Boolean:
|
23035 | case BuiltinType$1.Any << 8 | BuiltinType$1.Number:
|
23036 | case BuiltinType$1.Any << 8 | BuiltinType$1.Other:
|
23037 | case BuiltinType$1.Boolean << 8 | BuiltinType$1.Any:
|
23038 | case BuiltinType$1.Number << 8 | BuiltinType$1.Any:
|
23039 | case BuiltinType$1.Other << 8 | BuiltinType$1.Any:
|
23040 | return this.anyType;
|
23041 | case BuiltinType$1.Any << 8 | BuiltinType$1.String:
|
23042 | case BuiltinType$1.Boolean << 8 | BuiltinType$1.String:
|
23043 | case BuiltinType$1.Number << 8 | BuiltinType$1.String:
|
23044 | case BuiltinType$1.String << 8 | BuiltinType$1.Any:
|
23045 | case BuiltinType$1.String << 8 | BuiltinType$1.Boolean:
|
23046 | case BuiltinType$1.String << 8 | BuiltinType$1.Number:
|
23047 | case BuiltinType$1.String << 8 | BuiltinType$1.String:
|
23048 | case BuiltinType$1.String << 8 | BuiltinType$1.Other:
|
23049 | case BuiltinType$1.Other << 8 | BuiltinType$1.String:
|
23050 | return this.query.getBuiltinType(BuiltinType$1.String);
|
23051 | case BuiltinType$1.Number << 8 | BuiltinType$1.Number:
|
23052 | return this.query.getBuiltinType(BuiltinType$1.Number);
|
23053 | case BuiltinType$1.Boolean << 8 | BuiltinType$1.Number:
|
23054 | case BuiltinType$1.Other << 8 | BuiltinType$1.Number:
|
23055 | this.diagnostics.push(createDiagnostic(ast.left.span, Diagnostic.expected_a_number_type));
|
23056 | return this.anyType;
|
23057 | case BuiltinType$1.Number << 8 | BuiltinType$1.Boolean:
|
23058 | case BuiltinType$1.Number << 8 | BuiltinType$1.Other:
|
23059 | this.diagnostics.push(createDiagnostic(ast.right.span, Diagnostic.expected_a_number_type));
|
23060 | return this.anyType;
|
23061 | default:
|
23062 | this.diagnostics.push(createDiagnostic(refinedSpan(ast), Diagnostic.expected_a_string_or_number_type));
|
23063 | return this.anyType;
|
23064 | }
|
23065 | case '>':
|
23066 | case '<':
|
23067 | case '<=':
|
23068 | case '>=':
|
23069 | case '==':
|
23070 | case '!=':
|
23071 | case '===':
|
23072 | case '!==':
|
23073 | if (!(leftKind & rightKind) &&
|
23074 | !((leftKind | rightKind) & (BuiltinType$1.Null | BuiltinType$1.Undefined))) {
|
23075 | // Two values are comparable only if
|
23076 | // - they have some type overlap, or
|
23077 | // - at least one is not defined
|
23078 | this.diagnostics.push(createDiagnostic(refinedSpan(ast), Diagnostic.expected_operands_of_comparable_types_or_any));
|
23079 | }
|
23080 | return this.query.getBuiltinType(BuiltinType$1.Boolean);
|
23081 | case '&&':
|
23082 | return rightType;
|
23083 | case '||':
|
23084 | return this.query.getTypeUnion(leftType, rightType);
|
23085 | }
|
23086 | this.diagnostics.push(createDiagnostic(refinedSpan(ast), Diagnostic.unrecognized_operator, ast.operation));
|
23087 | return this.anyType;
|
23088 | }
|
23089 | visitChain(ast) {
|
23090 | // If we are producing diagnostics, visit the children
|
23091 | for (const expr of ast.expressions) {
|
23092 | expr.visit(this);
|
23093 | }
|
23094 | // The type of a chain is always undefined.
|
23095 | return this.query.getBuiltinType(BuiltinType$1.Undefined);
|
23096 | }
|
23097 | visitConditional(ast) {
|
23098 | // The type of a conditional is the union of the true and false conditions.
|
23099 | ast.condition.visit(this);
|
23100 | ast.trueExp.visit(this);
|
23101 | ast.falseExp.visit(this);
|
23102 | return this.query.getTypeUnion(this.getType(ast.trueExp), this.getType(ast.falseExp));
|
23103 | }
|
23104 | visitFunctionCall(ast) {
|
23105 | // The type of a function call is the return type of the selected signature.
|
23106 | // The signature is selected based on the types of the arguments. Angular doesn't
|
23107 | // support contextual typing of arguments so this is simpler than TypeScript's
|
23108 | // version.
|
23109 | const args = ast.args.map(arg => this.getType(arg));
|
23110 | const target = this.getType(ast.target);
|
23111 | if (!target || !target.callable) {
|
23112 | this.diagnostics.push(createDiagnostic(refinedSpan(ast), Diagnostic.call_target_not_callable, this.sourceOf(ast.target), target.name));
|
23113 | return this.anyType;
|
23114 | }
|
23115 | const signature = target.selectSignature(args);
|
23116 | if (signature) {
|
23117 | return signature.result;
|
23118 | }
|
23119 | // TODO: Consider a better error message here. See `typescript_symbols#selectSignature` for more
|
23120 | // details.
|
23121 | this.diagnostics.push(createDiagnostic(refinedSpan(ast), Diagnostic.unable_to_resolve_compatible_call_signature));
|
23122 | return this.anyType;
|
23123 | }
|
23124 | visitImplicitReceiver(_ast) {
|
23125 | const _this = this;
|
23126 | // Return a pseudo-symbol for the implicit receiver.
|
23127 | // The members of the implicit receiver are what is defined by the
|
23128 | // scope passed into this class.
|
23129 | return {
|
23130 | name: '$implicit',
|
23131 | kind: 'component',
|
23132 | language: 'ng-template',
|
23133 | type: undefined,
|
23134 | container: undefined,
|
23135 | callable: false,
|
23136 | nullable: false,
|
23137 | public: true,
|
23138 | definition: undefined,
|
23139 | documentation: [],
|
23140 | members() {
|
23141 | return _this.scope;
|
23142 | },
|
23143 | signatures() {
|
23144 | return [];
|
23145 | },
|
23146 | selectSignature(_types) {
|
23147 | return undefined;
|
23148 | },
|
23149 | indexed(_argument) {
|
23150 | return undefined;
|
23151 | },
|
23152 | typeArguments() {
|
23153 | return undefined;
|
23154 | },
|
23155 | };
|
23156 | }
|
23157 | visitThisReceiver(_ast) {
|
23158 | return this.visitImplicitReceiver(_ast);
|
23159 | }
|
23160 | visitInterpolation(ast) {
|
23161 | // If we are producing diagnostics, visit the children.
|
23162 | for (const expr of ast.expressions) {
|
23163 | expr.visit(this);
|
23164 | }
|
23165 | return this.undefinedType;
|
23166 | }
|
23167 | visitKeyedRead(ast) {
|
23168 | const targetType = this.getType(ast.obj);
|
23169 | const keyType = this.getType(ast.key);
|
23170 | const result = targetType.indexed(keyType, ast.key instanceof LiteralPrimitive ? ast.key.value : undefined);
|
23171 | return result || this.anyType;
|
23172 | }
|
23173 | visitKeyedWrite(ast) {
|
23174 | // The write of a type is the type of the value being written.
|
23175 | return this.getType(ast.value);
|
23176 | }
|
23177 | visitLiteralArray(ast) {
|
23178 | // A type literal is an array type of the union of the elements
|
23179 | return this.query.getArrayType(this.query.getTypeUnion(...ast.expressions.map(element => this.getType(element))));
|
23180 | }
|
23181 | visitLiteralMap(ast) {
|
23182 | // If we are producing diagnostics, visit the children
|
23183 | for (const value of ast.values) {
|
23184 | value.visit(this);
|
23185 | }
|
23186 | // TODO: Return a composite type.
|
23187 | return this.anyType;
|
23188 | }
|
23189 | visitLiteralPrimitive(ast) {
|
23190 | // The type of a literal primitive depends on the value of the literal.
|
23191 | switch (ast.value) {
|
23192 | case true:
|
23193 | case false:
|
23194 | return this.query.getBuiltinType(BuiltinType$1.Boolean);
|
23195 | case null:
|
23196 | return this.query.getBuiltinType(BuiltinType$1.Null);
|
23197 | case undefined:
|
23198 | return this.query.getBuiltinType(BuiltinType$1.Undefined);
|
23199 | default:
|
23200 | switch (typeof ast.value) {
|
23201 | case 'string':
|
23202 | return this.query.getBuiltinType(BuiltinType$1.String);
|
23203 | case 'number':
|
23204 | return this.query.getBuiltinType(BuiltinType$1.Number);
|
23205 | default:
|
23206 | this.diagnostics.push(createDiagnostic(refinedSpan(ast), Diagnostic.unrecognized_primitive, typeof ast.value));
|
23207 | return this.anyType;
|
23208 | }
|
23209 | }
|
23210 | }
|
23211 | visitMethodCall(ast) {
|
23212 | return this.resolveMethodCall(this.getType(ast.receiver), ast);
|
23213 | }
|
23214 | visitPipe(ast) {
|
23215 | // The type of a pipe node is the return type of the pipe's transform method. The table returned
|
23216 | // by getPipes() is expected to contain symbols with the corresponding transform method type.
|
23217 | const pipe = this.query.getPipes().get(ast.name);
|
23218 | if (!pipe) {
|
23219 | this.diagnostics.push(createDiagnostic(refinedSpan(ast), Diagnostic.no_pipe_found, ast.name));
|
23220 | return this.anyType;
|
23221 | }
|
23222 | const expType = this.getType(ast.exp);
|
23223 | const signature = pipe.selectSignature([expType].concat(ast.args.map(arg => this.getType(arg))));
|
23224 | if (!signature) {
|
23225 | this.diagnostics.push(createDiagnostic(refinedSpan(ast), Diagnostic.unable_to_resolve_signature, ast.name));
|
23226 | return this.anyType;
|
23227 | }
|
23228 | return signature.result;
|
23229 | }
|
23230 | visitPrefixNot(ast) {
|
23231 | // If we are producing diagnostics, visit the children
|
23232 | ast.expression.visit(this);
|
23233 | // The type of a prefix ! is always boolean.
|
23234 | return this.query.getBuiltinType(BuiltinType$1.Boolean);
|
23235 | }
|
23236 | visitNonNullAssert(ast) {
|
23237 | const expressionType = this.getType(ast.expression);
|
23238 | return this.query.getNonNullableType(expressionType);
|
23239 | }
|
23240 | visitPropertyRead(ast) {
|
23241 | return this.resolvePropertyRead(this.getType(ast.receiver), ast);
|
23242 | }
|
23243 | visitPropertyWrite(ast) {
|
23244 | // The type of a write is the type of the value being written.
|
23245 | return this.getType(ast.value);
|
23246 | }
|
23247 | visitQuote(_ast) {
|
23248 | // The type of a quoted expression is any.
|
23249 | return this.query.getBuiltinType(BuiltinType$1.Any);
|
23250 | }
|
23251 | visitSafeMethodCall(ast) {
|
23252 | return this.resolveMethodCall(this.query.getNonNullableType(this.getType(ast.receiver)), ast);
|
23253 | }
|
23254 | visitSafePropertyRead(ast) {
|
23255 | return this.resolvePropertyRead(this.query.getNonNullableType(this.getType(ast.receiver)), ast);
|
23256 | }
|
23257 | /**
|
23258 | * Gets the source of an expession AST.
|
23259 | * The AST's sourceSpan is relative to the start of the template source code, which is contained
|
23260 | * at this.source.
|
23261 | */
|
23262 | sourceOf(ast) {
|
23263 | return this.source.substring(ast.sourceSpan.start, ast.sourceSpan.end);
|
23264 | }
|
23265 | get anyType() {
|
23266 | let result = this._anyType;
|
23267 | if (!result) {
|
23268 | result = this._anyType = this.query.getBuiltinType(BuiltinType$1.Any);
|
23269 | }
|
23270 | return result;
|
23271 | }
|
23272 | get undefinedType() {
|
23273 | let result = this._undefinedType;
|
23274 | if (!result) {
|
23275 | result = this._undefinedType = this.query.getBuiltinType(BuiltinType$1.Undefined);
|
23276 | }
|
23277 | return result;
|
23278 | }
|
23279 | resolveMethodCall(receiverType, ast) {
|
23280 | if (this.isAny(receiverType)) {
|
23281 | return this.anyType;
|
23282 | }
|
23283 | const methodType = this.resolvePropertyRead(receiverType, ast);
|
23284 | if (!methodType) {
|
23285 | this.diagnostics.push(createDiagnostic(refinedSpan(ast), Diagnostic.could_not_resolve_type, ast.name));
|
23286 | return this.anyType;
|
23287 | }
|
23288 | if (this.isAny(methodType)) {
|
23289 | return this.anyType;
|
23290 | }
|
23291 | if (!methodType.callable) {
|
23292 | this.diagnostics.push(createDiagnostic(refinedSpan(ast), Diagnostic.identifier_not_callable, ast.name));
|
23293 | return this.anyType;
|
23294 | }
|
23295 | const signature = methodType.selectSignature(ast.args.map(arg => this.getType(arg)));
|
23296 | if (!signature) {
|
23297 | this.diagnostics.push(createDiagnostic(refinedSpan(ast), Diagnostic.unable_to_resolve_signature, ast.name));
|
23298 | return this.anyType;
|
23299 | }
|
23300 | return signature.result;
|
23301 | }
|
23302 | resolvePropertyRead(receiverType, ast) {
|
23303 | if (this.isAny(receiverType)) {
|
23304 | return this.anyType;
|
23305 | }
|
23306 | // The type of a property read is the seelcted member's type.
|
23307 | const member = receiverType.members().get(ast.name);
|
23308 | if (!member) {
|
23309 | if (receiverType.name === '$implicit') {
|
23310 | this.diagnostics.push(createDiagnostic(refinedSpan(ast), Diagnostic.identifier_not_defined_in_app_context, ast.name));
|
23311 | }
|
23312 | else if (receiverType.nullable && ast.receiver instanceof PropertyRead) {
|
23313 | const receiver = ast.receiver.name;
|
23314 | this.diagnostics.push(createDiagnostic(refinedSpan(ast), Diagnostic.identifier_possibly_undefined, receiver, `${receiver}?.${ast.name}`, `${receiver}!.${ast.name}`));
|
23315 | }
|
23316 | else {
|
23317 | this.diagnostics.push(createDiagnostic(refinedSpan(ast), Diagnostic.identifier_not_defined_on_receiver, ast.name, receiverType.name));
|
23318 | }
|
23319 | return this.anyType;
|
23320 | }
|
23321 | if (!member.public) {
|
23322 | const container = receiverType.name === '$implicit' ? 'the component' : `'${receiverType.name}'`;
|
23323 | this.diagnostics.push(createDiagnostic(refinedSpan(ast), Diagnostic.identifier_is_private, ast.name, container));
|
23324 | }
|
23325 | return member.type;
|
23326 | }
|
23327 | isAny(symbol) {
|
23328 | return !symbol || this.query.getTypeKind(symbol) === BuiltinType$1.Any ||
|
23329 | (!!symbol.type && this.isAny(symbol.type));
|
23330 | }
|
23331 | }
|
23332 | function refinedSpan(ast) {
|
23333 | // nameSpan is an absolute span, but the spans returned by the expression visitor are expected to
|
23334 | // be relative to the start of the expression.
|
23335 | // TODO: migrate to only using absolute spans
|
23336 | const absoluteOffset = ast.sourceSpan.start - ast.span.start;
|
23337 | if (ast instanceof ASTWithName) {
|
23338 | return offsetSpan(ast.nameSpan, -absoluteOffset);
|
23339 | }
|
23340 | return offsetSpan(ast.sourceSpan, -absoluteOffset);
|
23341 | }
|
23342 |
|
23343 | /**
|
23344 | * @license
|
23345 | * Copyright Google LLC All Rights Reserved.
|
23346 | *
|
23347 | * Use of this source code is governed by an MIT-style license that can be
|
23348 | * found in the LICENSE file at https://angular.io/license
|
23349 | */
|
23350 | function getTemplateExpressionDiagnostics(info) {
|
23351 | const visitor = new ExpressionDiagnosticsVisitor(info, (path) => getExpressionScope(info, path));
|
23352 | templateVisitAll(visitor, info.templateAst);
|
23353 | return visitor.diagnostics;
|
23354 | }
|
23355 | function getReferences(info) {
|
23356 | const result = [];
|
23357 | function processReferences(references) {
|
23358 | for (const reference of references) {
|
23359 | let type = undefined;
|
23360 | if (reference.value) {
|
23361 | type = info.query.getTypeSymbol(tokenReference(reference.value));
|
23362 | }
|
23363 | result.push({
|
23364 | name: reference.name,
|
23365 | kind: 'reference',
|
23366 | type: type || info.query.getBuiltinType(BuiltinType$1.Any),
|
23367 | get definition() {
|
23368 | return getDefinitionOf(info, reference);
|
23369 | }
|
23370 | });
|
23371 | }
|
23372 | }
|
23373 | const visitor = new class extends RecursiveTemplateAstVisitor {
|
23374 | visitEmbeddedTemplate(ast, context) {
|
23375 | super.visitEmbeddedTemplate(ast, context);
|
23376 | processReferences(ast.references);
|
23377 | }
|
23378 | visitElement(ast, context) {
|
23379 | super.visitElement(ast, context);
|
23380 | processReferences(ast.references);
|
23381 | }
|
23382 | };
|
23383 | templateVisitAll(visitor, info.templateAst);
|
23384 | return result;
|
23385 | }
|
23386 | function getDefinitionOf(info, ast) {
|
23387 | if (info.fileName) {
|
23388 | const templateOffset = info.offset;
|
23389 | return [{
|
23390 | fileName: info.fileName,
|
23391 | span: {
|
23392 | start: ast.sourceSpan.start.offset + templateOffset,
|
23393 | end: ast.sourceSpan.end.offset + templateOffset
|
23394 | }
|
23395 | }];
|
23396 | }
|
23397 | }
|
23398 | /**
|
23399 | * Resolve all variable declarations in a template by traversing the specified
|
23400 | * `path`.
|
23401 | * @param info
|
23402 | * @param path template AST path
|
23403 | */
|
23404 | function getVarDeclarations(info, path) {
|
23405 | const results = [];
|
23406 | for (let current = path.head; current; current = path.childOf(current)) {
|
23407 | if (!(current instanceof EmbeddedTemplateAst)) {
|
23408 | continue;
|
23409 | }
|
23410 | for (const variable of current.variables) {
|
23411 | let symbol = getVariableTypeFromDirectiveContext(variable.value, info.query, current);
|
23412 | const kind = info.query.getTypeKind(symbol);
|
23413 | if (kind === BuiltinType$1.Any || kind === BuiltinType$1.Unbound) {
|
23414 | // For special cases such as ngFor and ngIf, the any type is not very useful.
|
23415 | // We can do better by resolving the binding value.
|
23416 | const symbolsInScope = info.query.mergeSymbolTable([
|
23417 | info.members,
|
23418 | // Since we are traversing the AST path from head to tail, any variables
|
23419 | // that have been declared so far are also in scope.
|
23420 | info.query.createSymbolTable(results),
|
23421 | ]);
|
23422 | symbol = refinedVariableType(variable.value, symbolsInScope, info, current);
|
23423 | }
|
23424 | results.push({
|
23425 | name: variable.name,
|
23426 | kind: 'variable',
|
23427 | type: symbol,
|
23428 | get definition() {
|
23429 | return getDefinitionOf(info, variable);
|
23430 | },
|
23431 | });
|
23432 | }
|
23433 | }
|
23434 | return results;
|
23435 | }
|
23436 | /**
|
23437 | * Resolve the type for the variable in `templateElement` by finding the structural
|
23438 | * directive which has the context member. Returns any when not found.
|
23439 | * @param value variable value name
|
23440 | * @param query type symbol query
|
23441 | * @param templateElement
|
23442 | */
|
23443 | function getVariableTypeFromDirectiveContext(value, query, templateElement) {
|
23444 | for (const { directive } of templateElement.directives) {
|
23445 | const context = query.getTemplateContext(directive.type.reference);
|
23446 | if (context) {
|
23447 | const member = context.get(value);
|
23448 | if (member && member.type) {
|
23449 | return member.type;
|
23450 | }
|
23451 | }
|
23452 | }
|
23453 | return query.getBuiltinType(BuiltinType$1.Any);
|
23454 | }
|
23455 | /**
|
23456 | * Resolve a more specific type for the variable in `templateElement` by inspecting
|
23457 | * all variables that are in scope in the `mergedTable`. This function is a special
|
23458 | * case for `ngFor` and `ngIf`. If resolution fails, return the `any` type.
|
23459 | * @param value variable value name
|
23460 | * @param mergedTable symbol table for all variables in scope
|
23461 | * @param info available template information
|
23462 | * @param templateElement
|
23463 | */
|
23464 | function refinedVariableType(value, mergedTable, info, templateElement) {
|
23465 | if (value === '$implicit') {
|
23466 | // Special case: ngFor directive
|
23467 | const ngForDirective = templateElement.directives.find(d => {
|
23468 | const name = identifierName(d.directive.type);
|
23469 | return name == 'NgFor' || name == 'NgForOf';
|
23470 | });
|
23471 | if (ngForDirective) {
|
23472 | const ngForOfBinding = ngForDirective.inputs.find(i => i.directiveName == 'ngForOf');
|
23473 | if (ngForOfBinding) {
|
23474 | // Check if there is a known type for the ngFor binding.
|
23475 | const bindingType = new AstType(mergedTable, info.query, {}, info.source).getType(ngForOfBinding.value);
|
23476 | if (bindingType) {
|
23477 | const result = info.query.getElementType(bindingType);
|
23478 | if (result) {
|
23479 | return result;
|
23480 | }
|
23481 | }
|
23482 | }
|
23483 | }
|
23484 | }
|
23485 | if (value === 'ngIf' || value === '$implicit') {
|
23486 | const ngIfDirective = templateElement.directives.find(d => identifierName(d.directive.type) === 'NgIf');
|
23487 | if (ngIfDirective) {
|
23488 | // Special case: ngIf directive. The NgIf structural directive owns a template context with
|
23489 | // "$implicit" and "ngIf" members. These properties are typed as generics. Until the language
|
23490 | // service uses an Ivy and TypecheckBlock backend, we cannot bind these values to a concrete
|
23491 | // type without manual inference. To get the concrete type, look up the type of the "ngIf"
|
23492 | // import on the NgIf directive bound to the template.
|
23493 | //
|
23494 | // See @angular/common/ng_if.ts for more information.
|
23495 | const ngIfBinding = ngIfDirective.inputs.find(i => i.directiveName === 'ngIf');
|
23496 | if (ngIfBinding) {
|
23497 | // Check if there is a known type bound to the ngIf input.
|
23498 | const bindingType = new AstType(mergedTable, info.query, {}, info.source).getType(ngIfBinding.value);
|
23499 | if (bindingType) {
|
23500 | return bindingType;
|
23501 | }
|
23502 | }
|
23503 | }
|
23504 | }
|
23505 | // We can't do better, return any
|
23506 | return info.query.getBuiltinType(BuiltinType$1.Any);
|
23507 | }
|
23508 | function getEventDeclaration(info, path) {
|
23509 | const event = path.tail;
|
23510 | if (!(event instanceof BoundEventAst)) {
|
23511 | // No event available in this context.
|
23512 | return;
|
23513 | }
|
23514 | const genericEvent = {
|
23515 | name: '$event',
|
23516 | kind: 'variable',
|
23517 | type: info.query.getBuiltinType(BuiltinType$1.Any),
|
23518 | };
|
23519 | const outputSymbol = findOutputBinding(event, path, info.query);
|
23520 | if (!outputSymbol) {
|
23521 | // The `$event` variable doesn't belong to an output, so its type can't be refined.
|
23522 | // TODO: type `$event` variables in bindings to DOM events.
|
23523 | return genericEvent;
|
23524 | }
|
23525 | // The raw event type is wrapped in a generic, like EventEmitter<T> or Observable<T>.
|
23526 | const ta = outputSymbol.typeArguments();
|
23527 | if (!ta || ta.length !== 1)
|
23528 | return genericEvent;
|
23529 | const eventType = ta[0];
|
23530 | return Object.assign(Object.assign({}, genericEvent), { type: eventType });
|
23531 | }
|
23532 | /**
|
23533 | * Returns the symbols available in a particular scope of a template.
|
23534 | * @param info parsed template information
|
23535 | * @param path path of template nodes narrowing to the context the expression scope should be
|
23536 | * derived for.
|
23537 | */
|
23538 | function getExpressionScope(info, path) {
|
23539 | let result = info.members;
|
23540 | const references = getReferences(info);
|
23541 | const variables = getVarDeclarations(info, path);
|
23542 | const event = getEventDeclaration(info, path);
|
23543 | if (references.length || variables.length || event) {
|
23544 | const referenceTable = info.query.createSymbolTable(references);
|
23545 | const variableTable = info.query.createSymbolTable(variables);
|
23546 | const eventsTable = info.query.createSymbolTable(event ? [event] : []);
|
23547 | result = info.query.mergeSymbolTable([result, referenceTable, variableTable, eventsTable]);
|
23548 | }
|
23549 | return result;
|
23550 | }
|
23551 | class ExpressionDiagnosticsVisitor extends RecursiveTemplateAstVisitor {
|
23552 | constructor(info, getExpressionScope) {
|
23553 | super();
|
23554 | this.info = info;
|
23555 | this.getExpressionScope = getExpressionScope;
|
23556 | this.diagnostics = [];
|
23557 | this.path = new AstPath([]);
|
23558 | }
|
23559 | visitDirective(ast, context) {
|
23560 | // Override the default child visitor to ignore the host properties of a directive.
|
23561 | if (ast.inputs && ast.inputs.length) {
|
23562 | templateVisitAll(this, ast.inputs, context);
|
23563 | }
|
23564 | }
|
23565 | visitBoundText(ast) {
|
23566 | this.push(ast);
|
23567 | this.diagnoseExpression(ast.value, ast.sourceSpan.start.offset, false);
|
23568 | this.pop();
|
23569 | }
|
23570 | visitDirectiveProperty(ast) {
|
23571 | this.push(ast);
|
23572 | this.diagnoseExpression(ast.value, this.attributeValueLocation(ast), false);
|
23573 | this.pop();
|
23574 | }
|
23575 | visitElementProperty(ast) {
|
23576 | this.push(ast);
|
23577 | this.diagnoseExpression(ast.value, this.attributeValueLocation(ast), false);
|
23578 | this.pop();
|
23579 | }
|
23580 | visitEvent(ast) {
|
23581 | this.push(ast);
|
23582 | this.diagnoseExpression(ast.handler, this.attributeValueLocation(ast), true);
|
23583 | this.pop();
|
23584 | }
|
23585 | visitVariable(ast) {
|
23586 | const directive = this.directiveSummary;
|
23587 | if (directive && ast.value) {
|
23588 | const context = this.info.query.getTemplateContext(directive.type.reference);
|
23589 | if (context && !context.has(ast.value)) {
|
23590 | const missingMember = ast.value === '$implicit' ? 'an implicit value' : `a member called '${ast.value}'`;
|
23591 | const span = this.absSpan(spanOf$1(ast.sourceSpan));
|
23592 | this.diagnostics.push(createDiagnostic(span, Diagnostic.template_context_missing_member, directive.type.reference.name, missingMember));
|
23593 | }
|
23594 | }
|
23595 | }
|
23596 | visitElement(ast, context) {
|
23597 | this.push(ast);
|
23598 | super.visitElement(ast, context);
|
23599 | this.pop();
|
23600 | }
|
23601 | visitEmbeddedTemplate(ast, context) {
|
23602 | const previousDirectiveSummary = this.directiveSummary;
|
23603 | this.push(ast);
|
23604 | // Find directive that references this template
|
23605 | this.directiveSummary =
|
23606 | ast.directives.map(d => d.directive).find(d => hasTemplateReference(d.type));
|
23607 | // Process children
|
23608 | super.visitEmbeddedTemplate(ast, context);
|
23609 | this.pop();
|
23610 | this.directiveSummary = previousDirectiveSummary;
|
23611 | }
|
23612 | attributeValueLocation(ast) {
|
23613 | const path = getPathToNodeAtPosition(this.info.htmlAst, ast.sourceSpan.start.offset);
|
23614 | const last = path.tail;
|
23615 | if (last instanceof Attribute && last.valueSpan) {
|
23616 | return last.valueSpan.start.offset;
|
23617 | }
|
23618 | return ast.sourceSpan.start.offset;
|
23619 | }
|
23620 | diagnoseExpression(ast, offset, inEvent) {
|
23621 | const scope = this.getExpressionScope(this.path, inEvent);
|
23622 | const analyzer = new AstType(scope, this.info.query, { inEvent }, this.info.source);
|
23623 | for (const diagnostic of analyzer.getDiagnostics(ast)) {
|
23624 | diagnostic.span = this.absSpan(diagnostic.span, offset);
|
23625 | this.diagnostics.push(diagnostic);
|
23626 | }
|
23627 | }
|
23628 | push(ast) {
|
23629 | this.path.push(ast);
|
23630 | }
|
23631 | pop() {
|
23632 | this.path.pop();
|
23633 | }
|
23634 | absSpan(span, additionalOffset = 0) {
|
23635 | return {
|
23636 | start: span.start + this.info.offset + additionalOffset,
|
23637 | end: span.end + this.info.offset + additionalOffset,
|
23638 | };
|
23639 | }
|
23640 | }
|
23641 | function hasTemplateReference(type) {
|
23642 | if (type.diDeps) {
|
23643 | for (let diDep of type.diDeps) {
|
23644 | if (diDep.token && diDep.token.identifier &&
|
23645 | identifierName(diDep.token.identifier) == 'TemplateRef')
|
23646 | return true;
|
23647 | }
|
23648 | }
|
23649 | return false;
|
23650 | }
|
23651 | function spanOf$1(sourceSpan) {
|
23652 | return { start: sourceSpan.start.offset, end: sourceSpan.end.offset };
|
23653 | }
|
23654 |
|
23655 | /**
|
23656 | * @license
|
23657 | * Copyright Google LLC All Rights Reserved.
|
23658 | *
|
23659 | * Use of this source code is governed by an MIT-style license that can be
|
23660 | * found in the LICENSE file at https://angular.io/license
|
23661 | */
|
23662 | /**
|
23663 | * The type of Angular directive. Used for QuickInfo in template.
|
23664 | */
|
23665 | var DirectiveKind;
|
23666 | (function (DirectiveKind) {
|
23667 | DirectiveKind["COMPONENT"] = "component";
|
23668 | DirectiveKind["DIRECTIVE"] = "directive";
|
23669 | DirectiveKind["EVENT"] = "event";
|
23670 | })(DirectiveKind || (DirectiveKind = {}));
|
23671 | /**
|
23672 | * ScriptElementKind for completion.
|
23673 | */
|
23674 | var CompletionKind;
|
23675 | (function (CompletionKind) {
|
23676 | CompletionKind["ANGULAR_ELEMENT"] = "angular element";
|
23677 | CompletionKind["ATTRIBUTE"] = "attribute";
|
23678 | CompletionKind["COMPONENT"] = "component";
|
23679 | CompletionKind["ELEMENT"] = "element";
|
23680 | CompletionKind["ENTITY"] = "entity";
|
23681 | CompletionKind["HTML_ATTRIBUTE"] = "html attribute";
|
23682 | CompletionKind["HTML_ELEMENT"] = "html element";
|
23683 | CompletionKind["KEY"] = "key";
|
23684 | CompletionKind["METHOD"] = "method";
|
23685 | CompletionKind["PIPE"] = "pipe";
|
23686 | CompletionKind["PROPERTY"] = "property";
|
23687 | CompletionKind["REFERENCE"] = "reference";
|
23688 | CompletionKind["TYPE"] = "type";
|
23689 | CompletionKind["VARIABLE"] = "variable";
|
23690 | })(CompletionKind || (CompletionKind = {}));
|
23691 |
|
23692 | /**
|
23693 | * @license
|
23694 | * Copyright Google LLC All Rights Reserved.
|
23695 | *
|
23696 | * Use of this source code is governed by an MIT-style license that can be
|
23697 | * found in the LICENSE file at https://angular.io/license
|
23698 | */
|
23699 | function findAstAt(ast, position, excludeEmpty = false) {
|
23700 | const path = [];
|
23701 | const visitor = new class extends RecursiveAstVisitor {
|
23702 | visit(ast) {
|
23703 | if ((!excludeEmpty || ast.sourceSpan.start < ast.sourceSpan.end) &&
|
23704 | inSpan(position, ast.sourceSpan)) {
|
23705 | const isNotNarrower = path.length && !isNarrower(ast.span, path[path.length - 1].span);
|
23706 | if (!isNotNarrower) {
|
23707 | path.push(ast);
|
23708 | }
|
23709 | ast.visit(this);
|
23710 | }
|
23711 | }
|
23712 | };
|
23713 | // We never care about the ASTWithSource node and its visit() method calls its ast's visit so
|
23714 | // the visit() method above would never see it.
|
23715 | if (ast instanceof ASTWithSource) {
|
23716 | ast = ast.ast;
|
23717 | }
|
23718 | // `Interpolation` is useless here except the `expressions` of it.
|
23719 | if (ast instanceof Interpolation) {
|
23720 | ast = ast.expressions.filter((_ast) => inSpan(position, _ast.sourceSpan))[0];
|
23721 | }
|
23722 | if (ast) {
|
23723 | visitor.visit(ast);
|
23724 | }
|
23725 | return new AstPath(path, position);
|
23726 | }
|
23727 | function getExpressionCompletions(scope, ast, position, templateInfo) {
|
23728 | const path = findAstAt(ast, position);
|
23729 | if (path.empty)
|
23730 | return undefined;
|
23731 | const tail = path.tail;
|
23732 | let result = scope;
|
23733 | function getType(ast) {
|
23734 | return new AstType(scope, templateInfo.query, {}, templateInfo.source).getType(ast);
|
23735 | }
|
23736 | // If the completion request is in a not in a pipe or property access then the global scope
|
23737 | // (that is the scope of the implicit receiver) is the right scope as the user is typing the
|
23738 | // beginning of an expression.
|
23739 | tail.visit({
|
23740 | visitUnary(_ast) { },
|
23741 | visitBinary(_ast) { },
|
23742 | visitChain(_ast) { },
|
23743 | visitConditional(_ast) { },
|
23744 | visitFunctionCall(_ast) { },
|
23745 | visitImplicitReceiver(_ast) { },
|
23746 | visitThisReceiver(_ast) { },
|
23747 | visitInterpolation(_ast) {
|
23748 | result = undefined;
|
23749 | },
|
23750 | visitKeyedRead(_ast) { },
|
23751 | visitKeyedWrite(_ast) { },
|
23752 | visitLiteralArray(_ast) { },
|
23753 | visitLiteralMap(_ast) { },
|
23754 | visitLiteralPrimitive(ast) {
|
23755 | // The type `LiteralPrimitive` include the `ERROR`, and it's wrapped as `string`.
|
23756 | // packages/compiler/src/template_parser/binding_parser.ts#L308
|
23757 | // So exclude the `ERROR` here.
|
23758 | if (typeof ast.value === 'string' &&
|
23759 | ast.value ===
|
23760 | templateInfo.source.slice(ast.sourceSpan.start + 1, ast.sourceSpan.end - 1)) {
|
23761 | result = undefined;
|
23762 | }
|
23763 | },
|
23764 | visitMethodCall(_ast) { },
|
23765 | visitPipe(ast) {
|
23766 | if (position >= ast.exp.span.end &&
|
23767 | (!ast.args || !ast.args.length || position < ast.args[0].span.start)) {
|
23768 | // We are in a position a pipe name is expected.
|
23769 | result = templateInfo.query.getPipes();
|
23770 | }
|
23771 | },
|
23772 | visitPrefixNot(_ast) { },
|
23773 | visitNonNullAssert(_ast) { },
|
23774 | visitPropertyRead(ast) {
|
23775 | const receiverType = getType(ast.receiver);
|
23776 | result = receiverType ? receiverType.members() : scope;
|
23777 | },
|
23778 | visitPropertyWrite(ast) {
|
23779 | const receiverType = getType(ast.receiver);
|
23780 | result = receiverType ? receiverType.members() : scope;
|
23781 | },
|
23782 | visitQuote(_ast) {
|
23783 | // For a quote, return the members of any (if there are any).
|
23784 | result = templateInfo.query.getBuiltinType(BuiltinType$1.Any).members();
|
23785 | },
|
23786 | visitSafeMethodCall(ast) {
|
23787 | const receiverType = getType(ast.receiver);
|
23788 | result = receiverType ? receiverType.members() : scope;
|
23789 | },
|
23790 | visitSafePropertyRead(ast) {
|
23791 | const receiverType = getType(ast.receiver);
|
23792 | result = receiverType ? receiverType.members() : scope;
|
23793 | },
|
23794 | });
|
23795 | return result && result.values();
|
23796 | }
|
23797 | /**
|
23798 | * Retrieves the expression symbol at a particular position in a template.
|
23799 | *
|
23800 | * @param scope symbols in scope of the template
|
23801 | * @param ast template AST
|
23802 | * @param position absolute location in template to retrieve symbol at
|
23803 | * @param query type symbol query for the template scope
|
23804 | */
|
23805 | function getExpressionSymbol(scope, ast, position, templateInfo) {
|
23806 | const path = findAstAt(ast, position, /* excludeEmpty */ true);
|
23807 | if (path.empty)
|
23808 | return undefined;
|
23809 | const tail = path.tail;
|
23810 | function getType(ast) {
|
23811 | return new AstType(scope, templateInfo.query, {}, templateInfo.source).getType(ast);
|
23812 | }
|
23813 | function spanFromName(ast) {
|
23814 | // `nameSpan` is an absolute span, but the span expected by the result of this method is
|
23815 | // relative to the start of the expression.
|
23816 | // TODO(ayazhafiz): migrate to only using absolute spans
|
23817 | const offset = ast.sourceSpan.start - ast.span.start;
|
23818 | return {
|
23819 | start: ast.nameSpan.start - offset,
|
23820 | end: ast.nameSpan.end - offset,
|
23821 | };
|
23822 | }
|
23823 | let symbol = undefined;
|
23824 | let span = undefined;
|
23825 | // If the completion request is in a not in a pipe or property access then the global scope
|
23826 | // (that is the scope of the implicit receiver) is the right scope as the user is typing the
|
23827 | // beginning of an expression.
|
23828 | tail.visit({
|
23829 | visitUnary(_ast) { },
|
23830 | visitBinary(_ast) { },
|
23831 | visitChain(_ast) { },
|
23832 | visitConditional(_ast) { },
|
23833 | visitFunctionCall(_ast) { },
|
23834 | visitImplicitReceiver(_ast) { },
|
23835 | visitThisReceiver(_ast) { },
|
23836 | visitInterpolation(_ast) { },
|
23837 | visitKeyedRead(_ast) { },
|
23838 | visitKeyedWrite(_ast) { },
|
23839 | visitLiteralArray(_ast) { },
|
23840 | visitLiteralMap(_ast) { },
|
23841 | visitLiteralPrimitive(_ast) { },
|
23842 | visitMethodCall(ast) {
|
23843 | const receiverType = getType(ast.receiver);
|
23844 | symbol = receiverType && receiverType.members().get(ast.name);
|
23845 | span = spanFromName(ast);
|
23846 | },
|
23847 | visitPipe(ast) {
|
23848 | if (inSpan(position, ast.nameSpan, /* exclusive */ true)) {
|
23849 | // We are in a position a pipe name is expected.
|
23850 | const pipes = templateInfo.query.getPipes();
|
23851 | symbol = pipes.get(ast.name);
|
23852 | span = spanFromName(ast);
|
23853 | }
|
23854 | },
|
23855 | visitPrefixNot(_ast) { },
|
23856 | visitNonNullAssert(_ast) { },
|
23857 | visitPropertyRead(ast) {
|
23858 | const receiverType = getType(ast.receiver);
|
23859 | symbol = receiverType && receiverType.members().get(ast.name);
|
23860 | span = spanFromName(ast);
|
23861 | },
|
23862 | visitPropertyWrite(ast) {
|
23863 | const receiverType = getType(ast.receiver);
|
23864 | symbol = receiverType && receiverType.members().get(ast.name);
|
23865 | span = spanFromName(ast);
|
23866 | },
|
23867 | visitQuote(_ast) { },
|
23868 | visitSafeMethodCall(ast) {
|
23869 | const receiverType = getType(ast.receiver);
|
23870 | symbol = receiverType && receiverType.members().get(ast.name);
|
23871 | span = spanFromName(ast);
|
23872 | },
|
23873 | visitSafePropertyRead(ast) {
|
23874 | const receiverType = getType(ast.receiver);
|
23875 | symbol = receiverType && receiverType.members().get(ast.name);
|
23876 | span = spanFromName(ast);
|
23877 | },
|
23878 | });
|
23879 | if (symbol && span) {
|
23880 | return { symbol, span };
|
23881 | }
|
23882 | }
|
23883 |
|
23884 | /**
|
23885 | * @license
|
23886 | * Copyright Google LLC All Rights Reserved.
|
23887 | *
|
23888 | * Use of this source code is governed by an MIT-style license that can be
|
23889 | * found in the LICENSE file at https://angular.io/license
|
23890 | */
|
23891 | const values = [
|
23892 | 'ID',
|
23893 | 'CDATA',
|
23894 | 'NAME',
|
23895 | ['ltr', 'rtl'],
|
23896 | ['rect', 'circle', 'poly', 'default'],
|
23897 | 'NUMBER',
|
23898 | ['nohref'],
|
23899 | ['ismap'],
|
23900 | ['declare'],
|
23901 | ['DATA', 'REF', 'OBJECT'],
|
23902 | ['GET', 'POST'],
|
23903 | 'IDREF',
|
23904 | ['TEXT', 'PASSWORD', 'CHECKBOX', 'RADIO', 'SUBMIT', 'RESET', 'FILE', 'HIDDEN', 'IMAGE', 'BUTTON'],
|
23905 | ['checked'],
|
23906 | ['disabled'],
|
23907 | ['readonly'],
|
23908 | ['multiple'],
|
23909 | ['selected'],
|
23910 | ['button', 'submit', 'reset'],
|
23911 | ['void', 'above', 'below', 'hsides', 'lhs', 'rhs', 'vsides', 'box', 'border'],
|
23912 | ['none', 'groups', 'rows', 'cols', 'all'],
|
23913 | ['left', 'center', 'right', 'justify', 'char'],
|
23914 | ['top', 'middle', 'bottom', 'baseline'],
|
23915 | 'IDREFS',
|
23916 | ['row', 'col', 'rowgroup', 'colgroup'],
|
23917 | ['defer']
|
23918 | ];
|
23919 | const groups = [
|
23920 | { id: 0 },
|
23921 | {
|
23922 | onclick: 1,
|
23923 | ondblclick: 1,
|
23924 | onmousedown: 1,
|
23925 | onmouseup: 1,
|
23926 | onmouseover: 1,
|
23927 | onmousemove: 1,
|
23928 | onmouseout: 1,
|
23929 | onkeypress: 1,
|
23930 | onkeydown: 1,
|
23931 | onkeyup: 1
|
23932 | },
|
23933 | { lang: 2, dir: 3 },
|
23934 | { onload: 1, onunload: 1 },
|
23935 | { name: 1 },
|
23936 | { href: 1 },
|
23937 | { type: 1 },
|
23938 | { alt: 1 },
|
23939 | { tabindex: 5 },
|
23940 | { media: 1 },
|
23941 | { nohref: 6 },
|
23942 | { usemap: 1 },
|
23943 | { src: 1 },
|
23944 | { onfocus: 1, onblur: 1 },
|
23945 | { charset: 1 },
|
23946 | { declare: 8, classid: 1, codebase: 1, data: 1, codetype: 1, archive: 1, standby: 1 },
|
23947 | { title: 1 },
|
23948 | { value: 1 },
|
23949 | { cite: 1 },
|
23950 | { datetime: 1 },
|
23951 | { accept: 1 },
|
23952 | { shape: 4, coords: 1 },
|
23953 | { for: 11
|
23954 | },
|
23955 | { action: 1, method: 10, enctype: 1, onsubmit: 1, onreset: 1, 'accept-charset': 1 },
|
23956 | { valuetype: 9 },
|
23957 | { longdesc: 1 },
|
23958 | { width: 1 },
|
23959 | { disabled: 14 },
|
23960 | { readonly: 15, onselect: 1 },
|
23961 | { accesskey: 1 },
|
23962 | { size: 5, multiple: 16 },
|
23963 | { onchange: 1 },
|
23964 | { label: 1 },
|
23965 | { selected: 17 },
|
23966 | { type: 12, checked: 13, size: 1, maxlength: 5 },
|
23967 | { rows: 5, cols: 5 },
|
23968 | { type: 18 },
|
23969 | { height: 1 },
|
23970 | { summary: 1, border: 1, frame: 19, rules: 20, cellspacing: 1, cellpadding: 1, datapagesize: 1 },
|
23971 | { align: 21, char: 1, charoff: 1, valign: 22 },
|
23972 | { span: 5 },
|
23973 | { abbr: 1, axis: 1, headers: 23, scope: 24, rowspan: 5, colspan: 5 },
|
23974 | { profile: 1 },
|
23975 | { 'http-equiv': 2, name: 2, content: 1, scheme: 1 },
|
23976 | { class: 1, style: 1 },
|
23977 | { hreflang: 2, rel: 1, rev: 1 },
|
23978 | { ismap: 7 },
|
23979 | {
|
23980 | defer: 25, event: 1, for: 1
|
23981 | }
|
23982 | ];
|
23983 | const elements = {
|
23984 | TT: [0, 1, 2, 16, 44],
|
23985 | I: [0, 1, 2, 16, 44],
|
23986 | B: [0, 1, 2, 16, 44],
|
23987 | BIG: [0, 1, 2, 16, 44],
|
23988 | SMALL: [0, 1, 2, 16, 44],
|
23989 | EM: [0, 1, 2, 16, 44],
|
23990 | STRONG: [0, 1, 2, 16, 44],
|
23991 | DFN: [0, 1, 2, 16, 44],
|
23992 | CODE: [0, 1, 2, 16, 44],
|
23993 | SAMP: [0, 1, 2, 16, 44],
|
23994 | KBD: [0, 1, 2, 16, 44],
|
23995 | VAR: [0, 1, 2, 16, 44],
|
23996 | CITE: [0, 1, 2, 16, 44],
|
23997 | ABBR: [0, 1, 2, 16, 44],
|
23998 | ACRONYM: [0, 1, 2, 16, 44],
|
23999 | SUB: [0, 1, 2, 16, 44],
|
24000 | SUP: [0, 1, 2, 16, 44],
|
24001 | SPAN: [0, 1, 2, 16, 44],
|
24002 | BDO: [0, 2, 16, 44],
|
24003 | BR: [0, 16, 44],
|
24004 | BODY: [0, 1, 2, 3, 16, 44],
|
24005 | ADDRESS: [0, 1, 2, 16, 44],
|
24006 | DIV: [0, 1, 2, 16, 44],
|
24007 | A: [0, 1, 2, 4, 5, 6, 8, 13, 14, 16, 21, 29, 44, 45],
|
24008 | MAP: [0, 1, 2, 4, 16, 44],
|
24009 | AREA: [0, 1, 2, 5, 7, 8, 10, 13, 16, 21, 29, 44],
|
24010 | LINK: [0, 1, 2, 5, 6, 9, 14, 16, 44, 45],
|
24011 | IMG: [0, 1, 2, 4, 7, 11, 12, 16, 25, 26, 37, 44, 46],
|
24012 | OBJECT: [0, 1, 2, 4, 6, 8, 11, 15, 16, 26, 37, 44],
|
24013 | PARAM: [0, 4, 6, 17, 24],
|
24014 | HR: [0, 1, 2, 16, 44],
|
24015 | P: [0, 1, 2, 16, 44],
|
24016 | H1: [0, 1, 2, 16, 44],
|
24017 | H2: [0, 1, 2, 16, 44],
|
24018 | H3: [0, 1, 2, 16, 44],
|
24019 | H4: [0, 1, 2, 16, 44],
|
24020 | H5: [0, 1, 2, 16, 44],
|
24021 | H6: [0, 1, 2, 16, 44],
|
24022 | PRE: [0, 1, 2, 16, 44],
|
24023 | Q: [0, 1, 2, 16, 18, 44],
|
24024 | BLOCKQUOTE: [0, 1, 2, 16, 18, 44],
|
24025 | INS: [0, 1, 2, 16, 18, 19, 44],
|
24026 | DEL: [0, 1, 2, 16, 18, 19, 44],
|
24027 | DL: [0, 1, 2, 16, 44],
|
24028 | DT: [0, 1, 2, 16, 44],
|
24029 | DD: [0, 1, 2, 16, 44],
|
24030 | OL: [0, 1, 2, 16, 44],
|
24031 | UL: [0, 1, 2, 16, 44],
|
24032 | LI: [0, 1, 2, 16, 44],
|
24033 | FORM: [0, 1, 2, 4, 16, 20, 23, 44],
|
24034 | LABEL: [0, 1, 2, 13, 16, 22, 29, 44],
|
24035 | INPUT: [0, 1, 2, 4, 7, 8, 11, 12, 13, 16, 17, 20, 27, 28, 29, 31, 34, 44, 46],
|
24036 | SELECT: [0, 1, 2, 4, 8, 13, 16, 27, 30, 31, 44],
|
24037 | OPTGROUP: [0, 1, 2, 16, 27, 32, 44],
|
24038 | OPTION: [0, 1, 2, 16, 17, 27, 32, 33, 44],
|
24039 | TEXTAREA: [0, 1, 2, 4, 8, 13, 16, 27, 28, 29, 31, 35, 44],
|
24040 | FIELDSET: [0, 1, 2, 16, 44],
|
24041 | LEGEND: [0, 1, 2, 16, 29, 44],
|
24042 | BUTTON: [0, 1, 2, 4, 8, 13, 16, 17, 27, 29, 36, 44],
|
24043 | TABLE: [0, 1, 2, 16, 26, 38, 44],
|
24044 | CAPTION: [0, 1, 2, 16, 44],
|
24045 | COLGROUP: [0, 1, 2, 16, 26, 39, 40, 44],
|
24046 | COL: [0, 1, 2, 16, 26, 39, 40, 44],
|
24047 | THEAD: [0, 1, 2, 16, 39, 44],
|
24048 | TBODY: [0, 1, 2, 16, 39, 44],
|
24049 | TFOOT: [0, 1, 2, 16, 39, 44],
|
24050 | TR: [0, 1, 2, 16, 39, 44],
|
24051 | TH: [0, 1, 2, 16, 39, 41, 44],
|
24052 | TD: [0, 1, 2, 16, 39, 41, 44],
|
24053 | HEAD: [2, 42],
|
24054 | TITLE: [2],
|
24055 | BASE: [5],
|
24056 | META: [2, 43],
|
24057 | STYLE: [2, 6, 9, 16],
|
24058 | SCRIPT: [6, 12, 14, 47],
|
24059 | NOSCRIPT: [0, 1, 2, 16, 44],
|
24060 | HTML: [2]
|
24061 | };
|
24062 | const defaultAttributes = [0, 1, 2, 4];
|
24063 | function elementNames() {
|
24064 | return Object.keys(elements).sort().map(v => v.toLowerCase());
|
24065 | }
|
24066 | function compose(indexes) {
|
24067 | const result = {};
|
24068 | if (indexes) {
|
24069 | for (let index of indexes) {
|
24070 | const group = groups[index];
|
24071 | for (let name in group)
|
24072 | if (group.hasOwnProperty(name))
|
24073 | result[name] = values[group[name]];
|
24074 | }
|
24075 | }
|
24076 | return result;
|
24077 | }
|
24078 | function attributeNames(element) {
|
24079 | return Object.keys(compose(elements[element.toUpperCase()] || defaultAttributes)).sort();
|
24080 | }
|
24081 | // This section is describes the DOM property surface of a DOM element and is derivgulp formated
|
24082 | // from
|
24083 | // from the SCHEMA strings from the security context information. SCHEMA is copied here because
|
24084 | // it would be an unnecessary risk to allow this array to be imported from the security context
|
24085 | // schema registry.
|
24086 | const SCHEMA$1 = [
|
24087 | '[Element]|textContent,%classList,className,id,innerHTML,*beforecopy,*beforecut,*beforepaste,*copy,*cut,*paste,*search,*selectstart,*webkitfullscreenchange,*webkitfullscreenerror,*wheel,outerHTML,#scrollLeft,#scrollTop,slot' +
|
24088 | /* added manually to avoid breaking changes */
|
24089 | ',*message,*mozfullscreenchange,*mozfullscreenerror,*mozpointerlockchange,*mozpointerlockerror,*webglcontextcreationerror,*webglcontextlost,*webglcontextrestored',
|
24090 | '[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',
|
24091 | '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',
|
24092 | 'media^[HTMLElement]|!autoplay,!controls,%controlsList,%crossOrigin,#currentTime,!defaultMuted,#defaultPlaybackRate,!disableRemotePlayback,!loop,!muted,*encrypted,*waitingforkey,#playbackRate,preload,src,%srcObject,#volume',
|
24093 | ':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',
|
24094 | ':svg:graphics^:svg:|',
|
24095 | ':svg:animation^:svg:|*begin,*end,*repeat',
|
24096 | ':svg:geometry^:svg:|',
|
24097 | ':svg:componentTransferFunction^:svg:|',
|
24098 | ':svg:gradient^:svg:|',
|
24099 | ':svg:textContent^:svg:graphics|',
|
24100 | ':svg:textPositioning^:svg:textContent|',
|
24101 | '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',
|
24102 | 'area^[HTMLElement]|alt,coords,download,hash,host,hostname,href,!noHref,password,pathname,ping,port,protocol,referrerPolicy,rel,search,shape,target,username',
|
24103 | 'audio^media|',
|
24104 | 'br^[HTMLElement]|clear',
|
24105 | 'base^[HTMLElement]|href,target',
|
24106 | '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',
|
24107 | 'button^[HTMLElement]|!autofocus,!disabled,formAction,formEnctype,formMethod,!formNoValidate,formTarget,name,type,value',
|
24108 | 'canvas^[HTMLElement]|#height,#width',
|
24109 | 'content^[HTMLElement]|select',
|
24110 | 'dl^[HTMLElement]|!compact',
|
24111 | 'datalist^[HTMLElement]|',
|
24112 | 'details^[HTMLElement]|!open',
|
24113 | 'dialog^[HTMLElement]|!open,returnValue',
|
24114 | 'dir^[HTMLElement]|!compact',
|
24115 | 'div^[HTMLElement]|align',
|
24116 | 'embed^[HTMLElement]|align,height,name,src,type,width',
|
24117 | 'fieldset^[HTMLElement]|!disabled,name',
|
24118 | 'font^[HTMLElement]|color,face,size',
|
24119 | 'form^[HTMLElement]|acceptCharset,action,autocomplete,encoding,enctype,method,name,!noValidate,target',
|
24120 | 'frame^[HTMLElement]|frameBorder,longDesc,marginHeight,marginWidth,name,!noResize,scrolling,src',
|
24121 | 'frameset^[HTMLElement]|cols,*beforeunload,*blur,*error,*focus,*hashchange,*languagechange,*load,*message,*offline,*online,*pagehide,*pageshow,*popstate,*rejectionhandled,*resize,*scroll,*storage,*unhandledrejection,*unload,rows',
|
24122 | 'hr^[HTMLElement]|align,color,!noShade,size,width',
|
24123 | 'head^[HTMLElement]|',
|
24124 | 'h1,h2,h3,h4,h5,h6^[HTMLElement]|align',
|
24125 | 'html^[HTMLElement]|version',
|
24126 | 'iframe^[HTMLElement]|align,!allowFullscreen,frameBorder,height,longDesc,marginHeight,marginWidth,name,referrerPolicy,%sandbox,scrolling,src,srcdoc,width',
|
24127 | 'img^[HTMLElement]|align,alt,border,%crossOrigin,#height,#hspace,!isMap,longDesc,lowsrc,name,referrerPolicy,sizes,src,srcset,useMap,#vspace,#width',
|
24128 | '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',
|
24129 | 'li^[HTMLElement]|type,#value',
|
24130 | 'label^[HTMLElement]|htmlFor',
|
24131 | 'legend^[HTMLElement]|align',
|
24132 | 'link^[HTMLElement]|as,charset,%crossOrigin,!disabled,href,hreflang,integrity,media,referrerPolicy,rel,%relList,rev,%sizes,target,type',
|
24133 | 'map^[HTMLElement]|name',
|
24134 | 'marquee^[HTMLElement]|behavior,bgColor,direction,height,#hspace,#loop,#scrollAmount,#scrollDelay,!trueSpeed,#vspace,width',
|
24135 | 'menu^[HTMLElement]|!compact',
|
24136 | 'meta^[HTMLElement]|content,httpEquiv,name,scheme',
|
24137 | 'meter^[HTMLElement]|#high,#low,#max,#min,#optimum,#value',
|
24138 | 'ins,del^[HTMLElement]|cite,dateTime',
|
24139 | 'ol^[HTMLElement]|!compact,!reversed,#start,type',
|
24140 | 'object^[HTMLElement]|align,archive,border,code,codeBase,codeType,data,!declare,height,#hspace,name,standby,type,useMap,#vspace,width',
|
24141 | 'optgroup^[HTMLElement]|!disabled,label',
|
24142 | 'option^[HTMLElement]|!defaultSelected,!disabled,label,!selected,text,value',
|
24143 | 'output^[HTMLElement]|defaultValue,%htmlFor,name,value',
|
24144 | 'p^[HTMLElement]|align',
|
24145 | 'param^[HTMLElement]|name,type,value,valueType',
|
24146 | 'picture^[HTMLElement]|',
|
24147 | 'pre^[HTMLElement]|#width',
|
24148 | 'progress^[HTMLElement]|#max,#value',
|
24149 | 'q,blockquote,cite^[HTMLElement]|',
|
24150 | 'script^[HTMLElement]|!async,charset,%crossOrigin,!defer,event,htmlFor,integrity,src,text,type',
|
24151 | 'select^[HTMLElement]|!autofocus,!disabled,#length,!multiple,name,!required,#selectedIndex,#size,value',
|
24152 | 'shadow^[HTMLElement]|',
|
24153 | 'slot^[HTMLElement]|name',
|
24154 | 'source^[HTMLElement]|media,sizes,src,srcset,type',
|
24155 | 'span^[HTMLElement]|',
|
24156 | 'style^[HTMLElement]|!disabled,media,type',
|
24157 | 'caption^[HTMLElement]|align',
|
24158 | 'th,td^[HTMLElement]|abbr,align,axis,bgColor,ch,chOff,#colSpan,headers,height,!noWrap,#rowSpan,scope,vAlign,width',
|
24159 | 'col,colgroup^[HTMLElement]|align,ch,chOff,#span,vAlign,width',
|
24160 | 'table^[HTMLElement]|align,bgColor,border,%caption,cellPadding,cellSpacing,frame,rules,summary,%tFoot,%tHead,width',
|
24161 | 'tr^[HTMLElement]|align,bgColor,ch,chOff,vAlign',
|
24162 | 'tfoot,thead,tbody^[HTMLElement]|align,ch,chOff,vAlign',
|
24163 | 'template^[HTMLElement]|',
|
24164 | 'textarea^[HTMLElement]|autocapitalize,!autofocus,#cols,defaultValue,dirName,!disabled,#maxLength,#minLength,name,placeholder,!readOnly,!required,#rows,selectionDirection,#selectionEnd,#selectionStart,value,wrap',
|
24165 | 'title^[HTMLElement]|text',
|
24166 | 'track^[HTMLElement]|!default,kind,label,src,srclang',
|
24167 | 'ul^[HTMLElement]|!compact,type',
|
24168 | 'unknown^[HTMLElement]|',
|
24169 | 'video^media|#height,poster,#width',
|
24170 | ':svg:a^:svg:graphics|',
|
24171 | ':svg:animate^:svg:animation|',
|
24172 | ':svg:animateMotion^:svg:animation|',
|
24173 | ':svg:animateTransform^:svg:animation|',
|
24174 | ':svg:circle^:svg:geometry|',
|
24175 | ':svg:clipPath^:svg:graphics|',
|
24176 | ':svg:defs^:svg:graphics|',
|
24177 | ':svg:desc^:svg:|',
|
24178 | ':svg:discard^:svg:|',
|
24179 | ':svg:ellipse^:svg:geometry|',
|
24180 | ':svg:feBlend^:svg:|',
|
24181 | ':svg:feColorMatrix^:svg:|',
|
24182 | ':svg:feComponentTransfer^:svg:|',
|
24183 | ':svg:feComposite^:svg:|',
|
24184 | ':svg:feConvolveMatrix^:svg:|',
|
24185 | ':svg:feDiffuseLighting^:svg:|',
|
24186 | ':svg:feDisplacementMap^:svg:|',
|
24187 | ':svg:feDistantLight^:svg:|',
|
24188 | ':svg:feDropShadow^:svg:|',
|
24189 | ':svg:feFlood^:svg:|',
|
24190 | ':svg:feFuncA^:svg:componentTransferFunction|',
|
24191 | ':svg:feFuncB^:svg:componentTransferFunction|',
|
24192 | ':svg:feFuncG^:svg:componentTransferFunction|',
|
24193 | ':svg:feFuncR^:svg:componentTransferFunction|',
|
24194 | ':svg:feGaussianBlur^:svg:|',
|
24195 | ':svg:feImage^:svg:|',
|
24196 | ':svg:feMerge^:svg:|',
|
24197 | ':svg:feMergeNode^:svg:|',
|
24198 | ':svg:feMorphology^:svg:|',
|
24199 | ':svg:feOffset^:svg:|',
|
24200 | ':svg:fePointLight^:svg:|',
|
24201 | ':svg:feSpecularLighting^:svg:|',
|
24202 | ':svg:feSpotLight^:svg:|',
|
24203 | ':svg:feTile^:svg:|',
|
24204 | ':svg:feTurbulence^:svg:|',
|
24205 | ':svg:filter^:svg:|',
|
24206 | ':svg:foreignObject^:svg:graphics|',
|
24207 | ':svg:g^:svg:graphics|',
|
24208 | ':svg:image^:svg:graphics|',
|
24209 | ':svg:line^:svg:geometry|',
|
24210 | ':svg:linearGradient^:svg:gradient|',
|
24211 | ':svg:mpath^:svg:|',
|
24212 | ':svg:marker^:svg:|',
|
24213 | ':svg:mask^:svg:|',
|
24214 | ':svg:metadata^:svg:|',
|
24215 | ':svg:path^:svg:geometry|',
|
24216 | ':svg:pattern^:svg:|',
|
24217 | ':svg:polygon^:svg:geometry|',
|
24218 | ':svg:polyline^:svg:geometry|',
|
24219 | ':svg:radialGradient^:svg:gradient|',
|
24220 | ':svg:rect^:svg:geometry|',
|
24221 | ':svg:svg^:svg:graphics|#currentScale,#zoomAndPan',
|
24222 | ':svg:script^:svg:|type',
|
24223 | ':svg:set^:svg:animation|',
|
24224 | ':svg:stop^:svg:|',
|
24225 | ':svg:style^:svg:|!disabled,media,title,type',
|
24226 | ':svg:switch^:svg:graphics|',
|
24227 | ':svg:symbol^:svg:|',
|
24228 | ':svg:tspan^:svg:textPositioning|',
|
24229 | ':svg:text^:svg:textPositioning|',
|
24230 | ':svg:textPath^:svg:textContent|',
|
24231 | ':svg:title^:svg:|',
|
24232 | ':svg:use^:svg:graphics|',
|
24233 | ':svg:view^:svg:|#zoomAndPan',
|
24234 | 'data^[HTMLElement]|value',
|
24235 | 'keygen^[HTMLElement]|!autofocus,challenge,!disabled,form,keytype,name',
|
24236 | 'menuitem^[HTMLElement]|type,label,icon,!disabled,!checked,radiogroup,!default',
|
24237 | 'summary^[HTMLElement]|',
|
24238 | 'time^[HTMLElement]|dateTime',
|
24239 | ':svg:cursor^:svg:|',
|
24240 | ];
|
24241 | const EVENT = 'event';
|
24242 | const BOOLEAN$1 = 'boolean';
|
24243 | const NUMBER$1 = 'number';
|
24244 | const STRING$1 = 'string';
|
24245 | const OBJECT$1 = 'object';
|
24246 | class SchemaInformation {
|
24247 | constructor() {
|
24248 | this.schema = {};
|
24249 | SCHEMA$1.forEach(encodedType => {
|
24250 | const parts = encodedType.split('|');
|
24251 | const properties = parts[1].split(',');
|
24252 | const typeParts = (parts[0] + '^').split('^');
|
24253 | const typeName = typeParts[0];
|
24254 | const type = {};
|
24255 | typeName.split(',').forEach(tag => this.schema[tag.toLowerCase()] = type);
|
24256 | const superName = typeParts[1];
|
24257 | const superType = superName && this.schema[superName.toLowerCase()];
|
24258 | if (superType) {
|
24259 | for (const key in superType) {
|
24260 | type[key] = superType[key];
|
24261 | }
|
24262 | }
|
24263 | properties.forEach((property) => {
|
24264 | if (property === '') ;
|
24265 | else if (property.startsWith('*')) {
|
24266 | type[property.substring(1)] = EVENT;
|
24267 | }
|
24268 | else if (property.startsWith('!')) {
|
24269 | type[property.substring(1)] = BOOLEAN$1;
|
24270 | }
|
24271 | else if (property.startsWith('#')) {
|
24272 | type[property.substring(1)] = NUMBER$1;
|
24273 | }
|
24274 | else if (property.startsWith('%')) {
|
24275 | type[property.substring(1)] = OBJECT$1;
|
24276 | }
|
24277 | else {
|
24278 | type[property] = STRING$1;
|
24279 | }
|
24280 | });
|
24281 | });
|
24282 | }
|
24283 | allKnownElements() {
|
24284 | return Object.keys(this.schema);
|
24285 | }
|
24286 | eventsOf(elementName) {
|
24287 | const elementType = this.schema[elementName.toLowerCase()] || {};
|
24288 | return Object.keys(elementType).filter(property => elementType[property] === EVENT);
|
24289 | }
|
24290 | propertiesOf(elementName) {
|
24291 | const elementType = this.schema[elementName.toLowerCase()] || {};
|
24292 | return Object.keys(elementType).filter(property => elementType[property] !== EVENT);
|
24293 | }
|
24294 | typeOf(elementName, property) {
|
24295 | return (this.schema[elementName.toLowerCase()] || {})[property];
|
24296 | }
|
24297 | static get instance() {
|
24298 | let result = SchemaInformation._instance;
|
24299 | if (!result) {
|
24300 | result = SchemaInformation._instance = new SchemaInformation();
|
24301 | }
|
24302 | return result;
|
24303 | }
|
24304 | }
|
24305 | function eventNames(elementName) {
|
24306 | return SchemaInformation.instance.eventsOf(elementName);
|
24307 | }
|
24308 | function propertyNames(elementName) {
|
24309 | return SchemaInformation.instance.propertiesOf(elementName);
|
24310 | }
|
24311 |
|
24312 | /**
|
24313 | * @license
|
24314 | * Copyright Google LLC All Rights Reserved.
|
24315 | *
|
24316 | * Use of this source code is governed by an MIT-style license that can be
|
24317 | * found in the LICENSE file at https://angular.io/license
|
24318 | */
|
24319 | const EMPTY_SYMBOL_TABLE = {
|
24320 | size: 0,
|
24321 | get: () => undefined,
|
24322 | has: () => false,
|
24323 | values: () => [],
|
24324 | };
|
24325 | /**
|
24326 | * A factory function that returns a symbol table that contains all global symbols
|
24327 | * available in an interpolation scope in a template.
|
24328 | * This function creates the table the first time it is called, and return a cached
|
24329 | * value for all subsequent calls.
|
24330 | */
|
24331 | const createGlobalSymbolTable = (function () {
|
24332 | let GLOBAL_SYMBOL_TABLE;
|
24333 | return function (query) {
|
24334 | if (GLOBAL_SYMBOL_TABLE) {
|
24335 | return GLOBAL_SYMBOL_TABLE;
|
24336 | }
|
24337 | GLOBAL_SYMBOL_TABLE = query.createSymbolTable([
|
24338 | // The `$any()` method casts the type of an expression to `any`.
|
24339 | // https://angular.io/guide/template-syntax#the-any-type-cast-function
|
24340 | {
|
24341 | name: '$any',
|
24342 | kind: 'method',
|
24343 | type: {
|
24344 | name: '$any',
|
24345 | kind: 'method',
|
24346 | type: undefined,
|
24347 | language: 'typescript',
|
24348 | container: undefined,
|
24349 | public: true,
|
24350 | callable: true,
|
24351 | definition: undefined,
|
24352 | nullable: false,
|
24353 | documentation: [{
|
24354 | kind: 'text',
|
24355 | text: 'function to cast an expression to the `any` type',
|
24356 | }],
|
24357 | members: () => EMPTY_SYMBOL_TABLE,
|
24358 | signatures: () => [],
|
24359 | selectSignature(args) {
|
24360 | if (args.length !== 1) {
|
24361 | return;
|
24362 | }
|
24363 | return {
|
24364 | arguments: EMPTY_SYMBOL_TABLE,
|
24365 | result: query.getBuiltinType(BuiltinType$1.Any),
|
24366 | };
|
24367 | },
|
24368 | indexed: () => undefined,
|
24369 | typeArguments: () => undefined,
|
24370 | },
|
24371 | },
|
24372 | ]);
|
24373 | return GLOBAL_SYMBOL_TABLE;
|
24374 | };
|
24375 | })();
|
24376 |
|
24377 | /**
|
24378 | * @license
|
24379 | * Copyright Google LLC All Rights Reserved.
|
24380 | *
|
24381 | * Use of this source code is governed by an MIT-style license that can be
|
24382 | * found in the LICENSE file at https://angular.io/license
|
24383 | */
|
24384 | // In TypeScript 2.1 these flags moved
|
24385 | // These helpers work for both 2.0 and 2.1.
|
24386 | const isPrivate = ts.ModifierFlags ?
|
24387 | ((node) => !!(ts.getCombinedModifierFlags(node) & ts.ModifierFlags.Private)) :
|
24388 | ((node) => !!(node.flags & ts.NodeFlags.Private));
|
24389 | const isReferenceType = ts.ObjectFlags ?
|
24390 | ((type) => !!(type.flags & ts.TypeFlags.Object &&
|
24391 | type.objectFlags & ts.ObjectFlags.Reference)) :
|
24392 | ((type) => !!(type.flags & ts.TypeFlags.Reference));
|
24393 | function getSymbolQuery(program, checker, source, fetchPipes) {
|
24394 | return new TypeScriptSymbolQuery(program, checker, source, fetchPipes);
|
24395 | }
|
24396 | function getClassMembersFromDeclaration(program, checker, source, declaration) {
|
24397 | const type = checker.getTypeAtLocation(declaration);
|
24398 | return new TypeWrapper(type, { node: source, program, checker }).members();
|
24399 | }
|
24400 | function getPipesTable(source, program, checker, pipes) {
|
24401 | return new PipesTable(pipes, { program, checker, node: source });
|
24402 | }
|
24403 | class TypeScriptSymbolQuery {
|
24404 | constructor(program, checker, source, fetchPipes) {
|
24405 | this.program = program;
|
24406 | this.checker = checker;
|
24407 | this.source = source;
|
24408 | this.fetchPipes = fetchPipes;
|
24409 | this.typeCache = new Map();
|
24410 | }
|
24411 | getTypeKind(symbol) {
|
24412 | const type = symbol instanceof TypeWrapper ? symbol.tsType : undefined;
|
24413 | return typeKindOf(type);
|
24414 | }
|
24415 | getBuiltinType(kind) {
|
24416 | let result = this.typeCache.get(kind);
|
24417 | if (!result) {
|
24418 | const type = getTsTypeFromBuiltinType(kind, {
|
24419 | checker: this.checker,
|
24420 | node: this.source,
|
24421 | program: this.program,
|
24422 | });
|
24423 | result =
|
24424 | new TypeWrapper(type, { program: this.program, checker: this.checker, node: this.source });
|
24425 | this.typeCache.set(kind, result);
|
24426 | }
|
24427 | return result;
|
24428 | }
|
24429 | getTypeUnion(...types) {
|
24430 | // No API exists so return any if the types are not all the same type.
|
24431 | let result = undefined;
|
24432 | if (types.length) {
|
24433 | result = types[0];
|
24434 | for (let i = 1; i < types.length; i++) {
|
24435 | if (types[i] != result) {
|
24436 | result = undefined;
|
24437 | break;
|
24438 | }
|
24439 | }
|
24440 | }
|
24441 | return result || this.getBuiltinType(BuiltinType$1.Any);
|
24442 | }
|
24443 | getArrayType(_type) {
|
24444 | return this.getBuiltinType(BuiltinType$1.Any);
|
24445 | }
|
24446 | getElementType(type) {
|
24447 | if (type instanceof TypeWrapper) {
|
24448 | const ty = type.tsType;
|
24449 | const tyArgs = type.typeArguments();
|
24450 | // TODO(ayazhafiz): Track https://github.com/microsoft/TypeScript/issues/37711 to expose
|
24451 | // `isArrayLikeType` as a public method.
|
24452 | if (!this.checker.isArrayLikeType(ty) || (tyArgs === null || tyArgs === void 0 ? void 0 : tyArgs.length) !== 1)
|
24453 | return;
|
24454 | return tyArgs[0];
|
24455 | }
|
24456 | }
|
24457 | getNonNullableType(symbol) {
|
24458 | if (symbol instanceof TypeWrapper && (typeof this.checker.getNonNullableType == 'function')) {
|
24459 | const tsType = symbol.tsType;
|
24460 | const nonNullableType = this.checker.getNonNullableType(tsType);
|
24461 | if (nonNullableType != tsType) {
|
24462 | return new TypeWrapper(nonNullableType, symbol.context);
|
24463 | }
|
24464 | else if (nonNullableType == tsType) {
|
24465 | return symbol;
|
24466 | }
|
24467 | }
|
24468 | return this.getBuiltinType(BuiltinType$1.Any);
|
24469 | }
|
24470 | getPipes() {
|
24471 | let result = this.pipesCache;
|
24472 | if (!result) {
|
24473 | result = this.pipesCache = this.fetchPipes();
|
24474 | }
|
24475 | return result;
|
24476 | }
|
24477 | getTemplateContext(type) {
|
24478 | const context = { node: this.source, program: this.program, checker: this.checker };
|
24479 | const typeSymbol = findClassSymbolInContext(type, context);
|
24480 | if (typeSymbol) {
|
24481 | const contextType = this.getTemplateRefContextType(typeSymbol, context);
|
24482 | if (contextType)
|
24483 | return contextType.members();
|
24484 | }
|
24485 | }
|
24486 | getTypeSymbol(type) {
|
24487 | const context = { node: this.source, program: this.program, checker: this.checker };
|
24488 | const typeSymbol = findClassSymbolInContext(type, context);
|
24489 | return typeSymbol && new SymbolWrapper(typeSymbol, context);
|
24490 | }
|
24491 | createSymbolTable(symbols) {
|
24492 | const result = new MapSymbolTable();
|
24493 | result.addAll(symbols.map(s => new DeclaredSymbol(s)));
|
24494 | return result;
|
24495 | }
|
24496 | mergeSymbolTable(symbolTables) {
|
24497 | const result = new MapSymbolTable();
|
24498 | for (const symbolTable of symbolTables) {
|
24499 | result.addAll(symbolTable.values());
|
24500 | }
|
24501 | return result;
|
24502 | }
|
24503 | getSpanAt(line, column) {
|
24504 | return spanAt(this.source, line, column);
|
24505 | }
|
24506 | getTemplateRefContextType(typeSymbol, context) {
|
24507 | const type = this.checker.getTypeOfSymbolAtLocation(typeSymbol, this.source);
|
24508 | const constructor = type.symbol && type.symbol.members &&
|
24509 | getFromSymbolTable(type.symbol.members, '__constructor');
|
24510 | if (constructor) {
|
24511 | const constructorDeclaration = constructor.declarations[0];
|
24512 | for (const parameter of constructorDeclaration.parameters) {
|
24513 | const type = this.checker.getTypeAtLocation(parameter.type);
|
24514 | if (type.symbol.name == 'TemplateRef' && isReferenceType(type)) {
|
24515 | const typeWrapper = new TypeWrapper(type, context);
|
24516 | const typeArguments = typeWrapper.typeArguments();
|
24517 | if (typeArguments && typeArguments.length === 1) {
|
24518 | return typeArguments[0];
|
24519 | }
|
24520 | }
|
24521 | }
|
24522 | }
|
24523 | }
|
24524 | }
|
24525 | function typeCallable(type) {
|
24526 | const signatures = type.getCallSignatures();
|
24527 | return signatures && signatures.length != 0;
|
24528 | }
|
24529 | function signaturesOf(type, context) {
|
24530 | return type.getCallSignatures().map(s => new SignatureWrapper(s, context));
|
24531 | }
|
24532 | function selectSignature(type, context, types) {
|
24533 | // TODO: Do a better job of selecting the right signature. TypeScript does not currently support a
|
24534 | // Type Relationship API (see https://github.com/angular/vscode-ng-language-service/issues/143).
|
24535 | // Consider creating a TypeCheckBlock host in the language service that may also act as a
|
24536 | // scratchpad for type comparisons.
|
24537 | const signatures = type.getCallSignatures();
|
24538 | const passedInTypes = types.map(type => {
|
24539 | if (type instanceof TypeWrapper) {
|
24540 | return type.tsType;
|
24541 | }
|
24542 | });
|
24543 | // Try to select a matching signature in which all parameter types match.
|
24544 | // Note that this is just a best-effort approach, because we're checking for
|
24545 | // strict type equality rather than compatibility.
|
24546 | // For example, if the signature contains a ReadonlyArray<number> and the
|
24547 | // passed parameter type is an Array<number>, this will fail.
|
24548 | function allParameterTypesMatch(signature) {
|
24549 | const tc = context.checker;
|
24550 | return signature.getParameters().every((parameter, i) => {
|
24551 | const type = tc.getTypeOfSymbolAtLocation(parameter, parameter.valueDeclaration);
|
24552 | return type === passedInTypes[i];
|
24553 | });
|
24554 | }
|
24555 | const exactMatch = signatures.find(allParameterTypesMatch);
|
24556 | if (exactMatch) {
|
24557 | return new SignatureWrapper(exactMatch, context);
|
24558 | }
|
24559 | // If not, fallback to a naive selection
|
24560 | return signatures.length ? new SignatureWrapper(signatures[0], context) : undefined;
|
24561 | }
|
24562 | class TypeWrapper {
|
24563 | constructor(tsType, context) {
|
24564 | this.tsType = tsType;
|
24565 | this.context = context;
|
24566 | this.kind = 'type';
|
24567 | this.language = 'typescript';
|
24568 | this.type = undefined;
|
24569 | this.container = undefined;
|
24570 | this.public = true;
|
24571 | if (!tsType) {
|
24572 | throw Error('Internal: null type');
|
24573 | }
|
24574 | }
|
24575 | get name() {
|
24576 | return this.context.checker.typeToString(this.tsType);
|
24577 | }
|
24578 | get callable() {
|
24579 | return typeCallable(this.tsType);
|
24580 | }
|
24581 | get nullable() {
|
24582 | return this.context.checker.getNonNullableType(this.tsType) != this.tsType;
|
24583 | }
|
24584 | get documentation() {
|
24585 | const symbol = this.tsType.getSymbol();
|
24586 | if (!symbol) {
|
24587 | return [];
|
24588 | }
|
24589 | return symbol.getDocumentationComment(this.context.checker);
|
24590 | }
|
24591 | get definition() {
|
24592 | const symbol = this.tsType.getSymbol();
|
24593 | return symbol ? definitionFromTsSymbol(symbol) : undefined;
|
24594 | }
|
24595 | members() {
|
24596 | // Should call getApparentProperties() instead of getProperties() because
|
24597 | // the former includes properties on the base class whereas the latter does
|
24598 | // not. This provides properties like .bind(), .call(), .apply(), etc for
|
24599 | // functions.
|
24600 | return new SymbolTableWrapper(this.tsType.getApparentProperties(), this.context, this.tsType);
|
24601 | }
|
24602 | signatures() {
|
24603 | return signaturesOf(this.tsType, this.context);
|
24604 | }
|
24605 | selectSignature(types) {
|
24606 | return selectSignature(this.tsType, this.context, types);
|
24607 | }
|
24608 | indexed(type, value) {
|
24609 | if (!(type instanceof TypeWrapper))
|
24610 | return;
|
24611 | const typeKind = typeKindOf(type.tsType);
|
24612 | switch (typeKind) {
|
24613 | case BuiltinType$1.Number:
|
24614 | const nType = this.tsType.getNumberIndexType();
|
24615 | if (nType) {
|
24616 | // get the right tuple type by value, like 'var t: [number, string];'
|
24617 | if (nType.isUnion()) {
|
24618 | // return undefined if array index out of bound.
|
24619 | return nType.types[value] && new TypeWrapper(nType.types[value], this.context);
|
24620 | }
|
24621 | return new TypeWrapper(nType, this.context);
|
24622 | }
|
24623 | return undefined;
|
24624 | case BuiltinType$1.String:
|
24625 | const sType = this.tsType.getStringIndexType();
|
24626 | return sType && new TypeWrapper(sType, this.context);
|
24627 | }
|
24628 | }
|
24629 | typeArguments() {
|
24630 | if (!isReferenceType(this.tsType))
|
24631 | return;
|
24632 | const typeReference = this.tsType;
|
24633 | let typeArguments;
|
24634 | typeArguments = this.context.checker.getTypeArguments(typeReference);
|
24635 | if (!typeArguments)
|
24636 | return undefined;
|
24637 | return typeArguments.map(ta => new TypeWrapper(ta, this.context));
|
24638 | }
|
24639 | }
|
24640 | // If stringIndexType a primitive type(e.g. 'string'), the Symbol is undefined;
|
24641 | // and in AstType.resolvePropertyRead method, the Symbol.type should get the right type.
|
24642 | class StringIndexTypeWrapper extends TypeWrapper {
|
24643 | constructor() {
|
24644 | super(...arguments);
|
24645 | this.type = new TypeWrapper(this.tsType, this.context);
|
24646 | }
|
24647 | }
|
24648 | class SymbolWrapper {
|
24649 | constructor(symbol,
|
24650 | /** TypeScript type context of the symbol. */
|
24651 | context,
|
24652 | /**
|
24653 | * Type of the TypeScript symbol, if known. If not provided, the type of the symbol
|
24654 | * will be determined dynamically; see `SymbolWrapper#tsType`.
|
24655 | */
|
24656 | _tsType) {
|
24657 | this.context = context;
|
24658 | this._tsType = _tsType;
|
24659 | this.nullable = false;
|
24660 | this.language = 'typescript';
|
24661 | this.symbol = symbol && context && (symbol.flags & ts.SymbolFlags.Alias) ?
|
24662 | context.checker.getAliasedSymbol(symbol) :
|
24663 | symbol;
|
24664 | }
|
24665 | get name() {
|
24666 | return this.symbol.name;
|
24667 | }
|
24668 | get kind() {
|
24669 | return this.callable ? 'method' : 'property';
|
24670 | }
|
24671 | get type() {
|
24672 | return new TypeWrapper(this.tsType, this.context);
|
24673 | }
|
24674 | get container() {
|
24675 | return getContainerOf(this.symbol, this.context);
|
24676 | }
|
24677 | get public() {
|
24678 | // Symbols that are not explicitly made private are public.
|
24679 | return !isSymbolPrivate(this.symbol);
|
24680 | }
|
24681 | get callable() {
|
24682 | return typeCallable(this.tsType);
|
24683 | }
|
24684 | get definition() {
|
24685 | return definitionFromTsSymbol(this.symbol);
|
24686 | }
|
24687 | get documentation() {
|
24688 | return this.symbol.getDocumentationComment(this.context.checker);
|
24689 | }
|
24690 | members() {
|
24691 | if (!this._members) {
|
24692 | if ((this.symbol.flags & (ts.SymbolFlags.Class | ts.SymbolFlags.Interface)) != 0) {
|
24693 | const declaredType = this.context.checker.getDeclaredTypeOfSymbol(this.symbol);
|
24694 | const typeWrapper = new TypeWrapper(declaredType, this.context);
|
24695 | this._members = typeWrapper.members();
|
24696 | }
|
24697 | else {
|
24698 | this._members = new SymbolTableWrapper(this.symbol.members, this.context, this.tsType);
|
24699 | }
|
24700 | }
|
24701 | return this._members;
|
24702 | }
|
24703 | signatures() {
|
24704 | return signaturesOf(this.tsType, this.context);
|
24705 | }
|
24706 | selectSignature(types) {
|
24707 | return selectSignature(this.tsType, this.context, types);
|
24708 | }
|
24709 | indexed(_argument) {
|
24710 | return undefined;
|
24711 | }
|
24712 | typeArguments() {
|
24713 | return this.type.typeArguments();
|
24714 | }
|
24715 | get tsType() {
|
24716 | let type = this._tsType;
|
24717 | if (!type) {
|
24718 | type = this._tsType =
|
24719 | this.context.checker.getTypeOfSymbolAtLocation(this.symbol, this.context.node);
|
24720 | }
|
24721 | return type;
|
24722 | }
|
24723 | }
|
24724 | class DeclaredSymbol {
|
24725 | constructor(declaration) {
|
24726 | this.declaration = declaration;
|
24727 | this.language = 'ng-template';
|
24728 | this.nullable = false;
|
24729 | this.public = true;
|
24730 | }
|
24731 | get name() {
|
24732 | return this.declaration.name;
|
24733 | }
|
24734 | get kind() {
|
24735 | return this.declaration.kind;
|
24736 | }
|
24737 | get container() {
|
24738 | return undefined;
|
24739 | }
|
24740 | get type() {
|
24741 | return this.declaration.type;
|
24742 | }
|
24743 | get callable() {
|
24744 | return this.type.callable;
|
24745 | }
|
24746 | get definition() {
|
24747 | return this.declaration.definition;
|
24748 | }
|
24749 | get documentation() {
|
24750 | return this.declaration.type.documentation;
|
24751 | }
|
24752 | members() {
|
24753 | return this.type.members();
|
24754 | }
|
24755 | signatures() {
|
24756 | return this.type.signatures();
|
24757 | }
|
24758 | selectSignature(types) {
|
24759 | return this.type.selectSignature(types);
|
24760 | }
|
24761 | typeArguments() {
|
24762 | return this.type.typeArguments();
|
24763 | }
|
24764 | indexed(_argument) {
|
24765 | return undefined;
|
24766 | }
|
24767 | }
|
24768 | class SignatureWrapper {
|
24769 | constructor(signature, context) {
|
24770 | this.signature = signature;
|
24771 | this.context = context;
|
24772 | }
|
24773 | get arguments() {
|
24774 | return new SymbolTableWrapper(this.signature.getParameters(), this.context);
|
24775 | }
|
24776 | get result() {
|
24777 | return new TypeWrapper(this.signature.getReturnType(), this.context);
|
24778 | }
|
24779 | }
|
24780 | class SignatureResultOverride {
|
24781 | constructor(signature, resultType) {
|
24782 | this.signature = signature;
|
24783 | this.resultType = resultType;
|
24784 | }
|
24785 | get arguments() {
|
24786 | return this.signature.arguments;
|
24787 | }
|
24788 | get result() {
|
24789 | return this.resultType;
|
24790 | }
|
24791 | }
|
24792 | function toSymbolTableFactory(symbols) {
|
24793 | // ∀ Typescript version >= 2.2, `SymbolTable` is implemented as an ES6 `Map`
|
24794 | const result = new Map();
|
24795 | for (const symbol of symbols) {
|
24796 | result.set(symbol.name, symbol);
|
24797 | }
|
24798 | return result;
|
24799 | }
|
24800 | function toSymbols(symbolTable) {
|
24801 | if (!symbolTable)
|
24802 | return [];
|
24803 | const table = symbolTable;
|
24804 | if (typeof table.values === 'function') {
|
24805 | return Array.from(table.values());
|
24806 | }
|
24807 | const result = [];
|
24808 | const own = typeof table.hasOwnProperty === 'function' ?
|
24809 | (name) => table.hasOwnProperty(name) :
|
24810 | (name) => !!table[name];
|
24811 | for (const name in table) {
|
24812 | if (own(name)) {
|
24813 | result.push(table[name]);
|
24814 | }
|
24815 | }
|
24816 | return result;
|
24817 | }
|
24818 | class SymbolTableWrapper {
|
24819 | /**
|
24820 | * Creates a queryable table of symbols belonging to a TypeScript entity.
|
24821 | * @param symbols symbols to query belonging to the entity
|
24822 | * @param context program context
|
24823 | * @param type original TypeScript type of entity owning the symbols, if known
|
24824 | */
|
24825 | constructor(symbols, context, type) {
|
24826 | this.context = context;
|
24827 | symbols = symbols || [];
|
24828 | if (Array.isArray(symbols)) {
|
24829 | this.symbols = symbols;
|
24830 | this.symbolTable = toSymbolTableFactory(symbols);
|
24831 | }
|
24832 | else {
|
24833 | this.symbols = toSymbols(symbols);
|
24834 | this.symbolTable = symbols;
|
24835 | }
|
24836 | if (type) {
|
24837 | this.stringIndexType = type.getStringIndexType();
|
24838 | }
|
24839 | }
|
24840 | get size() {
|
24841 | return this.symbols.length;
|
24842 | }
|
24843 | get(key) {
|
24844 | const symbol = getFromSymbolTable(this.symbolTable, key);
|
24845 | if (symbol) {
|
24846 | return new SymbolWrapper(symbol, this.context);
|
24847 | }
|
24848 | if (this.stringIndexType) {
|
24849 | // If the key does not exist as an explicit symbol on the type, it may be accessing a string
|
24850 | // index signature using dot notation:
|
24851 | //
|
24852 | // const obj<T>: { [key: string]: T };
|
24853 | // obj.stringIndex // equivalent to obj['stringIndex'];
|
24854 | //
|
24855 | // In this case, return the type indexed by an arbitrary string key.
|
24856 | return new StringIndexTypeWrapper(this.stringIndexType, this.context);
|
24857 | }
|
24858 | return undefined;
|
24859 | }
|
24860 | has(key) {
|
24861 | const table = this.symbolTable;
|
24862 | return ((typeof table.has === 'function') ? table.has(key) : table[key] != null) ||
|
24863 | this.stringIndexType !== undefined;
|
24864 | }
|
24865 | values() {
|
24866 | return this.symbols.map(s => new SymbolWrapper(s, this.context));
|
24867 | }
|
24868 | }
|
24869 | class MapSymbolTable {
|
24870 | constructor() {
|
24871 | this.map = new Map();
|
24872 | this._values = [];
|
24873 | }
|
24874 | get size() {
|
24875 | return this.map.size;
|
24876 | }
|
24877 | get(key) {
|
24878 | return this.map.get(key);
|
24879 | }
|
24880 | add(symbol) {
|
24881 | if (this.map.has(symbol.name)) {
|
24882 | const previous = this.map.get(symbol.name);
|
24883 | this._values[this._values.indexOf(previous)] = symbol;
|
24884 | }
|
24885 | this.map.set(symbol.name, symbol);
|
24886 | this._values.push(symbol);
|
24887 | }
|
24888 | addAll(symbols) {
|
24889 | for (const symbol of symbols) {
|
24890 | this.add(symbol);
|
24891 | }
|
24892 | }
|
24893 | has(key) {
|
24894 | return this.map.has(key);
|
24895 | }
|
24896 | values() {
|
24897 | // Switch to this.map.values once iterables are supported by the target language.
|
24898 | return this._values;
|
24899 | }
|
24900 | }
|
24901 | class PipesTable {
|
24902 | constructor(pipes, context) {
|
24903 | this.pipes = pipes;
|
24904 | this.context = context;
|
24905 | }
|
24906 | get size() {
|
24907 | return this.pipes.length;
|
24908 | }
|
24909 | get(key) {
|
24910 | const pipe = this.pipes.find(pipe => pipe.name == key);
|
24911 | if (pipe) {
|
24912 | return new PipeSymbol(pipe, this.context);
|
24913 | }
|
24914 | }
|
24915 | has(key) {
|
24916 | return this.pipes.find(pipe => pipe.name == key) != null;
|
24917 | }
|
24918 | values() {
|
24919 | return this.pipes.map(pipe => new PipeSymbol(pipe, this.context));
|
24920 | }
|
24921 | }
|
24922 | // This matches .d.ts files that look like ".../<package-name>/<package-name>.d.ts",
|
24923 | const INDEX_PATTERN = /[\\/]([^\\/]+)[\\/]\1\.d\.ts$/;
|
24924 | class PipeSymbol {
|
24925 | constructor(pipe, context) {
|
24926 | this.pipe = pipe;
|
24927 | this.context = context;
|
24928 | this.kind = 'pipe';
|
24929 | this.language = 'typescript';
|
24930 | this.container = undefined;
|
24931 | this.callable = true;
|
24932 | this.nullable = false;
|
24933 | this.public = true;
|
24934 | }
|
24935 | get name() {
|
24936 | return this.pipe.name;
|
24937 | }
|
24938 | get type() {
|
24939 | return new TypeWrapper(this.tsType, this.context);
|
24940 | }
|
24941 | get definition() {
|
24942 | const symbol = this.tsType.getSymbol();
|
24943 | return symbol ? definitionFromTsSymbol(symbol) : undefined;
|
24944 | }
|
24945 | get documentation() {
|
24946 | const symbol = this.tsType.getSymbol();
|
24947 | if (!symbol) {
|
24948 | return [];
|
24949 | }
|
24950 | return symbol.getDocumentationComment(this.context.checker);
|
24951 | }
|
24952 | members() {
|
24953 | return EmptyTable.instance;
|
24954 | }
|
24955 | signatures() {
|
24956 | return signaturesOf(this.tsType, this.context);
|
24957 | }
|
24958 | selectSignature(types) {
|
24959 | let signature = selectSignature(this.tsType, this.context, types);
|
24960 | if (types.length > 0) {
|
24961 | const parameterType = types[0];
|
24962 | let resultType = undefined;
|
24963 | switch (this.name) {
|
24964 | case 'async':
|
24965 | // Get type argument of 'Observable', 'Promise', or 'EventEmitter'.
|
24966 | const tArgs = parameterType.typeArguments();
|
24967 | if (tArgs && tArgs.length === 1) {
|
24968 | resultType = tArgs[0];
|
24969 | }
|
24970 | break;
|
24971 | case 'slice':
|
24972 | resultType = parameterType;
|
24973 | break;
|
24974 | }
|
24975 | if (resultType) {
|
24976 | signature = new SignatureResultOverride(signature, resultType);
|
24977 | }
|
24978 | }
|
24979 | return signature;
|
24980 | }
|
24981 | indexed(_argument) {
|
24982 | return undefined;
|
24983 | }
|
24984 | typeArguments() {
|
24985 | return this.type.typeArguments();
|
24986 | }
|
24987 | get tsType() {
|
24988 | let type = this._tsType;
|
24989 | if (!type) {
|
24990 | const classSymbol = this.findClassSymbol(this.pipe.type.reference);
|
24991 | if (classSymbol) {
|
24992 | type = this._tsType = this.findTransformMethodType(classSymbol);
|
24993 | }
|
24994 | if (!type) {
|
24995 | type = this._tsType = getTsTypeFromBuiltinType(BuiltinType$1.Any, this.context);
|
24996 | }
|
24997 | }
|
24998 | return type;
|
24999 | }
|
25000 | findClassSymbol(type) {
|
25001 | return findClassSymbolInContext(type, this.context);
|
25002 | }
|
25003 | findTransformMethodType(classSymbol) {
|
25004 | const classType = this.context.checker.getDeclaredTypeOfSymbol(classSymbol);
|
25005 | if (classType) {
|
25006 | const transform = classType.getProperty('transform');
|
25007 | if (transform) {
|
25008 | return this.context.checker.getTypeOfSymbolAtLocation(transform, this.context.node);
|
25009 | }
|
25010 | }
|
25011 | }
|
25012 | }
|
25013 | function findClassSymbolInContext(type, context) {
|
25014 | let sourceFile = context.program.getSourceFile(type.filePath);
|
25015 | if (!sourceFile) {
|
25016 | // This handles a case where an <packageName>/index.d.ts and a <packageName>/<packageName>.d.ts
|
25017 | // are in the same directory. If we are looking for <packageName>/<packageName> and didn't
|
25018 | // find it, look for <packageName>/index.d.ts as the program might have found that instead.
|
25019 | const p = type.filePath;
|
25020 | const m = p.match(INDEX_PATTERN);
|
25021 | if (m) {
|
25022 | const indexVersion = path.join(path.dirname(p), 'index.d.ts');
|
25023 | sourceFile = context.program.getSourceFile(indexVersion);
|
25024 | }
|
25025 | }
|
25026 | if (sourceFile) {
|
25027 | const moduleSymbol = sourceFile.module || sourceFile.symbol;
|
25028 | const exports = context.checker.getExportsOfModule(moduleSymbol);
|
25029 | return (exports || []).find(symbol => symbol.name == type.name);
|
25030 | }
|
25031 | }
|
25032 | class EmptyTable {
|
25033 | constructor() {
|
25034 | this.size = 0;
|
25035 | }
|
25036 | get(_key) {
|
25037 | return undefined;
|
25038 | }
|
25039 | has(_key) {
|
25040 | return false;
|
25041 | }
|
25042 | values() {
|
25043 | return [];
|
25044 | }
|
25045 | }
|
25046 | EmptyTable.instance = new EmptyTable();
|
25047 | function isSymbolPrivate(s) {
|
25048 | return !!s.valueDeclaration && isPrivate(s.valueDeclaration);
|
25049 | }
|
25050 | function getTsTypeFromBuiltinType(builtinType, ctx) {
|
25051 | let syntaxKind;
|
25052 | switch (builtinType) {
|
25053 | case BuiltinType$1.Any:
|
25054 | syntaxKind = ts.SyntaxKind.AnyKeyword;
|
25055 | break;
|
25056 | case BuiltinType$1.Boolean:
|
25057 | syntaxKind = ts.SyntaxKind.BooleanKeyword;
|
25058 | break;
|
25059 | case BuiltinType$1.Null:
|
25060 | syntaxKind = ts.SyntaxKind.NullKeyword;
|
25061 | break;
|
25062 | case BuiltinType$1.Number:
|
25063 | syntaxKind = ts.SyntaxKind.NumberKeyword;
|
25064 | break;
|
25065 | case BuiltinType$1.String:
|
25066 | syntaxKind = ts.SyntaxKind.StringKeyword;
|
25067 | break;
|
25068 | case BuiltinType$1.Undefined:
|
25069 | syntaxKind = ts.SyntaxKind.UndefinedKeyword;
|
25070 | break;
|
25071 | default:
|
25072 | throw new Error(`Internal error, unhandled literal kind ${builtinType}:${BuiltinType$1[builtinType]}`);
|
25073 | }
|
25074 | const node = ts.createNode(syntaxKind);
|
25075 | node.parent = ts.createEmptyStatement();
|
25076 | return ctx.checker.getTypeAtLocation(node);
|
25077 | }
|
25078 | function spanAt(sourceFile, line, column) {
|
25079 | if (line != null && column != null) {
|
25080 | const position = ts.getPositionOfLineAndCharacter(sourceFile, line, column);
|
25081 | const findChild = function findChild(node) {
|
25082 | if (node.kind > ts.SyntaxKind.LastToken && node.pos <= position && node.end > position) {
|
25083 | const betterNode = ts.forEachChild(node, findChild);
|
25084 | return betterNode || node;
|
25085 | }
|
25086 | };
|
25087 | const node = ts.forEachChild(sourceFile, findChild);
|
25088 | if (node) {
|
25089 | return { start: node.getStart(), end: node.getEnd() };
|
25090 | }
|
25091 | }
|
25092 | }
|
25093 | function definitionFromTsSymbol(symbol) {
|
25094 | const declarations = symbol.declarations;
|
25095 | if (declarations) {
|
25096 | return declarations.map(declaration => {
|
25097 | const sourceFile = declaration.getSourceFile();
|
25098 | return {
|
25099 | fileName: sourceFile.fileName,
|
25100 | span: { start: declaration.getStart(), end: declaration.getEnd() }
|
25101 | };
|
25102 | });
|
25103 | }
|
25104 | }
|
25105 | function parentDeclarationOf(node) {
|
25106 | while (node) {
|
25107 | switch (node.kind) {
|
25108 | case ts.SyntaxKind.ClassDeclaration:
|
25109 | case ts.SyntaxKind.InterfaceDeclaration:
|
25110 | return node;
|
25111 | case ts.SyntaxKind.SourceFile:
|
25112 | return undefined;
|
25113 | }
|
25114 | node = node.parent;
|
25115 | }
|
25116 | }
|
25117 | function getContainerOf(symbol, context) {
|
25118 | if (symbol.getFlags() & ts.SymbolFlags.ClassMember && symbol.declarations) {
|
25119 | for (const declaration of symbol.declarations) {
|
25120 | const parent = parentDeclarationOf(declaration);
|
25121 | if (parent) {
|
25122 | const type = context.checker.getTypeAtLocation(parent);
|
25123 | if (type) {
|
25124 | return new TypeWrapper(type, context);
|
25125 | }
|
25126 | }
|
25127 | }
|
25128 | }
|
25129 | }
|
25130 | function typeKindOf(type) {
|
25131 | if (type) {
|
25132 | if (type.flags & ts.TypeFlags.Any) {
|
25133 | return BuiltinType$1.Any;
|
25134 | }
|
25135 | else if (type.flags & (ts.TypeFlags.String | ts.TypeFlags.StringLike | ts.TypeFlags.StringLiteral)) {
|
25136 | return BuiltinType$1.String;
|
25137 | }
|
25138 | else if (type.flags & (ts.TypeFlags.Number | ts.TypeFlags.NumberLike)) {
|
25139 | return BuiltinType$1.Number;
|
25140 | }
|
25141 | else if (type.flags & ts.TypeFlags.Object) {
|
25142 | return BuiltinType$1.Object;
|
25143 | }
|
25144 | else if (type.flags & (ts.TypeFlags.Undefined)) {
|
25145 | return BuiltinType$1.Undefined;
|
25146 | }
|
25147 | else if (type.flags & (ts.TypeFlags.Null)) {
|
25148 | return BuiltinType$1.Null;
|
25149 | }
|
25150 | else if (type.flags & ts.TypeFlags.Union) {
|
25151 | const unionType = type;
|
25152 | if (unionType.types.length === 0)
|
25153 | return BuiltinType$1.Other;
|
25154 | let ty = 0;
|
25155 | for (const subType of unionType.types) {
|
25156 | ty |= typeKindOf(subType);
|
25157 | }
|
25158 | return ty;
|
25159 | }
|
25160 | else if (type.flags & ts.TypeFlags.TypeParameter) {
|
25161 | return BuiltinType$1.Unbound;
|
25162 | }
|
25163 | }
|
25164 | return BuiltinType$1.Other;
|
25165 | }
|
25166 | function getFromSymbolTable(symbolTable, key) {
|
25167 | const table = symbolTable;
|
25168 | let symbol;
|
25169 | if (typeof table.get === 'function') {
|
25170 | // TS 2.2 uses a Map
|
25171 | symbol = table.get(key);
|
25172 | }
|
25173 | else {
|
25174 | // TS pre-2.2 uses an object
|
25175 | symbol = table[key];
|
25176 | }
|
25177 | return symbol;
|
25178 | }
|
25179 |
|
25180 | /**
|
25181 | * @license
|
25182 | * Copyright Google LLC All Rights Reserved.
|
25183 | *
|
25184 | * Use of this source code is governed by an MIT-style license that can be
|
25185 | * found in the LICENSE file at https://angular.io/license
|
25186 | */
|
25187 | /**
|
25188 | * A base class to represent a template and which component class it is
|
25189 | * associated with. A template source could answer basic questions about
|
25190 | * top-level declarations of its class through the members() and query()
|
25191 | * methods.
|
25192 | */
|
25193 | class BaseTemplate {
|
25194 | constructor(host, classDeclNode, classSymbol) {
|
25195 | this.host = host;
|
25196 | this.classDeclNode = classDeclNode;
|
25197 | this.classSymbol = classSymbol;
|
25198 | this.program = host.program;
|
25199 | }
|
25200 | /**
|
25201 | * Return the Angular StaticSymbol for the class that contains this template.
|
25202 | */
|
25203 | get type() {
|
25204 | return this.classSymbol;
|
25205 | }
|
25206 | /**
|
25207 | * Return a Map-like data structure that allows users to retrieve some or all
|
25208 | * top-level declarations in the associated component class.
|
25209 | */
|
25210 | get members() {
|
25211 | if (!this.membersTable) {
|
25212 | const typeChecker = this.program.getTypeChecker();
|
25213 | const sourceFile = this.classDeclNode.getSourceFile();
|
25214 | this.membersTable = this.query.mergeSymbolTable([
|
25215 | createGlobalSymbolTable(this.query),
|
25216 | getClassMembersFromDeclaration(this.program, typeChecker, sourceFile, this.classDeclNode),
|
25217 | ]);
|
25218 | }
|
25219 | return this.membersTable;
|
25220 | }
|
25221 | /**
|
25222 | * Return an engine that provides more information about symbols in the
|
25223 | * template.
|
25224 | */
|
25225 | get query() {
|
25226 | if (!this.queryCache) {
|
25227 | const program = this.program;
|
25228 | const typeChecker = program.getTypeChecker();
|
25229 | const sourceFile = this.classDeclNode.getSourceFile();
|
25230 | this.queryCache = getSymbolQuery(program, typeChecker, sourceFile, () => {
|
25231 | // Computing the ast is relatively expensive. Do it only when absolutely
|
25232 | // necessary.
|
25233 | // TODO: There is circular dependency here between TemplateSource and
|
25234 | // TypeScriptHost. Consider refactoring the code to break this cycle.
|
25235 | const ast = this.host.getTemplateAst(this);
|
25236 | const pipes = (ast && ast.pipes) || [];
|
25237 | return getPipesTable(sourceFile, program, typeChecker, pipes);
|
25238 | });
|
25239 | }
|
25240 | return this.queryCache;
|
25241 | }
|
25242 | }
|
25243 | /**
|
25244 | * An InlineTemplate represents template defined in a TS file through the
|
25245 | * `template` attribute in the decorator.
|
25246 | */
|
25247 | class InlineTemplate extends BaseTemplate {
|
25248 | constructor(templateNode, classDeclNode, classSymbol, host) {
|
25249 | super(host, classDeclNode, classSymbol);
|
25250 | const sourceFile = templateNode.getSourceFile();
|
25251 | if (sourceFile !== classDeclNode.getSourceFile()) {
|
25252 | throw new Error(`Inline template and component class should belong to the same source file`);
|
25253 | }
|
25254 | this.fileName = sourceFile.fileName;
|
25255 | // node.text returns the TS internal representation of the normalized text,
|
25256 | // and all CR characters are stripped. node.getText() returns the raw text.
|
25257 | this.source = templateNode.getText().slice(1, -1); // strip leading and trailing quotes
|
25258 | this.span = {
|
25259 | // TS string literal includes surrounding quotes in the start/end offsets.
|
25260 | start: templateNode.getStart() + 1,
|
25261 | end: templateNode.getEnd() - 1,
|
25262 | };
|
25263 | }
|
25264 | }
|
25265 | /**
|
25266 | * An ExternalTemplate represents template defined in an external (most likely
|
25267 | * HTML, but not necessarily) file through the `templateUrl` attribute in the
|
25268 | * decorator.
|
25269 | * Note that there is no ts.Node associated with the template because it's not
|
25270 | * a TS file.
|
25271 | */
|
25272 | class ExternalTemplate extends BaseTemplate {
|
25273 | constructor(source, fileName, classDeclNode, classSymbol, host) {
|
25274 | super(host, classDeclNode, classSymbol);
|
25275 | this.source = source;
|
25276 | this.fileName = fileName;
|
25277 | this.span = {
|
25278 | start: 0,
|
25279 | end: source.length,
|
25280 | };
|
25281 | }
|
25282 | }
|
25283 |
|
25284 | /**
|
25285 | * @license
|
25286 | * Copyright Google LLC All Rights Reserved.
|
25287 | *
|
25288 | * Use of this source code is governed by an MIT-style license that can be
|
25289 | * found in the LICENSE file at https://angular.io/license
|
25290 | */
|
25291 | const HIDDEN_HTML_ELEMENTS = new Set(['html', 'script', 'noscript', 'base', 'body', 'title', 'head', 'link']);
|
25292 | const HTML_ELEMENTS = elementNames().filter(name => !HIDDEN_HTML_ELEMENTS.has(name)).map(name => {
|
25293 | return {
|
25294 | name,
|
25295 | kind: CompletionKind.HTML_ELEMENT,
|
25296 | sortText: name,
|
25297 | };
|
25298 | });
|
25299 | const ANGULAR_ELEMENTS = [
|
25300 | {
|
25301 | name: 'ng-container',
|
25302 | kind: CompletionKind.ANGULAR_ELEMENT,
|
25303 | sortText: 'ng-container',
|
25304 | },
|
25305 | {
|
25306 | name: 'ng-content',
|
25307 | kind: CompletionKind.ANGULAR_ELEMENT,
|
25308 | sortText: 'ng-content',
|
25309 | },
|
25310 | {
|
25311 | name: 'ng-template',
|
25312 | kind: CompletionKind.ANGULAR_ELEMENT,
|
25313 | sortText: 'ng-template',
|
25314 | },
|
25315 | ];
|
25316 | function isIdentifierPart$1(code) {
|
25317 | // Identifiers consist of alphanumeric characters, '_', or '$'.
|
25318 | return isAsciiLetter(code) || isDigit(code) || code == $$ || code == $_;
|
25319 | }
|
25320 | /**
|
25321 | * Gets the span of word in a template that surrounds `position`. If there is no word around
|
25322 | * `position`, nothing is returned.
|
25323 | */
|
25324 | function getBoundedWordSpan(templateInfo, position, ast) {
|
25325 | const { template } = templateInfo;
|
25326 | const templateSrc = template.source;
|
25327 | if (!templateSrc)
|
25328 | return;
|
25329 | if (ast instanceof Element$1) {
|
25330 | // The HTML tag may include `-` (e.g. `app-root`),
|
25331 | // so use the HtmlAst to get the span before ayazhafiz refactor the code.
|
25332 | return {
|
25333 | start: templateInfo.template.span.start + ast.startSourceSpan.start.offset + 1,
|
25334 | length: ast.name.length
|
25335 | };
|
25336 | }
|
25337 | // TODO(ayazhafiz): A solution based on word expansion will always be expensive compared to one
|
25338 | // based on ASTs. Whatever penalty we incur is probably manageable for small-length (i.e. the
|
25339 | // majority of) identifiers, but the current solution involes a number of branchings and we can't
|
25340 | // control potentially very long identifiers. Consider moving to an AST-based solution once
|
25341 | // existing difficulties with AST spans are more clearly resolved (see #31898 for discussion of
|
25342 | // known problems, and #33091 for how they affect text replacement).
|
25343 | //
|
25344 | // `templatePosition` represents the right-bound location of a cursor in the template.
|
25345 | // key.ent|ry
|
25346 | // ^---- cursor, at position `r` is at.
|
25347 | // A cursor is not itself a character in the template; it has a left (lower) and right (upper)
|
25348 | // index bound that hugs the cursor itself.
|
25349 | let templatePosition = position - template.span.start;
|
25350 | // To perform word expansion, we want to determine the left and right indices that hug the cursor.
|
25351 | // There are three cases here.
|
25352 | let left, right;
|
25353 | if (templatePosition === 0) {
|
25354 | // 1. Case like
|
25355 | // |rest of template
|
25356 | // the cursor is at the start of the template, hugged only by the right side (0-index).
|
25357 | left = right = 0;
|
25358 | }
|
25359 | else if (templatePosition === templateSrc.length) {
|
25360 | // 2. Case like
|
25361 | // rest of template|
|
25362 | // the cursor is at the end of the template, hugged only by the left side (last-index).
|
25363 | left = right = templateSrc.length - 1;
|
25364 | }
|
25365 | else {
|
25366 | // 3. Case like
|
25367 | // wo|rd
|
25368 | // there is a clear left and right index.
|
25369 | left = templatePosition - 1;
|
25370 | right = templatePosition;
|
25371 | }
|
25372 | if (!isIdentifierPart$1(templateSrc.charCodeAt(left)) &&
|
25373 | !isIdentifierPart$1(templateSrc.charCodeAt(right))) {
|
25374 | // Case like
|
25375 | // .|.
|
25376 | // left ---^ ^--- right
|
25377 | // There is no word here.
|
25378 | return;
|
25379 | }
|
25380 | // Expand on the left and right side until a word boundary is hit. Back up one expansion on both
|
25381 | // side to stay inside the word.
|
25382 | while (left >= 0 && isIdentifierPart$1(templateSrc.charCodeAt(left)))
|
25383 | --left;
|
25384 | ++left;
|
25385 | while (right < templateSrc.length && isIdentifierPart$1(templateSrc.charCodeAt(right)))
|
25386 | ++right;
|
25387 | --right;
|
25388 | const absoluteStartPosition = position - (templatePosition - left);
|
25389 | const length = right - left + 1;
|
25390 | return { start: absoluteStartPosition, length };
|
25391 | }
|
25392 | function getTemplateCompletions(templateInfo, position) {
|
25393 | const { htmlAst, template } = templateInfo;
|
25394 | // Calculate the position relative to the start of the template. This is needed
|
25395 | // because spans in HTML AST are relative. Inline template has non-zero start position.
|
25396 | const templatePosition = position - template.span.start;
|
25397 | const htmlPath = getPathToNodeAtPosition(htmlAst, templatePosition);
|
25398 | const mostSpecific = htmlPath.tail;
|
25399 | const visitor = new HtmlVisitor(templateInfo, htmlPath);
|
25400 | const results = mostSpecific ?
|
25401 | mostSpecific.visit(visitor, null /* context */) :
|
25402 | elementCompletions(templateInfo);
|
25403 | const replacementSpan = getBoundedWordSpan(templateInfo, position, mostSpecific);
|
25404 | return results.map(entry => {
|
25405 | return Object.assign(Object.assign({}, entry), { replacementSpan });
|
25406 | });
|
25407 | }
|
25408 | class HtmlVisitor {
|
25409 | constructor(templateInfo, htmlPath) {
|
25410 | this.templateInfo = templateInfo;
|
25411 | this.htmlPath = htmlPath;
|
25412 | this.relativePosition = htmlPath.position;
|
25413 | }
|
25414 | // Note that every visitor method must explicitly specify return type because
|
25415 | // Visitor returns `any` for all methods.
|
25416 | visitElement(ast) {
|
25417 | const startTagSpan = spanOf(ast.sourceSpan);
|
25418 | const tagLen = ast.name.length;
|
25419 | // + 1 for the opening angle bracket
|
25420 | if (this.relativePosition <= startTagSpan.start + tagLen + 1) {
|
25421 | // If we are in the tag then return the element completions.
|
25422 | return elementCompletions(this.templateInfo);
|
25423 | }
|
25424 | if (this.relativePosition < startTagSpan.end) {
|
25425 | // We are in the attribute section of the element (but not in an attribute).
|
25426 | // Return the attribute completions.
|
25427 | return attributeCompletionsForElement(this.templateInfo, ast.name);
|
25428 | }
|
25429 | return [];
|
25430 | }
|
25431 | visitAttribute(ast) {
|
25432 | // An attribute consists of two parts, LHS="RHS".
|
25433 | // Determine if completions are requested for LHS or RHS
|
25434 | if (ast.valueSpan && inSpan(this.relativePosition, spanOf(ast.valueSpan))) {
|
25435 | // RHS completion
|
25436 | return attributeValueCompletions(this.templateInfo, this.htmlPath);
|
25437 | }
|
25438 | // LHS completion
|
25439 | return attributeCompletions(this.templateInfo, this.htmlPath);
|
25440 | }
|
25441 | visitText() {
|
25442 | var _a;
|
25443 | const templatePath = findTemplateAstAt(this.templateInfo.templateAst, this.relativePosition);
|
25444 | if (templatePath.tail instanceof BoundTextAst) {
|
25445 | // If we know that this is an interpolation then do not try other scenarios.
|
25446 | const visitor = new ExpressionVisitor(this.templateInfo, this.relativePosition, () => getExpressionScope(diagnosticInfoFromTemplateInfo(this.templateInfo), templatePath));
|
25447 | (_a = templatePath.tail) === null || _a === void 0 ? void 0 : _a.visit(visitor, null);
|
25448 | return visitor.results;
|
25449 | }
|
25450 | // TODO(kyliau): Not sure if this check is really needed since we don't have
|
25451 | // any test cases for it.
|
25452 | const element = this.htmlPath.first(Element$1);
|
25453 | if (element &&
|
25454 | getHtmlTagDefinition(element.name).getContentType() !== TagContentType.PARSABLE_DATA) {
|
25455 | return [];
|
25456 | }
|
25457 | // This is to account for cases like <h1> <a> text | </h1> where the
|
25458 | // closest element has no closing tag and thus is considered plain text.
|
25459 | const results = voidElementAttributeCompletions(this.templateInfo, this.htmlPath);
|
25460 | if (results.length) {
|
25461 | return results;
|
25462 | }
|
25463 | return elementCompletions(this.templateInfo);
|
25464 | }
|
25465 | visitComment() {
|
25466 | return [];
|
25467 | }
|
25468 | visitExpansion() {
|
25469 | return [];
|
25470 | }
|
25471 | visitExpansionCase() {
|
25472 | return [];
|
25473 | }
|
25474 | }
|
25475 | function attributeCompletions(info, path) {
|
25476 | const attr = path.tail;
|
25477 | const elem = path.parentOf(attr);
|
25478 | if (!(attr instanceof Attribute) || !(elem instanceof Element$1)) {
|
25479 | return [];
|
25480 | }
|
25481 | // TODO: Consider parsing the attrinute name to a proper AST instead of
|
25482 | // matching using regex. This is because the regexp would incorrectly identify
|
25483 | // bind parts for cases like [()|]
|
25484 | // ^ cursor is here
|
25485 | const binding = getBindingDescriptor(attr.name);
|
25486 | if (!binding) {
|
25487 | // This is a normal HTML attribute, not an Angular attribute.
|
25488 | return attributeCompletionsForElement(info, elem.name);
|
25489 | }
|
25490 | const results = [];
|
25491 | const ngAttrs = angularAttributes(info, elem.name);
|
25492 | switch (binding.kind) {
|
25493 | case ATTR.KW_MICROSYNTAX:
|
25494 | // template reference attribute: *attrName
|
25495 | results.push(...ngAttrs.templateRefs);
|
25496 | break;
|
25497 | case ATTR.KW_BIND:
|
25498 | case ATTR.IDENT_PROPERTY:
|
25499 | // property binding via bind- or []
|
25500 | results.push(...propertyNames(elem.name), ...ngAttrs.inputs);
|
25501 | break;
|
25502 | case ATTR.KW_ON:
|
25503 | case ATTR.IDENT_EVENT:
|
25504 | // event binding via on- or ()
|
25505 | results.push(...eventNames(elem.name), ...ngAttrs.outputs);
|
25506 | break;
|
25507 | case ATTR.KW_BINDON:
|
25508 | case ATTR.IDENT_BANANA_BOX:
|
25509 | // banana-in-a-box binding via bindon- or [()]
|
25510 | results.push(...ngAttrs.bananas);
|
25511 | break;
|
25512 | }
|
25513 | return results.map(name => {
|
25514 | return {
|
25515 | name,
|
25516 | kind: CompletionKind.ATTRIBUTE,
|
25517 | sortText: name,
|
25518 | };
|
25519 | });
|
25520 | }
|
25521 | function attributeCompletionsForElement(info, elementName) {
|
25522 | const results = [];
|
25523 | if (info.template instanceof InlineTemplate) {
|
25524 | // Provide HTML attributes completion only for inline templates
|
25525 | for (const name of attributeNames(elementName)) {
|
25526 | results.push({
|
25527 | name,
|
25528 | kind: CompletionKind.HTML_ATTRIBUTE,
|
25529 | sortText: name,
|
25530 | });
|
25531 | }
|
25532 | }
|
25533 | // Add Angular attributes
|
25534 | const ngAttrs = angularAttributes(info, elementName);
|
25535 | for (const name of ngAttrs.others) {
|
25536 | results.push({
|
25537 | name,
|
25538 | kind: CompletionKind.ATTRIBUTE,
|
25539 | sortText: name,
|
25540 | });
|
25541 | }
|
25542 | return results;
|
25543 | }
|
25544 | /**
|
25545 | * Provide completions to the RHS of an attribute, which is of the form
|
25546 | * LHS="RHS". The template path is computed from the specified `info` whereas
|
25547 | * the context is determined from the specified `htmlPath`.
|
25548 | * @param info Object that contains the template AST
|
25549 | * @param htmlPath Path to the HTML node
|
25550 | */
|
25551 | function attributeValueCompletions(info, htmlPath) {
|
25552 | // Find the corresponding Template AST path.
|
25553 | const templatePath = findTemplateAstAt(info.templateAst, htmlPath.position);
|
25554 | const visitor = new ExpressionVisitor(info, htmlPath.position, () => {
|
25555 | const dinfo = diagnosticInfoFromTemplateInfo(info);
|
25556 | return getExpressionScope(dinfo, templatePath);
|
25557 | });
|
25558 | if (templatePath.tail instanceof AttrAst ||
|
25559 | templatePath.tail instanceof BoundElementPropertyAst ||
|
25560 | templatePath.tail instanceof BoundEventAst) {
|
25561 | templatePath.tail.visit(visitor, null);
|
25562 | return visitor.results;
|
25563 | }
|
25564 | // In order to provide accurate attribute value completion, we need to know
|
25565 | // what the LHS is, and construct the proper AST if it is missing.
|
25566 | const htmlAttr = htmlPath.tail;
|
25567 | const binding = getBindingDescriptor(htmlAttr.name);
|
25568 | if (binding && binding.kind === ATTR.KW_REF) {
|
25569 | let refAst;
|
25570 | let elemAst;
|
25571 | if (templatePath.tail instanceof ReferenceAst) {
|
25572 | refAst = templatePath.tail;
|
25573 | const parent = templatePath.parentOf(refAst);
|
25574 | if (parent instanceof ElementAst) {
|
25575 | elemAst = parent;
|
25576 | }
|
25577 | }
|
25578 | else if (templatePath.tail instanceof ElementAst) {
|
25579 | refAst = new ReferenceAst(htmlAttr.name, null, htmlAttr.value, htmlAttr.valueSpan);
|
25580 | elemAst = templatePath.tail;
|
25581 | }
|
25582 | if (refAst && elemAst) {
|
25583 | refAst.visit(visitor, elemAst);
|
25584 | }
|
25585 | }
|
25586 | else {
|
25587 | // HtmlAst contains the `Attribute` node, however the corresponding `AttrAst`
|
25588 | // node is missing from the TemplateAst.
|
25589 | const attrAst = new AttrAst(htmlAttr.name, htmlAttr.value, htmlAttr.valueSpan);
|
25590 | attrAst.visit(visitor, null);
|
25591 | }
|
25592 | return visitor.results;
|
25593 | }
|
25594 | function elementCompletions(info) {
|
25595 | const results = [...ANGULAR_ELEMENTS];
|
25596 | if (info.template instanceof InlineTemplate) {
|
25597 | // Provide HTML elements completion only for inline templates
|
25598 | results.push(...HTML_ELEMENTS);
|
25599 | }
|
25600 | // Collect the elements referenced by the selectors
|
25601 | const components = new Set();
|
25602 | for (const selector of getSelectors(info).selectors) {
|
25603 | const name = selector.element;
|
25604 | if (name && !components.has(name)) {
|
25605 | components.add(name);
|
25606 | results.push({
|
25607 | name,
|
25608 | kind: CompletionKind.COMPONENT,
|
25609 | sortText: name,
|
25610 | });
|
25611 | }
|
25612 | }
|
25613 | return results;
|
25614 | }
|
25615 | // There is a special case of HTML where text that contains a unclosed tag is treated as
|
25616 | // text. For exaple '<h1> Some <a text </h1>' produces a text nodes inside of the H1
|
25617 | // element "Some <a text". We, however, want to treat this as if the user was requesting
|
25618 | // the attributes of an "a" element, not requesting completion in the a text element. This
|
25619 | // code checks for this case and returns element completions if it is detected or undefined
|
25620 | // if it is not.
|
25621 | function voidElementAttributeCompletions(info, path) {
|
25622 | const tail = path.tail;
|
25623 | if (tail instanceof Text$2) {
|
25624 | const match = tail.value.match(/<(\w(\w|\d|-)*:)?(\w(\w|\d|-)*)\s/);
|
25625 | // The position must be after the match, otherwise we are still in a place where elements
|
25626 | // are expected (such as `<|a` or `<a|`; we only want attributes for `<a |` or after).
|
25627 | if (match &&
|
25628 | path.position >= (match.index || 0) + match[0].length + tail.sourceSpan.start.offset) {
|
25629 | return attributeCompletionsForElement(info, match[3]);
|
25630 | }
|
25631 | }
|
25632 | return [];
|
25633 | }
|
25634 | class ExpressionVisitor extends NullTemplateVisitor {
|
25635 | constructor(info, position, getExpressionScope) {
|
25636 | super();
|
25637 | this.info = info;
|
25638 | this.position = position;
|
25639 | this.getExpressionScope = getExpressionScope;
|
25640 | this.completions = new Map();
|
25641 | }
|
25642 | get results() {
|
25643 | return Array.from(this.completions.values());
|
25644 | }
|
25645 | visitDirectiveProperty(ast) {
|
25646 | this.processExpressionCompletions(ast.value);
|
25647 | }
|
25648 | visitElementProperty(ast) {
|
25649 | this.processExpressionCompletions(ast.value);
|
25650 | }
|
25651 | visitEvent(ast) {
|
25652 | this.processExpressionCompletions(ast.handler);
|
25653 | }
|
25654 | visitElement() {
|
25655 | // no-op for now
|
25656 | }
|
25657 | visitAttr(ast) {
|
25658 | const binding = getBindingDescriptor(ast.name);
|
25659 | if (binding && binding.kind === ATTR.KW_MICROSYNTAX) {
|
25660 | // This a template binding given by micro syntax expression.
|
25661 | // First, verify the attribute consists of some binding we can give completions for.
|
25662 | // The sourceSpan of AttrAst points to the RHS of the attribute
|
25663 | const templateKey = binding.name;
|
25664 | const templateValue = ast.sourceSpan.toString();
|
25665 | const templateUrl = ast.sourceSpan.start.file.url;
|
25666 | // TODO(kyliau): We are unable to determine the absolute offset of the key
|
25667 | // but it is okay here, because we are only looking at the RHS of the attr
|
25668 | const absKeyOffset = 0;
|
25669 | const absValueOffset = ast.sourceSpan.start.offset;
|
25670 | const { templateBindings } = this.info.expressionParser.parseTemplateBindings(templateKey, templateValue, templateUrl, absKeyOffset, absValueOffset);
|
25671 | // Find the nearest template binding to the position.
|
25672 | const lastBindingEnd = templateBindings.length > 0 &&
|
25673 | templateBindings[templateBindings.length - 1].sourceSpan.end;
|
25674 | const normalizedPositionToBinding = lastBindingEnd && this.position > lastBindingEnd ? lastBindingEnd : this.position;
|
25675 | const templateBinding = templateBindings.find(b => inSpan(normalizedPositionToBinding, b.sourceSpan));
|
25676 | if (!templateBinding) {
|
25677 | return;
|
25678 | }
|
25679 | this.microSyntaxInAttributeValue(ast, templateBinding);
|
25680 | }
|
25681 | else {
|
25682 | const expressionAst = this.info.expressionParser.parseBinding(ast.value, ast.sourceSpan.toString(), ast.sourceSpan.start.offset);
|
25683 | this.processExpressionCompletions(expressionAst);
|
25684 | }
|
25685 | }
|
25686 | visitReference(_ast, context) {
|
25687 | context.directives.forEach(dir => {
|
25688 | const { exportAs } = dir.directive;
|
25689 | if (exportAs) {
|
25690 | this.completions.set(exportAs, { name: exportAs, kind: CompletionKind.REFERENCE, sortText: exportAs });
|
25691 | }
|
25692 | });
|
25693 | }
|
25694 | visitBoundText(ast) {
|
25695 | if (inSpan(this.position, ast.value.sourceSpan)) {
|
25696 | const completions = getExpressionCompletions(this.getExpressionScope(), ast.value, this.position, this.info.template);
|
25697 | if (completions) {
|
25698 | this.addSymbolsToCompletions(completions);
|
25699 | }
|
25700 | }
|
25701 | }
|
25702 | processExpressionCompletions(value) {
|
25703 | const symbols = getExpressionCompletions(this.getExpressionScope(), value, this.position, this.info.template);
|
25704 | if (symbols) {
|
25705 | this.addSymbolsToCompletions(symbols);
|
25706 | }
|
25707 | }
|
25708 | addSymbolsToCompletions(symbols) {
|
25709 | for (const s of symbols) {
|
25710 | if (s.name.startsWith('__') || !s.public || this.completions.has(s.name)) {
|
25711 | continue;
|
25712 | }
|
25713 | // The pipe method should not include parentheses.
|
25714 | // e.g. {{ value_expression | slice : start [ : end ] }}
|
25715 | const shouldInsertParentheses = s.callable && s.kind !== CompletionKind.PIPE;
|
25716 | this.completions.set(s.name, {
|
25717 | name: s.name,
|
25718 | kind: s.kind,
|
25719 | sortText: s.name,
|
25720 | insertText: shouldInsertParentheses ? `${s.name}()` : s.name,
|
25721 | });
|
25722 | }
|
25723 | }
|
25724 | /**
|
25725 | * This method handles the completions of attribute values for directives that
|
25726 | * support the microsyntax format. Examples are *ngFor and *ngIf.
|
25727 | * These directives allows declaration of "let" variables, adds context-specific
|
25728 | * symbols like $implicit, index, count, among other behaviors.
|
25729 | * For a complete description of such format, see
|
25730 | * https://angular.io/guide/structural-directives#the-asterisk--prefix
|
25731 | *
|
25732 | * @param attr descriptor for attribute name and value pair
|
25733 | * @param binding template binding for the expression in the attribute
|
25734 | */
|
25735 | microSyntaxInAttributeValue(attr, binding) {
|
25736 | var _a;
|
25737 | const key = attr.name.substring(1); // remove leading asterisk
|
25738 | // Find the selector - eg ngFor, ngIf, etc
|
25739 | const selectorInfo = getSelectors(this.info);
|
25740 | const selector = selectorInfo.selectors.find(s => {
|
25741 | // attributes are listed in (attribute, value) pairs
|
25742 | for (let i = 0; i < s.attrs.length; i += 2) {
|
25743 | if (s.attrs[i] === key) {
|
25744 | return true;
|
25745 | }
|
25746 | }
|
25747 | });
|
25748 | if (!selector) {
|
25749 | return;
|
25750 | }
|
25751 | const valueRelativePosition = this.position - attr.sourceSpan.start.offset;
|
25752 | if (binding instanceof VariableBinding) {
|
25753 | // TODO(kyliau): With expression sourceSpan we shouldn't have to search
|
25754 | // the attribute value string anymore. Just check if position is in the
|
25755 | // expression source span.
|
25756 | const equalLocation = attr.value.indexOf('=');
|
25757 | if (equalLocation > 0 && valueRelativePosition > equalLocation) {
|
25758 | // We are after the '=' in a let clause. The valid values here are the members of the
|
25759 | // template reference's type parameter.
|
25760 | const directiveMetadata = selectorInfo.map.get(selector);
|
25761 | if (directiveMetadata) {
|
25762 | const contextTable = this.info.template.query.getTemplateContext(directiveMetadata.type.reference);
|
25763 | if (contextTable) {
|
25764 | // This adds symbols like $implicit, index, count, etc.
|
25765 | this.addSymbolsToCompletions(contextTable.values());
|
25766 | return;
|
25767 | }
|
25768 | }
|
25769 | }
|
25770 | }
|
25771 | else if (binding instanceof ExpressionBinding) {
|
25772 | if (inSpan(this.position, (_a = binding.value) === null || _a === void 0 ? void 0 : _a.ast.sourceSpan)) {
|
25773 | this.processExpressionCompletions(binding.value.ast);
|
25774 | return;
|
25775 | }
|
25776 | else if (!binding.value && this.position > binding.key.span.end) {
|
25777 | // No expression is defined for the value of the key expression binding, but the cursor is
|
25778 | // in a location where the expression would be defined. This can happen in a case like
|
25779 | // let i of |
|
25780 | // ^-- cursor
|
25781 | // In this case, backfill the value to be an empty expression and retrieve completions.
|
25782 | this.processExpressionCompletions(new EmptyExpr(new ParseSpan(valueRelativePosition, valueRelativePosition), new AbsoluteSourceSpan(this.position, this.position)));
|
25783 | return;
|
25784 | }
|
25785 | }
|
25786 | }
|
25787 | }
|
25788 | /**
|
25789 | * Return all Angular-specific attributes for the element with `elementName`.
|
25790 | * @param info
|
25791 | * @param elementName
|
25792 | */
|
25793 | function angularAttributes(info, elementName) {
|
25794 | const { selectors, map: selectorMap } = getSelectors(info);
|
25795 | const templateRefs = new Set();
|
25796 | const inputs = new Set();
|
25797 | const outputs = new Set();
|
25798 | const bananas = new Set();
|
25799 | const others = new Set();
|
25800 | for (const selector of selectors) {
|
25801 | if (selector.element && selector.element !== elementName) {
|
25802 | continue;
|
25803 | }
|
25804 | const summary = selectorMap.get(selector);
|
25805 | const hasTemplateRef = isStructuralDirective(summary.type);
|
25806 | // attributes are listed in (attribute, value) pairs
|
25807 | for (let i = 0; i < selector.attrs.length; i += 2) {
|
25808 | const attr = selector.attrs[i];
|
25809 | if (hasTemplateRef) {
|
25810 | templateRefs.add(attr);
|
25811 | }
|
25812 | else {
|
25813 | others.add(attr);
|
25814 | }
|
25815 | }
|
25816 | for (const input of Object.values(summary.inputs)) {
|
25817 | inputs.add(input);
|
25818 | }
|
25819 | for (const output of Object.values(summary.outputs)) {
|
25820 | outputs.add(output);
|
25821 | }
|
25822 | }
|
25823 | for (const name of inputs) {
|
25824 | // Add banana-in-a-box syntax
|
25825 | // https://angular.io/guide/template-syntax#two-way-binding-
|
25826 | if (outputs.has(`${name}Change`)) {
|
25827 | bananas.add(name);
|
25828 | }
|
25829 | }
|
25830 | return { templateRefs, inputs, outputs, bananas, others };
|
25831 | }
|
25832 |
|
25833 | /**
|
25834 | * @license
|
25835 | * Copyright Google LLC All Rights Reserved.
|
25836 | *
|
25837 | * Use of this source code is governed by an MIT-style license that can be
|
25838 | * found in the LICENSE file at https://angular.io/license
|
25839 | */
|
25840 | /**
|
25841 | * Traverses a template AST and locates symbol(s) at a specified position.
|
25842 | * @param info template AST information set
|
25843 | * @param position location to locate symbols at
|
25844 | */
|
25845 | function locateSymbols(info, position) {
|
25846 | const templatePosition = position - info.template.span.start;
|
25847 | // TODO: update `findTemplateAstAt` to use absolute positions.
|
25848 | const path = findTemplateAstAt(info.templateAst, templatePosition);
|
25849 | const attribute = findAttribute(info, position);
|
25850 | if (!path.tail)
|
25851 | return [];
|
25852 | const narrowest = spanOf(path.tail);
|
25853 | const toVisit = [];
|
25854 | for (let node = path.tail; node && isNarrower(spanOf(node.sourceSpan), narrowest); node = path.parentOf(node)) {
|
25855 | toVisit.push(node);
|
25856 | }
|
25857 | // For the structural directive, only care about the last template AST.
|
25858 | if (attribute === null || attribute === void 0 ? void 0 : attribute.name.startsWith('*')) {
|
25859 | toVisit.splice(0, toVisit.length - 1);
|
25860 | }
|
25861 | return toVisit.map(ast => locateSymbol(ast, path, info))
|
25862 | .filter((sym) => sym !== undefined);
|
25863 | }
|
25864 | /**
|
25865 | * Visits a template node and locates the symbol in that node at a path position.
|
25866 | * @param ast template AST node to visit
|
25867 | * @param path non-empty set of narrowing AST nodes at a position
|
25868 | * @param info template AST information set
|
25869 | */
|
25870 | function locateSymbol(ast, path, info) {
|
25871 | const templatePosition = path.position;
|
25872 | const position = templatePosition + info.template.span.start;
|
25873 | let symbol;
|
25874 | let span;
|
25875 | let staticSymbol;
|
25876 | const attributeValueSymbol = (ast) => {
|
25877 | const attribute = findAttribute(info, position);
|
25878 | if (attribute) {
|
25879 | if (inSpan(templatePosition, spanOf(attribute.valueSpan))) {
|
25880 | let result;
|
25881 | if (attribute.name.startsWith('*')) {
|
25882 | result = getSymbolInMicrosyntax(info, path, attribute);
|
25883 | }
|
25884 | else {
|
25885 | const dinfo = diagnosticInfoFromTemplateInfo(info);
|
25886 | const scope = getExpressionScope(dinfo, path);
|
25887 | result = getExpressionSymbol(scope, ast, templatePosition, info.template);
|
25888 | }
|
25889 | if (result) {
|
25890 | symbol = result.symbol;
|
25891 | span = offsetSpan(result.span, attribute.valueSpan.start.offset);
|
25892 | }
|
25893 | return true;
|
25894 | }
|
25895 | }
|
25896 | return false;
|
25897 | };
|
25898 | ast.visit({
|
25899 | visitNgContent(_ast) { },
|
25900 | visitEmbeddedTemplate(_ast) { },
|
25901 | visitElement(ast) {
|
25902 | const component = ast.directives.find(d => d.directive.isComponent);
|
25903 | if (component) {
|
25904 | // Need to cast because 'reference' is typed as any
|
25905 | staticSymbol = component.directive.type.reference;
|
25906 | symbol = info.template.query.getTypeSymbol(staticSymbol);
|
25907 | symbol = symbol && new OverrideKindSymbol(symbol, DirectiveKind.COMPONENT);
|
25908 | span = spanOf(ast);
|
25909 | }
|
25910 | else {
|
25911 | // Find a directive that matches the element name
|
25912 | const directive = ast.directives.find(d => d.directive.selector != null && d.directive.selector.indexOf(ast.name) >= 0);
|
25913 | if (directive) {
|
25914 | // Need to cast because 'reference' is typed as any
|
25915 | staticSymbol = directive.directive.type.reference;
|
25916 | symbol = info.template.query.getTypeSymbol(staticSymbol);
|
25917 | symbol = symbol && new OverrideKindSymbol(symbol, DirectiveKind.DIRECTIVE);
|
25918 | span = spanOf(ast);
|
25919 | }
|
25920 | }
|
25921 | },
|
25922 | visitReference(ast) {
|
25923 | symbol = ast.value && info.template.query.getTypeSymbol(tokenReference(ast.value));
|
25924 | span = spanOf(ast);
|
25925 | },
|
25926 | visitVariable(_ast) { },
|
25927 | visitEvent(ast) {
|
25928 | if (!attributeValueSymbol(ast.handler)) {
|
25929 | symbol = findOutputBinding(ast, path, info.template.query);
|
25930 | symbol = symbol && new OverrideKindSymbol(symbol, DirectiveKind.EVENT);
|
25931 | span = spanOf(ast);
|
25932 | }
|
25933 | },
|
25934 | visitElementProperty(ast) {
|
25935 | attributeValueSymbol(ast.value);
|
25936 | },
|
25937 | visitAttr(ast) {
|
25938 | const element = path.first(ElementAst);
|
25939 | if (!element)
|
25940 | return;
|
25941 | // Create a mapping of all directives applied to the element from their selectors.
|
25942 | const matcher = new SelectorMatcher();
|
25943 | for (const dir of element.directives) {
|
25944 | if (!dir.directive.selector)
|
25945 | continue;
|
25946 | matcher.addSelectables(CssSelector.parse(dir.directive.selector), dir);
|
25947 | }
|
25948 | // See if this attribute matches the selector of any directive on the element.
|
25949 | const attributeSelector = `[${ast.name}=${ast.value}]`;
|
25950 | const parsedAttribute = CssSelector.parse(attributeSelector);
|
25951 | if (!parsedAttribute.length)
|
25952 | return;
|
25953 | matcher.match(parsedAttribute[0], (_, { directive }) => {
|
25954 | // Need to cast because 'reference' is typed as any
|
25955 | staticSymbol = directive.type.reference;
|
25956 | symbol = info.template.query.getTypeSymbol(staticSymbol);
|
25957 | symbol = symbol && new OverrideKindSymbol(symbol, DirectiveKind.DIRECTIVE);
|
25958 | span = spanOf(ast);
|
25959 | });
|
25960 | },
|
25961 | visitBoundText(ast) {
|
25962 | const expressionPosition = templatePosition - ast.sourceSpan.start.offset;
|
25963 | if (inSpan(expressionPosition, ast.value.span)) {
|
25964 | const dinfo = diagnosticInfoFromTemplateInfo(info);
|
25965 | const scope = getExpressionScope(dinfo, path);
|
25966 | const result = getExpressionSymbol(scope, ast.value, templatePosition, info.template);
|
25967 | if (result) {
|
25968 | symbol = result.symbol;
|
25969 | span = offsetSpan(result.span, ast.sourceSpan.start.offset);
|
25970 | }
|
25971 | }
|
25972 | },
|
25973 | visitText(_ast) { },
|
25974 | visitDirective(ast) {
|
25975 | // Need to cast because 'reference' is typed as any
|
25976 | staticSymbol = ast.directive.type.reference;
|
25977 | symbol = info.template.query.getTypeSymbol(staticSymbol);
|
25978 | span = spanOf(ast);
|
25979 | },
|
25980 | visitDirectiveProperty(ast) {
|
25981 | if (!attributeValueSymbol(ast.value)) {
|
25982 | const directive = findParentOfBinding(info.templateAst, ast, templatePosition);
|
25983 | const attribute = findAttribute(info, position);
|
25984 | if (directive && attribute) {
|
25985 | if (attribute.name.startsWith('*')) {
|
25986 | const compileTypeSummary = directive.directive;
|
25987 | symbol = info.template.query.getTypeSymbol(compileTypeSummary.type.reference);
|
25988 | symbol = symbol && new OverrideKindSymbol(symbol, DirectiveKind.DIRECTIVE);
|
25989 | // Use 'attribute.sourceSpan' instead of the directive's,
|
25990 | // because the span of the directive is the whole opening tag of an element.
|
25991 | span = spanOf(attribute.sourceSpan);
|
25992 | }
|
25993 | else {
|
25994 | symbol = findInputBinding(info, ast.templateName, directive);
|
25995 | span = spanOf(ast);
|
25996 | }
|
25997 | }
|
25998 | }
|
25999 | }
|
26000 | }, null);
|
26001 | if (symbol && span) {
|
26002 | const { start, end } = offsetSpan(span, info.template.span.start);
|
26003 | return {
|
26004 | symbol,
|
26005 | span: tss.createTextSpanFromBounds(start, end),
|
26006 | staticSymbol,
|
26007 | };
|
26008 | }
|
26009 | }
|
26010 | // Get the symbol in microsyntax at template position.
|
26011 | function getSymbolInMicrosyntax(info, path, attribute) {
|
26012 | var _a;
|
26013 | if (!attribute.valueSpan) {
|
26014 | return;
|
26015 | }
|
26016 | const absValueOffset = attribute.valueSpan.start.offset;
|
26017 | let result;
|
26018 | const { templateBindings } = info.expressionParser.parseTemplateBindings(attribute.name, attribute.value, attribute.sourceSpan.toString(), attribute.sourceSpan.start.offset, attribute.valueSpan.start.offset);
|
26019 | // Find the symbol that contains the position.
|
26020 | for (const tb of templateBindings) {
|
26021 | if (tb instanceof VariableBinding) {
|
26022 | // TODO(kyliau): if binding is variable we should still look for the value
|
26023 | // of the key. For example, "let i=index" => "index" should point to
|
26024 | // NgForOfContext.index
|
26025 | continue;
|
26026 | }
|
26027 | if (inSpan(path.position, (_a = tb.value) === null || _a === void 0 ? void 0 : _a.ast.sourceSpan)) {
|
26028 | const dinfo = diagnosticInfoFromTemplateInfo(info);
|
26029 | const scope = getExpressionScope(dinfo, path);
|
26030 | result = getExpressionSymbol(scope, tb.value, path.position, info.template);
|
26031 | }
|
26032 | else if (inSpan(path.position, tb.sourceSpan)) {
|
26033 | const template = path.first(EmbeddedTemplateAst);
|
26034 | if (template) {
|
26035 | // One element can only have one template binding.
|
26036 | const directiveAst = template.directives[0];
|
26037 | if (directiveAst) {
|
26038 | const symbol = findInputBinding(info, tb.key.source.substring(1), directiveAst);
|
26039 | if (symbol) {
|
26040 | result = {
|
26041 | symbol,
|
26042 | // the span here has to be relative to the start of the template
|
26043 | // value so deduct the absolute offset.
|
26044 | // TODO(kyliau): Use absolute source span throughout completions.
|
26045 | span: offsetSpan(tb.key.span, -absValueOffset),
|
26046 | };
|
26047 | }
|
26048 | }
|
26049 | }
|
26050 | }
|
26051 | }
|
26052 | return result;
|
26053 | }
|
26054 | function findAttribute(info, position) {
|
26055 | const templatePosition = position - info.template.span.start;
|
26056 | const path = getPathToNodeAtPosition(info.htmlAst, templatePosition);
|
26057 | return path.first(Attribute);
|
26058 | }
|
26059 | // TODO: remove this function after the path includes 'DirectiveAst'.
|
26060 | // Find the directive that corresponds to the specified 'binding'
|
26061 | // at the specified 'position' in the 'ast'.
|
26062 | function findParentOfBinding(ast, binding, position) {
|
26063 | let res;
|
26064 | const visitor = new class extends RecursiveTemplateAstVisitor {
|
26065 | visit(ast) {
|
26066 | const span = spanOf(ast);
|
26067 | if (!inSpan(position, span)) {
|
26068 | // Returning a value here will result in the children being skipped.
|
26069 | return true;
|
26070 | }
|
26071 | }
|
26072 | visitEmbeddedTemplate(ast, context) {
|
26073 | return this.visitChildren(context, visit => {
|
26074 | visit(ast.directives);
|
26075 | visit(ast.children);
|
26076 | });
|
26077 | }
|
26078 | visitElement(ast, context) {
|
26079 | return this.visitChildren(context, visit => {
|
26080 | visit(ast.directives);
|
26081 | visit(ast.children);
|
26082 | });
|
26083 | }
|
26084 | visitDirective(ast) {
|
26085 | const result = this.visitChildren(ast, visit => {
|
26086 | visit(ast.inputs);
|
26087 | });
|
26088 | return result;
|
26089 | }
|
26090 | visitDirectiveProperty(ast, context) {
|
26091 | if (ast === binding) {
|
26092 | res = context;
|
26093 | }
|
26094 | }
|
26095 | };
|
26096 | templateVisitAll(visitor, ast);
|
26097 | return res;
|
26098 | }
|
26099 | // Find the symbol of input binding in 'directiveAst' by 'name'.
|
26100 | function findInputBinding(info, name, directiveAst) {
|
26101 | const invertedInput = invertMap(directiveAst.directive.inputs);
|
26102 | const fieldName = invertedInput[name];
|
26103 | if (fieldName) {
|
26104 | const classSymbol = info.template.query.getTypeSymbol(directiveAst.directive.type.reference);
|
26105 | if (classSymbol) {
|
26106 | return classSymbol.members().get(fieldName);
|
26107 | }
|
26108 | }
|
26109 | }
|
26110 | /**
|
26111 | * Wrap a symbol and change its kind to component.
|
26112 | */
|
26113 | class OverrideKindSymbol {
|
26114 | constructor(sym, kindOverride) {
|
26115 | this.sym = sym;
|
26116 | this.kind = kindOverride;
|
26117 | }
|
26118 | get name() {
|
26119 | return this.sym.name;
|
26120 | }
|
26121 | get language() {
|
26122 | return this.sym.language;
|
26123 | }
|
26124 | get type() {
|
26125 | return this.sym.type;
|
26126 | }
|
26127 | get container() {
|
26128 | return this.sym.container;
|
26129 | }
|
26130 | get public() {
|
26131 | return this.sym.public;
|
26132 | }
|
26133 | get callable() {
|
26134 | return this.sym.callable;
|
26135 | }
|
26136 | get nullable() {
|
26137 | return this.sym.nullable;
|
26138 | }
|
26139 | get definition() {
|
26140 | return this.sym.definition;
|
26141 | }
|
26142 | get documentation() {
|
26143 | return this.sym.documentation;
|
26144 | }
|
26145 | members() {
|
26146 | return this.sym.members();
|
26147 | }
|
26148 | signatures() {
|
26149 | return this.sym.signatures();
|
26150 | }
|
26151 | selectSignature(types) {
|
26152 | return this.sym.selectSignature(types);
|
26153 | }
|
26154 | indexed(argument) {
|
26155 | return this.sym.indexed(argument);
|
26156 | }
|
26157 | typeArguments() {
|
26158 | return this.sym.typeArguments();
|
26159 | }
|
26160 | }
|
26161 |
|
26162 | /**
|
26163 | * @license
|
26164 | * Copyright Google LLC All Rights Reserved.
|
26165 | *
|
26166 | * Use of this source code is governed by an MIT-style license that can be
|
26167 | * found in the LICENSE file at https://angular.io/license
|
26168 | */
|
26169 | /**
|
26170 | * Return metadata about `node` if it looks like an Angular directive class.
|
26171 | * In this case, potential matches are `@NgModule`, `@Component`, `@Directive`,
|
26172 | * `@Pipe`, etc.
|
26173 | * These class declarations all share some common attributes, namely their
|
26174 | * decorator takes exactly one parameter and the parameter must be an object
|
26175 | * literal.
|
26176 | *
|
26177 | * For example,
|
26178 | * v---------- `decoratorId`
|
26179 | * @NgModule({ <
|
26180 | * declarations: [], < classDecln-al
|
26181 | * }) <
|
26182 | * class AppModule {} <
|
26183 | * ^----- `classId`
|
26184 | *
|
26185 | * @param node Potential node that represents an Angular directive.
|
26186 | */
|
26187 | function getDirectiveClassLike(node) {
|
26188 | if (!tss.isClassDeclaration(node) || !node.name || !node.decorators) {
|
26189 | return;
|
26190 | }
|
26191 | for (const d of node.decorators) {
|
26192 | const expr = d.expression;
|
26193 | if (!tss.isCallExpression(expr) || expr.arguments.length !== 1 ||
|
26194 | !tss.isIdentifier(expr.expression)) {
|
26195 | continue;
|
26196 | }
|
26197 | const arg = expr.arguments[0];
|
26198 | if (tss.isObjectLiteralExpression(arg)) {
|
26199 | return {
|
26200 | decoratorId: expr.expression,
|
26201 | classId: node.name,
|
26202 | };
|
26203 | }
|
26204 | }
|
26205 | }
|
26206 | /**
|
26207 | * Finds the value of a property assignment that is nested in a TypeScript node and is of a certain
|
26208 | * type T.
|
26209 | *
|
26210 | * @param startNode node to start searching for nested property assignment from
|
26211 | * @param propName property assignment name
|
26212 | * @param predicate function to verify that a node is of type T.
|
26213 | * @return node property assignment value of type T, or undefined if none is found
|
26214 | */
|
26215 | function findPropertyValueOfType(startNode, propName, predicate) {
|
26216 | if (tss.isPropertyAssignment(startNode) && startNode.name.getText() === propName) {
|
26217 | const { initializer } = startNode;
|
26218 | if (predicate(initializer))
|
26219 | return initializer;
|
26220 | }
|
26221 | return startNode.forEachChild(c => findPropertyValueOfType(c, propName, predicate));
|
26222 | }
|
26223 | /**
|
26224 | * Return the node that most tightly encompass the specified `position`.
|
26225 | * @param node
|
26226 | * @param position
|
26227 | */
|
26228 | function findTightestNode(node, position) {
|
26229 | if (node.getStart() <= position && position < node.getEnd()) {
|
26230 | return node.forEachChild(c => findTightestNode(c, position)) || node;
|
26231 | }
|
26232 | }
|
26233 | /**
|
26234 | * Returns a property assignment from the assignment value if the property name
|
26235 | * matches the specified `key`, or `undefined` if there is no match.
|
26236 | */
|
26237 | function getPropertyAssignmentFromValue(value, key) {
|
26238 | const propAssignment = value.parent;
|
26239 | if (!propAssignment || !tss.isPropertyAssignment(propAssignment) ||
|
26240 | propAssignment.name.getText() !== key) {
|
26241 | return;
|
26242 | }
|
26243 | return propAssignment;
|
26244 | }
|
26245 | /**
|
26246 | * Given a decorator property assignment, return the ClassDeclaration node that corresponds to the
|
26247 | * directive class the property applies to.
|
26248 | * If the property assignment is not on a class decorator, no declaration is returned.
|
26249 | *
|
26250 | * For example,
|
26251 | *
|
26252 | * @Component({
|
26253 | * template: '<div></div>'
|
26254 | * ^^^^^^^^^^^^^^^^^^^^^^^---- property assignment
|
26255 | * })
|
26256 | * class AppComponent {}
|
26257 | * ^---- class declaration node
|
26258 | *
|
26259 | * @param propAsgnNode property assignment
|
26260 | */
|
26261 | function getClassDeclFromDecoratorProp(propAsgnNode) {
|
26262 | if (!propAsgnNode.parent || !tss.isObjectLiteralExpression(propAsgnNode.parent)) {
|
26263 | return;
|
26264 | }
|
26265 | const objLitExprNode = propAsgnNode.parent;
|
26266 | if (!objLitExprNode.parent || !tss.isCallExpression(objLitExprNode.parent)) {
|
26267 | return;
|
26268 | }
|
26269 | const callExprNode = objLitExprNode.parent;
|
26270 | if (!callExprNode.parent || !tss.isDecorator(callExprNode.parent)) {
|
26271 | return;
|
26272 | }
|
26273 | const decorator = callExprNode.parent;
|
26274 | if (!decorator.parent || !tss.isClassDeclaration(decorator.parent)) {
|
26275 | return;
|
26276 | }
|
26277 | const classDeclNode = decorator.parent;
|
26278 | return classDeclNode;
|
26279 | }
|
26280 |
|
26281 | /**
|
26282 | * @license
|
26283 | * Copyright Google LLC All Rights Reserved.
|
26284 | *
|
26285 | * Use of this source code is governed by an MIT-style license that can be
|
26286 | * found in the LICENSE file at https://angular.io/license
|
26287 | */
|
26288 | /**
|
26289 | * Convert Angular Span to TypeScript TextSpan. Angular Span has 'start' and
|
26290 | * 'end' whereas TS TextSpan has 'start' and 'length'.
|
26291 | * @param span Angular Span
|
26292 | */
|
26293 | function ngSpanToTsTextSpan(span) {
|
26294 | return {
|
26295 | start: span.start,
|
26296 | length: span.end - span.start,
|
26297 | };
|
26298 | }
|
26299 | /**
|
26300 | * Attempts to get the definition of a file whose URL is specified in a property assignment in a
|
26301 | * directive decorator.
|
26302 | * Currently applies to `templateUrl` and `styleUrls` properties.
|
26303 | */
|
26304 | function getUrlFromProperty(urlNode, tsLsHost) {
|
26305 | // Get the property assignment node corresponding to the `templateUrl` or `styleUrls` assignment.
|
26306 | // These assignments are specified differently; `templateUrl` is a string, and `styleUrls` is
|
26307 | // an array of strings:
|
26308 | // {
|
26309 | // templateUrl: './template.ng.html',
|
26310 | // styleUrls: ['./style.css', './other-style.css']
|
26311 | // }
|
26312 | // `templateUrl`'s property assignment can be found from the string literal node;
|
26313 | // `styleUrls`'s property assignment can be found from the array (parent) node.
|
26314 | //
|
26315 | // First search for `templateUrl`.
|
26316 | let asgn = getPropertyAssignmentFromValue(urlNode, 'templateUrl');
|
26317 | if (!asgn) {
|
26318 | // `templateUrl` assignment not found; search for `styleUrls` array assignment.
|
26319 | asgn = getPropertyAssignmentFromValue(urlNode.parent, 'styleUrls');
|
26320 | if (!asgn) {
|
26321 | // Nothing found, bail.
|
26322 | return;
|
26323 | }
|
26324 | }
|
26325 | // If the property assignment is not a property of a class decorator, don't generate definitions
|
26326 | // for it.
|
26327 | if (!getClassDeclFromDecoratorProp(asgn)) {
|
26328 | return;
|
26329 | }
|
26330 | // Extract url path specified by the url node, which is relative to the TypeScript source file
|
26331 | // the url node is defined in.
|
26332 | const url = extractAbsoluteFilePath(urlNode);
|
26333 | // If the file does not exist, bail. It is possible that the TypeScript language service host
|
26334 | // does not have a `fileExists` method, in which case optimistically assume the file exists.
|
26335 | if (tsLsHost.fileExists && !tsLsHost.fileExists(url))
|
26336 | return;
|
26337 | const templateDefinitions = [{
|
26338 | kind: ts.ScriptElementKind.externalModuleName,
|
26339 | name: url,
|
26340 | containerKind: ts.ScriptElementKind.unknown,
|
26341 | containerName: '',
|
26342 | // Reading the template is expensive, so don't provide a preview.
|
26343 | textSpan: { start: 0, length: 0 },
|
26344 | fileName: url,
|
26345 | }];
|
26346 | return {
|
26347 | definitions: templateDefinitions,
|
26348 | textSpan: {
|
26349 | // Exclude opening and closing quotes in the url span.
|
26350 | start: urlNode.getStart() + 1,
|
26351 | length: urlNode.getWidth() - 2,
|
26352 | },
|
26353 | };
|
26354 | }
|
26355 | /**
|
26356 | * Traverse the template AST and look for the symbol located at `position`, then
|
26357 | * return its definition and span of bound text.
|
26358 | * @param info
|
26359 | * @param position
|
26360 | */
|
26361 | function getDefinitionAndBoundSpan(info, position) {
|
26362 | const symbols = locateSymbols(info, position);
|
26363 | if (!symbols.length) {
|
26364 | return;
|
26365 | }
|
26366 | const seen = new Set();
|
26367 | const definitions = [];
|
26368 | for (const symbolInfo of symbols) {
|
26369 | const { symbol } = symbolInfo;
|
26370 | // symbol.definition is really the locations of the symbol. There could be
|
26371 | // more than one. No meaningful info could be provided without any location.
|
26372 | const { kind, name, container, definition: locations } = symbol;
|
26373 | if (!locations || !locations.length) {
|
26374 | continue;
|
26375 | }
|
26376 | const containerKind = container ? container.kind : ts.ScriptElementKind.unknown;
|
26377 | const containerName = container ? container.name : '';
|
26378 | for (const { fileName, span } of locations) {
|
26379 | const textSpan = ngSpanToTsTextSpan(span);
|
26380 | // In cases like two-way bindings, a request for the definitions of an expression may return
|
26381 | // two of the same definition:
|
26382 | // [(ngModel)]="prop"
|
26383 | // ^^^^ -- one definition for the property binding, one for the event binding
|
26384 | // To prune duplicate definitions, tag definitions with unique location signatures and ignore
|
26385 | // definitions whose locations have already been seen.
|
26386 | const signature = `${textSpan.start}:${textSpan.length}@${fileName}`;
|
26387 | if (seen.has(signature))
|
26388 | continue;
|
26389 | definitions.push({
|
26390 | kind: kind,
|
26391 | name,
|
26392 | containerKind,
|
26393 | containerName,
|
26394 | textSpan: ngSpanToTsTextSpan(span),
|
26395 | fileName: fileName,
|
26396 | });
|
26397 | seen.add(signature);
|
26398 | }
|
26399 | }
|
26400 | return {
|
26401 | definitions,
|
26402 | textSpan: symbols[0].span,
|
26403 | };
|
26404 | }
|
26405 | /**
|
26406 | * Gets an Angular-specific definition in a TypeScript source file.
|
26407 | */
|
26408 | function getTsDefinitionAndBoundSpan(sf, position, tsLsHost) {
|
26409 | const node = findTightestNode(sf, position);
|
26410 | if (!node)
|
26411 | return;
|
26412 | switch (node.kind) {
|
26413 | case ts.SyntaxKind.StringLiteral:
|
26414 | case ts.SyntaxKind.NoSubstitutionTemplateLiteral:
|
26415 | // Attempt to extract definition of a URL in a property assignment.
|
26416 | return getUrlFromProperty(node, tsLsHost);
|
26417 | default:
|
26418 | return undefined;
|
26419 | }
|
26420 | }
|
26421 |
|
26422 | /**
|
26423 | * @license
|
26424 | * Copyright Google LLC All Rights Reserved.
|
26425 | *
|
26426 | * Use of this source code is governed by an MIT-style license that can be
|
26427 | * found in the LICENSE file at https://angular.io/license
|
26428 | */
|
26429 | /**
|
26430 | * Return diagnostic information for the parsed AST of the template.
|
26431 | * @param ast contains HTML and template AST
|
26432 | */
|
26433 | function getTemplateDiagnostics(ast) {
|
26434 | const { parseErrors, templateAst, htmlAst, template } = ast;
|
26435 | if (parseErrors && parseErrors.length) {
|
26436 | return parseErrors.map(e => {
|
26437 | return {
|
26438 | kind: ts.DiagnosticCategory.Error,
|
26439 | span: offsetSpan(spanOf(e.span), template.span.start),
|
26440 | message: e.msg,
|
26441 | };
|
26442 | });
|
26443 | }
|
26444 | return getTemplateExpressionDiagnostics({
|
26445 | templateAst: templateAst,
|
26446 | htmlAst: htmlAst,
|
26447 | offset: template.span.start,
|
26448 | query: template.query,
|
26449 | members: template.members,
|
26450 | source: ast.template.source,
|
26451 | });
|
26452 | }
|
26453 | /**
|
26454 | * Performs a variety diagnostics on directive declarations.
|
26455 | *
|
26456 | * @param declarations Angular directive declarations
|
26457 | * @param modules NgModules in the project
|
26458 | * @param host TypeScript service host used to perform TypeScript queries
|
26459 | * @return diagnosed errors, if any
|
26460 | */
|
26461 | function getDeclarationDiagnostics(declarations, modules, host) {
|
26462 | const directives = new Set();
|
26463 | for (const ngModule of modules.ngModules) {
|
26464 | for (const directive of ngModule.declaredDirectives) {
|
26465 | directives.add(directive.reference);
|
26466 | }
|
26467 | }
|
26468 | const results = [];
|
26469 | for (const declaration of declarations) {
|
26470 | const { errors, metadata, type, declarationSpan } = declaration;
|
26471 | const sf = host.getSourceFile(type.filePath);
|
26472 | if (!sf) {
|
26473 | host.error(`directive ${type.name} exists but has no source file`);
|
26474 | return [];
|
26475 | }
|
26476 | // TypeScript identifier of the directive declaration annotation (e.g. "Component" or
|
26477 | // "Directive") on a directive class.
|
26478 | const directiveIdentifier = findTightestNode(sf, declarationSpan.start);
|
26479 | if (!directiveIdentifier) {
|
26480 | host.error(`directive ${type.name} exists but has no identifier`);
|
26481 | return [];
|
26482 | }
|
26483 | for (const error of errors) {
|
26484 | results.push({
|
26485 | kind: ts.DiagnosticCategory.Error,
|
26486 | message: error.message,
|
26487 | span: error.span,
|
26488 | });
|
26489 | }
|
26490 | if (!modules.ngModuleByPipeOrDirective.has(declaration.type)) {
|
26491 | results.push(createDiagnostic(declarationSpan, Diagnostic.directive_not_in_module, metadata.isComponent ? 'Component' : 'Directive', type.name));
|
26492 | }
|
26493 | if (metadata.isComponent) {
|
26494 | const { template, templateUrl, styleUrls } = metadata.template;
|
26495 | if (template === null && !templateUrl) {
|
26496 | results.push(createDiagnostic(declarationSpan, Diagnostic.missing_template_and_templateurl, type.name));
|
26497 | }
|
26498 | else if (templateUrl) {
|
26499 | if (template) {
|
26500 | results.push(createDiagnostic(declarationSpan, Diagnostic.both_template_and_templateurl, type.name));
|
26501 | }
|
26502 | // Find templateUrl value from the directive call expression, which is the parent of the
|
26503 | // directive identifier.
|
26504 | //
|
26505 | // TODO: We should create an enum of the various properties a directive can have to use
|
26506 | // instead of string literals. We can then perform a mass migration of all literal usages.
|
26507 | const templateUrlNode = findPropertyValueOfType(directiveIdentifier.parent, 'templateUrl', ts.isLiteralExpression);
|
26508 | if (!templateUrlNode) {
|
26509 | host.error(`templateUrl ${templateUrl} exists but its TypeScript node doesn't`);
|
26510 | return [];
|
26511 | }
|
26512 | results.push(...validateUrls([templateUrlNode], host.tsLsHost));
|
26513 | }
|
26514 | if (styleUrls.length > 0) {
|
26515 | // Find styleUrls value from the directive call expression, which is the parent of the
|
26516 | // directive identifier.
|
26517 | const styleUrlsNode = findPropertyValueOfType(directiveIdentifier.parent, 'styleUrls', ts.isArrayLiteralExpression);
|
26518 | if (!styleUrlsNode) {
|
26519 | host.error(`styleUrls property exists but its TypeScript node doesn't'`);
|
26520 | return [];
|
26521 | }
|
26522 | results.push(...validateUrls(styleUrlsNode.elements, host.tsLsHost));
|
26523 | }
|
26524 | }
|
26525 | }
|
26526 | return results;
|
26527 | }
|
26528 | /**
|
26529 | * Checks that URLs on a directive point to a valid file.
|
26530 | * Note that this diagnostic check may require a filesystem hit, and thus may be slower than other
|
26531 | * checks.
|
26532 | *
|
26533 | * @param urls urls to check for validity
|
26534 | * @param tsLsHost TS LS host used for querying filesystem information
|
26535 | * @return diagnosed url errors, if any
|
26536 | */
|
26537 | function validateUrls(urls, tsLsHost) {
|
26538 | if (!tsLsHost.fileExists) {
|
26539 | return [];
|
26540 | }
|
26541 | const allErrors = [];
|
26542 | // TODO(ayazhafiz): most of this logic can be unified with the logic in
|
26543 | // definitions.ts#getUrlFromProperty. Create a utility function to be used by both.
|
26544 | for (let i = 0; i < urls.length; ++i) {
|
26545 | const urlNode = urls[i];
|
26546 | if (!ts.isStringLiteralLike(urlNode)) {
|
26547 | // If a non-string value is assigned to a URL node (like `templateUrl`), a type error will be
|
26548 | // picked up by the TS Language Server.
|
26549 | continue;
|
26550 | }
|
26551 | const url = extractAbsoluteFilePath(urlNode);
|
26552 | if (tsLsHost.fileExists(url))
|
26553 | continue;
|
26554 | // Exclude opening and closing quotes in the url span.
|
26555 | const urlSpan = { start: urlNode.getStart() + 1, end: urlNode.end - 1 };
|
26556 | allErrors.push(createDiagnostic(urlSpan, Diagnostic.invalid_templateurl));
|
26557 | }
|
26558 | return allErrors;
|
26559 | }
|
26560 | /**
|
26561 | * Return a recursive data structure that chains diagnostic messages.
|
26562 | * @param chain
|
26563 | */
|
26564 | function chainDiagnostics(chain) {
|
26565 | return {
|
26566 | messageText: chain.message,
|
26567 | category: ts.DiagnosticCategory.Error,
|
26568 | code: 0,
|
26569 | next: chain.next ? chain.next.map(chainDiagnostics) : undefined
|
26570 | };
|
26571 | }
|
26572 | /**
|
26573 | * Convert ng.Diagnostic to ts.Diagnostic.
|
26574 | * @param d diagnostic
|
26575 | * @param file
|
26576 | */
|
26577 | function ngDiagnosticToTsDiagnostic(d, file) {
|
26578 | return {
|
26579 | file,
|
26580 | start: d.span.start,
|
26581 | length: d.span.end - d.span.start,
|
26582 | messageText: typeof d.message === 'string' ? d.message : chainDiagnostics(d.message),
|
26583 | category: d.kind,
|
26584 | code: 0,
|
26585 | source: 'ng',
|
26586 | };
|
26587 | }
|
26588 |
|
26589 | /**
|
26590 | * @license
|
26591 | * Copyright Google LLC All Rights Reserved.
|
26592 | *
|
26593 | * Use of this source code is governed by an MIT-style license that can be
|
26594 | * found in the LICENSE file at https://angular.io/license
|
26595 | */
|
26596 | /**
|
26597 | * Traverse the template AST and look for the symbol located at `position`, then
|
26598 | * return the corresponding quick info.
|
26599 | * @param info template AST
|
26600 | * @param position location of the symbol
|
26601 | * @param analyzedModules all NgModules in the program.
|
26602 | */
|
26603 | function getTemplateHover(info, position, analyzedModules) {
|
26604 | var _a, _b;
|
26605 | const symbolInfo = locateSymbols(info, position)[0];
|
26606 | if (!symbolInfo) {
|
26607 | return;
|
26608 | }
|
26609 | const { symbol, span, staticSymbol } = symbolInfo;
|
26610 | // The container is either the symbol's container (for example, 'AppComponent'
|
26611 | // is the container of the symbol 'title' in its template) or the NgModule
|
26612 | // that the directive belongs to (the container of AppComponent is AppModule).
|
26613 | let containerName = (_a = symbol.container) === null || _a === void 0 ? void 0 : _a.name;
|
26614 | if (!containerName && staticSymbol) {
|
26615 | // If there is a static symbol then the target is a directive.
|
26616 | const ngModule = analyzedModules.ngModuleByPipeOrDirective.get(staticSymbol);
|
26617 | containerName = ngModule === null || ngModule === void 0 ? void 0 : ngModule.type.reference.name;
|
26618 | }
|
26619 | return createQuickInfo(symbol.name, symbol.kind, span, containerName, (_b = symbol.type) === null || _b === void 0 ? void 0 : _b.name, symbol.documentation);
|
26620 | }
|
26621 | /**
|
26622 | * Get quick info for Angular semantic entities in TypeScript files, like Directives.
|
26623 | * @param position location of the symbol in the source file
|
26624 | * @param declarations All Directive-like declarations in the source file.
|
26625 | * @param analyzedModules all NgModules in the program.
|
26626 | */
|
26627 | function getTsHover(position, declarations, analyzedModules) {
|
26628 | for (const { declarationSpan, metadata } of declarations) {
|
26629 | if (inSpan(position, declarationSpan)) {
|
26630 | const staticSymbol = metadata.type.reference;
|
26631 | const directiveName = staticSymbol.name;
|
26632 | const kind = metadata.isComponent ? 'component' : 'directive';
|
26633 | const textSpan = ts.createTextSpanFromBounds(declarationSpan.start, declarationSpan.end);
|
26634 | const ngModule = analyzedModules.ngModuleByPipeOrDirective.get(staticSymbol);
|
26635 | const moduleName = ngModule === null || ngModule === void 0 ? void 0 : ngModule.type.reference.name;
|
26636 | return createQuickInfo(directiveName, kind, textSpan, moduleName, ts.ScriptElementKind.classElement);
|
26637 | }
|
26638 | }
|
26639 | }
|
26640 | // Reverse mappings of enum would generate strings
|
26641 | const ALIAS_NAME = ts.SymbolDisplayPartKind[ts.SymbolDisplayPartKind.aliasName];
|
26642 | const SYMBOL_INTERFACE = ts.SymbolDisplayPartKind[ts.SymbolDisplayPartKind.interfaceName];
|
26643 | const SYMBOL_PUNC = ts.SymbolDisplayPartKind[ts.SymbolDisplayPartKind.punctuation];
|
26644 | const SYMBOL_SPACE = ts.SymbolDisplayPartKind[ts.SymbolDisplayPartKind.space];
|
26645 | const SYMBOL_TEXT = ts.SymbolDisplayPartKind[ts.SymbolDisplayPartKind.text];
|
26646 | /**
|
26647 | * Construct a QuickInfo object taking into account its container and type.
|
26648 | * @param name Name of the QuickInfo target
|
26649 | * @param kind component, directive, pipe, etc.
|
26650 | * @param textSpan span of the target
|
26651 | * @param containerName either the Symbol's container or the NgModule that contains the directive
|
26652 | * @param type user-friendly name of the type
|
26653 | * @param documentation docstring or comment
|
26654 | */
|
26655 | function createQuickInfo(name, kind, textSpan, containerName, type, documentation) {
|
26656 | const containerDisplayParts = containerName ?
|
26657 | [
|
26658 | { text: containerName, kind: SYMBOL_INTERFACE },
|
26659 | { text: '.', kind: SYMBOL_PUNC },
|
26660 | ] :
|
26661 | [];
|
26662 | const typeDisplayParts = type ?
|
26663 | [
|
26664 | { text: ':', kind: SYMBOL_PUNC },
|
26665 | { text: ' ', kind: SYMBOL_SPACE },
|
26666 | { text: type, kind: SYMBOL_INTERFACE },
|
26667 | ] :
|
26668 | [];
|
26669 | return {
|
26670 | kind: kind,
|
26671 | kindModifiers: ts.ScriptElementKindModifier.none,
|
26672 | textSpan: textSpan,
|
26673 | displayParts: [
|
26674 | { text: '(', kind: SYMBOL_PUNC },
|
26675 | { text: kind, kind: SYMBOL_TEXT },
|
26676 | { text: ')', kind: SYMBOL_PUNC },
|
26677 | { text: ' ', kind: SYMBOL_SPACE },
|
26678 | ...containerDisplayParts,
|
26679 | { text: name, kind: SYMBOL_INTERFACE },
|
26680 | ...typeDisplayParts,
|
26681 | ],
|
26682 | documentation,
|
26683 | };
|
26684 | }
|
26685 |
|
26686 | /**
|
26687 | * @license
|
26688 | * Copyright Google LLC All Rights Reserved.
|
26689 | *
|
26690 | * Use of this source code is governed by an MIT-style license that can be
|
26691 | * found in the LICENSE file at https://angular.io/license
|
26692 | */
|
26693 | /**
|
26694 | * Create an instance of an Angular `LanguageService`.
|
26695 | *
|
26696 | * @publicApi
|
26697 | */
|
26698 | function createLanguageService(host) {
|
26699 | return new LanguageServiceImpl(host);
|
26700 | }
|
26701 | class LanguageServiceImpl {
|
26702 | constructor(host) {
|
26703 | this.host = host;
|
26704 | }
|
26705 | getSemanticDiagnostics(fileName) {
|
26706 | const analyzedModules = this.host.getAnalyzedModules(); // same role as 'synchronizeHostData'
|
26707 | const ngDiagnostics = [];
|
26708 | const templates = this.host.getTemplates(fileName);
|
26709 | for (const template of templates) {
|
26710 | const ast = this.host.getTemplateAst(template);
|
26711 | if (ast) {
|
26712 | ngDiagnostics.push(...getTemplateDiagnostics(ast));
|
26713 | }
|
26714 | }
|
26715 | const declarations = this.host.getDeclarations(fileName);
|
26716 | ngDiagnostics.push(...getDeclarationDiagnostics(declarations, analyzedModules, this.host));
|
26717 | const sourceFile = fileName.endsWith('.ts') ? this.host.getSourceFile(fileName) : undefined;
|
26718 | const tsDiagnostics = ngDiagnostics.map(d => ngDiagnosticToTsDiagnostic(d, sourceFile));
|
26719 | return [...tss.sortAndDeduplicateDiagnostics(tsDiagnostics)];
|
26720 | }
|
26721 | getCompletionsAtPosition(fileName, position, _options) {
|
26722 | this.host.getAnalyzedModules(); // same role as 'synchronizeHostData'
|
26723 | const ast = this.host.getTemplateAstAtPosition(fileName, position);
|
26724 | if (!ast) {
|
26725 | return;
|
26726 | }
|
26727 | const results = getTemplateCompletions(ast, position);
|
26728 | if (!results || !results.length) {
|
26729 | return;
|
26730 | }
|
26731 | return {
|
26732 | isGlobalCompletion: false,
|
26733 | isMemberCompletion: false,
|
26734 | isNewIdentifierLocation: false,
|
26735 | // Cast CompletionEntry.kind from ng.CompletionKind to ts.ScriptElementKind
|
26736 | entries: results,
|
26737 | };
|
26738 | }
|
26739 | getDefinitionAndBoundSpan(fileName, position) {
|
26740 | this.host.getAnalyzedModules(); // same role as 'synchronizeHostData'
|
26741 | const templateInfo = this.host.getTemplateAstAtPosition(fileName, position);
|
26742 | if (templateInfo) {
|
26743 | return getDefinitionAndBoundSpan(templateInfo, position);
|
26744 | }
|
26745 | // Attempt to get Angular-specific definitions in a TypeScript file, like templates defined
|
26746 | // in a `templateUrl` property.
|
26747 | if (fileName.endsWith('.ts')) {
|
26748 | const sf = this.host.getSourceFile(fileName);
|
26749 | if (sf) {
|
26750 | return getTsDefinitionAndBoundSpan(sf, position, this.host.tsLsHost);
|
26751 | }
|
26752 | }
|
26753 | }
|
26754 | getQuickInfoAtPosition(fileName, position) {
|
26755 | const analyzedModules = this.host.getAnalyzedModules(); // same role as 'synchronizeHostData'
|
26756 | const templateInfo = this.host.getTemplateAstAtPosition(fileName, position);
|
26757 | if (templateInfo) {
|
26758 | return getTemplateHover(templateInfo, position, analyzedModules);
|
26759 | }
|
26760 | // Attempt to get Angular-specific hover information in a TypeScript file, the NgModule a
|
26761 | // directive belongs to.
|
26762 | const declarations = this.host.getDeclarations(fileName);
|
26763 | return getTsHover(position, declarations, analyzedModules);
|
26764 | }
|
26765 | getReferencesAtPosition(fileName, position) {
|
26766 | const defAndSpan = this.getDefinitionAndBoundSpan(fileName, position);
|
26767 | if (!(defAndSpan === null || defAndSpan === void 0 ? void 0 : defAndSpan.definitions)) {
|
26768 | return;
|
26769 | }
|
26770 | const { definitions } = defAndSpan;
|
26771 | const tsDef = definitions.find(def => def.fileName.endsWith('.ts'));
|
26772 | if (!tsDef) {
|
26773 | return;
|
26774 | }
|
26775 | return this.host.tsLS.getReferencesAtPosition(tsDef.fileName, tsDef.textSpan.start);
|
26776 | }
|
26777 | }
|
26778 |
|
26779 | /**
|
26780 | * @license
|
26781 | * Copyright Google LLC All Rights Reserved.
|
26782 | *
|
26783 | * Use of this source code is governed by an MIT-style license that can be
|
26784 | * found in the LICENSE file at https://angular.io/license
|
26785 | */
|
26786 | function getClosureSafeProperty(objWithPropertyToExtract) {
|
26787 | for (let key in objWithPropertyToExtract) {
|
26788 | if (objWithPropertyToExtract[key] === getClosureSafeProperty) {
|
26789 | return key;
|
26790 | }
|
26791 | }
|
26792 | throw Error('Could not find renamed property on target object.');
|
26793 | }
|
26794 |
|
26795 | /**
|
26796 | * @license
|
26797 | * Copyright Google LLC All Rights Reserved.
|
26798 | *
|
26799 | * Use of this source code is governed by an MIT-style license that can be
|
26800 | * found in the LICENSE file at https://angular.io/license
|
26801 | */
|
26802 | function stringify$1(token) {
|
26803 | if (typeof token === 'string') {
|
26804 | return token;
|
26805 | }
|
26806 | if (Array.isArray(token)) {
|
26807 | return '[' + token.map(stringify$1).join(', ') + ']';
|
26808 | }
|
26809 | if (token == null) {
|
26810 | return '' + token;
|
26811 | }
|
26812 | if (token.overriddenName) {
|
26813 | return `${token.overriddenName}`;
|
26814 | }
|
26815 | if (token.name) {
|
26816 | return `${token.name}`;
|
26817 | }
|
26818 | const res = token.toString();
|
26819 | if (res == null) {
|
26820 | return '' + res;
|
26821 | }
|
26822 | const newLineIndex = res.indexOf('\n');
|
26823 | return newLineIndex === -1 ? res : res.substring(0, newLineIndex);
|
26824 | }
|
26825 | /**
|
26826 | * Concatenates two strings with separator, allocating new strings only when necessary.
|
26827 | *
|
26828 | * @param before before string.
|
26829 | * @param separator separator string.
|
26830 | * @param after after string.
|
26831 | * @returns concatenated string.
|
26832 | */
|
26833 | function concatStringsWithSpace(before, after) {
|
26834 | return (before == null || before === '') ?
|
26835 | (after === null ? '' : after) :
|
26836 | ((after == null || after === '') ? before : before + ' ' + after);
|
26837 | }
|
26838 |
|
26839 | /**
|
26840 | * @license
|
26841 | * Copyright Google LLC All Rights Reserved.
|
26842 | *
|
26843 | * Use of this source code is governed by an MIT-style license that can be
|
26844 | * found in the LICENSE file at https://angular.io/license
|
26845 | */
|
26846 | const __forward_ref__ = getClosureSafeProperty({ __forward_ref__: getClosureSafeProperty });
|
26847 | /**
|
26848 | * Allows to refer to references which are not yet defined.
|
26849 | *
|
26850 | * For instance, `forwardRef` is used when the `token` which we need to refer to for the purposes of
|
26851 | * DI is declared, but not yet defined. It is also used when the `token` which we use when creating
|
26852 | * a query is not yet defined.
|
26853 | *
|
26854 | * @usageNotes
|
26855 | * ### Example
|
26856 | * {@example core/di/ts/forward_ref/forward_ref_spec.ts region='forward_ref'}
|
26857 | * @publicApi
|
26858 | */
|
26859 | function forwardRef(forwardRefFn) {
|
26860 | forwardRefFn.__forward_ref__ = forwardRef;
|
26861 | forwardRefFn.toString = function () {
|
26862 | return stringify$1(this());
|
26863 | };
|
26864 | return forwardRefFn;
|
26865 | }
|
26866 | /**
|
26867 | * Lazily retrieves the reference value from a forwardRef.
|
26868 | *
|
26869 | * Acts as the identity function when given a non-forward-ref value.
|
26870 | *
|
26871 | * @usageNotes
|
26872 | * ### Example
|
26873 | *
|
26874 | * {@example core/di/ts/forward_ref/forward_ref_spec.ts region='resolve_forward_ref'}
|
26875 | *
|
26876 | * @see `forwardRef`
|
26877 | * @publicApi
|
26878 | */
|
26879 | function resolveForwardRef$1(type) {
|
26880 | return isForwardRef(type) ? type() : type;
|
26881 | }
|
26882 | /** Checks whether a function is wrapped by a `forwardRef`. */
|
26883 | function isForwardRef(fn) {
|
26884 | return typeof fn === 'function' && fn.hasOwnProperty(__forward_ref__) &&
|
26885 | fn.__forward_ref__ === forwardRef;
|
26886 | }
|
26887 |
|
26888 | /**
|
26889 | * @license
|
26890 | * Copyright Google LLC All Rights Reserved.
|
26891 | *
|
26892 | * Use of this source code is governed by an MIT-style license that can be
|
26893 | * found in the LICENSE file at https://angular.io/license
|
26894 | */
|
26895 | function assertNumber(actual, msg) {
|
26896 | if (!(typeof actual === 'number')) {
|
26897 | throwError(msg, typeof actual, 'number', '===');
|
26898 | }
|
26899 | }
|
26900 | function assertString(actual, msg) {
|
26901 | if (!(typeof actual === 'string')) {
|
26902 | throwError(msg, actual === null ? 'null' : typeof actual, 'string', '===');
|
26903 | }
|
26904 | }
|
26905 | function assertFunction(actual, msg) {
|
26906 | if (!(typeof actual === 'function')) {
|
26907 | throwError(msg, actual === null ? 'null' : typeof actual, 'function', '===');
|
26908 | }
|
26909 | }
|
26910 | function assertEqual(actual, expected, msg) {
|
26911 | if (!(actual == expected)) {
|
26912 | throwError(msg, actual, expected, '==');
|
26913 | }
|
26914 | }
|
26915 | function assertNotEqual(actual, expected, msg) {
|
26916 | if (!(actual != expected)) {
|
26917 | throwError(msg, actual, expected, '!=');
|
26918 | }
|
26919 | }
|
26920 | function assertSame(actual, expected, msg) {
|
26921 | if (!(actual === expected)) {
|
26922 | throwError(msg, actual, expected, '===');
|
26923 | }
|
26924 | }
|
26925 | function assertNotSame(actual, expected, msg) {
|
26926 | if (!(actual !== expected)) {
|
26927 | throwError(msg, actual, expected, '!==');
|
26928 | }
|
26929 | }
|
26930 | function assertLessThan(actual, expected, msg) {
|
26931 | if (!(actual < expected)) {
|
26932 | throwError(msg, actual, expected, '<');
|
26933 | }
|
26934 | }
|
26935 | function assertGreaterThan(actual, expected, msg) {
|
26936 | if (!(actual > expected)) {
|
26937 | throwError(msg, actual, expected, '>');
|
26938 | }
|
26939 | }
|
26940 | function assertGreaterThanOrEqual(actual, expected, msg) {
|
26941 | if (!(actual >= expected)) {
|
26942 | throwError(msg, actual, expected, '>=');
|
26943 | }
|
26944 | }
|
26945 | function assertDefined(actual, msg) {
|
26946 | if (actual == null) {
|
26947 | throwError(msg, actual, null, '!=');
|
26948 | }
|
26949 | }
|
26950 | function throwError(msg, actual, expected, comparison) {
|
26951 | throw new Error(`ASSERTION ERROR: ${msg}` +
|
26952 | (comparison == null ? '' : ` [Expected=> ${expected} ${comparison} ${actual} <=Actual]`));
|
26953 | }
|
26954 | function assertDomNode(node) {
|
26955 | // If we're in a worker, `Node` will not be defined.
|
26956 | if (!(typeof Node !== 'undefined' && node instanceof Node) &&
|
26957 | !(typeof node === 'object' && node != null &&
|
26958 | node.constructor.name === 'WebWorkerRenderNode')) {
|
26959 | throwError(`The provided value must be an instance of a DOM Node but got ${stringify$1(node)}`);
|
26960 | }
|
26961 | }
|
26962 | function assertIndexInRange(arr, index) {
|
26963 | assertDefined(arr, 'Array must be defined.');
|
26964 | const maxLen = arr.length;
|
26965 | if (index < 0 || index >= maxLen) {
|
26966 | throwError(`Index expected to be less than ${maxLen} but got ${index}`);
|
26967 | }
|
26968 | }
|
26969 |
|
26970 | /**
|
26971 | * @license
|
26972 | * Copyright Google LLC All Rights Reserved.
|
26973 | *
|
26974 | * Use of this source code is governed by an MIT-style license that can be
|
26975 | * found in the LICENSE file at https://angular.io/license
|
26976 | */
|
26977 | /**
|
26978 | * Construct an `InjectableDef` which defines how a token will be constructed by the DI system, and
|
26979 | * in which injectors (if any) it will be available.
|
26980 | *
|
26981 | * This should be assigned to a static `ɵprov` field on a type, which will then be an
|
26982 | * `InjectableType`.
|
26983 | *
|
26984 | * Options:
|
26985 | * * `providedIn` determines which injectors will include the injectable, by either associating it
|
26986 | * with an `@NgModule` or other `InjectorType`, or by specifying that this injectable should be
|
26987 | * provided in the `'root'` injector, which will be the application-level injector in most apps.
|
26988 | * * `factory` gives the zero argument function which will create an instance of the injectable.
|
26989 | * The factory can call `inject` to access the `Injector` and request injection of dependencies.
|
26990 | *
|
26991 | * @codeGenApi
|
26992 | * @publicApi This instruction has been emitted by ViewEngine for some time and is deployed to npm.
|
26993 | */
|
26994 | function ɵɵdefineInjectable(opts) {
|
26995 | return {
|
26996 | token: opts.token,
|
26997 | providedIn: opts.providedIn || null,
|
26998 | factory: opts.factory,
|
26999 | value: undefined,
|
27000 | };
|
27001 | }
|
27002 | /**
|
27003 | * Construct an `InjectorDef` which configures an injector.
|
27004 | *
|
27005 | * This should be assigned to a static injector def (`ɵinj`) field on a type, which will then be an
|
27006 | * `InjectorType`.
|
27007 | *
|
27008 | * Options:
|
27009 | *
|
27010 | * * `factory`: an `InjectorType` is an instantiable type, so a zero argument `factory` function to
|
27011 | * create the type must be provided. If that factory function needs to inject arguments, it can
|
27012 | * use the `inject` function.
|
27013 | * * `providers`: an optional array of providers to add to the injector. Each provider must
|
27014 | * either have a factory or point to a type which has a `ɵprov` static property (the
|
27015 | * type must be an `InjectableType`).
|
27016 | * * `imports`: an optional array of imports of other `InjectorType`s or `InjectorTypeWithModule`s
|
27017 | * whose providers will also be added to the injector. Locally provided types will override
|
27018 | * providers from imports.
|
27019 | *
|
27020 | * @codeGenApi
|
27021 | */
|
27022 | function ɵɵdefineInjector(options) {
|
27023 | return {
|
27024 | factory: options.factory,
|
27025 | providers: options.providers || [],
|
27026 | imports: options.imports || [],
|
27027 | };
|
27028 | }
|
27029 | /**
|
27030 | * Read the injectable def (`ɵprov`) for `type` in a way which is immune to accidentally reading
|
27031 | * inherited value.
|
27032 | *
|
27033 | * @param type A type which may have its own (non-inherited) `ɵprov`.
|
27034 | */
|
27035 | function getInjectableDef(type) {
|
27036 | return getOwnDefinition(type, NG_PROV_DEF) || getOwnDefinition(type, NG_INJECTABLE_DEF);
|
27037 | }
|
27038 | /**
|
27039 | * Return definition only if it is defined directly on `type` and is not inherited from a base
|
27040 | * class of `type`.
|
27041 | */
|
27042 | function getOwnDefinition(type, field) {
|
27043 | return type.hasOwnProperty(field) ? type[field] : null;
|
27044 | }
|
27045 | const NG_PROV_DEF = getClosureSafeProperty({ ɵprov: getClosureSafeProperty });
|
27046 | const NG_INJ_DEF = getClosureSafeProperty({ ɵinj: getClosureSafeProperty });
|
27047 | // We need to keep these around so we can read off old defs if new defs are unavailable
|
27048 | const NG_INJECTABLE_DEF = getClosureSafeProperty({ ngInjectableDef: getClosureSafeProperty });
|
27049 | const NG_INJECTOR_DEF = getClosureSafeProperty({ ngInjectorDef: getClosureSafeProperty });
|
27050 |
|
27051 | /**
|
27052 | * @license
|
27053 | * Copyright Google LLC All Rights Reserved.
|
27054 | *
|
27055 | * Use of this source code is governed by an MIT-style license that can be
|
27056 | * found in the LICENSE file at https://angular.io/license
|
27057 | */
|
27058 | /**
|
27059 | * Injection flags for DI.
|
27060 | *
|
27061 | * @publicApi
|
27062 | */
|
27063 | var InjectFlags;
|
27064 | (function (InjectFlags) {
|
27065 | // TODO(alxhub): make this 'const' (and remove `InternalInjectFlags` enum) when ngc no longer
|
27066 | // writes exports of it into ngfactory files.
|
27067 | /** Check self and check parent injector if needed */
|
27068 | InjectFlags[InjectFlags["Default"] = 0] = "Default";
|
27069 | /**
|
27070 | * Specifies that an injector should retrieve a dependency from any injector until reaching the
|
27071 | * host element of the current component. (Only used with Element Injector)
|
27072 | */
|
27073 | InjectFlags[InjectFlags["Host"] = 1] = "Host";
|
27074 | /** Don't ascend to ancestors of the node requesting injection. */
|
27075 | InjectFlags[InjectFlags["Self"] = 2] = "Self";
|
27076 | /** Skip the node that is requesting injection. */
|
27077 | InjectFlags[InjectFlags["SkipSelf"] = 4] = "SkipSelf";
|
27078 | /** Inject `defaultValue` instead if token not found. */
|
27079 | InjectFlags[InjectFlags["Optional"] = 8] = "Optional";
|
27080 | })(InjectFlags || (InjectFlags = {}));
|
27081 |
|
27082 | /**
|
27083 | * @license
|
27084 | * Copyright Google LLC All Rights Reserved.
|
27085 | *
|
27086 | * Use of this source code is governed by an MIT-style license that can be
|
27087 | * found in the LICENSE file at https://angular.io/license
|
27088 | */
|
27089 | /**
|
27090 | * Current implementation of inject.
|
27091 | *
|
27092 | * By default, it is `injectInjectorOnly`, which makes it `Injector`-only aware. It can be changed
|
27093 | * to `directiveInject`, which brings in the `NodeInjector` system of ivy. It is designed this
|
27094 | * way for two reasons:
|
27095 | * 1. `Injector` should not depend on ivy logic.
|
27096 | * 2. To maintain tree shake-ability we don't want to bring in unnecessary code.
|
27097 | */
|
27098 | let _injectImplementation;
|
27099 | function getInjectImplementation() {
|
27100 | return _injectImplementation;
|
27101 | }
|
27102 | /**
|
27103 | * Sets the current inject implementation.
|
27104 | */
|
27105 | function setInjectImplementation(impl) {
|
27106 | const previous = _injectImplementation;
|
27107 | _injectImplementation = impl;
|
27108 | return previous;
|
27109 | }
|
27110 | /**
|
27111 | * Injects `root` tokens in limp mode.
|
27112 | *
|
27113 | * If no injector exists, we can still inject tree-shakable providers which have `providedIn` set to
|
27114 | * `"root"`. This is known as the limp mode injection. In such case the value is stored in the
|
27115 | * `InjectableDef`.
|
27116 | */
|
27117 | function injectRootLimpMode(token, notFoundValue, flags) {
|
27118 | const injectableDef = getInjectableDef(token);
|
27119 | if (injectableDef && injectableDef.providedIn == 'root') {
|
27120 | return injectableDef.value === undefined ? injectableDef.value = injectableDef.factory() :
|
27121 | injectableDef.value;
|
27122 | }
|
27123 | if (flags & InjectFlags.Optional)
|
27124 | return null;
|
27125 | if (notFoundValue !== undefined)
|
27126 | return notFoundValue;
|
27127 | throw new Error(`Injector: NOT_FOUND [${stringify$1(token)}]`);
|
27128 | }
|
27129 |
|
27130 | /**
|
27131 | * @license
|
27132 | * Copyright Google LLC All Rights Reserved.
|
27133 | *
|
27134 | * Use of this source code is governed by an MIT-style license that can be
|
27135 | * found in the LICENSE file at https://angular.io/license
|
27136 | */
|
27137 | /**
|
27138 | * Convince closure compiler that the wrapped function has no side-effects.
|
27139 | *
|
27140 | * Closure compiler always assumes that `toString` has no side-effects. We use this quirk to
|
27141 | * allow us to execute a function but have closure compiler mark the call as no-side-effects.
|
27142 | * It is important that the return value for the `noSideEffects` function be assigned
|
27143 | * to something which is retained otherwise the call to `noSideEffects` will be removed by closure
|
27144 | * compiler.
|
27145 | */
|
27146 | function noSideEffects(fn) {
|
27147 | return { toString: fn }.toString();
|
27148 | }
|
27149 |
|
27150 | /**
|
27151 | * @license
|
27152 | * Copyright Google LLC All Rights Reserved.
|
27153 | *
|
27154 | * Use of this source code is governed by an MIT-style license that can be
|
27155 | * found in the LICENSE file at https://angular.io/license
|
27156 | */
|
27157 | /**
|
27158 | * The strategy that the default change detector uses to detect changes.
|
27159 | * When set, takes effect the next time change detection is triggered.
|
27160 | *
|
27161 | * @see {@link ChangeDetectorRef#usage-notes Change detection usage}
|
27162 | *
|
27163 | * @publicApi
|
27164 | */
|
27165 | var ChangeDetectionStrategy$1;
|
27166 | (function (ChangeDetectionStrategy) {
|
27167 | /**
|
27168 | * Use the `CheckOnce` strategy, meaning that automatic change detection is deactivated
|
27169 | * until reactivated by setting the strategy to `Default` (`CheckAlways`).
|
27170 | * Change detection can still be explicitly invoked.
|
27171 | * This strategy applies to all child directives and cannot be overridden.
|
27172 | */
|
27173 | ChangeDetectionStrategy[ChangeDetectionStrategy["OnPush"] = 0] = "OnPush";
|
27174 | /**
|
27175 | * Use the default `CheckAlways` strategy, in which change detection is automatic until
|
27176 | * explicitly deactivated.
|
27177 | */
|
27178 | ChangeDetectionStrategy[ChangeDetectionStrategy["Default"] = 1] = "Default";
|
27179 | })(ChangeDetectionStrategy$1 || (ChangeDetectionStrategy$1 = {}));
|
27180 | /**
|
27181 | * Defines the possible states of the default change detector.
|
27182 | * @see `ChangeDetectorRef`
|
27183 | */
|
27184 | var ChangeDetectorStatus;
|
27185 | (function (ChangeDetectorStatus) {
|
27186 | /**
|
27187 | * A state in which, after calling `detectChanges()`, the change detector
|
27188 | * state becomes `Checked`, and must be explicitly invoked or reactivated.
|
27189 | */
|
27190 | ChangeDetectorStatus[ChangeDetectorStatus["CheckOnce"] = 0] = "CheckOnce";
|
27191 | /**
|
27192 | * A state in which change detection is skipped until the change detector mode
|
27193 | * becomes `CheckOnce`.
|
27194 | */
|
27195 | ChangeDetectorStatus[ChangeDetectorStatus["Checked"] = 1] = "Checked";
|
27196 | /**
|
27197 | * A state in which change detection continues automatically until explicitly
|
27198 | * deactivated.
|
27199 | */
|
27200 | ChangeDetectorStatus[ChangeDetectorStatus["CheckAlways"] = 2] = "CheckAlways";
|
27201 | /**
|
27202 | * A state in which a change detector sub tree is not a part of the main tree and
|
27203 | * should be skipped.
|
27204 | */
|
27205 | ChangeDetectorStatus[ChangeDetectorStatus["Detached"] = 3] = "Detached";
|
27206 | /**
|
27207 | * Indicates that the change detector encountered an error checking a binding
|
27208 | * or calling a directive lifecycle method and is now in an inconsistent state. Change
|
27209 | * detectors in this state do not detect changes.
|
27210 | */
|
27211 | ChangeDetectorStatus[ChangeDetectorStatus["Errored"] = 4] = "Errored";
|
27212 | /**
|
27213 | * Indicates that the change detector has been destroyed.
|
27214 | */
|
27215 | ChangeDetectorStatus[ChangeDetectorStatus["Destroyed"] = 5] = "Destroyed";
|
27216 | })(ChangeDetectorStatus || (ChangeDetectorStatus = {}));
|
27217 |
|
27218 | /**
|
27219 | * @license
|
27220 | * Copyright Google LLC All Rights Reserved.
|
27221 | *
|
27222 | * Use of this source code is governed by an MIT-style license that can be
|
27223 | * found in the LICENSE file at https://angular.io/license
|
27224 | */
|
27225 | /**
|
27226 | * Defines template and style encapsulation options available for Component's {@link Component}.
|
27227 | *
|
27228 | * See {@link Component#encapsulation encapsulation}.
|
27229 | *
|
27230 | * @usageNotes
|
27231 | * ### Example
|
27232 | *
|
27233 | * {@example core/ts/metadata/encapsulation.ts region='longform'}
|
27234 | *
|
27235 | * @publicApi
|
27236 | */
|
27237 | var ViewEncapsulation$1;
|
27238 | (function (ViewEncapsulation) {
|
27239 | /**
|
27240 | * Emulate `Native` scoping of styles by adding an attribute containing surrogate id to the Host
|
27241 | * Element and pre-processing the style rules provided via {@link Component#styles styles} or
|
27242 | * {@link Component#styleUrls styleUrls}, and adding the new Host Element attribute to all
|
27243 | * selectors.
|
27244 | *
|
27245 | * This is the default option.
|
27246 | */
|
27247 | ViewEncapsulation[ViewEncapsulation["Emulated"] = 0] = "Emulated";
|
27248 | // Historically the 1 value was for `Native` encapsulation which has been removed as of v11.
|
27249 | /**
|
27250 | * Don't provide any template or style encapsulation.
|
27251 | */
|
27252 | ViewEncapsulation[ViewEncapsulation["None"] = 2] = "None";
|
27253 | /**
|
27254 | * Use Shadow DOM to encapsulate styles.
|
27255 | *
|
27256 | * For the DOM this means using modern [Shadow
|
27257 | * DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM) and
|
27258 | * creating a ShadowRoot for Component's Host Element.
|
27259 | */
|
27260 | ViewEncapsulation[ViewEncapsulation["ShadowDom"] = 3] = "ShadowDom";
|
27261 | })(ViewEncapsulation$1 || (ViewEncapsulation$1 = {}));
|
27262 |
|
27263 | /**
|
27264 | * @license
|
27265 | * Copyright Google LLC All Rights Reserved.
|
27266 | *
|
27267 | * Use of this source code is governed by an MIT-style license that can be
|
27268 | * found in the LICENSE file at https://angular.io/license
|
27269 | */
|
27270 | const __globalThis = typeof globalThis !== 'undefined' && globalThis;
|
27271 | const __window$1 = typeof window !== 'undefined' && window;
|
27272 | const __self$1 = typeof self !== 'undefined' && typeof WorkerGlobalScope !== 'undefined' &&
|
27273 | self instanceof WorkerGlobalScope && self;
|
27274 | const __global$1 = typeof global !== 'undefined' && global;
|
27275 | // Always use __globalThis if available, which is the spec-defined global variable across all
|
27276 | // environments, then fallback to __global first, because in Node tests both __global and
|
27277 | // __window may be defined and _global should be __global in that case.
|
27278 | const _global$1 = __globalThis || __global$1 || __window$1 || __self$1;
|
27279 |
|
27280 | /**
|
27281 | * @license
|
27282 | * Copyright Google LLC All Rights Reserved.
|
27283 | *
|
27284 | * Use of this source code is governed by an MIT-style license that can be
|
27285 | * found in the LICENSE file at https://angular.io/license
|
27286 | */
|
27287 | function ngDevModeResetPerfCounters() {
|
27288 | const locationString = typeof location !== 'undefined' ? location.toString() : '';
|
27289 | const newCounters = {
|
27290 | namedConstructors: locationString.indexOf('ngDevMode=namedConstructors') != -1,
|
27291 | firstCreatePass: 0,
|
27292 | tNode: 0,
|
27293 | tView: 0,
|
27294 | rendererCreateTextNode: 0,
|
27295 | rendererSetText: 0,
|
27296 | rendererCreateElement: 0,
|
27297 | rendererAddEventListener: 0,
|
27298 | rendererSetAttribute: 0,
|
27299 | rendererRemoveAttribute: 0,
|
27300 | rendererSetProperty: 0,
|
27301 | rendererSetClassName: 0,
|
27302 | rendererAddClass: 0,
|
27303 | rendererRemoveClass: 0,
|
27304 | rendererSetStyle: 0,
|
27305 | rendererRemoveStyle: 0,
|
27306 | rendererDestroy: 0,
|
27307 | rendererDestroyNode: 0,
|
27308 | rendererMoveNode: 0,
|
27309 | rendererRemoveNode: 0,
|
27310 | rendererAppendChild: 0,
|
27311 | rendererInsertBefore: 0,
|
27312 | rendererCreateComment: 0,
|
27313 | };
|
27314 | // Make sure to refer to ngDevMode as ['ngDevMode'] for closure.
|
27315 | const allowNgDevModeTrue = locationString.indexOf('ngDevMode=false') === -1;
|
27316 | _global$1['ngDevMode'] = allowNgDevModeTrue && newCounters;
|
27317 | return newCounters;
|
27318 | }
|
27319 | /**
|
27320 | * This function checks to see if the `ngDevMode` has been set. If yes,
|
27321 | * then we honor it, otherwise we default to dev mode with additional checks.
|
27322 | *
|
27323 | * The idea is that unless we are doing production build where we explicitly
|
27324 | * set `ngDevMode == false` we should be helping the developer by providing
|
27325 | * as much early warning and errors as possible.
|
27326 | *
|
27327 | * `ɵɵdefineComponent` is guaranteed to have been called before any component template functions
|
27328 | * (and thus Ivy instructions), so a single initialization there is sufficient to ensure ngDevMode
|
27329 | * is defined for the entire instruction set.
|
27330 | *
|
27331 | * When checking `ngDevMode` on toplevel, always init it before referencing it
|
27332 | * (e.g. `((typeof ngDevMode === 'undefined' || ngDevMode) && initNgDevMode())`), otherwise you can
|
27333 | * get a `ReferenceError` like in https://github.com/angular/angular/issues/31595.
|
27334 | *
|
27335 | * Details on possible values for `ngDevMode` can be found on its docstring.
|
27336 | *
|
27337 | * NOTE:
|
27338 | * - changes to the `ngDevMode` name must be synced with `compiler-cli/src/tooling.ts`.
|
27339 | */
|
27340 | function initNgDevMode() {
|
27341 | // The below checks are to ensure that calling `initNgDevMode` multiple times does not
|
27342 | // reset the counters.
|
27343 | // If the `ngDevMode` is not an object, then it means we have not created the perf counters
|
27344 | // yet.
|
27345 | if (typeof ngDevMode === 'undefined' || ngDevMode) {
|
27346 | if (typeof ngDevMode !== 'object') {
|
27347 | ngDevModeResetPerfCounters();
|
27348 | }
|
27349 | return typeof ngDevMode !== 'undefined' && !!ngDevMode;
|
27350 | }
|
27351 | return false;
|
27352 | }
|
27353 |
|
27354 | /**
|
27355 | * @license
|
27356 | * Copyright Google LLC All Rights Reserved.
|
27357 | *
|
27358 | * Use of this source code is governed by an MIT-style license that can be
|
27359 | * found in the LICENSE file at https://angular.io/license
|
27360 | */
|
27361 | /**
|
27362 | * This file contains reuseable "empty" symbols that can be used as default return values
|
27363 | * in different parts of the rendering code. Because the same symbols are returned, this
|
27364 | * allows for identity checks against these values to be consistently used by the framework
|
27365 | * code.
|
27366 | */
|
27367 | const EMPTY_OBJ = {};
|
27368 | const EMPTY_ARRAY = [];
|
27369 | // freezing the values prevents any code from accidentally inserting new values in
|
27370 | if ((typeof ngDevMode === 'undefined' || ngDevMode) && initNgDevMode()) {
|
27371 | // These property accesses can be ignored because ngDevMode will be set to false
|
27372 | // when optimizing code and the whole if statement will be dropped.
|
27373 | // tslint:disable-next-line:no-toplevel-property-access
|
27374 | Object.freeze(EMPTY_OBJ);
|
27375 | // tslint:disable-next-line:no-toplevel-property-access
|
27376 | Object.freeze(EMPTY_ARRAY);
|
27377 | }
|
27378 |
|
27379 | /**
|
27380 | * @license
|
27381 | * Copyright Google LLC All Rights Reserved.
|
27382 | *
|
27383 | * Use of this source code is governed by an MIT-style license that can be
|
27384 | * found in the LICENSE file at https://angular.io/license
|
27385 | */
|
27386 | const NG_COMP_DEF = getClosureSafeProperty({ ɵcmp: getClosureSafeProperty });
|
27387 | const NG_DIR_DEF = getClosureSafeProperty({ ɵdir: getClosureSafeProperty });
|
27388 | const NG_PIPE_DEF = getClosureSafeProperty({ ɵpipe: getClosureSafeProperty });
|
27389 | const NG_MOD_DEF = getClosureSafeProperty({ ɵmod: getClosureSafeProperty });
|
27390 | const NG_LOC_ID_DEF = getClosureSafeProperty({ ɵloc: getClosureSafeProperty });
|
27391 | const NG_FACTORY_DEF = getClosureSafeProperty({ ɵfac: getClosureSafeProperty });
|
27392 | /**
|
27393 | * If a directive is diPublic, bloomAdd sets a property on the type with this constant as
|
27394 | * the key and the directive's unique ID as the value. This allows us to map directives to their
|
27395 | * bloom filter bit for DI.
|
27396 | */
|
27397 | // TODO(misko): This is wrong. The NG_ELEMENT_ID should never be minified.
|
27398 | const NG_ELEMENT_ID = getClosureSafeProperty({ __NG_ELEMENT_ID__: getClosureSafeProperty });
|
27399 |
|
27400 | /**
|
27401 | * @license
|
27402 | * Copyright Google LLC All Rights Reserved.
|
27403 | *
|
27404 | * Use of this source code is governed by an MIT-style license that can be
|
27405 | * found in the LICENSE file at https://angular.io/license
|
27406 | */
|
27407 | /**
|
27408 | * The following getter methods retrieve the definition from the type. Currently the retrieval
|
27409 | * honors inheritance, but in the future we may change the rule to require that definitions are
|
27410 | * explicit. This would require some sort of migration strategy.
|
27411 | */
|
27412 | function getComponentDef(type) {
|
27413 | return type[NG_COMP_DEF] || null;
|
27414 | }
|
27415 |
|
27416 | /**
|
27417 | * @license
|
27418 | * Copyright Google LLC All Rights Reserved.
|
27419 | *
|
27420 | * Use of this source code is governed by an MIT-style license that can be
|
27421 | * found in the LICENSE file at https://angular.io/license
|
27422 | */
|
27423 | // Below are constants for LView indices to help us look up LView members
|
27424 | // without having to remember the specific indices.
|
27425 | // Uglify will inline these when minifying so there shouldn't be a cost.
|
27426 | const HOST = 0;
|
27427 | const TVIEW = 1;
|
27428 | const FLAGS = 2;
|
27429 | const PARENT = 3;
|
27430 | const NEXT = 4;
|
27431 | const TRANSPLANTED_VIEWS_TO_REFRESH = 5;
|
27432 | const T_HOST = 6;
|
27433 | const CLEANUP = 7;
|
27434 | const CONTEXT = 8;
|
27435 | const INJECTOR = 9;
|
27436 | const RENDERER_FACTORY = 10;
|
27437 | const RENDERER = 11;
|
27438 | const SANITIZER = 12;
|
27439 | const CHILD_HEAD = 13;
|
27440 | const CHILD_TAIL = 14;
|
27441 | // FIXME(misko): Investigate if the three declarations aren't all same thing.
|
27442 | const DECLARATION_VIEW = 15;
|
27443 | const DECLARATION_COMPONENT_VIEW = 16;
|
27444 | const DECLARATION_LCONTAINER = 17;
|
27445 | const PREORDER_HOOK_FLAGS = 18;
|
27446 | const QUERIES = 19;
|
27447 | /**
|
27448 | * Size of LView's header. Necessary to adjust for it when setting slots.
|
27449 | *
|
27450 | * IMPORTANT: `HEADER_OFFSET` should only be referred to the in the `ɵɵ*` instructions to translate
|
27451 | * instruction index into `LView` index. All other indexes should be in the `LView` index space and
|
27452 | * there should be no need to refer to `HEADER_OFFSET` anywhere else.
|
27453 | */
|
27454 | const HEADER_OFFSET = 20;
|
27455 | /**
|
27456 | * Converts `TViewType` into human readable text.
|
27457 | * Make sure this matches with `TViewType`
|
27458 | */
|
27459 | const TViewTypeAsString = [
|
27460 | 'Root',
|
27461 | 'Component',
|
27462 | 'Embedded',
|
27463 | ];
|
27464 |
|
27465 | /**
|
27466 | * @license
|
27467 | * Copyright Google LLC All Rights Reserved.
|
27468 | *
|
27469 | * Use of this source code is governed by an MIT-style license that can be
|
27470 | * found in the LICENSE file at https://angular.io/license
|
27471 | */
|
27472 | /**
|
27473 | * Special location which allows easy identification of type. If we have an array which was
|
27474 | * retrieved from the `LView` and that array has `true` at `TYPE` location, we know it is
|
27475 | * `LContainer`.
|
27476 | */
|
27477 | const TYPE = 1;
|
27478 | /**
|
27479 | * Below are constants for LContainer indices to help us look up LContainer members
|
27480 | * without having to remember the specific indices.
|
27481 | * Uglify will inline these when minifying so there shouldn't be a cost.
|
27482 | */
|
27483 | /**
|
27484 | * Flag to signify that this `LContainer` may have transplanted views which need to be change
|
27485 | * detected. (see: `LView[DECLARATION_COMPONENT_VIEW])`.
|
27486 | *
|
27487 | * This flag, once set, is never unset for the `LContainer`. This means that when unset we can skip
|
27488 | * a lot of work in `refreshEmbeddedViews`. But when set we still need to verify
|
27489 | * that the `MOVED_VIEWS` are transplanted and on-push.
|
27490 | */
|
27491 | const HAS_TRANSPLANTED_VIEWS = 2;
|
27492 | // PARENT, NEXT, TRANSPLANTED_VIEWS_TO_REFRESH are indices 3, 4, and 5
|
27493 | // As we already have these constants in LView, we don't need to re-create them.
|
27494 | // T_HOST is index 6
|
27495 | // We already have this constants in LView, we don't need to re-create it.
|
27496 | const NATIVE = 7;
|
27497 | const VIEW_REFS = 8;
|
27498 | const MOVED_VIEWS = 9;
|
27499 | /**
|
27500 | * Size of LContainer's header. Represents the index after which all views in the
|
27501 | * container will be inserted. We need to keep a record of current views so we know
|
27502 | * which views are already in the DOM (and don't need to be re-added) and so we can
|
27503 | * remove views from the DOM when they are no longer required.
|
27504 | */
|
27505 | const CONTAINER_HEADER_OFFSET = 10;
|
27506 |
|
27507 | /**
|
27508 | * @license
|
27509 | * Copyright Google LLC All Rights Reserved.
|
27510 | *
|
27511 | * Use of this source code is governed by an MIT-style license that can be
|
27512 | * found in the LICENSE file at https://angular.io/license
|
27513 | */
|
27514 | /**
|
27515 | * True if `value` is `LView`.
|
27516 | * @param value wrapped value of `RNode`, `LView`, `LContainer`
|
27517 | */
|
27518 | function isLView(value) {
|
27519 | return Array.isArray(value) && typeof value[TYPE] === 'object';
|
27520 | }
|
27521 | /**
|
27522 | * True if `value` is `LContainer`.
|
27523 | * @param value wrapped value of `RNode`, `LView`, `LContainer`
|
27524 | */
|
27525 | function isLContainer(value) {
|
27526 | return Array.isArray(value) && value[TYPE] === true;
|
27527 | }
|
27528 | function isComponentHost(tNode) {
|
27529 | return (tNode.flags & 2 /* isComponentHost */) === 2 /* isComponentHost */;
|
27530 | }
|
27531 | function isComponentDef(def) {
|
27532 | return def.template !== null;
|
27533 | }
|
27534 | function isRootView(target) {
|
27535 | return (target[FLAGS] & 512 /* IsRoot */) !== 0;
|
27536 | }
|
27537 |
|
27538 | /**
|
27539 | * @license
|
27540 | * Copyright Google LLC All Rights Reserved.
|
27541 | *
|
27542 | * Use of this source code is governed by an MIT-style license that can be
|
27543 | * found in the LICENSE file at https://angular.io/license
|
27544 | */
|
27545 | // [Assert functions do not constraint type when they are guarded by a truthy
|
27546 | // expression.](https://github.com/microsoft/TypeScript/issues/37295)
|
27547 | function assertTNodeForLView(tNode, lView) {
|
27548 | assertTNodeForTView(tNode, lView[TVIEW]);
|
27549 | }
|
27550 | function assertTNodeForTView(tNode, tView) {
|
27551 | assertTNode(tNode);
|
27552 | tNode.hasOwnProperty('tView_') &&
|
27553 | assertEqual(tNode.tView_, tView, 'This TNode does not belong to this TView.');
|
27554 | }
|
27555 | function assertTNode(tNode) {
|
27556 | assertDefined(tNode, 'TNode must be defined');
|
27557 | if (!(tNode && typeof tNode === 'object' && tNode.hasOwnProperty('directiveStylingLast'))) {
|
27558 | throwError('Not of type TNode, got: ' + tNode);
|
27559 | }
|
27560 | }
|
27561 | function assertComponentType(actual, msg = 'Type passed in is not ComponentType, it does not have \'ɵcmp\' property.') {
|
27562 | if (!getComponentDef(actual)) {
|
27563 | throwError(msg);
|
27564 | }
|
27565 | }
|
27566 | function assertLContainer(value) {
|
27567 | assertDefined(value, 'LContainer must be defined');
|
27568 | assertEqual(isLContainer(value), true, 'Expecting LContainer');
|
27569 | }
|
27570 | function assertLViewOrUndefined(value) {
|
27571 | value && assertEqual(isLView(value), true, 'Expecting LView or undefined or null');
|
27572 | }
|
27573 | function assertLView(value) {
|
27574 | assertDefined(value, 'LView must be defined');
|
27575 | assertEqual(isLView(value), true, 'Expecting LView');
|
27576 | }
|
27577 | function assertFirstCreatePass(tView, errMessage) {
|
27578 | assertEqual(tView.firstCreatePass, true, errMessage || 'Should only be called in first create pass.');
|
27579 | }
|
27580 | function assertFirstUpdatePass(tView, errMessage) {
|
27581 | assertEqual(tView.firstUpdatePass, true, errMessage || 'Should only be called in first update pass.');
|
27582 | }
|
27583 | /**
|
27584 | * This is a basic sanity check that an object is probably a directive def. DirectiveDef is
|
27585 | * an interface, so we can't do a direct instanceof check.
|
27586 | */
|
27587 | function assertDirectiveDef(obj) {
|
27588 | if (obj.type === undefined || obj.selectors == undefined || obj.inputs === undefined) {
|
27589 | throwError(`Expected a DirectiveDef/ComponentDef and this object does not seem to have the expected shape.`);
|
27590 | }
|
27591 | }
|
27592 | function assertIndexInDeclRange(lView, index) {
|
27593 | const tView = lView[1];
|
27594 | assertBetween(HEADER_OFFSET, tView.bindingStartIndex, index);
|
27595 | }
|
27596 | function assertIndexInExpandoRange(lView, index) {
|
27597 | const tView = lView[1];
|
27598 | assertBetween(tView.expandoStartIndex, lView.length, index);
|
27599 | }
|
27600 | function assertBetween(lower, upper, index) {
|
27601 | if (!(lower <= index && index < upper)) {
|
27602 | throwError(`Index out of range (expecting ${lower} <= ${index} < ${upper})`);
|
27603 | }
|
27604 | }
|
27605 | function assertProjectionSlots(lView, errMessage) {
|
27606 | assertDefined(lView[DECLARATION_COMPONENT_VIEW], 'Component views should exist.');
|
27607 | assertDefined(lView[DECLARATION_COMPONENT_VIEW][T_HOST].projection, errMessage ||
|
27608 | 'Components with projection nodes (<ng-content>) must have projection slots defined.');
|
27609 | }
|
27610 | function assertParentView(lView, errMessage) {
|
27611 | assertDefined(lView, errMessage || 'Component views should always have a parent view (component\'s host view)');
|
27612 | }
|
27613 | /**
|
27614 | * This is a basic sanity check that the `injectorIndex` seems to point to what looks like a
|
27615 | * NodeInjector data structure.
|
27616 | *
|
27617 | * @param lView `LView` which should be checked.
|
27618 | * @param injectorIndex index into the `LView` where the `NodeInjector` is expected.
|
27619 | */
|
27620 | function assertNodeInjector(lView, injectorIndex) {
|
27621 | assertIndexInExpandoRange(lView, injectorIndex);
|
27622 | assertIndexInExpandoRange(lView, injectorIndex + 8 /* PARENT */);
|
27623 | assertNumber(lView[injectorIndex + 0], 'injectorIndex should point to a bloom filter');
|
27624 | assertNumber(lView[injectorIndex + 1], 'injectorIndex should point to a bloom filter');
|
27625 | assertNumber(lView[injectorIndex + 2], 'injectorIndex should point to a bloom filter');
|
27626 | assertNumber(lView[injectorIndex + 3], 'injectorIndex should point to a bloom filter');
|
27627 | assertNumber(lView[injectorIndex + 4], 'injectorIndex should point to a bloom filter');
|
27628 | assertNumber(lView[injectorIndex + 5], 'injectorIndex should point to a bloom filter');
|
27629 | assertNumber(lView[injectorIndex + 6], 'injectorIndex should point to a bloom filter');
|
27630 | assertNumber(lView[injectorIndex + 7], 'injectorIndex should point to a bloom filter');
|
27631 | assertNumber(lView[injectorIndex + 8 /* PARENT */], 'injectorIndex should point to parent injector');
|
27632 | }
|
27633 |
|
27634 | /**
|
27635 | * @license
|
27636 | * Copyright Google LLC All Rights Reserved.
|
27637 | *
|
27638 | * Use of this source code is governed by an MIT-style license that can be
|
27639 | * found in the LICENSE file at https://angular.io/license
|
27640 | */
|
27641 | function getFactoryDef(type, throwNotFound) {
|
27642 | const hasFactoryDef = type.hasOwnProperty(NG_FACTORY_DEF);
|
27643 | if (!hasFactoryDef && throwNotFound === true && ngDevMode) {
|
27644 | throw new Error(`Type ${stringify$1(type)} does not have 'ɵfac' property.`);
|
27645 | }
|
27646 | return hasFactoryDef ? type[NG_FACTORY_DEF] : null;
|
27647 | }
|
27648 |
|
27649 | /**
|
27650 | * @license
|
27651 | * Copyright Google LLC All Rights Reserved.
|
27652 | *
|
27653 | * Use of this source code is governed by an MIT-style license that can be
|
27654 | * found in the LICENSE file at https://angular.io/license
|
27655 | */
|
27656 | // Base URL for the error details page.
|
27657 | // Keep this value in sync with a similar const in
|
27658 | // `packages/compiler-cli/src/ngtsc/diagnostics/src/error_code.ts`.
|
27659 | const ERROR_DETAILS_PAGE_BASE_URL = 'https://angular.io/errors';
|
27660 | class RuntimeError extends Error {
|
27661 | constructor(code, message) {
|
27662 | super(formatRuntimeError(code, message));
|
27663 | this.code = code;
|
27664 | }
|
27665 | }
|
27666 | // Contains a set of error messages that have details guides at angular.io.
|
27667 | // Full list of available error guides can be found at https://angular.io/errors
|
27668 | /* tslint:disable:no-toplevel-property-access */
|
27669 | const RUNTIME_ERRORS_WITH_GUIDES = new Set([
|
27670 | "100" /* EXPRESSION_CHANGED_AFTER_CHECKED */,
|
27671 | "200" /* CYCLIC_DI_DEPENDENCY */,
|
27672 | "201" /* PROVIDER_NOT_FOUND */,
|
27673 | "300" /* MULTIPLE_COMPONENTS_MATCH */,
|
27674 | "301" /* EXPORT_NOT_FOUND */,
|
27675 | "302" /* PIPE_NOT_FOUND */,
|
27676 | ]);
|
27677 | /* tslint:enable:no-toplevel-property-access */
|
27678 | /** Called to format a runtime error */
|
27679 | function formatRuntimeError(code, message) {
|
27680 | const fullCode = code ? `NG0${code}: ` : '';
|
27681 | let errorMessage = `${fullCode}${message}`;
|
27682 | // Some runtime errors are still thrown without `ngDevMode` (for example
|
27683 | // `throwProviderNotFoundError`), so we add `ngDevMode` check here to avoid pulling
|
27684 | // `RUNTIME_ERRORS_WITH_GUIDES` symbol into prod bundles.
|
27685 | // TODO: revisit all instances where `RuntimeError` is thrown and see if `ngDevMode` can be added
|
27686 | // there instead to tree-shake more devmode-only code (and eventually remove `ngDevMode` check
|
27687 | // from this code).
|
27688 | if (ngDevMode && RUNTIME_ERRORS_WITH_GUIDES.has(code)) {
|
27689 | errorMessage = `${errorMessage}. Find more at ${ERROR_DETAILS_PAGE_BASE_URL}/NG0${code}`;
|
27690 | }
|
27691 | return errorMessage;
|
27692 | }
|
27693 |
|
27694 | /**
|
27695 | * @license
|
27696 | * Copyright Google LLC All Rights Reserved.
|
27697 | *
|
27698 | * Use of this source code is governed by an MIT-style license that can be
|
27699 | * found in the LICENSE file at https://angular.io/license
|
27700 | */
|
27701 | /**
|
27702 | * Used for stringify render output in Ivy.
|
27703 | * Important! This function is very performance-sensitive and we should
|
27704 | * be extra careful not to introduce megamorphic reads in it.
|
27705 | * Check `core/test/render3/perf/render_stringify` for benchmarks and alternate implementations.
|
27706 | */
|
27707 | function renderStringify(value) {
|
27708 | if (typeof value === 'string')
|
27709 | return value;
|
27710 | if (value == null)
|
27711 | return '';
|
27712 | // Use `String` so that it invokes the `toString` method of the value. Note that this
|
27713 | // appears to be faster than calling `value.toString` (see `render_stringify` benchmark).
|
27714 | return String(value);
|
27715 | }
|
27716 | /**
|
27717 | * Used to stringify a value so that it can be displayed in an error message.
|
27718 | * Important! This function contains a megamorphic read and should only be
|
27719 | * used for error messages.
|
27720 | */
|
27721 | function stringifyForError(value) {
|
27722 | if (typeof value === 'function')
|
27723 | return value.name || value.toString();
|
27724 | if (typeof value === 'object' && value != null && typeof value.type === 'function') {
|
27725 | return value.type.name || value.type.toString();
|
27726 | }
|
27727 | return renderStringify(value);
|
27728 | }
|
27729 |
|
27730 | /** Called when directives inject each other (creating a circular dependency) */
|
27731 | function throwCyclicDependencyError(token, path) {
|
27732 | const depPath = path ? `. Dependency path: ${path.join(' > ')} > ${token}` : '';
|
27733 | throw new RuntimeError("200" /* CYCLIC_DI_DEPENDENCY */, `Circular dependency in DI detected for ${token}${depPath}`);
|
27734 | }
|
27735 | /** Throws an error when a token is not found in DI. */
|
27736 | function throwProviderNotFoundError(token, injectorName) {
|
27737 | const injectorDetails = injectorName ? ` in ${injectorName}` : '';
|
27738 | throw new RuntimeError("201" /* PROVIDER_NOT_FOUND */, `No provider for ${stringifyForError(token)} found${injectorDetails}`);
|
27739 | }
|
27740 |
|
27741 | /**
|
27742 | * @license
|
27743 | * Copyright Google LLC All Rights Reserved.
|
27744 | *
|
27745 | * Use of this source code is governed by an MIT-style license that can be
|
27746 | * found in the LICENSE file at https://angular.io/license
|
27747 | */
|
27748 | /**
|
27749 | * Represents a basic change from a previous to a new value for a single
|
27750 | * property on a directive instance. Passed as a value in a
|
27751 | * {@link SimpleChanges} object to the `ngOnChanges` hook.
|
27752 | *
|
27753 | * @see `OnChanges`
|
27754 | *
|
27755 | * @publicApi
|
27756 | */
|
27757 | class SimpleChange {
|
27758 | constructor(previousValue, currentValue, firstChange) {
|
27759 | this.previousValue = previousValue;
|
27760 | this.currentValue = currentValue;
|
27761 | this.firstChange = firstChange;
|
27762 | }
|
27763 | /**
|
27764 | * Check whether the new value is the first value assigned.
|
27765 | */
|
27766 | isFirstChange() {
|
27767 | return this.firstChange;
|
27768 | }
|
27769 | }
|
27770 |
|
27771 | /**
|
27772 | * @license
|
27773 | * Copyright Google LLC All Rights Reserved.
|
27774 | *
|
27775 | * Use of this source code is governed by an MIT-style license that can be
|
27776 | * found in the LICENSE file at https://angular.io/license
|
27777 | */
|
27778 | function NgOnChangesFeatureImpl(definition) {
|
27779 | if (definition.type.prototype.ngOnChanges) {
|
27780 | definition.setInput = ngOnChangesSetInput;
|
27781 | }
|
27782 | return rememberChangeHistoryAndInvokeOnChangesHook;
|
27783 | }
|
27784 | /**
|
27785 | * This is a synthetic lifecycle hook which gets inserted into `TView.preOrderHooks` to simulate
|
27786 | * `ngOnChanges`.
|
27787 | *
|
27788 | * The hook reads the `NgSimpleChangesStore` data from the component instance and if changes are
|
27789 | * found it invokes `ngOnChanges` on the component instance.
|
27790 | *
|
27791 | * @param this Component instance. Because this function gets inserted into `TView.preOrderHooks`,
|
27792 | * it is guaranteed to be called with component instance.
|
27793 | */
|
27794 | function rememberChangeHistoryAndInvokeOnChangesHook() {
|
27795 | const simpleChangesStore = getSimpleChangesStore(this);
|
27796 | const current = simpleChangesStore === null || simpleChangesStore === void 0 ? void 0 : simpleChangesStore.current;
|
27797 | if (current) {
|
27798 | const previous = simpleChangesStore.previous;
|
27799 | if (previous === EMPTY_OBJ) {
|
27800 | simpleChangesStore.previous = current;
|
27801 | }
|
27802 | else {
|
27803 | // New changes are copied to the previous store, so that we don't lose history for inputs
|
27804 | // which were not changed this time
|
27805 | for (let key in current) {
|
27806 | previous[key] = current[key];
|
27807 | }
|
27808 | }
|
27809 | simpleChangesStore.current = null;
|
27810 | this.ngOnChanges(current);
|
27811 | }
|
27812 | }
|
27813 | function ngOnChangesSetInput(instance, value, publicName, privateName) {
|
27814 | const simpleChangesStore = getSimpleChangesStore(instance) ||
|
27815 | setSimpleChangesStore(instance, { previous: EMPTY_OBJ, current: null });
|
27816 | const current = simpleChangesStore.current || (simpleChangesStore.current = {});
|
27817 | const previous = simpleChangesStore.previous;
|
27818 | const declaredName = this.declaredInputs[publicName];
|
27819 | const previousChange = previous[declaredName];
|
27820 | current[declaredName] = new SimpleChange(previousChange && previousChange.currentValue, value, previous === EMPTY_OBJ);
|
27821 | instance[privateName] = value;
|
27822 | }
|
27823 | const SIMPLE_CHANGES_STORE = '__ngSimpleChanges__';
|
27824 | function getSimpleChangesStore(instance) {
|
27825 | return instance[SIMPLE_CHANGES_STORE] || null;
|
27826 | }
|
27827 | function setSimpleChangesStore(instance, store) {
|
27828 | return instance[SIMPLE_CHANGES_STORE] = store;
|
27829 | }
|
27830 |
|
27831 | /**
|
27832 | * @license
|
27833 | * Copyright Google LLC All Rights Reserved.
|
27834 | *
|
27835 | * Use of this source code is governed by an MIT-style license that can be
|
27836 | * found in the LICENSE file at https://angular.io/license
|
27837 | */
|
27838 | const SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
|
27839 | const MATH_ML_NAMESPACE = 'http://www.w3.org/1998/MathML/';
|
27840 |
|
27841 | /**
|
27842 | * @license
|
27843 | * Copyright Google LLC All Rights Reserved.
|
27844 | *
|
27845 | * Use of this source code is governed by an MIT-style license that can be
|
27846 | * found in the LICENSE file at https://angular.io/license
|
27847 | */
|
27848 | /**
|
27849 | * This property will be monkey-patched on elements, components and directives
|
27850 | */
|
27851 | const MONKEY_PATCH_KEY_NAME = '__ngContext__';
|
27852 |
|
27853 | /**
|
27854 | * @license
|
27855 | * Copyright Google LLC All Rights Reserved.
|
27856 | *
|
27857 | * Use of this source code is governed by an MIT-style license that can be
|
27858 | * found in the LICENSE file at https://angular.io/license
|
27859 | */
|
27860 | /**
|
27861 | * Access the object that represents the `document` for this platform.
|
27862 | *
|
27863 | * Ivy calls this whenever it needs to access the `document` object.
|
27864 | * For example to create the renderer or to do sanitization.
|
27865 | */
|
27866 | function getDocument() {
|
27867 | if (typeof document !== 'undefined') {
|
27868 | return document;
|
27869 | }
|
27870 | // No "document" can be found. This should only happen if we are running ivy outside Angular and
|
27871 | // the current platform is not a browser. Since this is not a supported scenario at the moment
|
27872 | // this should not happen in Angular apps.
|
27873 | // Once we support running ivy outside of Angular we will need to publish `setDocument()` as a
|
27874 | // public API. Meanwhile we just return `undefined` and let the application fail.
|
27875 | return undefined;
|
27876 | }
|
27877 |
|
27878 | /**
|
27879 | * @license
|
27880 | * Copyright Google LLC All Rights Reserved.
|
27881 | *
|
27882 | * Use of this source code is governed by an MIT-style license that can be
|
27883 | * found in the LICENSE file at https://angular.io/license
|
27884 | */
|
27885 | // TODO: cleanup once the code is merged in angular/angular
|
27886 | var RendererStyleFlags3;
|
27887 | (function (RendererStyleFlags3) {
|
27888 | RendererStyleFlags3[RendererStyleFlags3["Important"] = 1] = "Important";
|
27889 | RendererStyleFlags3[RendererStyleFlags3["DashCase"] = 2] = "DashCase";
|
27890 | })(RendererStyleFlags3 || (RendererStyleFlags3 = {}));
|
27891 | /** Returns whether the `renderer` is a `ProceduralRenderer3` */
|
27892 | function isProceduralRenderer(renderer) {
|
27893 | return !!(renderer.listen);
|
27894 | }
|
27895 | const ɵ0 = (hostElement, rendererType) => {
|
27896 | return getDocument();
|
27897 | };
|
27898 | const domRendererFactory3 = {
|
27899 | createRenderer: ɵ0
|
27900 | };
|
27901 |
|
27902 | /**
|
27903 | * @license
|
27904 | * Copyright Google LLC All Rights Reserved.
|
27905 | *
|
27906 | * Use of this source code is governed by an MIT-style license that can be
|
27907 | * found in the LICENSE file at https://angular.io/license
|
27908 | */
|
27909 | /**
|
27910 | * For efficiency reasons we often put several different data types (`RNode`, `LView`, `LContainer`)
|
27911 | * in same location in `LView`. This is because we don't want to pre-allocate space for it
|
27912 | * because the storage is sparse. This file contains utilities for dealing with such data types.
|
27913 | *
|
27914 | * How do we know what is stored at a given location in `LView`.
|
27915 | * - `Array.isArray(value) === false` => `RNode` (The normal storage value)
|
27916 | * - `Array.isArray(value) === true` => then the `value[0]` represents the wrapped value.
|
27917 | * - `typeof value[TYPE] === 'object'` => `LView`
|
27918 | * - This happens when we have a component at a given location
|
27919 | * - `typeof value[TYPE] === true` => `LContainer`
|
27920 | * - This happens when we have `LContainer` binding at a given location.
|
27921 | *
|
27922 | *
|
27923 | * NOTE: it is assumed that `Array.isArray` and `typeof` operations are very efficient.
|
27924 | */
|
27925 | /**
|
27926 | * Returns `RNode`.
|
27927 | * @param value wrapped value of `RNode`, `LView`, `LContainer`
|
27928 | */
|
27929 | function unwrapRNode(value) {
|
27930 | while (Array.isArray(value)) {
|
27931 | value = value[HOST];
|
27932 | }
|
27933 | return value;
|
27934 | }
|
27935 | /**
|
27936 | * Retrieve an `RNode` for a given `TNode` and `LView`.
|
27937 | *
|
27938 | * This function guarantees in dev mode to retrieve a non-null `RNode`.
|
27939 | *
|
27940 | * @param tNode
|
27941 | * @param lView
|
27942 | */
|
27943 | function getNativeByTNode(tNode, lView) {
|
27944 | ngDevMode && assertTNodeForLView(tNode, lView);
|
27945 | ngDevMode && assertIndexInRange(lView, tNode.index);
|
27946 | const node = unwrapRNode(lView[tNode.index]);
|
27947 | ngDevMode && !isProceduralRenderer(lView[RENDERER]) && assertDomNode(node);
|
27948 | return node;
|
27949 | }
|
27950 | // fixme(misko): The return Type should be `TNode|null`
|
27951 | function getTNode(tView, index) {
|
27952 | ngDevMode && assertGreaterThan(index, -1, 'wrong index for TNode');
|
27953 | ngDevMode && assertLessThan(index, tView.data.length, 'wrong index for TNode');
|
27954 | const tNode = tView.data[index];
|
27955 | ngDevMode && tNode !== null && assertTNode(tNode);
|
27956 | return tNode;
|
27957 | }
|
27958 | function getComponentLViewByIndex(nodeIndex, hostView) {
|
27959 | // Could be an LView or an LContainer. If LContainer, unwrap to find LView.
|
27960 | ngDevMode && assertIndexInRange(hostView, nodeIndex);
|
27961 | const slotValue = hostView[nodeIndex];
|
27962 | const lView = isLView(slotValue) ? slotValue : slotValue[HOST];
|
27963 | return lView;
|
27964 | }
|
27965 | /**
|
27966 | * Returns the monkey-patch value data present on the target (which could be
|
27967 | * a component, directive or a DOM node).
|
27968 | */
|
27969 | function readPatchedData(target) {
|
27970 | ngDevMode && assertDefined(target, 'Target expected');
|
27971 | return target[MONKEY_PATCH_KEY_NAME] || null;
|
27972 | }
|
27973 | function readPatchedLView(target) {
|
27974 | const value = readPatchedData(target);
|
27975 | if (value) {
|
27976 | return Array.isArray(value) ? value : value.lView;
|
27977 | }
|
27978 | return null;
|
27979 | }
|
27980 | /** Checks whether a given view is in creation mode */
|
27981 | function isCreationMode(view) {
|
27982 | return (view[FLAGS] & 4 /* CreationMode */) === 4 /* CreationMode */;
|
27983 | }
|
27984 | /**
|
27985 | * Returns a boolean for whether the view is attached to the change detection tree.
|
27986 | *
|
27987 | * Note: This determines whether a view should be checked, not whether it's inserted
|
27988 | * into a container. For that, you'll want `viewAttachedToContainer` below.
|
27989 | */
|
27990 | function viewAttachedToChangeDetector(view) {
|
27991 | return (view[FLAGS] & 128 /* Attached */) === 128 /* Attached */;
|
27992 | }
|
27993 | /**
|
27994 | * Resets the pre-order hook flags of the view.
|
27995 | * @param lView the LView on which the flags are reset
|
27996 | */
|
27997 | function resetPreOrderHookFlags(lView) {
|
27998 | lView[PREORDER_HOOK_FLAGS] = 0;
|
27999 | }
|
28000 | /**
|
28001 | * Updates the `TRANSPLANTED_VIEWS_TO_REFRESH` counter on the `LContainer` as well as the parents
|
28002 | * whose
|
28003 | * 1. counter goes from 0 to 1, indicating that there is a new child that has a view to refresh
|
28004 | * or
|
28005 | * 2. counter goes from 1 to 0, indicating there are no more descendant views to refresh
|
28006 | */
|
28007 | function updateTransplantedViewCount(lContainer, amount) {
|
28008 | lContainer[TRANSPLANTED_VIEWS_TO_REFRESH] += amount;
|
28009 | let viewOrContainer = lContainer;
|
28010 | let parent = lContainer[PARENT];
|
28011 | while (parent !== null &&
|
28012 | ((amount === 1 && viewOrContainer[TRANSPLANTED_VIEWS_TO_REFRESH] === 1) ||
|
28013 | (amount === -1 && viewOrContainer[TRANSPLANTED_VIEWS_TO_REFRESH] === 0))) {
|
28014 | parent[TRANSPLANTED_VIEWS_TO_REFRESH] += amount;
|
28015 | viewOrContainer = parent;
|
28016 | parent = parent[PARENT];
|
28017 | }
|
28018 | }
|
28019 |
|
28020 | /**
|
28021 | * @license
|
28022 | * Copyright Google LLC All Rights Reserved.
|
28023 | *
|
28024 | * Use of this source code is governed by an MIT-style license that can be
|
28025 | * found in the LICENSE file at https://angular.io/license
|
28026 | */
|
28027 | const instructionState = {
|
28028 | lFrame: createLFrame(null),
|
28029 | bindingsEnabled: true,
|
28030 | isInCheckNoChangesMode: false,
|
28031 | };
|
28032 | /**
|
28033 | * Return the current `LView`.
|
28034 | */
|
28035 | function getLView() {
|
28036 | return instructionState.lFrame.lView;
|
28037 | }
|
28038 | /**
|
28039 | * Return the current `TView`.
|
28040 | */
|
28041 | function getTView() {
|
28042 | return instructionState.lFrame.tView;
|
28043 | }
|
28044 | function getCurrentTNode() {
|
28045 | let currentTNode = getCurrentTNodePlaceholderOk();
|
28046 | while (currentTNode !== null && currentTNode.type === 64 /* Placeholder */) {
|
28047 | currentTNode = currentTNode.parent;
|
28048 | }
|
28049 | return currentTNode;
|
28050 | }
|
28051 | function getCurrentTNodePlaceholderOk() {
|
28052 | return instructionState.lFrame.currentTNode;
|
28053 | }
|
28054 | function getCurrentParentTNode() {
|
28055 | const lFrame = instructionState.lFrame;
|
28056 | const currentTNode = lFrame.currentTNode;
|
28057 | return lFrame.isParent ? currentTNode : currentTNode.parent;
|
28058 | }
|
28059 | function setCurrentTNode(tNode, isParent) {
|
28060 | ngDevMode && tNode && assertTNodeForTView(tNode, instructionState.lFrame.tView);
|
28061 | const lFrame = instructionState.lFrame;
|
28062 | lFrame.currentTNode = tNode;
|
28063 | lFrame.isParent = isParent;
|
28064 | }
|
28065 | function isCurrentTNodeParent() {
|
28066 | return instructionState.lFrame.isParent;
|
28067 | }
|
28068 | function isInCheckNoChangesMode() {
|
28069 | // TODO(misko): remove this from the LView since it is ngDevMode=true mode only.
|
28070 | return instructionState.isInCheckNoChangesMode;
|
28071 | }
|
28072 | function setIsInCheckNoChangesMode(mode) {
|
28073 | instructionState.isInCheckNoChangesMode = mode;
|
28074 | }
|
28075 | function setBindingIndex(value) {
|
28076 | return instructionState.lFrame.bindingIndex = value;
|
28077 | }
|
28078 | function isInI18nBlock() {
|
28079 | return instructionState.lFrame.inI18n;
|
28080 | }
|
28081 | /**
|
28082 | * Set a new binding root index so that host template functions can execute.
|
28083 | *
|
28084 | * Bindings inside the host template are 0 index. But because we don't know ahead of time
|
28085 | * how many host bindings we have we can't pre-compute them. For this reason they are all
|
28086 | * 0 index and we just shift the root so that they match next available location in the LView.
|
28087 | *
|
28088 | * @param bindingRootIndex Root index for `hostBindings`
|
28089 | * @param currentDirectiveIndex `TData[currentDirectiveIndex]` will point to the current directive
|
28090 | * whose `hostBindings` are being processed.
|
28091 | */
|
28092 | function setBindingRootForHostBindings(bindingRootIndex, currentDirectiveIndex) {
|
28093 | const lFrame = instructionState.lFrame;
|
28094 | lFrame.bindingIndex = lFrame.bindingRootIndex = bindingRootIndex;
|
28095 | setCurrentDirectiveIndex(currentDirectiveIndex);
|
28096 | }
|
28097 | /**
|
28098 | * Sets an index of a directive whose `hostBindings` are being processed.
|
28099 | *
|
28100 | * @param currentDirectiveIndex `TData` index where current directive instance can be found.
|
28101 | */
|
28102 | function setCurrentDirectiveIndex(currentDirectiveIndex) {
|
28103 | instructionState.lFrame.currentDirectiveIndex = currentDirectiveIndex;
|
28104 | }
|
28105 | function setCurrentQueryIndex(value) {
|
28106 | instructionState.lFrame.currentQueryIndex = value;
|
28107 | }
|
28108 | /**
|
28109 | * Returns a `TNode` of the location where the current `LView` is declared at.
|
28110 | *
|
28111 | * @param lView an `LView` that we want to find parent `TNode` for.
|
28112 | */
|
28113 | function getDeclarationTNode(lView) {
|
28114 | const tView = lView[TVIEW];
|
28115 | // Return the declaration parent for embedded views
|
28116 | if (tView.type === 2 /* Embedded */) {
|
28117 | ngDevMode && assertDefined(tView.declTNode, 'Embedded TNodes should have declaration parents.');
|
28118 | return tView.declTNode;
|
28119 | }
|
28120 | // Components don't have `TView.declTNode` because each instance of component could be
|
28121 | // inserted in different location, hence `TView.declTNode` is meaningless.
|
28122 | // Falling back to `T_HOST` in case we cross component boundary.
|
28123 | if (tView.type === 1 /* Component */) {
|
28124 | return lView[T_HOST];
|
28125 | }
|
28126 | // Remaining TNode type is `TViewType.Root` which doesn't have a parent TNode.
|
28127 | return null;
|
28128 | }
|
28129 | /**
|
28130 | * This is a light weight version of the `enterView` which is needed by the DI system.
|
28131 | *
|
28132 | * @param lView `LView` location of the DI context.
|
28133 | * @param tNode `TNode` for DI context
|
28134 | * @param flags DI context flags. if `SkipSelf` flag is set than we walk up the declaration
|
28135 | * tree from `tNode` until we find parent declared `TElementNode`.
|
28136 | * @returns `true` if we have successfully entered DI associated with `tNode` (or with declared
|
28137 | * `TNode` if `flags` has `SkipSelf`). Failing to enter DI implies that no associated
|
28138 | * `NodeInjector` can be found and we should instead use `ModuleInjector`.
|
28139 | * - If `true` than this call must be fallowed by `leaveDI`
|
28140 | * - If `false` than this call failed and we should NOT call `leaveDI`
|
28141 | */
|
28142 | function enterDI(lView, tNode, flags) {
|
28143 | ngDevMode && assertLViewOrUndefined(lView);
|
28144 | if (flags & InjectFlags.SkipSelf) {
|
28145 | ngDevMode && assertTNodeForTView(tNode, lView[TVIEW]);
|
28146 | let parentTNode = tNode;
|
28147 | let parentLView = lView;
|
28148 | while (true) {
|
28149 | ngDevMode && assertDefined(parentTNode, 'Parent TNode should be defined');
|
28150 | parentTNode = parentTNode.parent;
|
28151 | if (parentTNode === null && !(flags & InjectFlags.Host)) {
|
28152 | parentTNode = getDeclarationTNode(parentLView);
|
28153 | if (parentTNode === null)
|
28154 | break;
|
28155 | // In this case, a parent exists and is definitely an element. So it will definitely
|
28156 | // have an existing lView as the declaration view, which is why we can assume it's defined.
|
28157 | ngDevMode && assertDefined(parentLView, 'Parent LView should be defined');
|
28158 | parentLView = parentLView[DECLARATION_VIEW];
|
28159 | // In Ivy there are Comment nodes that correspond to ngIf and NgFor embedded directives
|
28160 | // We want to skip those and look only at Elements and ElementContainers to ensure
|
28161 | // we're looking at true parent nodes, and not content or other types.
|
28162 | if (parentTNode.type & (2 /* Element */ | 8 /* ElementContainer */)) {
|
28163 | break;
|
28164 | }
|
28165 | }
|
28166 | else {
|
28167 | break;
|
28168 | }
|
28169 | }
|
28170 | if (parentTNode === null) {
|
28171 | // If we failed to find a parent TNode this means that we should use module injector.
|
28172 | return false;
|
28173 | }
|
28174 | else {
|
28175 | tNode = parentTNode;
|
28176 | lView = parentLView;
|
28177 | }
|
28178 | }
|
28179 | ngDevMode && assertTNodeForLView(tNode, lView);
|
28180 | const lFrame = instructionState.lFrame = allocLFrame();
|
28181 | lFrame.currentTNode = tNode;
|
28182 | lFrame.lView = lView;
|
28183 | return true;
|
28184 | }
|
28185 | /**
|
28186 | * Swap the current lView with a new lView.
|
28187 | *
|
28188 | * For performance reasons we store the lView in the top level of the module.
|
28189 | * This way we minimize the number of properties to read. Whenever a new view
|
28190 | * is entered we have to store the lView for later, and when the view is
|
28191 | * exited the state has to be restored
|
28192 | *
|
28193 | * @param newView New lView to become active
|
28194 | * @returns the previously active lView;
|
28195 | */
|
28196 | function enterView(newView) {
|
28197 | ngDevMode && assertNotEqual(newView[0], newView[1], '????');
|
28198 | ngDevMode && assertLViewOrUndefined(newView);
|
28199 | const newLFrame = allocLFrame();
|
28200 | if (ngDevMode) {
|
28201 | assertEqual(newLFrame.isParent, true, 'Expected clean LFrame');
|
28202 | assertEqual(newLFrame.lView, null, 'Expected clean LFrame');
|
28203 | assertEqual(newLFrame.tView, null, 'Expected clean LFrame');
|
28204 | assertEqual(newLFrame.selectedIndex, -1, 'Expected clean LFrame');
|
28205 | assertEqual(newLFrame.elementDepthCount, 0, 'Expected clean LFrame');
|
28206 | assertEqual(newLFrame.currentDirectiveIndex, -1, 'Expected clean LFrame');
|
28207 | assertEqual(newLFrame.currentNamespace, null, 'Expected clean LFrame');
|
28208 | assertEqual(newLFrame.bindingRootIndex, -1, 'Expected clean LFrame');
|
28209 | assertEqual(newLFrame.currentQueryIndex, 0, 'Expected clean LFrame');
|
28210 | }
|
28211 | const tView = newView[TVIEW];
|
28212 | instructionState.lFrame = newLFrame;
|
28213 | ngDevMode && tView.firstChild && assertTNodeForTView(tView.firstChild, tView);
|
28214 | newLFrame.currentTNode = tView.firstChild;
|
28215 | newLFrame.lView = newView;
|
28216 | newLFrame.tView = tView;
|
28217 | newLFrame.contextLView = newView;
|
28218 | newLFrame.bindingIndex = tView.bindingStartIndex;
|
28219 | newLFrame.inI18n = false;
|
28220 | }
|
28221 | /**
|
28222 | * Allocates next free LFrame. This function tries to reuse the `LFrame`s to lower memory pressure.
|
28223 | */
|
28224 | function allocLFrame() {
|
28225 | const currentLFrame = instructionState.lFrame;
|
28226 | const childLFrame = currentLFrame === null ? null : currentLFrame.child;
|
28227 | const newLFrame = childLFrame === null ? createLFrame(currentLFrame) : childLFrame;
|
28228 | return newLFrame;
|
28229 | }
|
28230 | function createLFrame(parent) {
|
28231 | const lFrame = {
|
28232 | currentTNode: null,
|
28233 | isParent: true,
|
28234 | lView: null,
|
28235 | tView: null,
|
28236 | selectedIndex: -1,
|
28237 | contextLView: null,
|
28238 | elementDepthCount: 0,
|
28239 | currentNamespace: null,
|
28240 | currentDirectiveIndex: -1,
|
28241 | bindingRootIndex: -1,
|
28242 | bindingIndex: -1,
|
28243 | currentQueryIndex: 0,
|
28244 | parent: parent,
|
28245 | child: null,
|
28246 | inI18n: false,
|
28247 | };
|
28248 | parent !== null && (parent.child = lFrame); // link the new LFrame for reuse.
|
28249 | return lFrame;
|
28250 | }
|
28251 | /**
|
28252 | * A lightweight version of leave which is used with DI.
|
28253 | *
|
28254 | * This function only resets `currentTNode` and `LView` as those are the only properties
|
28255 | * used with DI (`enterDI()`).
|
28256 | *
|
28257 | * NOTE: This function is reexported as `leaveDI`. However `leaveDI` has return type of `void` where
|
28258 | * as `leaveViewLight` has `LFrame`. This is so that `leaveViewLight` can be used in `leaveView`.
|
28259 | */
|
28260 | function leaveViewLight() {
|
28261 | const oldLFrame = instructionState.lFrame;
|
28262 | instructionState.lFrame = oldLFrame.parent;
|
28263 | oldLFrame.currentTNode = null;
|
28264 | oldLFrame.lView = null;
|
28265 | return oldLFrame;
|
28266 | }
|
28267 | /**
|
28268 | * This is a lightweight version of the `leaveView` which is needed by the DI system.
|
28269 | *
|
28270 | * NOTE: this function is an alias so that we can change the type of the function to have `void`
|
28271 | * return type.
|
28272 | */
|
28273 | const leaveDI = leaveViewLight;
|
28274 | /**
|
28275 | * Leave the current `LView`
|
28276 | *
|
28277 | * This pops the `LFrame` with the associated `LView` from the stack.
|
28278 | *
|
28279 | * IMPORTANT: We must zero out the `LFrame` values here otherwise they will be retained. This is
|
28280 | * because for performance reasons we don't release `LFrame` but rather keep it for next use.
|
28281 | */
|
28282 | function leaveView() {
|
28283 | const oldLFrame = leaveViewLight();
|
28284 | oldLFrame.isParent = true;
|
28285 | oldLFrame.tView = null;
|
28286 | oldLFrame.selectedIndex = -1;
|
28287 | oldLFrame.contextLView = null;
|
28288 | oldLFrame.elementDepthCount = 0;
|
28289 | oldLFrame.currentDirectiveIndex = -1;
|
28290 | oldLFrame.currentNamespace = null;
|
28291 | oldLFrame.bindingRootIndex = -1;
|
28292 | oldLFrame.bindingIndex = -1;
|
28293 | oldLFrame.currentQueryIndex = 0;
|
28294 | }
|
28295 | /**
|
28296 | * Gets the currently selected element index.
|
28297 | *
|
28298 | * Used with {@link property} instruction (and more in the future) to identify the index in the
|
28299 | * current `LView` to act on.
|
28300 | */
|
28301 | function getSelectedIndex() {
|
28302 | return instructionState.lFrame.selectedIndex;
|
28303 | }
|
28304 | /**
|
28305 | * Sets the most recent index passed to {@link select}
|
28306 | *
|
28307 | * Used with {@link property} instruction (and more in the future) to identify the index in the
|
28308 | * current `LView` to act on.
|
28309 | *
|
28310 | * (Note that if an "exit function" was set earlier (via `setElementExitFn()`) then that will be
|
28311 | * run if and when the provided `index` value is different from the current selected index value.)
|
28312 | */
|
28313 | function setSelectedIndex(index) {
|
28314 | ngDevMode && index !== -1 &&
|
28315 | assertGreaterThanOrEqual(index, HEADER_OFFSET, 'Index must be past HEADER_OFFSET (or -1).');
|
28316 | ngDevMode &&
|
28317 | assertLessThan(index, instructionState.lFrame.lView.length, 'Can\'t set index passed end of LView');
|
28318 | instructionState.lFrame.selectedIndex = index;
|
28319 | }
|
28320 |
|
28321 | /**
|
28322 | * @license
|
28323 | * Copyright Google LLC All Rights Reserved.
|
28324 | *
|
28325 | * Use of this source code is governed by an MIT-style license that can be
|
28326 | * found in the LICENSE file at https://angular.io/license
|
28327 | */
|
28328 | /**
|
28329 | * Adds all directive lifecycle hooks from the given `DirectiveDef` to the given `TView`.
|
28330 | *
|
28331 | * Must be run *only* on the first template pass.
|
28332 | *
|
28333 | * Sets up the pre-order hooks on the provided `tView`,
|
28334 | * see {@link HookData} for details about the data structure.
|
28335 | *
|
28336 | * @param directiveIndex The index of the directive in LView
|
28337 | * @param directiveDef The definition containing the hooks to setup in tView
|
28338 | * @param tView The current TView
|
28339 | */
|
28340 | function registerPreOrderHooks(directiveIndex, directiveDef, tView) {
|
28341 | ngDevMode && assertFirstCreatePass(tView);
|
28342 | const { ngOnChanges, ngOnInit, ngDoCheck } = directiveDef.type.prototype;
|
28343 | if (ngOnChanges) {
|
28344 | const wrappedOnChanges = NgOnChangesFeatureImpl(directiveDef);
|
28345 | (tView.preOrderHooks || (tView.preOrderHooks = [])).push(directiveIndex, wrappedOnChanges);
|
28346 | (tView.preOrderCheckHooks || (tView.preOrderCheckHooks = []))
|
28347 | .push(directiveIndex, wrappedOnChanges);
|
28348 | }
|
28349 | if (ngOnInit) {
|
28350 | (tView.preOrderHooks || (tView.preOrderHooks = [])).push(0 - directiveIndex, ngOnInit);
|
28351 | }
|
28352 | if (ngDoCheck) {
|
28353 | (tView.preOrderHooks || (tView.preOrderHooks = [])).push(directiveIndex, ngDoCheck);
|
28354 | (tView.preOrderCheckHooks || (tView.preOrderCheckHooks = [])).push(directiveIndex, ngDoCheck);
|
28355 | }
|
28356 | }
|
28357 | /**
|
28358 | *
|
28359 | * Loops through the directives on the provided `tNode` and queues hooks to be
|
28360 | * run that are not initialization hooks.
|
28361 | *
|
28362 | * Should be executed during `elementEnd()` and similar to
|
28363 | * preserve hook execution order. Content, view, and destroy hooks for projected
|
28364 | * components and directives must be called *before* their hosts.
|
28365 | *
|
28366 | * Sets up the content, view, and destroy hooks on the provided `tView`,
|
28367 | * see {@link HookData} for details about the data structure.
|
28368 | *
|
28369 | * NOTE: This does not set up `onChanges`, `onInit` or `doCheck`, those are set up
|
28370 | * separately at `elementStart`.
|
28371 | *
|
28372 | * @param tView The current TView
|
28373 | * @param tNode The TNode whose directives are to be searched for hooks to queue
|
28374 | */
|
28375 | function registerPostOrderHooks(tView, tNode) {
|
28376 | ngDevMode && assertFirstCreatePass(tView);
|
28377 | // It's necessary to loop through the directives at elementEnd() (rather than processing in
|
28378 | // directiveCreate) so we can preserve the current hook order. Content, view, and destroy
|
28379 | // hooks for projected components and directives must be called *before* their hosts.
|
28380 | for (let i = tNode.directiveStart, end = tNode.directiveEnd; i < end; i++) {
|
28381 | const directiveDef = tView.data[i];
|
28382 | ngDevMode && assertDefined(directiveDef, 'Expecting DirectiveDef');
|
28383 | const lifecycleHooks = directiveDef.type.prototype;
|
28384 | const { ngAfterContentInit, ngAfterContentChecked, ngAfterViewInit, ngAfterViewChecked, ngOnDestroy } = lifecycleHooks;
|
28385 | if (ngAfterContentInit) {
|
28386 | (tView.contentHooks || (tView.contentHooks = [])).push(-i, ngAfterContentInit);
|
28387 | }
|
28388 | if (ngAfterContentChecked) {
|
28389 | (tView.contentHooks || (tView.contentHooks = [])).push(i, ngAfterContentChecked);
|
28390 | (tView.contentCheckHooks || (tView.contentCheckHooks = [])).push(i, ngAfterContentChecked);
|
28391 | }
|
28392 | if (ngAfterViewInit) {
|
28393 | (tView.viewHooks || (tView.viewHooks = [])).push(-i, ngAfterViewInit);
|
28394 | }
|
28395 | if (ngAfterViewChecked) {
|
28396 | (tView.viewHooks || (tView.viewHooks = [])).push(i, ngAfterViewChecked);
|
28397 | (tView.viewCheckHooks || (tView.viewCheckHooks = [])).push(i, ngAfterViewChecked);
|
28398 | }
|
28399 | if (ngOnDestroy != null) {
|
28400 | (tView.destroyHooks || (tView.destroyHooks = [])).push(i, ngOnDestroy);
|
28401 | }
|
28402 | }
|
28403 | }
|
28404 | /**
|
28405 | * Executing hooks requires complex logic as we need to deal with 2 constraints.
|
28406 | *
|
28407 | * 1. Init hooks (ngOnInit, ngAfterContentInit, ngAfterViewInit) must all be executed once and only
|
28408 | * once, across many change detection cycles. This must be true even if some hooks throw, or if
|
28409 | * some recursively trigger a change detection cycle.
|
28410 | * To solve that, it is required to track the state of the execution of these init hooks.
|
28411 | * This is done by storing and maintaining flags in the view: the {@link InitPhaseState},
|
28412 | * and the index within that phase. They can be seen as a cursor in the following structure:
|
28413 | * [[onInit1, onInit2], [afterContentInit1], [afterViewInit1, afterViewInit2, afterViewInit3]]
|
28414 | * They are are stored as flags in LView[FLAGS].
|
28415 | *
|
28416 | * 2. Pre-order hooks can be executed in batches, because of the select instruction.
|
28417 | * To be able to pause and resume their execution, we also need some state about the hook's array
|
28418 | * that is being processed:
|
28419 | * - the index of the next hook to be executed
|
28420 | * - the number of init hooks already found in the processed part of the array
|
28421 | * They are are stored as flags in LView[PREORDER_HOOK_FLAGS].
|
28422 | */
|
28423 | /**
|
28424 | * Executes pre-order check hooks ( OnChanges, DoChanges) given a view where all the init hooks were
|
28425 | * executed once. This is a light version of executeInitAndCheckPreOrderHooks where we can skip read
|
28426 | * / write of the init-hooks related flags.
|
28427 | * @param lView The LView where hooks are defined
|
28428 | * @param hooks Hooks to be run
|
28429 | * @param nodeIndex 3 cases depending on the value:
|
28430 | * - undefined: all hooks from the array should be executed (post-order case)
|
28431 | * - null: execute hooks only from the saved index until the end of the array (pre-order case, when
|
28432 | * flushing the remaining hooks)
|
28433 | * - number: execute hooks only from the saved index until that node index exclusive (pre-order
|
28434 | * case, when executing select(number))
|
28435 | */
|
28436 | function executeCheckHooks(lView, hooks, nodeIndex) {
|
28437 | callHooks(lView, hooks, 3 /* InitPhaseCompleted */, nodeIndex);
|
28438 | }
|
28439 | /**
|
28440 | * Executes post-order init and check hooks (one of AfterContentInit, AfterContentChecked,
|
28441 | * AfterViewInit, AfterViewChecked) given a view where there are pending init hooks to be executed.
|
28442 | * @param lView The LView where hooks are defined
|
28443 | * @param hooks Hooks to be run
|
28444 | * @param initPhase A phase for which hooks should be run
|
28445 | * @param nodeIndex 3 cases depending on the value:
|
28446 | * - undefined: all hooks from the array should be executed (post-order case)
|
28447 | * - null: execute hooks only from the saved index until the end of the array (pre-order case, when
|
28448 | * flushing the remaining hooks)
|
28449 | * - number: execute hooks only from the saved index until that node index exclusive (pre-order
|
28450 | * case, when executing select(number))
|
28451 | */
|
28452 | function executeInitAndCheckHooks(lView, hooks, initPhase, nodeIndex) {
|
28453 | ngDevMode &&
|
28454 | assertNotEqual(initPhase, 3 /* InitPhaseCompleted */, 'Init pre-order hooks should not be called more than once');
|
28455 | if ((lView[FLAGS] & 3 /* InitPhaseStateMask */) === initPhase) {
|
28456 | callHooks(lView, hooks, initPhase, nodeIndex);
|
28457 | }
|
28458 | }
|
28459 | function incrementInitPhaseFlags(lView, initPhase) {
|
28460 | ngDevMode &&
|
28461 | assertNotEqual(initPhase, 3 /* InitPhaseCompleted */, 'Init hooks phase should not be incremented after all init hooks have been run.');
|
28462 | let flags = lView[FLAGS];
|
28463 | if ((flags & 3 /* InitPhaseStateMask */) === initPhase) {
|
28464 | flags &= 2047 /* IndexWithinInitPhaseReset */;
|
28465 | flags += 1 /* InitPhaseStateIncrementer */;
|
28466 | lView[FLAGS] = flags;
|
28467 | }
|
28468 | }
|
28469 | /**
|
28470 | * Calls lifecycle hooks with their contexts, skipping init hooks if it's not
|
28471 | * the first LView pass
|
28472 | *
|
28473 | * @param currentView The current view
|
28474 | * @param arr The array in which the hooks are found
|
28475 | * @param initPhaseState the current state of the init phase
|
28476 | * @param currentNodeIndex 3 cases depending on the value:
|
28477 | * - undefined: all hooks from the array should be executed (post-order case)
|
28478 | * - null: execute hooks only from the saved index until the end of the array (pre-order case, when
|
28479 | * flushing the remaining hooks)
|
28480 | * - number: execute hooks only from the saved index until that node index exclusive (pre-order
|
28481 | * case, when executing select(number))
|
28482 | */
|
28483 | function callHooks(currentView, arr, initPhase, currentNodeIndex) {
|
28484 | ngDevMode &&
|
28485 | assertEqual(isInCheckNoChangesMode(), false, 'Hooks should never be run when in check no changes mode.');
|
28486 | const startIndex = currentNodeIndex !== undefined ?
|
28487 | (currentView[PREORDER_HOOK_FLAGS] & 65535 /* IndexOfTheNextPreOrderHookMaskMask */) :
|
28488 | 0;
|
28489 | const nodeIndexLimit = currentNodeIndex != null ? currentNodeIndex : -1;
|
28490 | const max = arr.length - 1; // Stop the loop at length - 1, because we look for the hook at i + 1
|
28491 | let lastNodeIndexFound = 0;
|
28492 | for (let i = startIndex; i < max; i++) {
|
28493 | const hook = arr[i + 1];
|
28494 | if (typeof hook === 'number') {
|
28495 | lastNodeIndexFound = arr[i];
|
28496 | if (currentNodeIndex != null && lastNodeIndexFound >= currentNodeIndex) {
|
28497 | break;
|
28498 | }
|
28499 | }
|
28500 | else {
|
28501 | const isInitHook = arr[i] < 0;
|
28502 | if (isInitHook)
|
28503 | currentView[PREORDER_HOOK_FLAGS] += 65536 /* NumberOfInitHooksCalledIncrementer */;
|
28504 | if (lastNodeIndexFound < nodeIndexLimit || nodeIndexLimit == -1) {
|
28505 | callHook(currentView, initPhase, arr, i);
|
28506 | currentView[PREORDER_HOOK_FLAGS] =
|
28507 | (currentView[PREORDER_HOOK_FLAGS] & 4294901760 /* NumberOfInitHooksCalledMask */) + i +
|
28508 | 2;
|
28509 | }
|
28510 | i++;
|
28511 | }
|
28512 | }
|
28513 | }
|
28514 | /**
|
28515 | * Execute one hook against the current `LView`.
|
28516 | *
|
28517 | * @param currentView The current view
|
28518 | * @param initPhaseState the current state of the init phase
|
28519 | * @param arr The array in which the hooks are found
|
28520 | * @param i The current index within the hook data array
|
28521 | */
|
28522 | function callHook(currentView, initPhase, arr, i) {
|
28523 | const isInitHook = arr[i] < 0;
|
28524 | const hook = arr[i + 1];
|
28525 | const directiveIndex = isInitHook ? -arr[i] : arr[i];
|
28526 | const directive = currentView[directiveIndex];
|
28527 | if (isInitHook) {
|
28528 | const indexWithintInitPhase = currentView[FLAGS] >> 11 /* IndexWithinInitPhaseShift */;
|
28529 | // The init phase state must be always checked here as it may have been recursively updated.
|
28530 | if (indexWithintInitPhase <
|
28531 | (currentView[PREORDER_HOOK_FLAGS] >> 16 /* NumberOfInitHooksCalledShift */) &&
|
28532 | (currentView[FLAGS] & 3 /* InitPhaseStateMask */) === initPhase) {
|
28533 | currentView[FLAGS] += 2048 /* IndexWithinInitPhaseIncrementer */;
|
28534 | hook.call(directive);
|
28535 | }
|
28536 | }
|
28537 | else {
|
28538 | hook.call(directive);
|
28539 | }
|
28540 | }
|
28541 |
|
28542 | /**
|
28543 | * @license
|
28544 | * Copyright Google LLC All Rights Reserved.
|
28545 | *
|
28546 | * Use of this source code is governed by an MIT-style license that can be
|
28547 | * found in the LICENSE file at https://angular.io/license
|
28548 | */
|
28549 | const NO_PARENT_INJECTOR = -1;
|
28550 | /**
|
28551 | * Each injector is saved in 9 contiguous slots in `LView` and 9 contiguous slots in
|
28552 | * `TView.data`. This allows us to store information about the current node's tokens (which
|
28553 | * can be shared in `TView`) as well as the tokens of its ancestor nodes (which cannot be
|
28554 | * shared, so they live in `LView`).
|
28555 | *
|
28556 | * Each of these slots (aside from the last slot) contains a bloom filter. This bloom filter
|
28557 | * determines whether a directive is available on the associated node or not. This prevents us
|
28558 | * from searching the directives array at this level unless it's probable the directive is in it.
|
28559 | *
|
28560 | * See: https://en.wikipedia.org/wiki/Bloom_filter for more about bloom filters.
|
28561 | *
|
28562 | * Because all injectors have been flattened into `LView` and `TViewData`, they cannot typed
|
28563 | * using interfaces as they were previously. The start index of each `LInjector` and `TInjector`
|
28564 | * will differ based on where it is flattened into the main array, so it's not possible to know
|
28565 | * the indices ahead of time and save their types here. The interfaces are still included here
|
28566 | * for documentation purposes.
|
28567 | *
|
28568 | * export interface LInjector extends Array<any> {
|
28569 | *
|
28570 | * // Cumulative bloom for directive IDs 0-31 (IDs are % BLOOM_SIZE)
|
28571 | * [0]: number;
|
28572 | *
|
28573 | * // Cumulative bloom for directive IDs 32-63
|
28574 | * [1]: number;
|
28575 | *
|
28576 | * // Cumulative bloom for directive IDs 64-95
|
28577 | * [2]: number;
|
28578 | *
|
28579 | * // Cumulative bloom for directive IDs 96-127
|
28580 | * [3]: number;
|
28581 | *
|
28582 | * // Cumulative bloom for directive IDs 128-159
|
28583 | * [4]: number;
|
28584 | *
|
28585 | * // Cumulative bloom for directive IDs 160 - 191
|
28586 | * [5]: number;
|
28587 | *
|
28588 | * // Cumulative bloom for directive IDs 192 - 223
|
28589 | * [6]: number;
|
28590 | *
|
28591 | * // Cumulative bloom for directive IDs 224 - 255
|
28592 | * [7]: number;
|
28593 | *
|
28594 | * // We need to store a reference to the injector's parent so DI can keep looking up
|
28595 | * // the injector tree until it finds the dependency it's looking for.
|
28596 | * [PARENT_INJECTOR]: number;
|
28597 | * }
|
28598 | *
|
28599 | * export interface TInjector extends Array<any> {
|
28600 | *
|
28601 | * // Shared node bloom for directive IDs 0-31 (IDs are % BLOOM_SIZE)
|
28602 | * [0]: number;
|
28603 | *
|
28604 | * // Shared node bloom for directive IDs 32-63
|
28605 | * [1]: number;
|
28606 | *
|
28607 | * // Shared node bloom for directive IDs 64-95
|
28608 | * [2]: number;
|
28609 | *
|
28610 | * // Shared node bloom for directive IDs 96-127
|
28611 | * [3]: number;
|
28612 | *
|
28613 | * // Shared node bloom for directive IDs 128-159
|
28614 | * [4]: number;
|
28615 | *
|
28616 | * // Shared node bloom for directive IDs 160 - 191
|
28617 | * [5]: number;
|
28618 | *
|
28619 | * // Shared node bloom for directive IDs 192 - 223
|
28620 | * [6]: number;
|
28621 | *
|
28622 | * // Shared node bloom for directive IDs 224 - 255
|
28623 | * [7]: number;
|
28624 | *
|
28625 | * // Necessary to find directive indices for a particular node.
|
28626 | * [TNODE]: TElementNode|TElementContainerNode|TContainerNode;
|
28627 | * }
|
28628 | */
|
28629 | /**
|
28630 | * Factory for creating instances of injectors in the NodeInjector.
|
28631 | *
|
28632 | * This factory is complicated by the fact that it can resolve `multi` factories as well.
|
28633 | *
|
28634 | * NOTE: Some of the fields are optional which means that this class has two hidden classes.
|
28635 | * - One without `multi` support (most common)
|
28636 | * - One with `multi` values, (rare).
|
28637 | *
|
28638 | * Since VMs can cache up to 4 inline hidden classes this is OK.
|
28639 | *
|
28640 | * - Single factory: Only `resolving` and `factory` is defined.
|
28641 | * - `providers` factory: `componentProviders` is a number and `index = -1`.
|
28642 | * - `viewProviders` factory: `componentProviders` is a number and `index` points to `providers`.
|
28643 | */
|
28644 | class NodeInjectorFactory {
|
28645 | constructor(
|
28646 | /**
|
28647 | * Factory to invoke in order to create a new instance.
|
28648 | */
|
28649 | factory,
|
28650 | /**
|
28651 | * Set to `true` if the token is declared in `viewProviders` (or if it is component).
|
28652 | */
|
28653 | isViewProvider, injectImplementation) {
|
28654 | this.factory = factory;
|
28655 | /**
|
28656 | * Marker set to true during factory invocation to see if we get into recursive loop.
|
28657 | * Recursive loop causes an error to be displayed.
|
28658 | */
|
28659 | this.resolving = false;
|
28660 | ngDevMode && assertDefined(factory, 'Factory not specified');
|
28661 | ngDevMode && assertEqual(typeof factory, 'function', 'Expected factory function.');
|
28662 | this.canSeeViewProviders = isViewProvider;
|
28663 | this.injectImpl = injectImplementation;
|
28664 | }
|
28665 | }
|
28666 | function isFactory(obj) {
|
28667 | return obj instanceof NodeInjectorFactory;
|
28668 | }
|
28669 |
|
28670 | /**
|
28671 | * Converts `TNodeType` into human readable text.
|
28672 | * Make sure this matches with `TNodeType`
|
28673 | */
|
28674 | function toTNodeTypeAsString(tNodeType) {
|
28675 | let text = '';
|
28676 | (tNodeType & 1 /* Text */) && (text += '|Text');
|
28677 | (tNodeType & 2 /* Element */) && (text += '|Element');
|
28678 | (tNodeType & 4 /* Container */) && (text += '|Container');
|
28679 | (tNodeType & 8 /* ElementContainer */) && (text += '|ElementContainer');
|
28680 | (tNodeType & 16 /* Projection */) && (text += '|Projection');
|
28681 | (tNodeType & 32 /* Icu */) && (text += '|IcuContainer');
|
28682 | (tNodeType & 64 /* Placeholder */) && (text += '|Placeholder');
|
28683 | return text.length > 0 ? text.substring(1) : text;
|
28684 | }
|
28685 |
|
28686 | /**
|
28687 | * @license
|
28688 | * Copyright Google LLC All Rights Reserved.
|
28689 | *
|
28690 | * Use of this source code is governed by an MIT-style license that can be
|
28691 | * found in the LICENSE file at https://angular.io/license
|
28692 | */
|
28693 | function assertTNodeType(tNode, expectedTypes, message) {
|
28694 | assertDefined(tNode, 'should be called with a TNode');
|
28695 | if ((tNode.type & expectedTypes) === 0) {
|
28696 | throwError(message ||
|
28697 | `Expected [${toTNodeTypeAsString(expectedTypes)}] but got ${toTNodeTypeAsString(tNode.type)}.`);
|
28698 | }
|
28699 | }
|
28700 | function assertPureTNodeType(type) {
|
28701 | if (!(type === 2 /* Element */ || //
|
28702 | type === 1 /* Text */ || //
|
28703 | type === 4 /* Container */ || //
|
28704 | type === 8 /* ElementContainer */ || //
|
28705 | type === 32 /* Icu */ || //
|
28706 | type === 16 /* Projection */ || //
|
28707 | type === 64 /* Placeholder */)) {
|
28708 | throwError(`Expected TNodeType to have only a single type selected, but got ${toTNodeTypeAsString(type)}.`);
|
28709 | }
|
28710 | }
|
28711 |
|
28712 | /**
|
28713 | * Assigns all attribute values to the provided element via the inferred renderer.
|
28714 | *
|
28715 | * This function accepts two forms of attribute entries:
|
28716 | *
|
28717 | * default: (key, value):
|
28718 | * attrs = [key1, value1, key2, value2]
|
28719 | *
|
28720 | * namespaced: (NAMESPACE_MARKER, uri, name, value)
|
28721 | * attrs = [NAMESPACE_MARKER, uri, name, value, NAMESPACE_MARKER, uri, name, value]
|
28722 | *
|
28723 | * The `attrs` array can contain a mix of both the default and namespaced entries.
|
28724 | * The "default" values are set without a marker, but if the function comes across
|
28725 | * a marker value then it will attempt to set a namespaced value. If the marker is
|
28726 | * not of a namespaced value then the function will quit and return the index value
|
28727 | * where it stopped during the iteration of the attrs array.
|
28728 | *
|
28729 | * See [AttributeMarker] to understand what the namespace marker value is.
|
28730 | *
|
28731 | * Note that this instruction does not support assigning style and class values to
|
28732 | * an element. See `elementStart` and `elementHostAttrs` to learn how styling values
|
28733 | * are applied to an element.
|
28734 | * @param renderer The renderer to be used
|
28735 | * @param native The element that the attributes will be assigned to
|
28736 | * @param attrs The attribute array of values that will be assigned to the element
|
28737 | * @returns the index value that was last accessed in the attributes array
|
28738 | */
|
28739 | function setUpAttributes(renderer, native, attrs) {
|
28740 | const isProc = isProceduralRenderer(renderer);
|
28741 | let i = 0;
|
28742 | while (i < attrs.length) {
|
28743 | const value = attrs[i];
|
28744 | if (typeof value === 'number') {
|
28745 | // only namespaces are supported. Other value types (such as style/class
|
28746 | // entries) are not supported in this function.
|
28747 | if (value !== 0 /* NamespaceURI */) {
|
28748 | break;
|
28749 | }
|
28750 | // we just landed on the marker value ... therefore
|
28751 | // we should skip to the next entry
|
28752 | i++;
|
28753 | const namespaceURI = attrs[i++];
|
28754 | const attrName = attrs[i++];
|
28755 | const attrVal = attrs[i++];
|
28756 | ngDevMode && ngDevMode.rendererSetAttribute++;
|
28757 | isProc ?
|
28758 | renderer.setAttribute(native, attrName, attrVal, namespaceURI) :
|
28759 | native.setAttributeNS(namespaceURI, attrName, attrVal);
|
28760 | }
|
28761 | else {
|
28762 | // attrName is string;
|
28763 | const attrName = value;
|
28764 | const attrVal = attrs[++i];
|
28765 | // Standard attributes
|
28766 | ngDevMode && ngDevMode.rendererSetAttribute++;
|
28767 | if (isAnimationProp(attrName)) {
|
28768 | if (isProc) {
|
28769 | renderer.setProperty(native, attrName, attrVal);
|
28770 | }
|
28771 | }
|
28772 | else {
|
28773 | isProc ?
|
28774 | renderer.setAttribute(native, attrName, attrVal) :
|
28775 | native.setAttribute(attrName, attrVal);
|
28776 | }
|
28777 | i++;
|
28778 | }
|
28779 | }
|
28780 | // another piece of code may iterate over the same attributes array. Therefore
|
28781 | // it may be helpful to return the exact spot where the attributes array exited
|
28782 | // whether by running into an unsupported marker or if all the static values were
|
28783 | // iterated over.
|
28784 | return i;
|
28785 | }
|
28786 | function isAnimationProp(name) {
|
28787 | // Perf note: accessing charCodeAt to check for the first character of a string is faster as
|
28788 | // compared to accessing a character at index 0 (ex. name[0]). The main reason for this is that
|
28789 | // charCodeAt doesn't allocate memory to return a substring.
|
28790 | return name.charCodeAt(0) === 64 /* AT_SIGN */;
|
28791 | }
|
28792 |
|
28793 | /**
|
28794 | * @license
|
28795 | * Copyright Google LLC All Rights Reserved.
|
28796 | *
|
28797 | * Use of this source code is governed by an MIT-style license that can be
|
28798 | * found in the LICENSE file at https://angular.io/license
|
28799 | */
|
28800 | /// Parent Injector Utils ///////////////////////////////////////////////////////////////
|
28801 | function hasParentInjector(parentLocation) {
|
28802 | return parentLocation !== NO_PARENT_INJECTOR;
|
28803 | }
|
28804 | function getParentInjectorIndex(parentLocation) {
|
28805 | ngDevMode && assertNumber(parentLocation, 'Number expected');
|
28806 | ngDevMode && assertNotEqual(parentLocation, -1, 'Not a valid state.');
|
28807 | const parentInjectorIndex = parentLocation & 32767 /* InjectorIndexMask */;
|
28808 | ngDevMode &&
|
28809 | assertGreaterThan(parentInjectorIndex, HEADER_OFFSET, 'Parent injector must be pointing past HEADER_OFFSET.');
|
28810 | return parentLocation & 32767 /* InjectorIndexMask */;
|
28811 | }
|
28812 | function getParentInjectorViewOffset(parentLocation) {
|
28813 | return parentLocation >> 16 /* ViewOffsetShift */;
|
28814 | }
|
28815 | /**
|
28816 | * Unwraps a parent injector location number to find the view offset from the current injector,
|
28817 | * then walks up the declaration view tree until the view is found that contains the parent
|
28818 | * injector.
|
28819 | *
|
28820 | * @param location The location of the parent injector, which contains the view offset
|
28821 | * @param startView The LView instance from which to start walking up the view tree
|
28822 | * @returns The LView instance that contains the parent injector
|
28823 | */
|
28824 | function getParentInjectorView(location, startView) {
|
28825 | let viewOffset = getParentInjectorViewOffset(location);
|
28826 | let parentView = startView;
|
28827 | // For most cases, the parent injector can be found on the host node (e.g. for component
|
28828 | // or container), but we must keep the loop here to support the rarer case of deeply nested
|
28829 | // <ng-template> tags or inline views, where the parent injector might live many views
|
28830 | // above the child injector.
|
28831 | while (viewOffset > 0) {
|
28832 | parentView = parentView[DECLARATION_VIEW];
|
28833 | viewOffset--;
|
28834 | }
|
28835 | return parentView;
|
28836 | }
|
28837 |
|
28838 | /**
|
28839 | * @license
|
28840 | * Copyright Google LLC All Rights Reserved.
|
28841 | *
|
28842 | * Use of this source code is governed by an MIT-style license that can be
|
28843 | * found in the LICENSE file at https://angular.io/license
|
28844 | */
|
28845 | /**
|
28846 | * Defines if the call to `inject` should include `viewProviders` in its resolution.
|
28847 | *
|
28848 | * This is set to true when we try to instantiate a component. This value is reset in
|
28849 | * `getNodeInjectable` to a value which matches the declaration location of the token about to be
|
28850 | * instantiated. This is done so that if we are injecting a token which was declared outside of
|
28851 | * `viewProviders` we don't accidentally pull `viewProviders` in.
|
28852 | *
|
28853 | * Example:
|
28854 | *
|
28855 | * ```
|
28856 | * @Injectable()
|
28857 | * class MyService {
|
28858 | * constructor(public value: String) {}
|
28859 | * }
|
28860 | *
|
28861 | * @Component({
|
28862 | * providers: [
|
28863 | * MyService,
|
28864 | * {provide: String, value: 'providers' }
|
28865 | * ]
|
28866 | * viewProviders: [
|
28867 | * {provide: String, value: 'viewProviders'}
|
28868 | * ]
|
28869 | * })
|
28870 | * class MyComponent {
|
28871 | * constructor(myService: MyService, value: String) {
|
28872 | * // We expect that Component can see into `viewProviders`.
|
28873 | * expect(value).toEqual('viewProviders');
|
28874 | * // `MyService` was not declared in `viewProviders` hence it can't see it.
|
28875 | * expect(myService.value).toEqual('providers');
|
28876 | * }
|
28877 | * }
|
28878 | *
|
28879 | * ```
|
28880 | */
|
28881 | let includeViewProviders = true;
|
28882 | function setIncludeViewProviders(v) {
|
28883 | const oldValue = includeViewProviders;
|
28884 | includeViewProviders = v;
|
28885 | return oldValue;
|
28886 | }
|
28887 | /**
|
28888 | * The number of slots in each bloom filter (used by DI). The larger this number, the fewer
|
28889 | * directives that will share slots, and thus, the fewer false positives when checking for
|
28890 | * the existence of a directive.
|
28891 | */
|
28892 | const BLOOM_SIZE = 256;
|
28893 | const BLOOM_MASK = BLOOM_SIZE - 1;
|
28894 | /**
|
28895 | * The number of bits that is represented by a single bloom bucket. JS bit operations are 32 bits,
|
28896 | * so each bucket represents 32 distinct tokens which accounts for log2(32) = 5 bits of a bloom hash
|
28897 | * number.
|
28898 | */
|
28899 | const BLOOM_BUCKET_BITS = 5;
|
28900 | /** Counter used to generate unique IDs for directives. */
|
28901 | let nextNgElementId = 0;
|
28902 | /**
|
28903 | * Registers this directive as present in its node's injector by flipping the directive's
|
28904 | * corresponding bit in the injector's bloom filter.
|
28905 | *
|
28906 | * @param injectorIndex The index of the node injector where this token should be registered
|
28907 | * @param tView The TView for the injector's bloom filters
|
28908 | * @param type The directive token to register
|
28909 | */
|
28910 | function bloomAdd(injectorIndex, tView, type) {
|
28911 | ngDevMode && assertEqual(tView.firstCreatePass, true, 'expected firstCreatePass to be true');
|
28912 | let id;
|
28913 | if (typeof type === 'string') {
|
28914 | id = type.charCodeAt(0) || 0;
|
28915 | }
|
28916 | else if (type.hasOwnProperty(NG_ELEMENT_ID)) {
|
28917 | id = type[NG_ELEMENT_ID];
|
28918 | }
|
28919 | // Set a unique ID on the directive type, so if something tries to inject the directive,
|
28920 | // we can easily retrieve the ID and hash it into the bloom bit that should be checked.
|
28921 | if (id == null) {
|
28922 | id = type[NG_ELEMENT_ID] = nextNgElementId++;
|
28923 | }
|
28924 | // We only have BLOOM_SIZE (256) slots in our bloom filter (8 buckets * 32 bits each),
|
28925 | // so all unique IDs must be modulo-ed into a number from 0 - 255 to fit into the filter.
|
28926 | const bloomHash = id & BLOOM_MASK;
|
28927 | // Create a mask that targets the specific bit associated with the directive.
|
28928 | // JS bit operations are 32 bits, so this will be a number between 2^0 and 2^31, corresponding
|
28929 | // to bit positions 0 - 31 in a 32 bit integer.
|
28930 | const mask = 1 << bloomHash;
|
28931 | // Each bloom bucket in `tData` represents `BLOOM_BUCKET_BITS` number of bits of `bloomHash`.
|
28932 | // Any bits in `bloomHash` beyond `BLOOM_BUCKET_BITS` indicate the bucket offset that the mask
|
28933 | // should be written to.
|
28934 | tView.data[injectorIndex + (bloomHash >> BLOOM_BUCKET_BITS)] |= mask;
|
28935 | }
|
28936 | /**
|
28937 | * Creates (or gets an existing) injector for a given element or container.
|
28938 | *
|
28939 | * @param tNode for which an injector should be retrieved / created.
|
28940 | * @param lView View where the node is stored
|
28941 | * @returns Node injector
|
28942 | */
|
28943 | function getOrCreateNodeInjectorForNode(tNode, lView) {
|
28944 | const existingInjectorIndex = getInjectorIndex(tNode, lView);
|
28945 | if (existingInjectorIndex !== -1) {
|
28946 | return existingInjectorIndex;
|
28947 | }
|
28948 | const tView = lView[TVIEW];
|
28949 | if (tView.firstCreatePass) {
|
28950 | tNode.injectorIndex = lView.length;
|
28951 | insertBloom(tView.data, tNode); // foundation for node bloom
|
28952 | insertBloom(lView, null); // foundation for cumulative bloom
|
28953 | insertBloom(tView.blueprint, null);
|
28954 | }
|
28955 | const parentLoc = getParentInjectorLocation(tNode, lView);
|
28956 | const injectorIndex = tNode.injectorIndex;
|
28957 | // If a parent injector can't be found, its location is set to -1.
|
28958 | // In that case, we don't need to set up a cumulative bloom
|
28959 | if (hasParentInjector(parentLoc)) {
|
28960 | const parentIndex = getParentInjectorIndex(parentLoc);
|
28961 | const parentLView = getParentInjectorView(parentLoc, lView);
|
28962 | const parentData = parentLView[TVIEW].data;
|
28963 | // Creates a cumulative bloom filter that merges the parent's bloom filter
|
28964 | // and its own cumulative bloom (which contains tokens for all ancestors)
|
28965 | for (let i = 0; i < 8 /* BLOOM_SIZE */; i++) {
|
28966 | lView[injectorIndex + i] = parentLView[parentIndex + i] | parentData[parentIndex + i];
|
28967 | }
|
28968 | }
|
28969 | lView[injectorIndex + 8 /* PARENT */] = parentLoc;
|
28970 | return injectorIndex;
|
28971 | }
|
28972 | function insertBloom(arr, footer) {
|
28973 | arr.push(0, 0, 0, 0, 0, 0, 0, 0, footer);
|
28974 | }
|
28975 | function getInjectorIndex(tNode, lView) {
|
28976 | if (tNode.injectorIndex === -1 ||
|
28977 | // If the injector index is the same as its parent's injector index, then the index has been
|
28978 | // copied down from the parent node. No injector has been created yet on this node.
|
28979 | (tNode.parent && tNode.parent.injectorIndex === tNode.injectorIndex) ||
|
28980 | // After the first template pass, the injector index might exist but the parent values
|
28981 | // might not have been calculated yet for this instance
|
28982 | lView[tNode.injectorIndex + 8 /* PARENT */] === null) {
|
28983 | return -1;
|
28984 | }
|
28985 | else {
|
28986 | ngDevMode && assertIndexInRange(lView, tNode.injectorIndex);
|
28987 | return tNode.injectorIndex;
|
28988 | }
|
28989 | }
|
28990 | /**
|
28991 | * Finds the index of the parent injector, with a view offset if applicable. Used to set the
|
28992 | * parent injector initially.
|
28993 | *
|
28994 | * @returns Returns a number that is the combination of the number of LViews that we have to go up
|
28995 | * to find the LView containing the parent inject AND the index of the injector within that LView.
|
28996 | */
|
28997 | function getParentInjectorLocation(tNode, lView) {
|
28998 | if (tNode.parent && tNode.parent.injectorIndex !== -1) {
|
28999 | // If we have a parent `TNode` and there is an injector associated with it we are done, because
|
29000 | // the parent injector is within the current `LView`.
|
29001 | return tNode.parent.injectorIndex; // ViewOffset is 0
|
29002 | }
|
29003 | // When parent injector location is computed it may be outside of the current view. (ie it could
|
29004 | // be pointing to a declared parent location). This variable stores number of declaration parents
|
29005 | // we need to walk up in order to find the parent injector location.
|
29006 | let declarationViewOffset = 0;
|
29007 | let parentTNode = null;
|
29008 | let lViewCursor = lView;
|
29009 | // The parent injector is not in the current `LView`. We will have to walk the declared parent
|
29010 | // `LView` hierarchy and look for it. If we walk of the top, that means that there is no parent
|
29011 | // `NodeInjector`.
|
29012 | while (lViewCursor !== null) {
|
29013 | // First determine the `parentTNode` location. The parent pointer differs based on `TView.type`.
|
29014 | const tView = lViewCursor[TVIEW];
|
29015 | const tViewType = tView.type;
|
29016 | if (tViewType === 2 /* Embedded */) {
|
29017 | ngDevMode &&
|
29018 | assertDefined(tView.declTNode, 'Embedded TNodes should have declaration parents.');
|
29019 | parentTNode = tView.declTNode;
|
29020 | }
|
29021 | else if (tViewType === 1 /* Component */) {
|
29022 | // Components don't have `TView.declTNode` because each instance of component could be
|
29023 | // inserted in different location, hence `TView.declTNode` is meaningless.
|
29024 | parentTNode = lViewCursor[T_HOST];
|
29025 | }
|
29026 | else {
|
29027 | ngDevMode && assertEqual(tView.type, 0 /* Root */, 'Root type expected');
|
29028 | parentTNode = null;
|
29029 | }
|
29030 | if (parentTNode === null) {
|
29031 | // If we have no parent, than we are done.
|
29032 | return NO_PARENT_INJECTOR;
|
29033 | }
|
29034 | ngDevMode && parentTNode && assertTNodeForLView(parentTNode, lViewCursor[DECLARATION_VIEW]);
|
29035 | // Every iteration of the loop requires that we go to the declared parent.
|
29036 | declarationViewOffset++;
|
29037 | lViewCursor = lViewCursor[DECLARATION_VIEW];
|
29038 | if (parentTNode.injectorIndex !== -1) {
|
29039 | // We found a NodeInjector which points to something.
|
29040 | return (parentTNode.injectorIndex |
|
29041 | (declarationViewOffset << 16 /* ViewOffsetShift */));
|
29042 | }
|
29043 | }
|
29044 | return NO_PARENT_INJECTOR;
|
29045 | }
|
29046 | /**
|
29047 | * Makes a type or an injection token public to the DI system by adding it to an
|
29048 | * injector's bloom filter.
|
29049 | *
|
29050 | * @param di The node injector in which a directive will be added
|
29051 | * @param token The type or the injection token to be made public
|
29052 | */
|
29053 | function diPublicInInjector(injectorIndex, tView, token) {
|
29054 | bloomAdd(injectorIndex, tView, token);
|
29055 | }
|
29056 | function notFoundValueOrThrow(notFoundValue, token, flags) {
|
29057 | if (flags & InjectFlags.Optional) {
|
29058 | return notFoundValue;
|
29059 | }
|
29060 | else {
|
29061 | throwProviderNotFoundError(token, 'NodeInjector');
|
29062 | }
|
29063 | }
|
29064 | /**
|
29065 | * Returns the value associated to the given token from the ModuleInjector or throws exception
|
29066 | *
|
29067 | * @param lView The `LView` that contains the `tNode`
|
29068 | * @param token The token to look for
|
29069 | * @param flags Injection flags
|
29070 | * @param notFoundValue The value to return when the injection flags is `InjectFlags.Optional`
|
29071 | * @returns the value from the injector or throws an exception
|
29072 | */
|
29073 | function lookupTokenUsingModuleInjector(lView, token, flags, notFoundValue) {
|
29074 | if (flags & InjectFlags.Optional && notFoundValue === undefined) {
|
29075 | // This must be set or the NullInjector will throw for optional deps
|
29076 | notFoundValue = null;
|
29077 | }
|
29078 | if ((flags & (InjectFlags.Self | InjectFlags.Host)) === 0) {
|
29079 | const moduleInjector = lView[INJECTOR];
|
29080 | // switch to `injectInjectorOnly` implementation for module injector, since module injector
|
29081 | // should not have access to Component/Directive DI scope (that may happen through
|
29082 | // `directiveInject` implementation)
|
29083 | const previousInjectImplementation = setInjectImplementation(undefined);
|
29084 | try {
|
29085 | if (moduleInjector) {
|
29086 | return moduleInjector.get(token, notFoundValue, flags & InjectFlags.Optional);
|
29087 | }
|
29088 | else {
|
29089 | return injectRootLimpMode(token, notFoundValue, flags & InjectFlags.Optional);
|
29090 | }
|
29091 | }
|
29092 | finally {
|
29093 | setInjectImplementation(previousInjectImplementation);
|
29094 | }
|
29095 | }
|
29096 | return notFoundValueOrThrow(notFoundValue, token, flags);
|
29097 | }
|
29098 | /**
|
29099 | * Returns the value associated to the given token from the NodeInjectors => ModuleInjector.
|
29100 | *
|
29101 | * Look for the injector providing the token by walking up the node injector tree and then
|
29102 | * the module injector tree.
|
29103 | *
|
29104 | * This function patches `token` with `__NG_ELEMENT_ID__` which contains the id for the bloom
|
29105 | * filter. `-1` is reserved for injecting `Injector` (implemented by `NodeInjector`)
|
29106 | *
|
29107 | * @param tNode The Node where the search for the injector should start
|
29108 | * @param lView The `LView` that contains the `tNode`
|
29109 | * @param token The token to look for
|
29110 | * @param flags Injection flags
|
29111 | * @param notFoundValue The value to return when the injection flags is `InjectFlags.Optional`
|
29112 | * @returns the value from the injector, `null` when not found, or `notFoundValue` if provided
|
29113 | */
|
29114 | function getOrCreateInjectable(tNode, lView, token, flags = InjectFlags.Default, notFoundValue) {
|
29115 | if (tNode !== null) {
|
29116 | const bloomHash = bloomHashBitOrFactory(token);
|
29117 | // If the ID stored here is a function, this is a special object like ElementRef or TemplateRef
|
29118 | // so just call the factory function to create it.
|
29119 | if (typeof bloomHash === 'function') {
|
29120 | if (!enterDI(lView, tNode, flags)) {
|
29121 | // Failed to enter DI, try module injector instead. If a token is injected with the @Host
|
29122 | // flag, the module injector is not searched for that token in Ivy.
|
29123 | return (flags & InjectFlags.Host) ?
|
29124 | notFoundValueOrThrow(notFoundValue, token, flags) :
|
29125 | lookupTokenUsingModuleInjector(lView, token, flags, notFoundValue);
|
29126 | }
|
29127 | try {
|
29128 | const value = bloomHash();
|
29129 | if (value == null && !(flags & InjectFlags.Optional)) {
|
29130 | throwProviderNotFoundError(token);
|
29131 | }
|
29132 | else {
|
29133 | return value;
|
29134 | }
|
29135 | }
|
29136 | finally {
|
29137 | leaveDI();
|
29138 | }
|
29139 | }
|
29140 | else if (typeof bloomHash === 'number') {
|
29141 | // A reference to the previous injector TView that was found while climbing the element
|
29142 | // injector tree. This is used to know if viewProviders can be accessed on the current
|
29143 | // injector.
|
29144 | let previousTView = null;
|
29145 | let injectorIndex = getInjectorIndex(tNode, lView);
|
29146 | let parentLocation = NO_PARENT_INJECTOR;
|
29147 | let hostTElementNode = flags & InjectFlags.Host ? lView[DECLARATION_COMPONENT_VIEW][T_HOST] : null;
|
29148 | // If we should skip this injector, or if there is no injector on this node, start by
|
29149 | // searching the parent injector.
|
29150 | if (injectorIndex === -1 || flags & InjectFlags.SkipSelf) {
|
29151 | parentLocation = injectorIndex === -1 ? getParentInjectorLocation(tNode, lView) :
|
29152 | lView[injectorIndex + 8 /* PARENT */];
|
29153 | if (parentLocation === NO_PARENT_INJECTOR || !shouldSearchParent(flags, false)) {
|
29154 | injectorIndex = -1;
|
29155 | }
|
29156 | else {
|
29157 | previousTView = lView[TVIEW];
|
29158 | injectorIndex = getParentInjectorIndex(parentLocation);
|
29159 | lView = getParentInjectorView(parentLocation, lView);
|
29160 | }
|
29161 | }
|
29162 | // Traverse up the injector tree until we find a potential match or until we know there
|
29163 | // *isn't* a match.
|
29164 | while (injectorIndex !== -1) {
|
29165 | ngDevMode && assertNodeInjector(lView, injectorIndex);
|
29166 | // Check the current injector. If it matches, see if it contains token.
|
29167 | const tView = lView[TVIEW];
|
29168 | ngDevMode &&
|
29169 | assertTNodeForLView(tView.data[injectorIndex + 8 /* TNODE */], lView);
|
29170 | if (bloomHasToken(bloomHash, injectorIndex, tView.data)) {
|
29171 | // At this point, we have an injector which *may* contain the token, so we step through
|
29172 | // the providers and directives associated with the injector's corresponding node to get
|
29173 | // the instance.
|
29174 | const instance = searchTokensOnInjector(injectorIndex, lView, token, previousTView, flags, hostTElementNode);
|
29175 | if (instance !== NOT_FOUND) {
|
29176 | return instance;
|
29177 | }
|
29178 | }
|
29179 | parentLocation = lView[injectorIndex + 8 /* PARENT */];
|
29180 | if (parentLocation !== NO_PARENT_INJECTOR &&
|
29181 | shouldSearchParent(flags, lView[TVIEW].data[injectorIndex + 8 /* TNODE */] === hostTElementNode) &&
|
29182 | bloomHasToken(bloomHash, injectorIndex, lView)) {
|
29183 | // The def wasn't found anywhere on this node, so it was a false positive.
|
29184 | // Traverse up the tree and continue searching.
|
29185 | previousTView = tView;
|
29186 | injectorIndex = getParentInjectorIndex(parentLocation);
|
29187 | lView = getParentInjectorView(parentLocation, lView);
|
29188 | }
|
29189 | else {
|
29190 | // If we should not search parent OR If the ancestor bloom filter value does not have the
|
29191 | // bit corresponding to the directive we can give up on traversing up to find the specific
|
29192 | // injector.
|
29193 | injectorIndex = -1;
|
29194 | }
|
29195 | }
|
29196 | }
|
29197 | }
|
29198 | return lookupTokenUsingModuleInjector(lView, token, flags, notFoundValue);
|
29199 | }
|
29200 | const NOT_FOUND = {};
|
29201 | function createNodeInjector() {
|
29202 | return new NodeInjector(getCurrentTNode(), getLView());
|
29203 | }
|
29204 | function searchTokensOnInjector(injectorIndex, lView, token, previousTView, flags, hostTElementNode) {
|
29205 | const currentTView = lView[TVIEW];
|
29206 | const tNode = currentTView.data[injectorIndex + 8 /* TNODE */];
|
29207 | // First, we need to determine if view providers can be accessed by the starting element.
|
29208 | // There are two possibilities
|
29209 | const canAccessViewProviders = previousTView == null ?
|
29210 | // 1) This is the first invocation `previousTView == null` which means that we are at the
|
29211 | // `TNode` of where injector is starting to look. In such a case the only time we are allowed
|
29212 | // to look into the ViewProviders is if:
|
29213 | // - we are on a component
|
29214 | // - AND the injector set `includeViewProviders` to true (implying that the token can see
|
29215 | // ViewProviders because it is the Component or a Service which itself was declared in
|
29216 | // ViewProviders)
|
29217 | (isComponentHost(tNode) && includeViewProviders) :
|
29218 | // 2) `previousTView != null` which means that we are now walking across the parent nodes.
|
29219 | // In such a case we are only allowed to look into the ViewProviders if:
|
29220 | // - We just crossed from child View to Parent View `previousTView != currentTView`
|
29221 | // - AND the parent TNode is an Element.
|
29222 | // This means that we just came from the Component's View and therefore are allowed to see
|
29223 | // into the ViewProviders.
|
29224 | (previousTView != currentTView && ((tNode.type & 3 /* AnyRNode */) !== 0));
|
29225 | // This special case happens when there is a @host on the inject and when we are searching
|
29226 | // on the host element node.
|
29227 | const isHostSpecialCase = (flags & InjectFlags.Host) && hostTElementNode === tNode;
|
29228 | const injectableIdx = locateDirectiveOrProvider(tNode, currentTView, token, canAccessViewProviders, isHostSpecialCase);
|
29229 | if (injectableIdx !== null) {
|
29230 | return getNodeInjectable(lView, currentTView, injectableIdx, tNode);
|
29231 | }
|
29232 | else {
|
29233 | return NOT_FOUND;
|
29234 | }
|
29235 | }
|
29236 | /**
|
29237 | * Searches for the given token among the node's directives and providers.
|
29238 | *
|
29239 | * @param tNode TNode on which directives are present.
|
29240 | * @param tView The tView we are currently processing
|
29241 | * @param token Provider token or type of a directive to look for.
|
29242 | * @param canAccessViewProviders Whether view providers should be considered.
|
29243 | * @param isHostSpecialCase Whether the host special case applies.
|
29244 | * @returns Index of a found directive or provider, or null when none found.
|
29245 | */
|
29246 | function locateDirectiveOrProvider(tNode, tView, token, canAccessViewProviders, isHostSpecialCase) {
|
29247 | const nodeProviderIndexes = tNode.providerIndexes;
|
29248 | const tInjectables = tView.data;
|
29249 | const injectablesStart = nodeProviderIndexes & 1048575 /* ProvidersStartIndexMask */;
|
29250 | const directivesStart = tNode.directiveStart;
|
29251 | const directiveEnd = tNode.directiveEnd;
|
29252 | const cptViewProvidersCount = nodeProviderIndexes >> 20 /* CptViewProvidersCountShift */;
|
29253 | const startingIndex = canAccessViewProviders ? injectablesStart : injectablesStart + cptViewProvidersCount;
|
29254 | // When the host special case applies, only the viewProviders and the component are visible
|
29255 | const endIndex = isHostSpecialCase ? injectablesStart + cptViewProvidersCount : directiveEnd;
|
29256 | for (let i = startingIndex; i < endIndex; i++) {
|
29257 | const providerTokenOrDef = tInjectables[i];
|
29258 | if (i < directivesStart && token === providerTokenOrDef ||
|
29259 | i >= directivesStart && providerTokenOrDef.type === token) {
|
29260 | return i;
|
29261 | }
|
29262 | }
|
29263 | if (isHostSpecialCase) {
|
29264 | const dirDef = tInjectables[directivesStart];
|
29265 | if (dirDef && isComponentDef(dirDef) && dirDef.type === token) {
|
29266 | return directivesStart;
|
29267 | }
|
29268 | }
|
29269 | return null;
|
29270 | }
|
29271 | /**
|
29272 | * Retrieve or instantiate the injectable from the `LView` at particular `index`.
|
29273 | *
|
29274 | * This function checks to see if the value has already been instantiated and if so returns the
|
29275 | * cached `injectable`. Otherwise if it detects that the value is still a factory it
|
29276 | * instantiates the `injectable` and caches the value.
|
29277 | */
|
29278 | function getNodeInjectable(lView, tView, index, tNode) {
|
29279 | let value = lView[index];
|
29280 | const tData = tView.data;
|
29281 | if (isFactory(value)) {
|
29282 | const factory = value;
|
29283 | if (factory.resolving) {
|
29284 | throwCyclicDependencyError(stringifyForError(tData[index]));
|
29285 | }
|
29286 | const previousIncludeViewProviders = setIncludeViewProviders(factory.canSeeViewProviders);
|
29287 | factory.resolving = true;
|
29288 | const previousInjectImplementation = factory.injectImpl ? setInjectImplementation(factory.injectImpl) : null;
|
29289 | const success = enterDI(lView, tNode, InjectFlags.Default);
|
29290 | ngDevMode &&
|
29291 | assertEqual(success, true, 'Because flags do not contain \`SkipSelf\' we expect this to always succeed.');
|
29292 | try {
|
29293 | value = lView[index] = factory.factory(undefined, tData, lView, tNode);
|
29294 | // This code path is hit for both directives and providers.
|
29295 | // For perf reasons, we want to avoid searching for hooks on providers.
|
29296 | // It does no harm to try (the hooks just won't exist), but the extra
|
29297 | // checks are unnecessary and this is a hot path. So we check to see
|
29298 | // if the index of the dependency is in the directive range for this
|
29299 | // tNode. If it's not, we know it's a provider and skip hook registration.
|
29300 | if (tView.firstCreatePass && index >= tNode.directiveStart) {
|
29301 | ngDevMode && assertDirectiveDef(tData[index]);
|
29302 | registerPreOrderHooks(index, tData[index], tView);
|
29303 | }
|
29304 | }
|
29305 | finally {
|
29306 | previousInjectImplementation !== null &&
|
29307 | setInjectImplementation(previousInjectImplementation);
|
29308 | setIncludeViewProviders(previousIncludeViewProviders);
|
29309 | factory.resolving = false;
|
29310 | leaveDI();
|
29311 | }
|
29312 | }
|
29313 | return value;
|
29314 | }
|
29315 | /**
|
29316 | * Returns the bit in an injector's bloom filter that should be used to determine whether or not
|
29317 | * the directive might be provided by the injector.
|
29318 | *
|
29319 | * When a directive is public, it is added to the bloom filter and given a unique ID that can be
|
29320 | * retrieved on the Type. When the directive isn't public or the token is not a directive `null`
|
29321 | * is returned as the node injector can not possibly provide that token.
|
29322 | *
|
29323 | * @param token the injection token
|
29324 | * @returns the matching bit to check in the bloom filter or `null` if the token is not known.
|
29325 | * When the returned value is negative then it represents special values such as `Injector`.
|
29326 | */
|
29327 | function bloomHashBitOrFactory(token) {
|
29328 | ngDevMode && assertDefined(token, 'token must be defined');
|
29329 | if (typeof token === 'string') {
|
29330 | return token.charCodeAt(0) || 0;
|
29331 | }
|
29332 | const tokenId =
|
29333 | // First check with `hasOwnProperty` so we don't get an inherited ID.
|
29334 | token.hasOwnProperty(NG_ELEMENT_ID) ? token[NG_ELEMENT_ID] : undefined;
|
29335 | // Negative token IDs are used for special objects such as `Injector`
|
29336 | if (typeof tokenId === 'number') {
|
29337 | if (tokenId >= 0) {
|
29338 | return tokenId & BLOOM_MASK;
|
29339 | }
|
29340 | else {
|
29341 | ngDevMode &&
|
29342 | assertEqual(tokenId, -1 /* Injector */, 'Expecting to get Special Injector Id');
|
29343 | return createNodeInjector;
|
29344 | }
|
29345 | }
|
29346 | else {
|
29347 | return tokenId;
|
29348 | }
|
29349 | }
|
29350 | function bloomHasToken(bloomHash, injectorIndex, injectorView) {
|
29351 | // Create a mask that targets the specific bit associated with the directive we're looking for.
|
29352 | // JS bit operations are 32 bits, so this will be a number between 2^0 and 2^31, corresponding
|
29353 | // to bit positions 0 - 31 in a 32 bit integer.
|
29354 | const mask = 1 << bloomHash;
|
29355 | // Each bloom bucket in `injectorView` represents `BLOOM_BUCKET_BITS` number of bits of
|
29356 | // `bloomHash`. Any bits in `bloomHash` beyond `BLOOM_BUCKET_BITS` indicate the bucket offset
|
29357 | // that should be used.
|
29358 | const value = injectorView[injectorIndex + (bloomHash >> BLOOM_BUCKET_BITS)];
|
29359 | // If the bloom filter value has the bit corresponding to the directive's bloomBit flipped on,
|
29360 | // this injector is a potential match.
|
29361 | return !!(value & mask);
|
29362 | }
|
29363 | /** Returns true if flags prevent parent injector from being searched for tokens */
|
29364 | function shouldSearchParent(flags, isFirstHostTNode) {
|
29365 | return !(flags & InjectFlags.Self) && !(flags & InjectFlags.Host && isFirstHostTNode);
|
29366 | }
|
29367 | class NodeInjector {
|
29368 | constructor(_tNode, _lView) {
|
29369 | this._tNode = _tNode;
|
29370 | this._lView = _lView;
|
29371 | }
|
29372 | get(token, notFoundValue) {
|
29373 | return getOrCreateInjectable(this._tNode, this._lView, token, undefined, notFoundValue);
|
29374 | }
|
29375 | }
|
29376 |
|
29377 | /**
|
29378 | * @license
|
29379 | * Copyright Google LLC All Rights Reserved.
|
29380 | *
|
29381 | * Use of this source code is governed by an MIT-style license that can be
|
29382 | * found in the LICENSE file at https://angular.io/license
|
29383 | */
|
29384 | const ANNOTATIONS = '__annotations__';
|
29385 | const PARAMETERS = '__parameters__';
|
29386 | const PROP_METADATA = '__prop__metadata__';
|
29387 | /**
|
29388 | * @suppress {globalThis}
|
29389 | */
|
29390 | function makeDecorator(name, props, parentClass, additionalProcessing, typeFn) {
|
29391 | return noSideEffects(() => {
|
29392 | const metaCtor = makeMetadataCtor(props);
|
29393 | function DecoratorFactory(...args) {
|
29394 | if (this instanceof DecoratorFactory) {
|
29395 | metaCtor.call(this, ...args);
|
29396 | return this;
|
29397 | }
|
29398 | const annotationInstance = new DecoratorFactory(...args);
|
29399 | return function TypeDecorator(cls) {
|
29400 | if (typeFn)
|
29401 | typeFn(cls, ...args);
|
29402 | // Use of Object.defineProperty is important since it creates non-enumerable property which
|
29403 | // prevents the property is copied during subclassing.
|
29404 | const annotations = cls.hasOwnProperty(ANNOTATIONS) ?
|
29405 | cls[ANNOTATIONS] :
|
29406 | Object.defineProperty(cls, ANNOTATIONS, { value: [] })[ANNOTATIONS];
|
29407 | annotations.push(annotationInstance);
|
29408 | if (additionalProcessing)
|
29409 | additionalProcessing(cls);
|
29410 | return cls;
|
29411 | };
|
29412 | }
|
29413 | if (parentClass) {
|
29414 | DecoratorFactory.prototype = Object.create(parentClass.prototype);
|
29415 | }
|
29416 | DecoratorFactory.prototype.ngMetadataName = name;
|
29417 | DecoratorFactory.annotationCls = DecoratorFactory;
|
29418 | return DecoratorFactory;
|
29419 | });
|
29420 | }
|
29421 | function makeMetadataCtor(props) {
|
29422 | return function ctor(...args) {
|
29423 | if (props) {
|
29424 | const values = props(...args);
|
29425 | for (const propName in values) {
|
29426 | this[propName] = values[propName];
|
29427 | }
|
29428 | }
|
29429 | };
|
29430 | }
|
29431 | function makeParamDecorator(name, props, parentClass) {
|
29432 | return noSideEffects(() => {
|
29433 | const metaCtor = makeMetadataCtor(props);
|
29434 | function ParamDecoratorFactory(...args) {
|
29435 | if (this instanceof ParamDecoratorFactory) {
|
29436 | metaCtor.apply(this, args);
|
29437 | return this;
|
29438 | }
|
29439 | const annotationInstance = new ParamDecoratorFactory(...args);
|
29440 | ParamDecorator.annotation = annotationInstance;
|
29441 | return ParamDecorator;
|
29442 | function ParamDecorator(cls, unusedKey, index) {
|
29443 | // Use of Object.defineProperty is important since it creates non-enumerable property which
|
29444 | // prevents the property is copied during subclassing.
|
29445 | const parameters = cls.hasOwnProperty(PARAMETERS) ?
|
29446 | cls[PARAMETERS] :
|
29447 | Object.defineProperty(cls, PARAMETERS, { value: [] })[PARAMETERS];
|
29448 | // there might be gaps if some in between parameters do not have annotations.
|
29449 | // we pad with nulls.
|
29450 | while (parameters.length <= index) {
|
29451 | parameters.push(null);
|
29452 | }
|
29453 | (parameters[index] = parameters[index] || []).push(annotationInstance);
|
29454 | return cls;
|
29455 | }
|
29456 | }
|
29457 | if (parentClass) {
|
29458 | ParamDecoratorFactory.prototype = Object.create(parentClass.prototype);
|
29459 | }
|
29460 | ParamDecoratorFactory.prototype.ngMetadataName = name;
|
29461 | ParamDecoratorFactory.annotationCls = ParamDecoratorFactory;
|
29462 | return ParamDecoratorFactory;
|
29463 | });
|
29464 | }
|
29465 | function makePropDecorator(name, props, parentClass, additionalProcessing) {
|
29466 | return noSideEffects(() => {
|
29467 | const metaCtor = makeMetadataCtor(props);
|
29468 | function PropDecoratorFactory(...args) {
|
29469 | if (this instanceof PropDecoratorFactory) {
|
29470 | metaCtor.apply(this, args);
|
29471 | return this;
|
29472 | }
|
29473 | const decoratorInstance = new PropDecoratorFactory(...args);
|
29474 | function PropDecorator(target, name) {
|
29475 | const constructor = target.constructor;
|
29476 | // Use of Object.defineProperty is important because it creates a non-enumerable property
|
29477 | // which prevents the property from being copied during subclassing.
|
29478 | const meta = constructor.hasOwnProperty(PROP_METADATA) ?
|
29479 | constructor[PROP_METADATA] :
|
29480 | Object.defineProperty(constructor, PROP_METADATA, { value: {} })[PROP_METADATA];
|
29481 | meta[name] = meta.hasOwnProperty(name) && meta[name] || [];
|
29482 | meta[name].unshift(decoratorInstance);
|
29483 | if (additionalProcessing)
|
29484 | additionalProcessing(target, name, ...args);
|
29485 | }
|
29486 | return PropDecorator;
|
29487 | }
|
29488 | if (parentClass) {
|
29489 | PropDecoratorFactory.prototype = Object.create(parentClass.prototype);
|
29490 | }
|
29491 | PropDecoratorFactory.prototype.ngMetadataName = name;
|
29492 | PropDecoratorFactory.annotationCls = PropDecoratorFactory;
|
29493 | return PropDecoratorFactory;
|
29494 | });
|
29495 | }
|
29496 |
|
29497 | /**
|
29498 | * @license
|
29499 | * Copyright Google LLC All Rights Reserved.
|
29500 | *
|
29501 | * Use of this source code is governed by an MIT-style license that can be
|
29502 | * found in the LICENSE file at https://angular.io/license
|
29503 | */
|
29504 | function CREATE_ATTRIBUTE_DECORATOR__PRE_R3__() {
|
29505 | return makeParamDecorator('Attribute', (attributeName) => ({ attributeName }));
|
29506 | }
|
29507 | const CREATE_ATTRIBUTE_DECORATOR_IMPL = CREATE_ATTRIBUTE_DECORATOR__PRE_R3__;
|
29508 | /**
|
29509 | * Attribute decorator and metadata.
|
29510 | *
|
29511 | * @Annotation
|
29512 | * @publicApi
|
29513 | */
|
29514 | const Attribute$1 = CREATE_ATTRIBUTE_DECORATOR_IMPL();
|
29515 |
|
29516 | /**
|
29517 | * @license
|
29518 | * Copyright Google LLC All Rights Reserved.
|
29519 | *
|
29520 | * Use of this source code is governed by an MIT-style license that can be
|
29521 | * found in the LICENSE file at https://angular.io/license
|
29522 | */
|
29523 | /**
|
29524 | * Creates a token that can be used in a DI Provider.
|
29525 | *
|
29526 | * Use an `InjectionToken` whenever the type you are injecting is not reified (does not have a
|
29527 | * runtime representation) such as when injecting an interface, callable type, array or
|
29528 | * parameterized type.
|
29529 | *
|
29530 | * `InjectionToken` is parameterized on `T` which is the type of object which will be returned by
|
29531 | * the `Injector`. This provides additional level of type safety.
|
29532 | *
|
29533 | * ```
|
29534 | * interface MyInterface {...}
|
29535 | * var myInterface = injector.get(new InjectionToken<MyInterface>('SomeToken'));
|
29536 | * // myInterface is inferred to be MyInterface.
|
29537 | * ```
|
29538 | *
|
29539 | * When creating an `InjectionToken`, you can optionally specify a factory function which returns
|
29540 | * (possibly by creating) a default value of the parameterized type `T`. This sets up the
|
29541 | * `InjectionToken` using this factory as a provider as if it was defined explicitly in the
|
29542 | * application's root injector. If the factory function, which takes zero arguments, needs to inject
|
29543 | * dependencies, it can do so using the `inject` function. See below for an example.
|
29544 | *
|
29545 | * Additionally, if a `factory` is specified you can also specify the `providedIn` option, which
|
29546 | * overrides the above behavior and marks the token as belonging to a particular `@NgModule`. As
|
29547 | * mentioned above, `'root'` is the default value for `providedIn`.
|
29548 | *
|
29549 | * @usageNotes
|
29550 | * ### Basic Example
|
29551 | *
|
29552 | * ### Plain InjectionToken
|
29553 | *
|
29554 | * {@example core/di/ts/injector_spec.ts region='InjectionToken'}
|
29555 | *
|
29556 | * ### Tree-shakable InjectionToken
|
29557 | *
|
29558 | * {@example core/di/ts/injector_spec.ts region='ShakableInjectionToken'}
|
29559 | *
|
29560 | *
|
29561 | * @publicApi
|
29562 | */
|
29563 | class InjectionToken {
|
29564 | constructor(_desc, options) {
|
29565 | this._desc = _desc;
|
29566 | /** @internal */
|
29567 | this.ngMetadataName = 'InjectionToken';
|
29568 | this.ɵprov = undefined;
|
29569 | if (typeof options == 'number') {
|
29570 | (typeof ngDevMode === 'undefined' || ngDevMode) &&
|
29571 | assertLessThan(options, 0, 'Only negative numbers are supported here');
|
29572 | // This is a special hack to assign __NG_ELEMENT_ID__ to this instance.
|
29573 | // See `InjectorMarkers`
|
29574 | this.__NG_ELEMENT_ID__ = options;
|
29575 | }
|
29576 | else if (options !== undefined) {
|
29577 | this.ɵprov = ɵɵdefineInjectable({
|
29578 | token: this,
|
29579 | providedIn: options.providedIn || 'root',
|
29580 | factory: options.factory,
|
29581 | });
|
29582 | }
|
29583 | }
|
29584 | toString() {
|
29585 | return `InjectionToken ${this._desc}`;
|
29586 | }
|
29587 | }
|
29588 |
|
29589 | /**
|
29590 | * @license
|
29591 | * Copyright Google LLC All Rights Reserved.
|
29592 | *
|
29593 | * Use of this source code is governed by an MIT-style license that can be
|
29594 | * found in the LICENSE file at https://angular.io/license
|
29595 | */
|
29596 | /**
|
29597 | * A DI token that you can use to create a virtual [provider](guide/glossary#provider)
|
29598 | * that will populate the `entryComponents` field of components and NgModules
|
29599 | * based on its `useValue` property value.
|
29600 | * All components that are referenced in the `useValue` value (either directly
|
29601 | * or in a nested array or map) are added to the `entryComponents` property.
|
29602 | *
|
29603 | * @usageNotes
|
29604 | *
|
29605 | * The following example shows how the router can populate the `entryComponents`
|
29606 | * field of an NgModule based on a router configuration that refers
|
29607 | * to components.
|
29608 | *
|
29609 | * ```typescript
|
29610 | * // helper function inside the router
|
29611 | * function provideRoutes(routes) {
|
29612 | * return [
|
29613 | * {provide: ROUTES, useValue: routes},
|
29614 | * {provide: ANALYZE_FOR_ENTRY_COMPONENTS, useValue: routes, multi: true}
|
29615 | * ];
|
29616 | * }
|
29617 | *
|
29618 | * // user code
|
29619 | * let routes = [
|
29620 | * {path: '/root', component: RootComp},
|
29621 | * {path: '/teams', component: TeamsComp}
|
29622 | * ];
|
29623 | *
|
29624 | * @NgModule({
|
29625 | * providers: [provideRoutes(routes)]
|
29626 | * })
|
29627 | * class ModuleWithRoutes {}
|
29628 | * ```
|
29629 | *
|
29630 | * @publicApi
|
29631 | * @deprecated Since 9.0.0. With Ivy, this property is no longer necessary.
|
29632 | */
|
29633 | const ANALYZE_FOR_ENTRY_COMPONENTS = new InjectionToken('AnalyzeForEntryComponents');
|
29634 | // Stores the default value of `emitDistinctChangesOnly` when the `emitDistinctChangesOnly` is not
|
29635 | // explicitly set. This value will be changed to `true` in v12.
|
29636 | // TODO(misko): switch the default in v12 to `true`. See: packages/compiler/src/core.ts
|
29637 | const emitDistinctChangesOnlyDefaultValue$1 = false;
|
29638 | /**
|
29639 | * Base class for query metadata.
|
29640 | *
|
29641 | * @see `ContentChildren`.
|
29642 | * @see `ContentChild`.
|
29643 | * @see `ViewChildren`.
|
29644 | * @see `ViewChild`.
|
29645 | *
|
29646 | * @publicApi
|
29647 | */
|
29648 | class Query {
|
29649 | }
|
29650 | const ɵ0$1 = (selector, data = {}) => (Object.assign({ selector, first: false, isViewQuery: false, descendants: false, emitDistinctChangesOnly: emitDistinctChangesOnlyDefaultValue$1 }, data));
|
29651 | /**
|
29652 | * ContentChildren decorator and metadata.
|
29653 | *
|
29654 | *
|
29655 | * @Annotation
|
29656 | * @publicApi
|
29657 | */
|
29658 | const ContentChildren = makePropDecorator('ContentChildren', ɵ0$1, Query);
|
29659 | const ɵ1 = (selector, data = {}) => (Object.assign({ selector, first: true, isViewQuery: false, descendants: true }, data));
|
29660 | /**
|
29661 | * ContentChild decorator and metadata.
|
29662 | *
|
29663 | *
|
29664 | * @Annotation
|
29665 | *
|
29666 | * @publicApi
|
29667 | */
|
29668 | const ContentChild = makePropDecorator('ContentChild', ɵ1, Query);
|
29669 | const ɵ2 = (selector, data = {}) => (Object.assign({ selector, first: false, isViewQuery: true, descendants: true, emitDistinctChangesOnly: emitDistinctChangesOnlyDefaultValue$1 }, data));
|
29670 | /**
|
29671 | * ViewChildren decorator and metadata.
|
29672 | *
|
29673 | * @Annotation
|
29674 | * @publicApi
|
29675 | */
|
29676 | const ViewChildren = makePropDecorator('ViewChildren', ɵ2, Query);
|
29677 | const ɵ3 = (selector, data) => (Object.assign({ selector, first: true, isViewQuery: true, descendants: true }, data));
|
29678 | /**
|
29679 | * ViewChild decorator and metadata.
|
29680 | *
|
29681 | * @Annotation
|
29682 | * @publicApi
|
29683 | */
|
29684 | const ViewChild = makePropDecorator('ViewChild', ɵ3, Query);
|
29685 |
|
29686 | /**
|
29687 | * @license
|
29688 | * Copyright Google LLC All Rights Reserved.
|
29689 | *
|
29690 | * Use of this source code is governed by an MIT-style license that can be
|
29691 | * found in the LICENSE file at https://angular.io/license
|
29692 | */
|
29693 | var R3ResolvedDependencyType$1;
|
29694 | (function (R3ResolvedDependencyType) {
|
29695 | R3ResolvedDependencyType[R3ResolvedDependencyType["Token"] = 0] = "Token";
|
29696 | R3ResolvedDependencyType[R3ResolvedDependencyType["Attribute"] = 1] = "Attribute";
|
29697 | R3ResolvedDependencyType[R3ResolvedDependencyType["ChangeDetectorRef"] = 2] = "ChangeDetectorRef";
|
29698 | R3ResolvedDependencyType[R3ResolvedDependencyType["Invalid"] = 3] = "Invalid";
|
29699 | })(R3ResolvedDependencyType$1 || (R3ResolvedDependencyType$1 = {}));
|
29700 | var R3FactoryTarget$1;
|
29701 | (function (R3FactoryTarget) {
|
29702 | R3FactoryTarget[R3FactoryTarget["Directive"] = 0] = "Directive";
|
29703 | R3FactoryTarget[R3FactoryTarget["Component"] = 1] = "Component";
|
29704 | R3FactoryTarget[R3FactoryTarget["Injectable"] = 2] = "Injectable";
|
29705 | R3FactoryTarget[R3FactoryTarget["Pipe"] = 3] = "Pipe";
|
29706 | R3FactoryTarget[R3FactoryTarget["NgModule"] = 4] = "NgModule";
|
29707 | })(R3FactoryTarget$1 || (R3FactoryTarget$1 = {}));
|
29708 | var ViewEncapsulation$2;
|
29709 | (function (ViewEncapsulation) {
|
29710 | ViewEncapsulation[ViewEncapsulation["Emulated"] = 0] = "Emulated";
|
29711 | // Historically the 1 value was for `Native` encapsulation which has been removed as of v11.
|
29712 | ViewEncapsulation[ViewEncapsulation["None"] = 2] = "None";
|
29713 | ViewEncapsulation[ViewEncapsulation["ShadowDom"] = 3] = "ShadowDom";
|
29714 | })(ViewEncapsulation$2 || (ViewEncapsulation$2 = {}));
|
29715 |
|
29716 | /**
|
29717 | * @license
|
29718 | * Copyright Google LLC All Rights Reserved.
|
29719 | *
|
29720 | * Use of this source code is governed by an MIT-style license that can be
|
29721 | * found in the LICENSE file at https://angular.io/license
|
29722 | */
|
29723 | /**
|
29724 | * @description
|
29725 | *
|
29726 | * Represents a type that a Component or other object is instances of.
|
29727 | *
|
29728 | * An example of a `Type` is `MyCustomComponent` class, which in JavaScript is represented by
|
29729 | * the `MyCustomComponent` constructor function.
|
29730 | *
|
29731 | * @publicApi
|
29732 | */
|
29733 | const Type$2 = Function;
|
29734 | function isType(v) {
|
29735 | return typeof v === 'function';
|
29736 | }
|
29737 |
|
29738 | /**
|
29739 | * @license
|
29740 | * Copyright Google LLC All Rights Reserved.
|
29741 | *
|
29742 | * Use of this source code is governed by an MIT-style license that can be
|
29743 | * found in the LICENSE file at https://angular.io/license
|
29744 | */
|
29745 | function removeFromArray(arr, index) {
|
29746 | // perf: array.pop is faster than array.splice!
|
29747 | if (index >= arr.length - 1) {
|
29748 | return arr.pop();
|
29749 | }
|
29750 | else {
|
29751 | return arr.splice(index, 1)[0];
|
29752 | }
|
29753 | }
|
29754 | function newArray$1(size, value) {
|
29755 | const list = [];
|
29756 | for (let i = 0; i < size; i++) {
|
29757 | list.push(value);
|
29758 | }
|
29759 | return list;
|
29760 | }
|
29761 |
|
29762 | /**
|
29763 | * @license
|
29764 | * Copyright Google LLC All Rights Reserved.
|
29765 | *
|
29766 | * Use of this source code is governed by an MIT-style license that can be
|
29767 | * found in the LICENSE file at https://angular.io/license
|
29768 | */
|
29769 | /*
|
29770 | * #########################
|
29771 | * Attention: These Regular expressions have to hold even if the code is minified!
|
29772 | * ##########################
|
29773 | */
|
29774 | /**
|
29775 | * Regular expression that detects pass-through constructors for ES5 output. This Regex
|
29776 | * intends to capture the common delegation pattern emitted by TypeScript and Babel. Also
|
29777 | * it intends to capture the pattern where existing constructors have been downleveled from
|
29778 | * ES2015 to ES5 using TypeScript w/ downlevel iteration. e.g.
|
29779 | *
|
29780 | * ```
|
29781 | * function MyClass() {
|
29782 | * var _this = _super.apply(this, arguments) || this;
|
29783 | * ```
|
29784 | *
|
29785 | * ```
|
29786 | * function MyClass() {
|
29787 | * var _this = _super.apply(this, __spread(arguments)) || this;
|
29788 | * ```
|
29789 | *
|
29790 | * More details can be found in: https://github.com/angular/angular/issues/38453.
|
29791 | */
|
29792 | const ES5_DELEGATE_CTOR = /^function\s+\S+\(\)\s*{[\s\S]+\.apply\(this,\s*(arguments|[^()]+\(arguments\))\)/;
|
29793 | /** Regular expression that detects ES2015 classes which extend from other classes. */
|
29794 | const ES2015_INHERITED_CLASS = /^class\s+[A-Za-z\d$_]*\s*extends\s+[^{]+{/;
|
29795 | /**
|
29796 | * Regular expression that detects ES2015 classes which extend from other classes and
|
29797 | * have an explicit constructor defined.
|
29798 | */
|
29799 | const ES2015_INHERITED_CLASS_WITH_CTOR = /^class\s+[A-Za-z\d$_]*\s*extends\s+[^{]+{[\s\S]*constructor\s*\(/;
|
29800 | /**
|
29801 | * Regular expression that detects ES2015 classes which extend from other classes
|
29802 | * and inherit a constructor.
|
29803 | */
|
29804 | const ES2015_INHERITED_CLASS_WITH_DELEGATE_CTOR = /^class\s+[A-Za-z\d$_]*\s*extends\s+[^{]+{[\s\S]*constructor\s*\(\)\s*{\s*super\(\.\.\.arguments\)/;
|
29805 | /**
|
29806 | * Determine whether a stringified type is a class which delegates its constructor
|
29807 | * to its parent.
|
29808 | *
|
29809 | * This is not trivial since compiled code can actually contain a constructor function
|
29810 | * even if the original source code did not. For instance, when the child class contains
|
29811 | * an initialized instance property.
|
29812 | */
|
29813 | function isDelegateCtor(typeStr) {
|
29814 | return ES5_DELEGATE_CTOR.test(typeStr) ||
|
29815 | ES2015_INHERITED_CLASS_WITH_DELEGATE_CTOR.test(typeStr) ||
|
29816 | (ES2015_INHERITED_CLASS.test(typeStr) && !ES2015_INHERITED_CLASS_WITH_CTOR.test(typeStr));
|
29817 | }
|
29818 | class ReflectionCapabilities {
|
29819 | constructor(reflect) {
|
29820 | this._reflect = reflect || _global$1['Reflect'];
|
29821 | }
|
29822 | isReflectionEnabled() {
|
29823 | return true;
|
29824 | }
|
29825 | factory(t) {
|
29826 | return (...args) => new t(...args);
|
29827 | }
|
29828 | /** @internal */
|
29829 | _zipTypesAndAnnotations(paramTypes, paramAnnotations) {
|
29830 | let result;
|
29831 | if (typeof paramTypes === 'undefined') {
|
29832 | result = newArray$1(paramAnnotations.length);
|
29833 | }
|
29834 | else {
|
29835 | result = newArray$1(paramTypes.length);
|
29836 | }
|
29837 | for (let i = 0; i < result.length; i++) {
|
29838 | // TS outputs Object for parameters without types, while Traceur omits
|
29839 | // the annotations. For now we preserve the Traceur behavior to aid
|
29840 | // migration, but this can be revisited.
|
29841 | if (typeof paramTypes === 'undefined') {
|
29842 | result[i] = [];
|
29843 | }
|
29844 | else if (paramTypes[i] && paramTypes[i] != Object) {
|
29845 | result[i] = [paramTypes[i]];
|
29846 | }
|
29847 | else {
|
29848 | result[i] = [];
|
29849 | }
|
29850 | if (paramAnnotations && paramAnnotations[i] != null) {
|
29851 | result[i] = result[i].concat(paramAnnotations[i]);
|
29852 | }
|
29853 | }
|
29854 | return result;
|
29855 | }
|
29856 | _ownParameters(type, parentCtor) {
|
29857 | const typeStr = type.toString();
|
29858 | // If we have no decorators, we only have function.length as metadata.
|
29859 | // In that case, to detect whether a child class declared an own constructor or not,
|
29860 | // we need to look inside of that constructor to check whether it is
|
29861 | // just calling the parent.
|
29862 | // This also helps to work around for https://github.com/Microsoft/TypeScript/issues/12439
|
29863 | // that sets 'design:paramtypes' to []
|
29864 | // if a class inherits from another class but has no ctor declared itself.
|
29865 | if (isDelegateCtor(typeStr)) {
|
29866 | return null;
|
29867 | }
|
29868 | // Prefer the direct API.
|
29869 | if (type.parameters && type.parameters !== parentCtor.parameters) {
|
29870 | return type.parameters;
|
29871 | }
|
29872 | // API of tsickle for lowering decorators to properties on the class.
|
29873 | const tsickleCtorParams = type.ctorParameters;
|
29874 | if (tsickleCtorParams && tsickleCtorParams !== parentCtor.ctorParameters) {
|
29875 | // Newer tsickle uses a function closure
|
29876 | // Retain the non-function case for compatibility with older tsickle
|
29877 | const ctorParameters = typeof tsickleCtorParams === 'function' ? tsickleCtorParams() : tsickleCtorParams;
|
29878 | const paramTypes = ctorParameters.map((ctorParam) => ctorParam && ctorParam.type);
|
29879 | const paramAnnotations = ctorParameters.map((ctorParam) => ctorParam && convertTsickleDecoratorIntoMetadata(ctorParam.decorators));
|
29880 | return this._zipTypesAndAnnotations(paramTypes, paramAnnotations);
|
29881 | }
|
29882 | // API for metadata created by invoking the decorators.
|
29883 | const paramAnnotations = type.hasOwnProperty(PARAMETERS) && type[PARAMETERS];
|
29884 | const paramTypes = this._reflect && this._reflect.getOwnMetadata &&
|
29885 | this._reflect.getOwnMetadata('design:paramtypes', type);
|
29886 | if (paramTypes || paramAnnotations) {
|
29887 | return this._zipTypesAndAnnotations(paramTypes, paramAnnotations);
|
29888 | }
|
29889 | // If a class has no decorators, at least create metadata
|
29890 | // based on function.length.
|
29891 | // Note: We know that this is a real constructor as we checked
|
29892 | // the content of the constructor above.
|
29893 | return newArray$1(type.length);
|
29894 | }
|
29895 | parameters(type) {
|
29896 | // Note: only report metadata if we have at least one class decorator
|
29897 | // to stay in sync with the static reflector.
|
29898 | if (!isType(type)) {
|
29899 | return [];
|
29900 | }
|
29901 | const parentCtor = getParentCtor(type);
|
29902 | let parameters = this._ownParameters(type, parentCtor);
|
29903 | if (!parameters && parentCtor !== Object) {
|
29904 | parameters = this.parameters(parentCtor);
|
29905 | }
|
29906 | return parameters || [];
|
29907 | }
|
29908 | _ownAnnotations(typeOrFunc, parentCtor) {
|
29909 | // Prefer the direct API.
|
29910 | if (typeOrFunc.annotations && typeOrFunc.annotations !== parentCtor.annotations) {
|
29911 | let annotations = typeOrFunc.annotations;
|
29912 | if (typeof annotations === 'function' && annotations.annotations) {
|
29913 | annotations = annotations.annotations;
|
29914 | }
|
29915 | return annotations;
|
29916 | }
|
29917 | // API of tsickle for lowering decorators to properties on the class.
|
29918 | if (typeOrFunc.decorators && typeOrFunc.decorators !== parentCtor.decorators) {
|
29919 | return convertTsickleDecoratorIntoMetadata(typeOrFunc.decorators);
|
29920 | }
|
29921 | // API for metadata created by invoking the decorators.
|
29922 | if (typeOrFunc.hasOwnProperty(ANNOTATIONS)) {
|
29923 | return typeOrFunc[ANNOTATIONS];
|
29924 | }
|
29925 | return null;
|
29926 | }
|
29927 | annotations(typeOrFunc) {
|
29928 | if (!isType(typeOrFunc)) {
|
29929 | return [];
|
29930 | }
|
29931 | const parentCtor = getParentCtor(typeOrFunc);
|
29932 | const ownAnnotations = this._ownAnnotations(typeOrFunc, parentCtor) || [];
|
29933 | const parentAnnotations = parentCtor !== Object ? this.annotations(parentCtor) : [];
|
29934 | return parentAnnotations.concat(ownAnnotations);
|
29935 | }
|
29936 | _ownPropMetadata(typeOrFunc, parentCtor) {
|
29937 | // Prefer the direct API.
|
29938 | if (typeOrFunc.propMetadata &&
|
29939 | typeOrFunc.propMetadata !== parentCtor.propMetadata) {
|
29940 | let propMetadata = typeOrFunc.propMetadata;
|
29941 | if (typeof propMetadata === 'function' && propMetadata.propMetadata) {
|
29942 | propMetadata = propMetadata.propMetadata;
|
29943 | }
|
29944 | return propMetadata;
|
29945 | }
|
29946 | // API of tsickle for lowering decorators to properties on the class.
|
29947 | if (typeOrFunc.propDecorators &&
|
29948 | typeOrFunc.propDecorators !== parentCtor.propDecorators) {
|
29949 | const propDecorators = typeOrFunc.propDecorators;
|
29950 | const propMetadata = {};
|
29951 | Object.keys(propDecorators).forEach(prop => {
|
29952 | propMetadata[prop] = convertTsickleDecoratorIntoMetadata(propDecorators[prop]);
|
29953 | });
|
29954 | return propMetadata;
|
29955 | }
|
29956 | // API for metadata created by invoking the decorators.
|
29957 | if (typeOrFunc.hasOwnProperty(PROP_METADATA)) {
|
29958 | return typeOrFunc[PROP_METADATA];
|
29959 | }
|
29960 | return null;
|
29961 | }
|
29962 | propMetadata(typeOrFunc) {
|
29963 | if (!isType(typeOrFunc)) {
|
29964 | return {};
|
29965 | }
|
29966 | const parentCtor = getParentCtor(typeOrFunc);
|
29967 | const propMetadata = {};
|
29968 | if (parentCtor !== Object) {
|
29969 | const parentPropMetadata = this.propMetadata(parentCtor);
|
29970 | Object.keys(parentPropMetadata).forEach((propName) => {
|
29971 | propMetadata[propName] = parentPropMetadata[propName];
|
29972 | });
|
29973 | }
|
29974 | const ownPropMetadata = this._ownPropMetadata(typeOrFunc, parentCtor);
|
29975 | if (ownPropMetadata) {
|
29976 | Object.keys(ownPropMetadata).forEach((propName) => {
|
29977 | const decorators = [];
|
29978 | if (propMetadata.hasOwnProperty(propName)) {
|
29979 | decorators.push(...propMetadata[propName]);
|
29980 | }
|
29981 | decorators.push(...ownPropMetadata[propName]);
|
29982 | propMetadata[propName] = decorators;
|
29983 | });
|
29984 | }
|
29985 | return propMetadata;
|
29986 | }
|
29987 | ownPropMetadata(typeOrFunc) {
|
29988 | if (!isType(typeOrFunc)) {
|
29989 | return {};
|
29990 | }
|
29991 | return this._ownPropMetadata(typeOrFunc, getParentCtor(typeOrFunc)) || {};
|
29992 | }
|
29993 | hasLifecycleHook(type, lcProperty) {
|
29994 | return type instanceof Type$2 && lcProperty in type.prototype;
|
29995 | }
|
29996 | guards(type) {
|
29997 | return {};
|
29998 | }
|
29999 | getter(name) {
|
30000 | return new Function('o', 'return o.' + name + ';');
|
30001 | }
|
30002 | setter(name) {
|
30003 | return new Function('o', 'v', 'return o.' + name + ' = v;');
|
30004 | }
|
30005 | method(name) {
|
30006 | const functionBody = `if (!o.${name}) throw new Error('"${name}" is undefined');
|
30007 | return o.${name}.apply(o, args);`;
|
30008 | return new Function('o', 'args', functionBody);
|
30009 | }
|
30010 | // There is not a concept of import uri in Js, but this is useful in developing Dart applications.
|
30011 | importUri(type) {
|
30012 | // StaticSymbol
|
30013 | if (typeof type === 'object' && type['filePath']) {
|
30014 | return type['filePath'];
|
30015 | }
|
30016 | // Runtime type
|
30017 | return `./${stringify$1(type)}`;
|
30018 | }
|
30019 | resourceUri(type) {
|
30020 | return `./${stringify$1(type)}`;
|
30021 | }
|
30022 | resolveIdentifier(name, moduleUrl, members, runtime) {
|
30023 | return runtime;
|
30024 | }
|
30025 | resolveEnum(enumIdentifier, name) {
|
30026 | return enumIdentifier[name];
|
30027 | }
|
30028 | }
|
30029 | function convertTsickleDecoratorIntoMetadata(decoratorInvocations) {
|
30030 | if (!decoratorInvocations) {
|
30031 | return [];
|
30032 | }
|
30033 | return decoratorInvocations.map(decoratorInvocation => {
|
30034 | const decoratorType = decoratorInvocation.type;
|
30035 | const annotationCls = decoratorType.annotationCls;
|
30036 | const annotationArgs = decoratorInvocation.args ? decoratorInvocation.args : [];
|
30037 | return new annotationCls(...annotationArgs);
|
30038 | });
|
30039 | }
|
30040 | function getParentCtor(ctor) {
|
30041 | const parentProto = ctor.prototype ? Object.getPrototypeOf(ctor.prototype) : null;
|
30042 | const parentCtor = parentProto ? parentProto.constructor : null;
|
30043 | // Note: We always use `Object` as the null value
|
30044 | // to simplify checking later on.
|
30045 | return parentCtor || Object;
|
30046 | }
|
30047 |
|
30048 | /**
|
30049 | * @license
|
30050 | * Copyright Google LLC All Rights Reserved.
|
30051 | *
|
30052 | * Use of this source code is governed by an MIT-style license that can be
|
30053 | * found in the LICENSE file at https://angular.io/license
|
30054 | */
|
30055 | const _THROW_IF_NOT_FOUND = {};
|
30056 | const THROW_IF_NOT_FOUND = _THROW_IF_NOT_FOUND;
|
30057 | /*
|
30058 | * Name of a property (that we patch onto DI decorator), which is used as an annotation of which
|
30059 | * InjectFlag this decorator represents. This allows to avoid direct references to the DI decorators
|
30060 | * in the code, thus making them tree-shakable.
|
30061 | */
|
30062 | const DI_DECORATOR_FLAG = '__NG_DI_FLAG__';
|
30063 | const NG_TEMP_TOKEN_PATH = 'ngTempTokenPath';
|
30064 | const NG_TOKEN_PATH = 'ngTokenPath';
|
30065 | const NEW_LINE = /\n/gm;
|
30066 | const NO_NEW_LINE = 'ɵ';
|
30067 | const SOURCE = '__source';
|
30068 | const ɵ0$2 = getClosureSafeProperty;
|
30069 | const USE_VALUE$2 = getClosureSafeProperty({ provide: String, useValue: ɵ0$2 });
|
30070 | /**
|
30071 | * Current injector value used by `inject`.
|
30072 | * - `undefined`: it is an error to call `inject`
|
30073 | * - `null`: `inject` can be called but there is no injector (limp-mode).
|
30074 | * - Injector instance: Use the injector for resolution.
|
30075 | */
|
30076 | let _currentInjector = undefined;
|
30077 | function setCurrentInjector(injector) {
|
30078 | const former = _currentInjector;
|
30079 | _currentInjector = injector;
|
30080 | return former;
|
30081 | }
|
30082 | function injectInjectorOnly(token, flags = InjectFlags.Default) {
|
30083 | if (_currentInjector === undefined) {
|
30084 | throw new Error(`inject() must be called from an injection context`);
|
30085 | }
|
30086 | else if (_currentInjector === null) {
|
30087 | return injectRootLimpMode(token, undefined, flags);
|
30088 | }
|
30089 | else {
|
30090 | return _currentInjector.get(token, flags & InjectFlags.Optional ? null : undefined, flags);
|
30091 | }
|
30092 | }
|
30093 | function ɵɵinject(token, flags = InjectFlags.Default) {
|
30094 | return (getInjectImplementation() || injectInjectorOnly)(resolveForwardRef$1(token), flags);
|
30095 | }
|
30096 | function injectArgs(types) {
|
30097 | const args = [];
|
30098 | for (let i = 0; i < types.length; i++) {
|
30099 | const arg = resolveForwardRef$1(types[i]);
|
30100 | if (Array.isArray(arg)) {
|
30101 | if (arg.length === 0) {
|
30102 | throw new Error('Arguments array must have arguments.');
|
30103 | }
|
30104 | let type = undefined;
|
30105 | let flags = InjectFlags.Default;
|
30106 | for (let j = 0; j < arg.length; j++) {
|
30107 | const meta = arg[j];
|
30108 | const flag = getInjectFlag(meta);
|
30109 | if (typeof flag === 'number') {
|
30110 | // Special case when we handle @Inject decorator.
|
30111 | if (flag === -1 /* Inject */) {
|
30112 | type = meta.token;
|
30113 | }
|
30114 | else {
|
30115 | flags |= flag;
|
30116 | }
|
30117 | }
|
30118 | else {
|
30119 | type = meta;
|
30120 | }
|
30121 | }
|
30122 | args.push(ɵɵinject(type, flags));
|
30123 | }
|
30124 | else {
|
30125 | args.push(ɵɵinject(arg));
|
30126 | }
|
30127 | }
|
30128 | return args;
|
30129 | }
|
30130 | /**
|
30131 | * Attaches a given InjectFlag to a given decorator using monkey-patching.
|
30132 | * Since DI decorators can be used in providers `deps` array (when provider is configured using
|
30133 | * `useFactory`) without initialization (e.g. `Host`) and as an instance (e.g. `new Host()`), we
|
30134 | * attach the flag to make it available both as a static property and as a field on decorator
|
30135 | * instance.
|
30136 | *
|
30137 | * @param decorator Provided DI decorator.
|
30138 | * @param flag InjectFlag that should be applied.
|
30139 | */
|
30140 | function attachInjectFlag(decorator, flag) {
|
30141 | decorator[DI_DECORATOR_FLAG] = flag;
|
30142 | decorator.prototype[DI_DECORATOR_FLAG] = flag;
|
30143 | return decorator;
|
30144 | }
|
30145 | /**
|
30146 | * Reads monkey-patched property that contains InjectFlag attached to a decorator.
|
30147 | *
|
30148 | * @param token Token that may contain monkey-patched DI flags property.
|
30149 | */
|
30150 | function getInjectFlag(token) {
|
30151 | return token[DI_DECORATOR_FLAG];
|
30152 | }
|
30153 | function catchInjectorError(e, token, injectorErrorName, source) {
|
30154 | const tokenPath = e[NG_TEMP_TOKEN_PATH];
|
30155 | if (token[SOURCE]) {
|
30156 | tokenPath.unshift(token[SOURCE]);
|
30157 | }
|
30158 | e.message = formatError('\n' + e.message, tokenPath, injectorErrorName, source);
|
30159 | e[NG_TOKEN_PATH] = tokenPath;
|
30160 | e[NG_TEMP_TOKEN_PATH] = null;
|
30161 | throw e;
|
30162 | }
|
30163 | function formatError(text, obj, injectorErrorName, source = null) {
|
30164 | text = text && text.charAt(0) === '\n' && text.charAt(1) == NO_NEW_LINE ? text.substr(2) : text;
|
30165 | let context = stringify$1(obj);
|
30166 | if (Array.isArray(obj)) {
|
30167 | context = obj.map(stringify$1).join(' -> ');
|
30168 | }
|
30169 | else if (typeof obj === 'object') {
|
30170 | let parts = [];
|
30171 | for (let key in obj) {
|
30172 | if (obj.hasOwnProperty(key)) {
|
30173 | let value = obj[key];
|
30174 | parts.push(key + ':' + (typeof value === 'string' ? JSON.stringify(value) : stringify$1(value)));
|
30175 | }
|
30176 | }
|
30177 | context = `{${parts.join(', ')}}`;
|
30178 | }
|
30179 | return `${injectorErrorName}${source ? '(' + source + ')' : ''}[${context}]: ${text.replace(NEW_LINE, '\n ')}`;
|
30180 | }
|
30181 |
|
30182 | /**
|
30183 | * @license
|
30184 | * Copyright Google LLC All Rights Reserved.
|
30185 | *
|
30186 | * Use of this source code is governed by an MIT-style license that can be
|
30187 | * found in the LICENSE file at https://angular.io/license
|
30188 | */
|
30189 | const ɵ0$3 = (token) => ({ token });
|
30190 | /**
|
30191 | * Inject decorator and metadata.
|
30192 | *
|
30193 | * @Annotation
|
30194 | * @publicApi
|
30195 | */
|
30196 | const Inject = attachInjectFlag(
|
30197 | // Disable tslint because `DecoratorFlags` is a const enum which gets inlined.
|
30198 | // tslint:disable-next-line: no-toplevel-property-access
|
30199 | makeParamDecorator('Inject', ɵ0$3), -1 /* Inject */);
|
30200 | /**
|
30201 | * Optional decorator and metadata.
|
30202 | *
|
30203 | * @Annotation
|
30204 | * @publicApi
|
30205 | */
|
30206 | const Optional =
|
30207 | // Disable tslint because `InternalInjectFlags` is a const enum which gets inlined.
|
30208 | // tslint:disable-next-line: no-toplevel-property-access
|
30209 | attachInjectFlag(makeParamDecorator('Optional'), 8 /* Optional */);
|
30210 | /**
|
30211 | * Self decorator and metadata.
|
30212 | *
|
30213 | * @Annotation
|
30214 | * @publicApi
|
30215 | */
|
30216 | const Self =
|
30217 | // Disable tslint because `InternalInjectFlags` is a const enum which gets inlined.
|
30218 | // tslint:disable-next-line: no-toplevel-property-access
|
30219 | attachInjectFlag(makeParamDecorator('Self'), 2 /* Self */);
|
30220 | /**
|
30221 | * `SkipSelf` decorator and metadata.
|
30222 | *
|
30223 | * @Annotation
|
30224 | * @publicApi
|
30225 | */
|
30226 | const SkipSelf =
|
30227 | // Disable tslint because `InternalInjectFlags` is a const enum which gets inlined.
|
30228 | // tslint:disable-next-line: no-toplevel-property-access
|
30229 | attachInjectFlag(makeParamDecorator('SkipSelf'), 4 /* SkipSelf */);
|
30230 | /**
|
30231 | * Host decorator and metadata.
|
30232 | *
|
30233 | * @Annotation
|
30234 | * @publicApi
|
30235 | */
|
30236 | const Host =
|
30237 | // Disable tslint because `InternalInjectFlags` is a const enum which gets inlined.
|
30238 | // tslint:disable-next-line: no-toplevel-property-access
|
30239 | attachInjectFlag(makeParamDecorator('Host'), 1 /* Host */);
|
30240 |
|
30241 | /**
|
30242 | * @license
|
30243 | * Copyright Google LLC All Rights Reserved.
|
30244 | *
|
30245 | * Use of this source code is governed by an MIT-style license that can be
|
30246 | * found in the LICENSE file at https://angular.io/license
|
30247 | */
|
30248 | /**
|
30249 | * The Trusted Types policy, or null if Trusted Types are not
|
30250 | * enabled/supported, or undefined if the policy has not been created yet.
|
30251 | */
|
30252 | let policy$1;
|
30253 | /**
|
30254 | * Returns the Trusted Types policy, or null if Trusted Types are not
|
30255 | * enabled/supported. The first call to this function will create the policy.
|
30256 | */
|
30257 | function getPolicy$1() {
|
30258 | if (policy$1 === undefined) {
|
30259 | policy$1 = null;
|
30260 | if (_global$1.trustedTypes) {
|
30261 | try {
|
30262 | policy$1 = _global$1.trustedTypes.createPolicy('angular', {
|
30263 | createHTML: (s) => s,
|
30264 | createScript: (s) => s,
|
30265 | createScriptURL: (s) => s,
|
30266 | });
|
30267 | }
|
30268 | catch (_a) {
|
30269 | // trustedTypes.createPolicy throws if called with a name that is
|
30270 | // already registered, even in report-only mode. Until the API changes,
|
30271 | // catch the error not to break the applications functionally. In such
|
30272 | // cases, the code will fall back to using strings.
|
30273 | }
|
30274 | }
|
30275 | }
|
30276 | return policy$1;
|
30277 | }
|
30278 | /**
|
30279 | * Unsafely promote a string to a TrustedScript, falling back to strings when
|
30280 | * Trusted Types are not available.
|
30281 | * @security In particular, it must be assured that the provided string will
|
30282 | * never cause an XSS vulnerability if used in a context that will be
|
30283 | * interpreted and executed as a script by a browser, e.g. when calling eval.
|
30284 | */
|
30285 | function trustedScriptFromString$1(script) {
|
30286 | var _a;
|
30287 | return ((_a = getPolicy$1()) === null || _a === void 0 ? void 0 : _a.createScript(script)) || script;
|
30288 | }
|
30289 | /**
|
30290 | * Unsafely call the Function constructor with the given string arguments. It
|
30291 | * is only available in development mode, and should be stripped out of
|
30292 | * production code.
|
30293 | * @security This is a security-sensitive function; any use of this function
|
30294 | * must go through security review. In particular, it must be assured that it
|
30295 | * is only called from development code, as use in production code can lead to
|
30296 | * XSS vulnerabilities.
|
30297 | */
|
30298 | function newTrustedFunctionForDev(...args) {
|
30299 | if (typeof ngDevMode === 'undefined') {
|
30300 | throw new Error('newTrustedFunctionForDev should never be called in production');
|
30301 | }
|
30302 | if (!_global$1.trustedTypes) {
|
30303 | // In environments that don't support Trusted Types, fall back to the most
|
30304 | // straightforward implementation:
|
30305 | return new Function(...args);
|
30306 | }
|
30307 | // Chrome currently does not support passing TrustedScript to the Function
|
30308 | // constructor. The following implements the workaround proposed on the page
|
30309 | // below, where the Chromium bug is also referenced:
|
30310 | // https://github.com/w3c/webappsec-trusted-types/wiki/Trusted-Types-for-function-constructor
|
30311 | const fnArgs = args.slice(0, -1).join(',');
|
30312 | const fnBody = args.pop().toString();
|
30313 | const body = `(function anonymous(${fnArgs}
|
30314 | ) { ${fnBody}
|
30315 | })`;
|
30316 | // Using eval directly confuses the compiler and prevents this module from
|
30317 | // being stripped out of JS binaries even if not used. The global['eval']
|
30318 | // indirection fixes that.
|
30319 | const fn = _global$1['eval'](trustedScriptFromString$1(body));
|
30320 | // To completely mimic the behavior of calling "new Function", two more
|
30321 | // things need to happen:
|
30322 | // 1. Stringifying the resulting function should return its source code
|
30323 | fn.toString = () => body;
|
30324 | // 2. When calling the resulting function, `this` should refer to `global`
|
30325 | return fn.bind(_global$1);
|
30326 | // When Trusted Types support in Function constructors is widely available,
|
30327 | // the implementation of this function can be simplified to:
|
30328 | // return new Function(...args.map(a => trustedScriptFromString(a)));
|
30329 | }
|
30330 |
|
30331 | /**
|
30332 | * @license
|
30333 | * Copyright Google LLC All Rights Reserved.
|
30334 | *
|
30335 | * Use of this source code is governed by an MIT-style license that can be
|
30336 | * found in the LICENSE file at https://angular.io/license
|
30337 | */
|
30338 | function tagSet(tags) {
|
30339 | const res = {};
|
30340 | for (const t of tags.split(','))
|
30341 | res[t] = true;
|
30342 | return res;
|
30343 | }
|
30344 | function merge(...sets) {
|
30345 | const res = {};
|
30346 | for (const s of sets) {
|
30347 | for (const v in s) {
|
30348 | if (s.hasOwnProperty(v))
|
30349 | res[v] = true;
|
30350 | }
|
30351 | }
|
30352 | return res;
|
30353 | }
|
30354 | // Good source of info about elements and attributes
|
30355 | // https://html.spec.whatwg.org/#semantics
|
30356 | // https://simon.html5.org/html-elements
|
30357 | // Safe Void Elements - HTML5
|
30358 | // https://html.spec.whatwg.org/#void-elements
|
30359 | const VOID_ELEMENTS = tagSet('area,br,col,hr,img,wbr');
|
30360 | // Elements that you can, intentionally, leave open (and which close themselves)
|
30361 | // https://html.spec.whatwg.org/#optional-tags
|
30362 | const OPTIONAL_END_TAG_BLOCK_ELEMENTS = tagSet('colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr');
|
30363 | const OPTIONAL_END_TAG_INLINE_ELEMENTS = tagSet('rp,rt');
|
30364 | const OPTIONAL_END_TAG_ELEMENTS = merge(OPTIONAL_END_TAG_INLINE_ELEMENTS, OPTIONAL_END_TAG_BLOCK_ELEMENTS);
|
30365 | // Safe Block Elements - HTML5
|
30366 | const BLOCK_ELEMENTS = merge(OPTIONAL_END_TAG_BLOCK_ELEMENTS, tagSet('address,article,' +
|
30367 | 'aside,blockquote,caption,center,del,details,dialog,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5,' +
|
30368 | 'h6,header,hgroup,hr,ins,main,map,menu,nav,ol,pre,section,summary,table,ul'));
|
30369 | // Inline Elements - HTML5
|
30370 | const INLINE_ELEMENTS = merge(OPTIONAL_END_TAG_INLINE_ELEMENTS, tagSet('a,abbr,acronym,audio,b,' +
|
30371 | 'bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,picture,q,ruby,rp,rt,s,' +
|
30372 | 'samp,small,source,span,strike,strong,sub,sup,time,track,tt,u,var,video'));
|
30373 | const VALID_ELEMENTS = merge(VOID_ELEMENTS, BLOCK_ELEMENTS, INLINE_ELEMENTS, OPTIONAL_END_TAG_ELEMENTS);
|
30374 | // Attributes that have href and hence need to be sanitized
|
30375 | const URI_ATTRS = tagSet('background,cite,href,itemtype,longdesc,poster,src,xlink:href');
|
30376 | // Attributes that have special href set hence need to be sanitized
|
30377 | const SRCSET_ATTRS = tagSet('srcset');
|
30378 | const HTML_ATTRS = tagSet('abbr,accesskey,align,alt,autoplay,axis,bgcolor,border,cellpadding,cellspacing,class,clear,color,cols,colspan,' +
|
30379 | 'compact,controls,coords,datetime,default,dir,download,face,headers,height,hidden,hreflang,hspace,' +
|
30380 | 'ismap,itemscope,itemprop,kind,label,lang,language,loop,media,muted,nohref,nowrap,open,preload,rel,rev,role,rows,rowspan,rules,' +
|
30381 | 'scope,scrolling,shape,size,sizes,span,srclang,start,summary,tabindex,target,title,translate,type,usemap,' +
|
30382 | 'valign,value,vspace,width');
|
30383 | // Accessibility attributes as per WAI-ARIA 1.1 (W3C Working Draft 14 December 2018)
|
30384 | const ARIA_ATTRS = tagSet('aria-activedescendant,aria-atomic,aria-autocomplete,aria-busy,aria-checked,aria-colcount,aria-colindex,' +
|
30385 | 'aria-colspan,aria-controls,aria-current,aria-describedby,aria-details,aria-disabled,aria-dropeffect,' +
|
30386 | 'aria-errormessage,aria-expanded,aria-flowto,aria-grabbed,aria-haspopup,aria-hidden,aria-invalid,' +
|
30387 | 'aria-keyshortcuts,aria-label,aria-labelledby,aria-level,aria-live,aria-modal,aria-multiline,' +
|
30388 | 'aria-multiselectable,aria-orientation,aria-owns,aria-placeholder,aria-posinset,aria-pressed,aria-readonly,' +
|
30389 | 'aria-relevant,aria-required,aria-roledescription,aria-rowcount,aria-rowindex,aria-rowspan,aria-selected,' +
|
30390 | 'aria-setsize,aria-sort,aria-valuemax,aria-valuemin,aria-valuenow,aria-valuetext');
|
30391 | // NB: This currently consciously doesn't support SVG. SVG sanitization has had several security
|
30392 | // issues in the past, so it seems safer to leave it out if possible. If support for binding SVG via
|
30393 | // innerHTML is required, SVG attributes should be added here.
|
30394 | // NB: Sanitization does not allow <form> elements or other active elements (<button> etc). Those
|
30395 | // can be sanitized, but they increase security surface area without a legitimate use case, so they
|
30396 | // are left out here.
|
30397 | const VALID_ATTRS = merge(URI_ATTRS, SRCSET_ATTRS, HTML_ATTRS, ARIA_ATTRS);
|
30398 | // Elements whose content should not be traversed/preserved, if the elements themselves are invalid.
|
30399 | //
|
30400 | // Typically, `<invalid>Some content</invalid>` would traverse (and in this case preserve)
|
30401 | // `Some content`, but strip `invalid-element` opening/closing tags. For some elements, though, we
|
30402 | // don't want to preserve the content, if the elements themselves are going to be removed.
|
30403 | const SKIP_TRAVERSING_CONTENT_IF_INVALID_ELEMENTS = tagSet('script,style,template');
|
30404 |
|
30405 | /**
|
30406 | * @license
|
30407 | * Copyright Google LLC All Rights Reserved.
|
30408 | *
|
30409 | * Use of this source code is governed by an MIT-style license that can be
|
30410 | * found in the LICENSE file at https://angular.io/license
|
30411 | */
|
30412 | /**
|
30413 | * A SecurityContext marks a location that has dangerous security implications, e.g. a DOM property
|
30414 | * like `innerHTML` that could cause Cross Site Scripting (XSS) security bugs when improperly
|
30415 | * handled.
|
30416 | *
|
30417 | * See DomSanitizer for more details on security in Angular applications.
|
30418 | *
|
30419 | * @publicApi
|
30420 | */
|
30421 | var SecurityContext$1;
|
30422 | (function (SecurityContext) {
|
30423 | SecurityContext[SecurityContext["NONE"] = 0] = "NONE";
|
30424 | SecurityContext[SecurityContext["HTML"] = 1] = "HTML";
|
30425 | SecurityContext[SecurityContext["STYLE"] = 2] = "STYLE";
|
30426 | SecurityContext[SecurityContext["SCRIPT"] = 3] = "SCRIPT";
|
30427 | SecurityContext[SecurityContext["URL"] = 4] = "URL";
|
30428 | SecurityContext[SecurityContext["RESOURCE_URL"] = 5] = "RESOURCE_URL";
|
30429 | })(SecurityContext$1 || (SecurityContext$1 = {}));
|
30430 |
|
30431 | /**
|
30432 | * @license
|
30433 | * Copyright Google LLC All Rights Reserved.
|
30434 | *
|
30435 | * Use of this source code is governed by an MIT-style license that can be
|
30436 | * found in the LICENSE file at https://angular.io/license
|
30437 | */
|
30438 | const ERROR_DEBUG_CONTEXT = 'ngDebugContext';
|
30439 | const ERROR_ORIGINAL_ERROR = 'ngOriginalError';
|
30440 | const ERROR_LOGGER = 'ngErrorLogger';
|
30441 | function wrappedError(message, originalError) {
|
30442 | const msg = `${message} caused by: ${originalError instanceof Error ? originalError.message : originalError}`;
|
30443 | const error = Error(msg);
|
30444 | error[ERROR_ORIGINAL_ERROR] = originalError;
|
30445 | return error;
|
30446 | }
|
30447 |
|
30448 | /**
|
30449 | * @license
|
30450 | * Copyright Google LLC All Rights Reserved.
|
30451 | *
|
30452 | * Use of this source code is governed by an MIT-style license that can be
|
30453 | * found in the LICENSE file at https://angular.io/license
|
30454 | */
|
30455 | function getDebugContext(error) {
|
30456 | return error[ERROR_DEBUG_CONTEXT];
|
30457 | }
|
30458 | function getOriginalError(error) {
|
30459 | return error[ERROR_ORIGINAL_ERROR];
|
30460 | }
|
30461 | function getErrorLogger(error) {
|
30462 | return error[ERROR_LOGGER] || defaultErrorLogger;
|
30463 | }
|
30464 | function defaultErrorLogger(console, ...values) {
|
30465 | console.error(...values);
|
30466 | }
|
30467 |
|
30468 | /**
|
30469 | * @license
|
30470 | * Copyright Google LLC All Rights Reserved.
|
30471 | *
|
30472 | * Use of this source code is governed by an MIT-style license that can be
|
30473 | * found in the LICENSE file at https://angular.io/license
|
30474 | */
|
30475 | /**
|
30476 | * Provides a hook for centralized exception handling.
|
30477 | *
|
30478 | * The default implementation of `ErrorHandler` prints error messages to the `console`. To
|
30479 | * intercept error handling, write a custom exception handler that replaces this default as
|
30480 | * appropriate for your app.
|
30481 | *
|
30482 | * @usageNotes
|
30483 | * ### Example
|
30484 | *
|
30485 | * ```
|
30486 | * class MyErrorHandler implements ErrorHandler {
|
30487 | * handleError(error) {
|
30488 | * // do something with the exception
|
30489 | * }
|
30490 | * }
|
30491 | *
|
30492 | * @NgModule({
|
30493 | * providers: [{provide: ErrorHandler, useClass: MyErrorHandler}]
|
30494 | * })
|
30495 | * class MyModule {}
|
30496 | * ```
|
30497 | *
|
30498 | * @publicApi
|
30499 | */
|
30500 | class ErrorHandler {
|
30501 | constructor() {
|
30502 | /**
|
30503 | * @internal
|
30504 | */
|
30505 | this._console = console;
|
30506 | }
|
30507 | handleError(error) {
|
30508 | const originalError = this._findOriginalError(error);
|
30509 | const context = this._findContext(error);
|
30510 | // Note: Browser consoles show the place from where console.error was called.
|
30511 | // We can use this to give users additional information about the error.
|
30512 | const errorLogger = getErrorLogger(error);
|
30513 | errorLogger(this._console, `ERROR`, error);
|
30514 | if (originalError) {
|
30515 | errorLogger(this._console, `ORIGINAL ERROR`, originalError);
|
30516 | }
|
30517 | if (context) {
|
30518 | errorLogger(this._console, 'ERROR CONTEXT', context);
|
30519 | }
|
30520 | }
|
30521 | /** @internal */
|
30522 | _findContext(error) {
|
30523 | if (error) {
|
30524 | return getDebugContext(error) ? getDebugContext(error) :
|
30525 | this._findContext(getOriginalError(error));
|
30526 | }
|
30527 | return null;
|
30528 | }
|
30529 | /** @internal */
|
30530 | _findOriginalError(error) {
|
30531 | let e = getOriginalError(error);
|
30532 | while (e && getOriginalError(e)) {
|
30533 | e = getOriginalError(e);
|
30534 | }
|
30535 | return e;
|
30536 | }
|
30537 | }
|
30538 |
|
30539 | /**
|
30540 | * @license
|
30541 | * Copyright Google LLC All Rights Reserved.
|
30542 | *
|
30543 | * Use of this source code is governed by an MIT-style license that can be
|
30544 | * found in the LICENSE file at https://angular.io/license
|
30545 | */
|
30546 | /**
|
30547 | * THIS FILE CONTAINS CODE WHICH SHOULD BE TREE SHAKEN AND NEVER CALLED FROM PRODUCTION CODE!!!
|
30548 | */
|
30549 | /**
|
30550 | * Creates an `Array` construction with a given name. This is useful when
|
30551 | * looking for memory consumption to see what time of array it is.
|
30552 | *
|
30553 | *
|
30554 | * @param name Name to give to the constructor
|
30555 | * @returns A subclass of `Array` if possible. This can only be done in
|
30556 | * environments which support `class` construct.
|
30557 | */
|
30558 | function createNamedArrayType(name) {
|
30559 | // This should never be called in prod mode, so let's verify that is the case.
|
30560 | if (ngDevMode) {
|
30561 | try {
|
30562 | // If this function were compromised the following could lead to arbitrary
|
30563 | // script execution. We bless it with Trusted Types anyway since this
|
30564 | // function is stripped out of production binaries.
|
30565 | return (newTrustedFunctionForDev('Array', `return class ${name} extends Array{}`))(Array);
|
30566 | }
|
30567 | catch (e) {
|
30568 | // If it does not work just give up and fall back to regular Array.
|
30569 | return Array;
|
30570 | }
|
30571 | }
|
30572 | else {
|
30573 | throw new Error('Looks like we are in \'prod mode\', but we are creating a named Array type, which is wrong! Check your code');
|
30574 | }
|
30575 | }
|
30576 |
|
30577 | /**
|
30578 | * @license
|
30579 | * Copyright Google LLC All Rights Reserved.
|
30580 | *
|
30581 | * Use of this source code is governed by an MIT-style license that can be
|
30582 | * found in the LICENSE file at https://angular.io/license
|
30583 | */
|
30584 | /**
|
30585 | * Assigns the given data to the given target (which could be a component,
|
30586 | * directive or DOM node instance) using monkey-patching.
|
30587 | */
|
30588 | function attachPatchData(target, data) {
|
30589 | target[MONKEY_PATCH_KEY_NAME] = data;
|
30590 | }
|
30591 |
|
30592 | /**
|
30593 | * @license
|
30594 | * Copyright Google LLC All Rights Reserved.
|
30595 | *
|
30596 | * Use of this source code is governed by an MIT-style license that can be
|
30597 | * found in the LICENSE file at https://angular.io/license
|
30598 | */
|
30599 | const ɵ0$4 = () => (typeof requestAnimationFrame !== 'undefined' &&
|
30600 | requestAnimationFrame || // browser only
|
30601 | setTimeout // everything else
|
30602 | )
|
30603 | .bind(_global$1);
|
30604 | const defaultScheduler = (ɵ0$4)();
|
30605 |
|
30606 | /**
|
30607 | * @license
|
30608 | * Copyright Google LLC All Rights Reserved.
|
30609 | *
|
30610 | * Use of this source code is governed by an MIT-style license that can be
|
30611 | * found in the LICENSE file at https://angular.io/license
|
30612 | */
|
30613 | /**
|
30614 | * Flags for renderer-specific style modifiers.
|
30615 | * @publicApi
|
30616 | */
|
30617 | var RendererStyleFlags2;
|
30618 | (function (RendererStyleFlags2) {
|
30619 | // TODO(misko): This needs to be refactored into a separate file so that it can be imported from
|
30620 | // `node_manipulation.ts` Currently doing the import cause resolution order to change and fails
|
30621 | // the tests. The work around is to have hard coded value in `node_manipulation.ts` for now.
|
30622 | /**
|
30623 | * Marks a style as important.
|
30624 | */
|
30625 | RendererStyleFlags2[RendererStyleFlags2["Important"] = 1] = "Important";
|
30626 | /**
|
30627 | * Marks a style as using dash case naming (this-is-dash-case).
|
30628 | */
|
30629 | RendererStyleFlags2[RendererStyleFlags2["DashCase"] = 2] = "DashCase";
|
30630 | })(RendererStyleFlags2 || (RendererStyleFlags2 = {}));
|
30631 |
|
30632 | /**
|
30633 | * @license
|
30634 | * Copyright Google LLC All Rights Reserved.
|
30635 | *
|
30636 | * Use of this source code is governed by an MIT-style license that can be
|
30637 | * found in the LICENSE file at https://angular.io/license
|
30638 | */
|
30639 | let _icuContainerIterate;
|
30640 | /**
|
30641 | * Iterator which provides ability to visit all of the `TIcuContainerNode` root `RNode`s.
|
30642 | */
|
30643 | function icuContainerIterate(tIcuContainerNode, lView) {
|
30644 | return _icuContainerIterate();
|
30645 | }
|
30646 |
|
30647 | /**
|
30648 | * @license
|
30649 | * Copyright Google LLC All Rights Reserved.
|
30650 | *
|
30651 | * Use of this source code is governed by an MIT-style license that can be
|
30652 | * found in the LICENSE file at https://angular.io/license
|
30653 | */
|
30654 | /**
|
30655 | * Gets the parent LView of the passed LView, if the PARENT is an LContainer, will get the parent of
|
30656 | * that LContainer, which is an LView
|
30657 | * @param lView the lView whose parent to get
|
30658 | */
|
30659 | function getLViewParent(lView) {
|
30660 | ngDevMode && assertLView(lView);
|
30661 | const parent = lView[PARENT];
|
30662 | return isLContainer(parent) ? parent[PARENT] : parent;
|
30663 | }
|
30664 | /**
|
30665 | * Gets the first `LContainer` in the LView or `null` if none exists.
|
30666 | */
|
30667 | function getFirstLContainer(lView) {
|
30668 | return getNearestLContainer(lView[CHILD_HEAD]);
|
30669 | }
|
30670 | /**
|
30671 | * Gets the next `LContainer` that is a sibling of the given container.
|
30672 | */
|
30673 | function getNextLContainer(container) {
|
30674 | return getNearestLContainer(container[NEXT]);
|
30675 | }
|
30676 | function getNearestLContainer(viewOrContainer) {
|
30677 | while (viewOrContainer !== null && !isLContainer(viewOrContainer)) {
|
30678 | viewOrContainer = viewOrContainer[NEXT];
|
30679 | }
|
30680 | return viewOrContainer;
|
30681 | }
|
30682 |
|
30683 | /**
|
30684 | * @license
|
30685 | * Copyright Google LLC All Rights Reserved.
|
30686 | *
|
30687 | * Use of this source code is governed by an MIT-style license that can be
|
30688 | * found in the LICENSE file at https://angular.io/license
|
30689 | */
|
30690 | /**
|
30691 | * NOTE: for performance reasons, the possible actions are inlined within the function instead of
|
30692 | * being passed as an argument.
|
30693 | */
|
30694 | function applyToElementOrContainer(action, renderer, parent, lNodeToHandle, beforeNode) {
|
30695 | // If this slot was allocated for a text node dynamically created by i18n, the text node itself
|
30696 | // won't be created until i18nApply() in the update block, so this node should be skipped.
|
30697 | // For more info, see "ICU expressions should work inside an ngTemplateOutlet inside an ngFor"
|
30698 | // in `i18n_spec.ts`.
|
30699 | if (lNodeToHandle != null) {
|
30700 | let lContainer;
|
30701 | let isComponent = false;
|
30702 | // We are expecting an RNode, but in the case of a component or LContainer the `RNode` is
|
30703 | // wrapped in an array which needs to be unwrapped. We need to know if it is a component and if
|
30704 | // it has LContainer so that we can process all of those cases appropriately.
|
30705 | if (isLContainer(lNodeToHandle)) {
|
30706 | lContainer = lNodeToHandle;
|
30707 | }
|
30708 | else if (isLView(lNodeToHandle)) {
|
30709 | isComponent = true;
|
30710 | ngDevMode && assertDefined(lNodeToHandle[HOST], 'HOST must be defined for a component LView');
|
30711 | lNodeToHandle = lNodeToHandle[HOST];
|
30712 | }
|
30713 | const rNode = unwrapRNode(lNodeToHandle);
|
30714 | ngDevMode && !isProceduralRenderer(renderer) && assertDomNode(rNode);
|
30715 | if (action === 0 /* Create */ && parent !== null) {
|
30716 | if (beforeNode == null) {
|
30717 | nativeAppendChild(renderer, parent, rNode);
|
30718 | }
|
30719 | else {
|
30720 | nativeInsertBefore(renderer, parent, rNode, beforeNode || null, true);
|
30721 | }
|
30722 | }
|
30723 | else if (action === 1 /* Insert */ && parent !== null) {
|
30724 | nativeInsertBefore(renderer, parent, rNode, beforeNode || null, true);
|
30725 | }
|
30726 | else if (action === 2 /* Detach */) {
|
30727 | nativeRemoveNode(renderer, rNode, isComponent);
|
30728 | }
|
30729 | else if (action === 3 /* Destroy */) {
|
30730 | ngDevMode && ngDevMode.rendererDestroyNode++;
|
30731 | renderer.destroyNode(rNode);
|
30732 | }
|
30733 | if (lContainer != null) {
|
30734 | applyContainer(renderer, action, lContainer, parent, beforeNode);
|
30735 | }
|
30736 | }
|
30737 | }
|
30738 | /**
|
30739 | * Creates a native element from a tag name, using a renderer.
|
30740 | * @param renderer A renderer to use
|
30741 | * @param name the tag name
|
30742 | * @param namespace Optional namespace for element.
|
30743 | * @returns the element created
|
30744 | */
|
30745 | function createElementNode(renderer, name, namespace) {
|
30746 | ngDevMode && ngDevMode.rendererCreateElement++;
|
30747 | if (isProceduralRenderer(renderer)) {
|
30748 | return renderer.createElement(name, namespace);
|
30749 | }
|
30750 | else {
|
30751 | return namespace === null ? renderer.createElement(name) :
|
30752 | renderer.createElementNS(namespace, name);
|
30753 | }
|
30754 | }
|
30755 | /**
|
30756 | * Removes all DOM elements associated with a view.
|
30757 | *
|
30758 | * Because some root nodes of the view may be containers, we sometimes need
|
30759 | * to propagate deeply into the nested containers to remove all elements in the
|
30760 | * views beneath it.
|
30761 | *
|
30762 | * @param tView The `TView' of the `LView` from which elements should be added or removed
|
30763 | * @param lView The view from which elements should be added or removed
|
30764 | */
|
30765 | function removeViewFromContainer(tView, lView) {
|
30766 | const renderer = lView[RENDERER];
|
30767 | applyView(tView, lView, renderer, 2 /* Detach */, null, null);
|
30768 | lView[HOST] = null;
|
30769 | lView[T_HOST] = null;
|
30770 | }
|
30771 | /**
|
30772 | * Detach a `LView` from the DOM by detaching its nodes.
|
30773 | *
|
30774 | * @param tView The `TView' of the `LView` to be detached
|
30775 | * @param lView the `LView` to be detached.
|
30776 | */
|
30777 | function renderDetachView(tView, lView) {
|
30778 | applyView(tView, lView, lView[RENDERER], 2 /* Detach */, null, null);
|
30779 | }
|
30780 | /**
|
30781 | * Traverses down and up the tree of views and containers to remove listeners and
|
30782 | * call onDestroy callbacks.
|
30783 | *
|
30784 | * Notes:
|
30785 | * - Because it's used for onDestroy calls, it needs to be bottom-up.
|
30786 | * - Must process containers instead of their views to avoid splicing
|
30787 | * when views are destroyed and re-added.
|
30788 | * - Using a while loop because it's faster than recursion
|
30789 | * - Destroy only called on movement to sibling or movement to parent (laterally or up)
|
30790 | *
|
30791 | * @param rootView The view to destroy
|
30792 | */
|
30793 | function destroyViewTree(rootView) {
|
30794 | // If the view has no children, we can clean it up and return early.
|
30795 | let lViewOrLContainer = rootView[CHILD_HEAD];
|
30796 | if (!lViewOrLContainer) {
|
30797 | return cleanUpView(rootView[TVIEW], rootView);
|
30798 | }
|
30799 | while (lViewOrLContainer) {
|
30800 | let next = null;
|
30801 | if (isLView(lViewOrLContainer)) {
|
30802 | // If LView, traverse down to child.
|
30803 | next = lViewOrLContainer[CHILD_HEAD];
|
30804 | }
|
30805 | else {
|
30806 | ngDevMode && assertLContainer(lViewOrLContainer);
|
30807 | // If container, traverse down to its first LView.
|
30808 | const firstView = lViewOrLContainer[CONTAINER_HEADER_OFFSET];
|
30809 | if (firstView)
|
30810 | next = firstView;
|
30811 | }
|
30812 | if (!next) {
|
30813 | // Only clean up view when moving to the side or up, as destroy hooks
|
30814 | // should be called in order from the bottom up.
|
30815 | while (lViewOrLContainer && !lViewOrLContainer[NEXT] && lViewOrLContainer !== rootView) {
|
30816 | if (isLView(lViewOrLContainer)) {
|
30817 | cleanUpView(lViewOrLContainer[TVIEW], lViewOrLContainer);
|
30818 | }
|
30819 | lViewOrLContainer = lViewOrLContainer[PARENT];
|
30820 | }
|
30821 | if (lViewOrLContainer === null)
|
30822 | lViewOrLContainer = rootView;
|
30823 | if (isLView(lViewOrLContainer)) {
|
30824 | cleanUpView(lViewOrLContainer[TVIEW], lViewOrLContainer);
|
30825 | }
|
30826 | next = lViewOrLContainer && lViewOrLContainer[NEXT];
|
30827 | }
|
30828 | lViewOrLContainer = next;
|
30829 | }
|
30830 | }
|
30831 | function detachMovedView(declarationContainer, lView) {
|
30832 | ngDevMode && assertLContainer(declarationContainer);
|
30833 | ngDevMode &&
|
30834 | assertDefined(declarationContainer[MOVED_VIEWS], 'A projected view should belong to a non-empty projected views collection');
|
30835 | const movedViews = declarationContainer[MOVED_VIEWS];
|
30836 | const declarationViewIndex = movedViews.indexOf(lView);
|
30837 | const insertionLContainer = lView[PARENT];
|
30838 | ngDevMode && assertLContainer(insertionLContainer);
|
30839 | // If the view was marked for refresh but then detached before it was checked (where the flag
|
30840 | // would be cleared and the counter decremented), we need to decrement the view counter here
|
30841 | // instead.
|
30842 | if (lView[FLAGS] & 1024 /* RefreshTransplantedView */) {
|
30843 | lView[FLAGS] &= ~1024 /* RefreshTransplantedView */;
|
30844 | updateTransplantedViewCount(insertionLContainer, -1);
|
30845 | }
|
30846 | movedViews.splice(declarationViewIndex, 1);
|
30847 | }
|
30848 | /**
|
30849 | * Detaches a view from a container.
|
30850 | *
|
30851 | * This method removes the view from the container's array of active views. It also
|
30852 | * removes the view's elements from the DOM.
|
30853 | *
|
30854 | * @param lContainer The container from which to detach a view
|
30855 | * @param removeIndex The index of the view to detach
|
30856 | * @returns Detached LView instance.
|
30857 | */
|
30858 | function detachView(lContainer, removeIndex) {
|
30859 | if (lContainer.length <= CONTAINER_HEADER_OFFSET)
|
30860 | return;
|
30861 | const indexInContainer = CONTAINER_HEADER_OFFSET + removeIndex;
|
30862 | const viewToDetach = lContainer[indexInContainer];
|
30863 | if (viewToDetach) {
|
30864 | const declarationLContainer = viewToDetach[DECLARATION_LCONTAINER];
|
30865 | if (declarationLContainer !== null && declarationLContainer !== lContainer) {
|
30866 | detachMovedView(declarationLContainer, viewToDetach);
|
30867 | }
|
30868 | if (removeIndex > 0) {
|
30869 | lContainer[indexInContainer - 1][NEXT] = viewToDetach[NEXT];
|
30870 | }
|
30871 | const removedLView = removeFromArray(lContainer, CONTAINER_HEADER_OFFSET + removeIndex);
|
30872 | removeViewFromContainer(viewToDetach[TVIEW], viewToDetach);
|
30873 | // notify query that a view has been removed
|
30874 | const lQueries = removedLView[QUERIES];
|
30875 | if (lQueries !== null) {
|
30876 | lQueries.detachView(removedLView[TVIEW]);
|
30877 | }
|
30878 | viewToDetach[PARENT] = null;
|
30879 | viewToDetach[NEXT] = null;
|
30880 | // Unsets the attached flag
|
30881 | viewToDetach[FLAGS] &= ~128 /* Attached */;
|
30882 | }
|
30883 | return viewToDetach;
|
30884 | }
|
30885 | /**
|
30886 | * A standalone function which destroys an LView,
|
30887 | * conducting clean up (e.g. removing listeners, calling onDestroys).
|
30888 | *
|
30889 | * @param tView The `TView' of the `LView` to be destroyed
|
30890 | * @param lView The view to be destroyed.
|
30891 | */
|
30892 | function destroyLView(tView, lView) {
|
30893 | if (!(lView[FLAGS] & 256 /* Destroyed */)) {
|
30894 | const renderer = lView[RENDERER];
|
30895 | if (isProceduralRenderer(renderer) && renderer.destroyNode) {
|
30896 | applyView(tView, lView, renderer, 3 /* Destroy */, null, null);
|
30897 | }
|
30898 | destroyViewTree(lView);
|
30899 | }
|
30900 | }
|
30901 | /**
|
30902 | * Calls onDestroys hooks for all directives and pipes in a given view and then removes all
|
30903 | * listeners. Listeners are removed as the last step so events delivered in the onDestroys hooks
|
30904 | * can be propagated to @Output listeners.
|
30905 | *
|
30906 | * @param tView `TView` for the `LView` to clean up.
|
30907 | * @param lView The LView to clean up
|
30908 | */
|
30909 | function cleanUpView(tView, lView) {
|
30910 | if (!(lView[FLAGS] & 256 /* Destroyed */)) {
|
30911 | // Usually the Attached flag is removed when the view is detached from its parent, however
|
30912 | // if it's a root view, the flag won't be unset hence why we're also removing on destroy.
|
30913 | lView[FLAGS] &= ~128 /* Attached */;
|
30914 | // Mark the LView as destroyed *before* executing the onDestroy hooks. An onDestroy hook
|
30915 | // runs arbitrary user code, which could include its own `viewRef.destroy()` (or similar). If
|
30916 | // We don't flag the view as destroyed before the hooks, this could lead to an infinite loop.
|
30917 | // This also aligns with the ViewEngine behavior. It also means that the onDestroy hook is
|
30918 | // really more of an "afterDestroy" hook if you think about it.
|
30919 | lView[FLAGS] |= 256 /* Destroyed */;
|
30920 | executeOnDestroys(tView, lView);
|
30921 | processCleanups(tView, lView);
|
30922 | // For component views only, the local renderer is destroyed at clean up time.
|
30923 | if (lView[TVIEW].type === 1 /* Component */ && isProceduralRenderer(lView[RENDERER])) {
|
30924 | ngDevMode && ngDevMode.rendererDestroy++;
|
30925 | lView[RENDERER].destroy();
|
30926 | }
|
30927 | const declarationContainer = lView[DECLARATION_LCONTAINER];
|
30928 | // we are dealing with an embedded view that is still inserted into a container
|
30929 | if (declarationContainer !== null && isLContainer(lView[PARENT])) {
|
30930 | // and this is a projected view
|
30931 | if (declarationContainer !== lView[PARENT]) {
|
30932 | detachMovedView(declarationContainer, lView);
|
30933 | }
|
30934 | // For embedded views still attached to a container: remove query result from this view.
|
30935 | const lQueries = lView[QUERIES];
|
30936 | if (lQueries !== null) {
|
30937 | lQueries.detachView(tView);
|
30938 | }
|
30939 | }
|
30940 | }
|
30941 | }
|
30942 | /** Removes listeners and unsubscribes from output subscriptions */
|
30943 | function processCleanups(tView, lView) {
|
30944 | const tCleanup = tView.cleanup;
|
30945 | const lCleanup = lView[CLEANUP];
|
30946 | // `LCleanup` contains both share information with `TCleanup` as well as instance specific
|
30947 | // information appended at the end. We need to know where the end of the `TCleanup` information
|
30948 | // is, and we track this with `lastLCleanupIndex`.
|
30949 | let lastLCleanupIndex = -1;
|
30950 | if (tCleanup !== null) {
|
30951 | for (let i = 0; i < tCleanup.length - 1; i += 2) {
|
30952 | if (typeof tCleanup[i] === 'string') {
|
30953 | // This is a native DOM listener
|
30954 | const idxOrTargetGetter = tCleanup[i + 1];
|
30955 | const target = typeof idxOrTargetGetter === 'function' ?
|
30956 | idxOrTargetGetter(lView) :
|
30957 | unwrapRNode(lView[idxOrTargetGetter]);
|
30958 | const listener = lCleanup[lastLCleanupIndex = tCleanup[i + 2]];
|
30959 | const useCaptureOrSubIdx = tCleanup[i + 3];
|
30960 | if (typeof useCaptureOrSubIdx === 'boolean') {
|
30961 | // native DOM listener registered with Renderer3
|
30962 | target.removeEventListener(tCleanup[i], listener, useCaptureOrSubIdx);
|
30963 | }
|
30964 | else {
|
30965 | if (useCaptureOrSubIdx >= 0) {
|
30966 | // unregister
|
30967 | lCleanup[lastLCleanupIndex = useCaptureOrSubIdx]();
|
30968 | }
|
30969 | else {
|
30970 | // Subscription
|
30971 | lCleanup[lastLCleanupIndex = -useCaptureOrSubIdx].unsubscribe();
|
30972 | }
|
30973 | }
|
30974 | i += 2;
|
30975 | }
|
30976 | else {
|
30977 | // This is a cleanup function that is grouped with the index of its context
|
30978 | const context = lCleanup[lastLCleanupIndex = tCleanup[i + 1]];
|
30979 | tCleanup[i].call(context);
|
30980 | }
|
30981 | }
|
30982 | }
|
30983 | if (lCleanup !== null) {
|
30984 | for (let i = lastLCleanupIndex + 1; i < lCleanup.length; i++) {
|
30985 | const instanceCleanupFn = lCleanup[i];
|
30986 | ngDevMode && assertFunction(instanceCleanupFn, 'Expecting instance cleanup function.');
|
30987 | instanceCleanupFn();
|
30988 | }
|
30989 | lView[CLEANUP] = null;
|
30990 | }
|
30991 | }
|
30992 | /** Calls onDestroy hooks for this view */
|
30993 | function executeOnDestroys(tView, lView) {
|
30994 | let destroyHooks;
|
30995 | if (tView != null && (destroyHooks = tView.destroyHooks) != null) {
|
30996 | for (let i = 0; i < destroyHooks.length; i += 2) {
|
30997 | const context = lView[destroyHooks[i]];
|
30998 | // Only call the destroy hook if the context has been requested.
|
30999 | if (!(context instanceof NodeInjectorFactory)) {
|
31000 | const toCall = destroyHooks[i + 1];
|
31001 | if (Array.isArray(toCall)) {
|
31002 | for (let j = 0; j < toCall.length; j += 2) {
|
31003 | toCall[j + 1].call(context[toCall[j]]);
|
31004 | }
|
31005 | }
|
31006 | else {
|
31007 | toCall.call(context);
|
31008 | }
|
31009 | }
|
31010 | }
|
31011 | }
|
31012 | }
|
31013 | /**
|
31014 | * Inserts a native node before another native node for a given parent using {@link Renderer3}.
|
31015 | * This is a utility function that can be used when native nodes were determined - it abstracts an
|
31016 | * actual renderer being used.
|
31017 | */
|
31018 | function nativeInsertBefore(renderer, parent, child, beforeNode, isMove) {
|
31019 | ngDevMode && ngDevMode.rendererInsertBefore++;
|
31020 | if (isProceduralRenderer(renderer)) {
|
31021 | renderer.insertBefore(parent, child, beforeNode, isMove);
|
31022 | }
|
31023 | else {
|
31024 | parent.insertBefore(child, beforeNode, isMove);
|
31025 | }
|
31026 | }
|
31027 | function nativeAppendChild(renderer, parent, child) {
|
31028 | ngDevMode && ngDevMode.rendererAppendChild++;
|
31029 | ngDevMode && assertDefined(parent, 'parent node must be defined');
|
31030 | if (isProceduralRenderer(renderer)) {
|
31031 | renderer.appendChild(parent, child);
|
31032 | }
|
31033 | else {
|
31034 | parent.appendChild(child);
|
31035 | }
|
31036 | }
|
31037 | /** Removes a node from the DOM given its native parent. */
|
31038 | function nativeRemoveChild(renderer, parent, child, isHostElement) {
|
31039 | if (isProceduralRenderer(renderer)) {
|
31040 | renderer.removeChild(parent, child, isHostElement);
|
31041 | }
|
31042 | else {
|
31043 | parent.removeChild(child);
|
31044 | }
|
31045 | }
|
31046 | /**
|
31047 | * Returns a native parent of a given native node.
|
31048 | */
|
31049 | function nativeParentNode(renderer, node) {
|
31050 | return (isProceduralRenderer(renderer) ? renderer.parentNode(node) : node.parentNode);
|
31051 | }
|
31052 | function getProjectionNodes(lView, tNode) {
|
31053 | if (tNode !== null) {
|
31054 | const componentView = lView[DECLARATION_COMPONENT_VIEW];
|
31055 | const componentHost = componentView[T_HOST];
|
31056 | const slotIdx = tNode.projection;
|
31057 | ngDevMode && assertProjectionSlots(lView);
|
31058 | return componentHost.projection[slotIdx];
|
31059 | }
|
31060 | return null;
|
31061 | }
|
31062 | /**
|
31063 | * Removes a native node itself using a given renderer. To remove the node we are looking up its
|
31064 | * parent from the native tree as not all platforms / browsers support the equivalent of
|
31065 | * node.remove().
|
31066 | *
|
31067 | * @param renderer A renderer to be used
|
31068 | * @param rNode The native node that should be removed
|
31069 | * @param isHostElement A flag indicating if a node to be removed is a host of a component.
|
31070 | */
|
31071 | function nativeRemoveNode(renderer, rNode, isHostElement) {
|
31072 | ngDevMode && ngDevMode.rendererRemoveNode++;
|
31073 | const nativeParent = nativeParentNode(renderer, rNode);
|
31074 | if (nativeParent) {
|
31075 | nativeRemoveChild(renderer, nativeParent, rNode, isHostElement);
|
31076 | }
|
31077 | }
|
31078 | /**
|
31079 | * Performs the operation of `action` on the node. Typically this involves inserting or removing
|
31080 | * nodes on the LView or projection boundary.
|
31081 | */
|
31082 | function applyNodes(renderer, action, tNode, lView, parentRElement, beforeNode, isProjection) {
|
31083 | while (tNode != null) {
|
31084 | ngDevMode && assertTNodeForLView(tNode, lView);
|
31085 | ngDevMode &&
|
31086 | assertTNodeType(tNode, 3 /* AnyRNode */ | 12 /* AnyContainer */ | 16 /* Projection */ | 32 /* Icu */);
|
31087 | const rawSlotValue = lView[tNode.index];
|
31088 | const tNodeType = tNode.type;
|
31089 | if (isProjection) {
|
31090 | if (action === 0 /* Create */) {
|
31091 | rawSlotValue && attachPatchData(unwrapRNode(rawSlotValue), lView);
|
31092 | tNode.flags |= 4 /* isProjected */;
|
31093 | }
|
31094 | }
|
31095 | if ((tNode.flags & 64 /* isDetached */) !== 64 /* isDetached */) {
|
31096 | if (tNodeType & 8 /* ElementContainer */) {
|
31097 | applyNodes(renderer, action, tNode.child, lView, parentRElement, beforeNode, false);
|
31098 | applyToElementOrContainer(action, renderer, parentRElement, rawSlotValue, beforeNode);
|
31099 | }
|
31100 | else if (tNodeType & 32 /* Icu */) {
|
31101 | const nextRNode = icuContainerIterate();
|
31102 | let rNode;
|
31103 | while (rNode = nextRNode()) {
|
31104 | applyToElementOrContainer(action, renderer, parentRElement, rNode, beforeNode);
|
31105 | }
|
31106 | applyToElementOrContainer(action, renderer, parentRElement, rawSlotValue, beforeNode);
|
31107 | }
|
31108 | else if (tNodeType & 16 /* Projection */) {
|
31109 | applyProjectionRecursive(renderer, action, lView, tNode, parentRElement, beforeNode);
|
31110 | }
|
31111 | else {
|
31112 | ngDevMode && assertTNodeType(tNode, 3 /* AnyRNode */ | 4 /* Container */);
|
31113 | applyToElementOrContainer(action, renderer, parentRElement, rawSlotValue, beforeNode);
|
31114 | }
|
31115 | }
|
31116 | tNode = isProjection ? tNode.projectionNext : tNode.next;
|
31117 | }
|
31118 | }
|
31119 | function applyView(tView, lView, renderer, action, parentRElement, beforeNode) {
|
31120 | applyNodes(renderer, action, tView.firstChild, lView, parentRElement, beforeNode, false);
|
31121 | }
|
31122 | /**
|
31123 | * `applyProjectionRecursive` performs operation on the projection specified by `action` (insert,
|
31124 | * detach, destroy)
|
31125 | *
|
31126 | * Inserting a projection requires us to locate the projected nodes from the parent component. The
|
31127 | * complication is that those nodes themselves could be re-projected from their parent component.
|
31128 | *
|
31129 | * @param renderer Render to use
|
31130 | * @param action action to perform (insert, detach, destroy)
|
31131 | * @param lView The LView which needs to be inserted, detached, destroyed.
|
31132 | * @param tProjectionNode node to project
|
31133 | * @param parentRElement parent DOM element for insertion/removal.
|
31134 | * @param beforeNode Before which node the insertions should happen.
|
31135 | */
|
31136 | function applyProjectionRecursive(renderer, action, lView, tProjectionNode, parentRElement, beforeNode) {
|
31137 | const componentLView = lView[DECLARATION_COMPONENT_VIEW];
|
31138 | const componentNode = componentLView[T_HOST];
|
31139 | ngDevMode &&
|
31140 | assertEqual(typeof tProjectionNode.projection, 'number', 'expecting projection index');
|
31141 | const nodeToProjectOrRNodes = componentNode.projection[tProjectionNode.projection];
|
31142 | if (Array.isArray(nodeToProjectOrRNodes)) {
|
31143 | // This should not exist, it is a bit of a hack. When we bootstrap a top level node and we
|
31144 | // need to support passing projectable nodes, so we cheat and put them in the TNode
|
31145 | // of the Host TView. (Yes we put instance info at the T Level). We can get away with it
|
31146 | // because we know that that TView is not shared and therefore it will not be a problem.
|
31147 | // This should be refactored and cleaned up.
|
31148 | for (let i = 0; i < nodeToProjectOrRNodes.length; i++) {
|
31149 | const rNode = nodeToProjectOrRNodes[i];
|
31150 | applyToElementOrContainer(action, renderer, parentRElement, rNode, beforeNode);
|
31151 | }
|
31152 | }
|
31153 | else {
|
31154 | let nodeToProject = nodeToProjectOrRNodes;
|
31155 | const projectedComponentLView = componentLView[PARENT];
|
31156 | applyNodes(renderer, action, nodeToProject, projectedComponentLView, parentRElement, beforeNode, true);
|
31157 | }
|
31158 | }
|
31159 | /**
|
31160 | * `applyContainer` performs an operation on the container and its views as specified by
|
31161 | * `action` (insert, detach, destroy)
|
31162 | *
|
31163 | * Inserting a Container is complicated by the fact that the container may have Views which
|
31164 | * themselves have containers or projections.
|
31165 | *
|
31166 | * @param renderer Renderer to use
|
31167 | * @param action action to perform (insert, detach, destroy)
|
31168 | * @param lContainer The LContainer which needs to be inserted, detached, destroyed.
|
31169 | * @param parentRElement parent DOM element for insertion/removal.
|
31170 | * @param beforeNode Before which node the insertions should happen.
|
31171 | */
|
31172 | function applyContainer(renderer, action, lContainer, parentRElement, beforeNode) {
|
31173 | ngDevMode && assertLContainer(lContainer);
|
31174 | const anchor = lContainer[NATIVE]; // LContainer has its own before node.
|
31175 | const native = unwrapRNode(lContainer);
|
31176 | // An LContainer can be created dynamically on any node by injecting ViewContainerRef.
|
31177 | // Asking for a ViewContainerRef on an element will result in a creation of a separate anchor
|
31178 | // node (comment in the DOM) that will be different from the LContainer's host node. In this
|
31179 | // particular case we need to execute action on 2 nodes:
|
31180 | // - container's host node (this is done in the executeActionOnElementOrContainer)
|
31181 | // - container's host node (this is done here)
|
31182 | if (anchor !== native) {
|
31183 | // This is very strange to me (Misko). I would expect that the native is same as anchor. I
|
31184 | // don't see a reason why they should be different, but they are.
|
31185 | //
|
31186 | // If they are we need to process the second anchor as well.
|
31187 | applyToElementOrContainer(action, renderer, parentRElement, anchor, beforeNode);
|
31188 | }
|
31189 | for (let i = CONTAINER_HEADER_OFFSET; i < lContainer.length; i++) {
|
31190 | const lView = lContainer[i];
|
31191 | applyView(lView[TVIEW], lView, renderer, action, parentRElement, anchor);
|
31192 | }
|
31193 | }
|
31194 | /**
|
31195 | * Write `cssText` to `RElement`.
|
31196 | *
|
31197 | * This function does direct write without any reconciliation. Used for writing initial values, so
|
31198 | * that static styling values do not pull in the style parser.
|
31199 | *
|
31200 | * @param renderer Renderer to use
|
31201 | * @param element The element which needs to be updated.
|
31202 | * @param newValue The new class list to write.
|
31203 | */
|
31204 | function writeDirectStyle(renderer, element, newValue) {
|
31205 | ngDevMode && assertString(newValue, '\'newValue\' should be a string');
|
31206 | if (isProceduralRenderer(renderer)) {
|
31207 | renderer.setAttribute(element, 'style', newValue);
|
31208 | }
|
31209 | else {
|
31210 | element.style.cssText = newValue;
|
31211 | }
|
31212 | ngDevMode && ngDevMode.rendererSetStyle++;
|
31213 | }
|
31214 | /**
|
31215 | * Write `className` to `RElement`.
|
31216 | *
|
31217 | * This function does direct write without any reconciliation. Used for writing initial values, so
|
31218 | * that static styling values do not pull in the style parser.
|
31219 | *
|
31220 | * @param renderer Renderer to use
|
31221 | * @param element The element which needs to be updated.
|
31222 | * @param newValue The new class list to write.
|
31223 | */
|
31224 | function writeDirectClass(renderer, element, newValue) {
|
31225 | ngDevMode && assertString(newValue, '\'newValue\' should be a string');
|
31226 | if (isProceduralRenderer(renderer)) {
|
31227 | if (newValue === '') {
|
31228 | // There are tests in `google3` which expect `element.getAttribute('class')` to be `null`.
|
31229 | renderer.removeAttribute(element, 'class');
|
31230 | }
|
31231 | else {
|
31232 | renderer.setAttribute(element, 'class', newValue);
|
31233 | }
|
31234 | }
|
31235 | else {
|
31236 | element.className = newValue;
|
31237 | }
|
31238 | ngDevMode && ngDevMode.rendererSetClassName++;
|
31239 | }
|
31240 |
|
31241 | /**
|
31242 | * @license
|
31243 | * Copyright Google LLC All Rights Reserved.
|
31244 | *
|
31245 | * Use of this source code is governed by an MIT-style license that can be
|
31246 | * found in the LICENSE file at https://angular.io/license
|
31247 | */
|
31248 | function isPositive(mode) {
|
31249 | return (mode & 1 /* NOT */) === 0;
|
31250 | }
|
31251 | function maybeWrapInNotSelector(isNegativeMode, chunk) {
|
31252 | return isNegativeMode ? ':not(' + chunk.trim() + ')' : chunk;
|
31253 | }
|
31254 | function stringifyCSSSelector(selector) {
|
31255 | let result = selector[0];
|
31256 | let i = 1;
|
31257 | let mode = 2 /* ATTRIBUTE */;
|
31258 | let currentChunk = '';
|
31259 | let isNegativeMode = false;
|
31260 | while (i < selector.length) {
|
31261 | let valueOrMarker = selector[i];
|
31262 | if (typeof valueOrMarker === 'string') {
|
31263 | if (mode & 2 /* ATTRIBUTE */) {
|
31264 | const attrValue = selector[++i];
|
31265 | currentChunk +=
|
31266 | '[' + valueOrMarker + (attrValue.length > 0 ? '="' + attrValue + '"' : '') + ']';
|
31267 | }
|
31268 | else if (mode & 8 /* CLASS */) {
|
31269 | currentChunk += '.' + valueOrMarker;
|
31270 | }
|
31271 | else if (mode & 4 /* ELEMENT */) {
|
31272 | currentChunk += ' ' + valueOrMarker;
|
31273 | }
|
31274 | }
|
31275 | else {
|
31276 | //
|
31277 | // Append current chunk to the final result in case we come across SelectorFlag, which
|
31278 | // indicates that the previous section of a selector is over. We need to accumulate content
|
31279 | // between flags to make sure we wrap the chunk later in :not() selector if needed, e.g.
|
31280 | // ```
|
31281 | // ['', Flags.CLASS, '.classA', Flags.CLASS | Flags.NOT, '.classB', '.classC']
|
31282 | // ```
|
31283 | // should be transformed to `.classA :not(.classB .classC)`.
|
31284 | //
|
31285 | // Note: for negative selector part, we accumulate content between flags until we find the
|
31286 | // next negative flag. This is needed to support a case where `:not()` rule contains more than
|
31287 | // one chunk, e.g. the following selector:
|
31288 | // ```
|
31289 | // ['', Flags.ELEMENT | Flags.NOT, 'p', Flags.CLASS, 'foo', Flags.CLASS | Flags.NOT, 'bar']
|
31290 | // ```
|
31291 | // should be stringified to `:not(p.foo) :not(.bar)`
|
31292 | //
|
31293 | if (currentChunk !== '' && !isPositive(valueOrMarker)) {
|
31294 | result += maybeWrapInNotSelector(isNegativeMode, currentChunk);
|
31295 | currentChunk = '';
|
31296 | }
|
31297 | mode = valueOrMarker;
|
31298 | // According to CssSelector spec, once we come across `SelectorFlags.NOT` flag, the negative
|
31299 | // mode is maintained for remaining chunks of a selector.
|
31300 | isNegativeMode = isNegativeMode || !isPositive(mode);
|
31301 | }
|
31302 | i++;
|
31303 | }
|
31304 | if (currentChunk !== '') {
|
31305 | result += maybeWrapInNotSelector(isNegativeMode, currentChunk);
|
31306 | }
|
31307 | return result;
|
31308 | }
|
31309 | /**
|
31310 | * Generates string representation of CSS selector in parsed form.
|
31311 | *
|
31312 | * ComponentDef and DirectiveDef are generated with the selector in parsed form to avoid doing
|
31313 | * additional parsing at runtime (for example, for directive matching). However in some cases (for
|
31314 | * example, while bootstrapping a component), a string version of the selector is required to query
|
31315 | * for the host element on the page. This function takes the parsed form of a selector and returns
|
31316 | * its string representation.
|
31317 | *
|
31318 | * @param selectorList selector in parsed form
|
31319 | * @returns string representation of a given selector
|
31320 | */
|
31321 | function stringifyCSSSelectorList(selectorList) {
|
31322 | return selectorList.map(stringifyCSSSelector).join(',');
|
31323 | }
|
31324 | /**
|
31325 | * Extracts attributes and classes information from a given CSS selector.
|
31326 | *
|
31327 | * This function is used while creating a component dynamically. In this case, the host element
|
31328 | * (that is created dynamically) should contain attributes and classes specified in component's CSS
|
31329 | * selector.
|
31330 | *
|
31331 | * @param selector CSS selector in parsed form (in a form of array)
|
31332 | * @returns object with `attrs` and `classes` fields that contain extracted information
|
31333 | */
|
31334 | function extractAttrsAndClassesFromSelector(selector) {
|
31335 | const attrs = [];
|
31336 | const classes = [];
|
31337 | let i = 1;
|
31338 | let mode = 2 /* ATTRIBUTE */;
|
31339 | while (i < selector.length) {
|
31340 | let valueOrMarker = selector[i];
|
31341 | if (typeof valueOrMarker === 'string') {
|
31342 | if (mode === 2 /* ATTRIBUTE */) {
|
31343 | if (valueOrMarker !== '') {
|
31344 | attrs.push(valueOrMarker, selector[++i]);
|
31345 | }
|
31346 | }
|
31347 | else if (mode === 8 /* CLASS */) {
|
31348 | classes.push(valueOrMarker);
|
31349 | }
|
31350 | }
|
31351 | else {
|
31352 | // According to CssSelector spec, once we come across `SelectorFlags.NOT` flag, the negative
|
31353 | // mode is maintained for remaining chunks of a selector. Since attributes and classes are
|
31354 | // extracted only for "positive" part of the selector, we can stop here.
|
31355 | if (!isPositive(mode))
|
31356 | break;
|
31357 | mode = valueOrMarker;
|
31358 | }
|
31359 | i++;
|
31360 | }
|
31361 | return { attrs, classes };
|
31362 | }
|
31363 |
|
31364 | /**
|
31365 | * @license
|
31366 | * Copyright Google LLC All Rights Reserved.
|
31367 | *
|
31368 | * Use of this source code is governed by an MIT-style license that can be
|
31369 | * found in the LICENSE file at https://angular.io/license
|
31370 | */
|
31371 | /** A special value which designates that a value has not changed. */
|
31372 | const NO_CHANGE = (typeof ngDevMode === 'undefined' || ngDevMode) ? { __brand__: 'NO_CHANGE' } : {};
|
31373 |
|
31374 | /**
|
31375 | * @license
|
31376 | * Copyright Google LLC All Rights Reserved.
|
31377 | *
|
31378 | * Use of this source code is governed by an MIT-style license that can be
|
31379 | * found in the LICENSE file at https://angular.io/license
|
31380 | */
|
31381 | function selectIndexInternal(tView, lView, index, checkNoChangesMode) {
|
31382 | ngDevMode && assertIndexInDeclRange(lView, index);
|
31383 | // Flush the initial hooks for elements in the view that have been added up to this point.
|
31384 | // PERF WARNING: do NOT extract this to a separate function without running benchmarks
|
31385 | if (!checkNoChangesMode) {
|
31386 | const hooksInitPhaseCompleted = (lView[FLAGS] & 3 /* InitPhaseStateMask */) === 3 /* InitPhaseCompleted */;
|
31387 | if (hooksInitPhaseCompleted) {
|
31388 | const preOrderCheckHooks = tView.preOrderCheckHooks;
|
31389 | if (preOrderCheckHooks !== null) {
|
31390 | executeCheckHooks(lView, preOrderCheckHooks, index);
|
31391 | }
|
31392 | }
|
31393 | else {
|
31394 | const preOrderHooks = tView.preOrderHooks;
|
31395 | if (preOrderHooks !== null) {
|
31396 | executeInitAndCheckHooks(lView, preOrderHooks, 0 /* OnInitHooksToBeRun */, index);
|
31397 | }
|
31398 | }
|
31399 | }
|
31400 | // We must set the selected index *after* running the hooks, because hooks may have side-effects
|
31401 | // that cause other template functions to run, thus updating the selected index, which is global
|
31402 | // state. If we run `setSelectedIndex` *before* we run the hooks, in some cases the selected index
|
31403 | // will be altered by the time we leave the `ɵɵadvance` instruction.
|
31404 | setSelectedIndex(index);
|
31405 | }
|
31406 |
|
31407 | /**
|
31408 | * @license
|
31409 | * Copyright Google LLC All Rights Reserved.
|
31410 | *
|
31411 | * Use of this source code is governed by an MIT-style license that can be
|
31412 | * found in the LICENSE file at https://angular.io/license
|
31413 | */
|
31414 | function getTStylingRangePrev(tStylingRange) {
|
31415 | ngDevMode && assertNumber(tStylingRange, 'expected number');
|
31416 | return (tStylingRange >> 17 /* PREV_SHIFT */) & 32767 /* UNSIGNED_MASK */;
|
31417 | }
|
31418 | function getTStylingRangePrevDuplicate(tStylingRange) {
|
31419 | ngDevMode && assertNumber(tStylingRange, 'expected number');
|
31420 | return (tStylingRange & 2 /* PREV_DUPLICATE */) ==
|
31421 | 2 /* PREV_DUPLICATE */;
|
31422 | }
|
31423 | function getTStylingRangeNext(tStylingRange) {
|
31424 | ngDevMode && assertNumber(tStylingRange, 'expected number');
|
31425 | return (tStylingRange & 131068 /* NEXT_MASK */) >> 2 /* NEXT_SHIFT */;
|
31426 | }
|
31427 | function getTStylingRangeNextDuplicate(tStylingRange) {
|
31428 | ngDevMode && assertNumber(tStylingRange, 'expected number');
|
31429 | return (tStylingRange & 1 /* NEXT_DUPLICATE */) ===
|
31430 | 1 /* NEXT_DUPLICATE */;
|
31431 | }
|
31432 |
|
31433 | /**
|
31434 | * @license
|
31435 | * Copyright Google LLC All Rights Reserved.
|
31436 | *
|
31437 | * Use of this source code is governed by an MIT-style license that can be
|
31438 | * found in the LICENSE file at https://angular.io/license
|
31439 | */
|
31440 | /**
|
31441 | * Patch a `debug` property on top of the existing object.
|
31442 | *
|
31443 | * NOTE: always call this method with `ngDevMode && attachDebugObject(...)`
|
31444 | *
|
31445 | * @param obj Object to patch
|
31446 | * @param debug Value to patch
|
31447 | */
|
31448 | function attachDebugObject(obj, debug) {
|
31449 | if (ngDevMode) {
|
31450 | Object.defineProperty(obj, 'debug', { value: debug, enumerable: false });
|
31451 | }
|
31452 | else {
|
31453 | throw new Error('This method should be guarded with `ngDevMode` so that it can be tree shaken in production!');
|
31454 | }
|
31455 | }
|
31456 |
|
31457 | /**
|
31458 | * @license
|
31459 | * Copyright Google LLC All Rights Reserved.
|
31460 | *
|
31461 | * Use of this source code is governed by an MIT-style license that can be
|
31462 | * found in the LICENSE file at https://angular.io/license
|
31463 | */
|
31464 | const NG_DEV_MODE = ((typeof ngDevMode === 'undefined' || !!ngDevMode) && initNgDevMode());
|
31465 | /*
|
31466 | * This file contains conditionally attached classes which provide human readable (debug) level
|
31467 | * information for `LView`, `LContainer` and other internal data structures. These data structures
|
31468 | * are stored internally as array which makes it very difficult during debugging to reason about the
|
31469 | * current state of the system.
|
31470 | *
|
31471 | * Patching the array with extra property does change the array's hidden class' but it does not
|
31472 | * change the cost of access, therefore this patching should not have significant if any impact in
|
31473 | * `ngDevMode` mode. (see: https://jsperf.com/array-vs-monkey-patch-array)
|
31474 | *
|
31475 | * So instead of seeing:
|
31476 | * ```
|
31477 | * Array(30) [Object, 659, null, …]
|
31478 | * ```
|
31479 | *
|
31480 | * You get to see:
|
31481 | * ```
|
31482 | * LViewDebug {
|
31483 | * views: [...],
|
31484 | * flags: {attached: true, ...}
|
31485 | * nodes: [
|
31486 | * {html: '<div id="123">', ..., nodes: [
|
31487 | * {html: '<span>', ..., nodes: null}
|
31488 | * ]}
|
31489 | * ]
|
31490 | * }
|
31491 | * ```
|
31492 | */
|
31493 | let LVIEW_COMPONENT_CACHE;
|
31494 | let LVIEW_EMBEDDED_CACHE;
|
31495 | let LVIEW_ROOT;
|
31496 | /**
|
31497 | * This function clones a blueprint and creates LView.
|
31498 | *
|
31499 | * Simple slice will keep the same type, and we need it to be LView
|
31500 | */
|
31501 | function cloneToLViewFromTViewBlueprint(tView) {
|
31502 | const debugTView = tView;
|
31503 | const lView = getLViewToClone(debugTView.type, tView.template && tView.template.name);
|
31504 | return lView.concat(tView.blueprint);
|
31505 | }
|
31506 | function getLViewToClone(type, name) {
|
31507 | switch (type) {
|
31508 | case 0 /* Root */:
|
31509 | if (LVIEW_ROOT === undefined)
|
31510 | LVIEW_ROOT = new (createNamedArrayType('LRootView'))();
|
31511 | return LVIEW_ROOT;
|
31512 | case 1 /* Component */:
|
31513 | if (LVIEW_COMPONENT_CACHE === undefined)
|
31514 | LVIEW_COMPONENT_CACHE = new Map();
|
31515 | let componentArray = LVIEW_COMPONENT_CACHE.get(name);
|
31516 | if (componentArray === undefined) {
|
31517 | componentArray = new (createNamedArrayType('LComponentView' + nameSuffix(name)))();
|
31518 | LVIEW_COMPONENT_CACHE.set(name, componentArray);
|
31519 | }
|
31520 | return componentArray;
|
31521 | case 2 /* Embedded */:
|
31522 | if (LVIEW_EMBEDDED_CACHE === undefined)
|
31523 | LVIEW_EMBEDDED_CACHE = new Map();
|
31524 | let embeddedArray = LVIEW_EMBEDDED_CACHE.get(name);
|
31525 | if (embeddedArray === undefined) {
|
31526 | embeddedArray = new (createNamedArrayType('LEmbeddedView' + nameSuffix(name)))();
|
31527 | LVIEW_EMBEDDED_CACHE.set(name, embeddedArray);
|
31528 | }
|
31529 | return embeddedArray;
|
31530 | }
|
31531 | throw new Error('unreachable code');
|
31532 | }
|
31533 | function nameSuffix(text) {
|
31534 | if (text == null)
|
31535 | return '';
|
31536 | const index = text.lastIndexOf('_Template');
|
31537 | return '_' + (index === -1 ? text : text.substr(0, index));
|
31538 | }
|
31539 | /**
|
31540 | * This class is a debug version of Object literal so that we can have constructor name show up
|
31541 | * in
|
31542 | * debug tools in ngDevMode.
|
31543 | */
|
31544 | const TViewConstructor = class TView {
|
31545 | 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) {
|
31546 | this.type = type;
|
31547 | this.blueprint = blueprint;
|
31548 | this.template = template;
|
31549 | this.queries = queries;
|
31550 | this.viewQuery = viewQuery;
|
31551 | this.declTNode = declTNode;
|
31552 | this.data = data;
|
31553 | this.bindingStartIndex = bindingStartIndex;
|
31554 | this.expandoStartIndex = expandoStartIndex;
|
31555 | this.hostBindingOpCodes = hostBindingOpCodes;
|
31556 | this.firstCreatePass = firstCreatePass;
|
31557 | this.firstUpdatePass = firstUpdatePass;
|
31558 | this.staticViewQueries = staticViewQueries;
|
31559 | this.staticContentQueries = staticContentQueries;
|
31560 | this.preOrderHooks = preOrderHooks;
|
31561 | this.preOrderCheckHooks = preOrderCheckHooks;
|
31562 | this.contentHooks = contentHooks;
|
31563 | this.contentCheckHooks = contentCheckHooks;
|
31564 | this.viewHooks = viewHooks;
|
31565 | this.viewCheckHooks = viewCheckHooks;
|
31566 | this.destroyHooks = destroyHooks;
|
31567 | this.cleanup = cleanup;
|
31568 | this.contentQueries = contentQueries;
|
31569 | this.components = components;
|
31570 | this.directiveRegistry = directiveRegistry;
|
31571 | this.pipeRegistry = pipeRegistry;
|
31572 | this.firstChild = firstChild;
|
31573 | this.schemas = schemas;
|
31574 | this.consts = consts;
|
31575 | this.incompleteFirstPass = incompleteFirstPass;
|
31576 | this._decls = _decls;
|
31577 | this._vars = _vars;
|
31578 | }
|
31579 | get template_() {
|
31580 | const buf = [];
|
31581 | processTNodeChildren(this.firstChild, buf);
|
31582 | return buf.join('');
|
31583 | }
|
31584 | get type_() {
|
31585 | return TViewTypeAsString[this.type] || `TViewType.?${this.type}?`;
|
31586 | }
|
31587 | };
|
31588 | class TNode {
|
31589 | constructor(tView_, //
|
31590 | type, //
|
31591 | index, //
|
31592 | insertBeforeIndex, //
|
31593 | injectorIndex, //
|
31594 | directiveStart, //
|
31595 | directiveEnd, //
|
31596 | directiveStylingLast, //
|
31597 | propertyBindings, //
|
31598 | flags, //
|
31599 | providerIndexes, //
|
31600 | value, //
|
31601 | attrs, //
|
31602 | mergedAttrs, //
|
31603 | localNames, //
|
31604 | initialInputs, //
|
31605 | inputs, //
|
31606 | outputs, //
|
31607 | tViews, //
|
31608 | next, //
|
31609 | projectionNext, //
|
31610 | child, //
|
31611 | parent, //
|
31612 | projection, //
|
31613 | styles, //
|
31614 | stylesWithoutHost, //
|
31615 | residualStyles, //
|
31616 | classes, //
|
31617 | classesWithoutHost, //
|
31618 | residualClasses, //
|
31619 | classBindings, //
|
31620 | styleBindings) {
|
31621 | this.tView_ = tView_;
|
31622 | this.type = type;
|
31623 | this.index = index;
|
31624 | this.insertBeforeIndex = insertBeforeIndex;
|
31625 | this.injectorIndex = injectorIndex;
|
31626 | this.directiveStart = directiveStart;
|
31627 | this.directiveEnd = directiveEnd;
|
31628 | this.directiveStylingLast = directiveStylingLast;
|
31629 | this.propertyBindings = propertyBindings;
|
31630 | this.flags = flags;
|
31631 | this.providerIndexes = providerIndexes;
|
31632 | this.value = value;
|
31633 | this.attrs = attrs;
|
31634 | this.mergedAttrs = mergedAttrs;
|
31635 | this.localNames = localNames;
|
31636 | this.initialInputs = initialInputs;
|
31637 | this.inputs = inputs;
|
31638 | this.outputs = outputs;
|
31639 | this.tViews = tViews;
|
31640 | this.next = next;
|
31641 | this.projectionNext = projectionNext;
|
31642 | this.child = child;
|
31643 | this.parent = parent;
|
31644 | this.projection = projection;
|
31645 | this.styles = styles;
|
31646 | this.stylesWithoutHost = stylesWithoutHost;
|
31647 | this.residualStyles = residualStyles;
|
31648 | this.classes = classes;
|
31649 | this.classesWithoutHost = classesWithoutHost;
|
31650 | this.residualClasses = residualClasses;
|
31651 | this.classBindings = classBindings;
|
31652 | this.styleBindings = styleBindings;
|
31653 | }
|
31654 | /**
|
31655 | * Return a human debug version of the set of `NodeInjector`s which will be consulted when
|
31656 | * resolving tokens from this `TNode`.
|
31657 | *
|
31658 | * When debugging applications, it is often difficult to determine which `NodeInjector`s will be
|
31659 | * consulted. This method shows a list of `DebugNode`s representing the `TNode`s which will be
|
31660 | * consulted in order when resolving a token starting at this `TNode`.
|
31661 | *
|
31662 | * The original data is stored in `LView` and `TView` with a lot of offset indexes, and so it is
|
31663 | * difficult to reason about.
|
31664 | *
|
31665 | * @param lView The `LView` instance for this `TNode`.
|
31666 | */
|
31667 | debugNodeInjectorPath(lView) {
|
31668 | const path = [];
|
31669 | let injectorIndex = getInjectorIndex(this, lView);
|
31670 | if (injectorIndex === -1) {
|
31671 | // Looks like the current `TNode` does not have `NodeInjector` associated with it => look for
|
31672 | // parent NodeInjector.
|
31673 | const parentLocation = getParentInjectorLocation(this, lView);
|
31674 | if (parentLocation !== NO_PARENT_INJECTOR) {
|
31675 | // We found a parent, so start searching from the parent location.
|
31676 | injectorIndex = getParentInjectorIndex(parentLocation);
|
31677 | lView = getParentInjectorView(parentLocation, lView);
|
31678 | }
|
31679 | }
|
31680 | while (injectorIndex !== -1) {
|
31681 | ngDevMode && assertNodeInjector(lView, injectorIndex);
|
31682 | const tNode = lView[TVIEW].data[injectorIndex + 8 /* TNODE */];
|
31683 | path.push(buildDebugNode(tNode, lView));
|
31684 | const parentLocation = lView[injectorIndex + 8 /* PARENT */];
|
31685 | if (parentLocation === NO_PARENT_INJECTOR) {
|
31686 | injectorIndex = -1;
|
31687 | }
|
31688 | else {
|
31689 | injectorIndex = getParentInjectorIndex(parentLocation);
|
31690 | lView = getParentInjectorView(parentLocation, lView);
|
31691 | }
|
31692 | }
|
31693 | return path;
|
31694 | }
|
31695 | get type_() {
|
31696 | return toTNodeTypeAsString(this.type) || `TNodeType.?${this.type}?`;
|
31697 | }
|
31698 | get flags_() {
|
31699 | const flags = [];
|
31700 | if (this.flags & 16 /* hasClassInput */)
|
31701 | flags.push('TNodeFlags.hasClassInput');
|
31702 | if (this.flags & 8 /* hasContentQuery */)
|
31703 | flags.push('TNodeFlags.hasContentQuery');
|
31704 | if (this.flags & 32 /* hasStyleInput */)
|
31705 | flags.push('TNodeFlags.hasStyleInput');
|
31706 | if (this.flags & 128 /* hasHostBindings */)
|
31707 | flags.push('TNodeFlags.hasHostBindings');
|
31708 | if (this.flags & 2 /* isComponentHost */)
|
31709 | flags.push('TNodeFlags.isComponentHost');
|
31710 | if (this.flags & 1 /* isDirectiveHost */)
|
31711 | flags.push('TNodeFlags.isDirectiveHost');
|
31712 | if (this.flags & 64 /* isDetached */)
|
31713 | flags.push('TNodeFlags.isDetached');
|
31714 | if (this.flags & 4 /* isProjected */)
|
31715 | flags.push('TNodeFlags.isProjected');
|
31716 | return flags.join('|');
|
31717 | }
|
31718 | get template_() {
|
31719 | if (this.type & 1 /* Text */)
|
31720 | return this.value;
|
31721 | const buf = [];
|
31722 | const tagName = typeof this.value === 'string' && this.value || this.type_;
|
31723 | buf.push('<', tagName);
|
31724 | if (this.flags) {
|
31725 | buf.push(' ', this.flags_);
|
31726 | }
|
31727 | if (this.attrs) {
|
31728 | for (let i = 0; i < this.attrs.length;) {
|
31729 | const attrName = this.attrs[i++];
|
31730 | if (typeof attrName == 'number') {
|
31731 | break;
|
31732 | }
|
31733 | const attrValue = this.attrs[i++];
|
31734 | buf.push(' ', attrName, '="', attrValue, '"');
|
31735 | }
|
31736 | }
|
31737 | buf.push('>');
|
31738 | processTNodeChildren(this.child, buf);
|
31739 | buf.push('</', tagName, '>');
|
31740 | return buf.join('');
|
31741 | }
|
31742 | get styleBindings_() {
|
31743 | return toDebugStyleBinding(this, false);
|
31744 | }
|
31745 | get classBindings_() {
|
31746 | return toDebugStyleBinding(this, true);
|
31747 | }
|
31748 | get providerIndexStart_() {
|
31749 | return this.providerIndexes & 1048575 /* ProvidersStartIndexMask */;
|
31750 | }
|
31751 | get providerIndexEnd_() {
|
31752 | return this.providerIndexStart_ +
|
31753 | (this.providerIndexes >>> 20 /* CptViewProvidersCountShift */);
|
31754 | }
|
31755 | }
|
31756 | const TNodeDebug = TNode;
|
31757 | function toDebugStyleBinding(tNode, isClassBased) {
|
31758 | const tData = tNode.tView_.data;
|
31759 | const bindings = [];
|
31760 | const range = isClassBased ? tNode.classBindings : tNode.styleBindings;
|
31761 | const prev = getTStylingRangePrev(range);
|
31762 | const next = getTStylingRangeNext(range);
|
31763 | let isTemplate = next !== 0;
|
31764 | let cursor = isTemplate ? next : prev;
|
31765 | while (cursor !== 0) {
|
31766 | const itemKey = tData[cursor];
|
31767 | const itemRange = tData[cursor + 1];
|
31768 | bindings.unshift({
|
31769 | key: itemKey,
|
31770 | index: cursor,
|
31771 | isTemplate: isTemplate,
|
31772 | prevDuplicate: getTStylingRangePrevDuplicate(itemRange),
|
31773 | nextDuplicate: getTStylingRangeNextDuplicate(itemRange),
|
31774 | nextIndex: getTStylingRangeNext(itemRange),
|
31775 | prevIndex: getTStylingRangePrev(itemRange),
|
31776 | });
|
31777 | if (cursor === prev)
|
31778 | isTemplate = false;
|
31779 | cursor = getTStylingRangePrev(itemRange);
|
31780 | }
|
31781 | bindings.push((isClassBased ? tNode.residualClasses : tNode.residualStyles) || null);
|
31782 | return bindings;
|
31783 | }
|
31784 | function processTNodeChildren(tNode, buf) {
|
31785 | while (tNode) {
|
31786 | buf.push(tNode.template_);
|
31787 | tNode = tNode.next;
|
31788 | }
|
31789 | }
|
31790 | const TViewData = NG_DEV_MODE && createNamedArrayType('TViewData') || null;
|
31791 | let TVIEWDATA_EMPTY; // can't initialize here or it will not be tree shaken, because
|
31792 | // `LView` constructor could have side-effects.
|
31793 | /**
|
31794 | * This function clones a blueprint and creates TData.
|
31795 | *
|
31796 | * Simple slice will keep the same type, and we need it to be TData
|
31797 | */
|
31798 | function cloneToTViewData(list) {
|
31799 | if (TVIEWDATA_EMPTY === undefined)
|
31800 | TVIEWDATA_EMPTY = new TViewData();
|
31801 | return TVIEWDATA_EMPTY.concat(list);
|
31802 | }
|
31803 | const LViewBlueprint = NG_DEV_MODE && createNamedArrayType('LViewBlueprint') || null;
|
31804 | const MatchesArray = NG_DEV_MODE && createNamedArrayType('MatchesArray') || null;
|
31805 | const TViewComponents = NG_DEV_MODE && createNamedArrayType('TViewComponents') || null;
|
31806 | const TNodeLocalNames = NG_DEV_MODE && createNamedArrayType('TNodeLocalNames') || null;
|
31807 | const TNodeInitialInputs = NG_DEV_MODE && createNamedArrayType('TNodeInitialInputs') || null;
|
31808 | const TNodeInitialData = NG_DEV_MODE && createNamedArrayType('TNodeInitialData') || null;
|
31809 | const LCleanup = NG_DEV_MODE && createNamedArrayType('LCleanup') || null;
|
31810 | const TCleanup = NG_DEV_MODE && createNamedArrayType('TCleanup') || null;
|
31811 | function attachLViewDebug(lView) {
|
31812 | attachDebugObject(lView, new LViewDebug(lView));
|
31813 | }
|
31814 | function toDebug(obj) {
|
31815 | if (obj) {
|
31816 | const debug = obj.debug;
|
31817 | assertDefined(debug, 'Object does not have a debug representation.');
|
31818 | return debug;
|
31819 | }
|
31820 | else {
|
31821 | return obj;
|
31822 | }
|
31823 | }
|
31824 | /**
|
31825 | * Use this method to unwrap a native element in `LView` and convert it into HTML for easier
|
31826 | * reading.
|
31827 | *
|
31828 | * @param value possibly wrapped native DOM node.
|
31829 | * @param includeChildren If `true` then the serialized HTML form will include child elements
|
31830 | * (same
|
31831 | * as `outerHTML`). If `false` then the serialized HTML form will only contain the element
|
31832 | * itself
|
31833 | * (will not serialize child elements).
|
31834 | */
|
31835 | function toHtml(value, includeChildren = false) {
|
31836 | const node = unwrapRNode(value);
|
31837 | if (node) {
|
31838 | switch (node.nodeType) {
|
31839 | case Node.TEXT_NODE:
|
31840 | return node.textContent;
|
31841 | case Node.COMMENT_NODE:
|
31842 | return `<!--${node.textContent}-->`;
|
31843 | case Node.ELEMENT_NODE:
|
31844 | const outerHTML = node.outerHTML;
|
31845 | if (includeChildren) {
|
31846 | return outerHTML;
|
31847 | }
|
31848 | else {
|
31849 | const innerHTML = '>' + node.innerHTML + '<';
|
31850 | return (outerHTML.split(innerHTML)[0]) + '>';
|
31851 | }
|
31852 | }
|
31853 | }
|
31854 | return null;
|
31855 | }
|
31856 | class LViewDebug {
|
31857 | constructor(_raw_lView) {
|
31858 | this._raw_lView = _raw_lView;
|
31859 | }
|
31860 | /**
|
31861 | * Flags associated with the `LView` unpacked into a more readable state.
|
31862 | */
|
31863 | get flags() {
|
31864 | const flags = this._raw_lView[FLAGS];
|
31865 | return {
|
31866 | __raw__flags__: flags,
|
31867 | initPhaseState: flags & 3 /* InitPhaseStateMask */,
|
31868 | creationMode: !!(flags & 4 /* CreationMode */),
|
31869 | firstViewPass: !!(flags & 8 /* FirstLViewPass */),
|
31870 | checkAlways: !!(flags & 16 /* CheckAlways */),
|
31871 | dirty: !!(flags & 64 /* Dirty */),
|
31872 | attached: !!(flags & 128 /* Attached */),
|
31873 | destroyed: !!(flags & 256 /* Destroyed */),
|
31874 | isRoot: !!(flags & 512 /* IsRoot */),
|
31875 | indexWithinInitPhase: flags >> 11 /* IndexWithinInitPhaseShift */,
|
31876 | };
|
31877 | }
|
31878 | get parent() {
|
31879 | return toDebug(this._raw_lView[PARENT]);
|
31880 | }
|
31881 | get hostHTML() {
|
31882 | return toHtml(this._raw_lView[HOST], true);
|
31883 | }
|
31884 | get html() {
|
31885 | return (this.nodes || []).map(mapToHTML).join('');
|
31886 | }
|
31887 | get context() {
|
31888 | return this._raw_lView[CONTEXT];
|
31889 | }
|
31890 | /**
|
31891 | * The tree of nodes associated with the current `LView`. The nodes have been normalized into
|
31892 | * a tree structure with relevant details pulled out for readability.
|
31893 | */
|
31894 | get nodes() {
|
31895 | const lView = this._raw_lView;
|
31896 | const tNode = lView[TVIEW].firstChild;
|
31897 | return toDebugNodes(tNode, lView);
|
31898 | }
|
31899 | get template() {
|
31900 | return this.tView.template_;
|
31901 | }
|
31902 | get tView() {
|
31903 | return this._raw_lView[TVIEW];
|
31904 | }
|
31905 | get cleanup() {
|
31906 | return this._raw_lView[CLEANUP];
|
31907 | }
|
31908 | get injector() {
|
31909 | return this._raw_lView[INJECTOR];
|
31910 | }
|
31911 | get rendererFactory() {
|
31912 | return this._raw_lView[RENDERER_FACTORY];
|
31913 | }
|
31914 | get renderer() {
|
31915 | return this._raw_lView[RENDERER];
|
31916 | }
|
31917 | get sanitizer() {
|
31918 | return this._raw_lView[SANITIZER];
|
31919 | }
|
31920 | get childHead() {
|
31921 | return toDebug(this._raw_lView[CHILD_HEAD]);
|
31922 | }
|
31923 | get next() {
|
31924 | return toDebug(this._raw_lView[NEXT]);
|
31925 | }
|
31926 | get childTail() {
|
31927 | return toDebug(this._raw_lView[CHILD_TAIL]);
|
31928 | }
|
31929 | get declarationView() {
|
31930 | return toDebug(this._raw_lView[DECLARATION_VIEW]);
|
31931 | }
|
31932 | get queries() {
|
31933 | return this._raw_lView[QUERIES];
|
31934 | }
|
31935 | get tHost() {
|
31936 | return this._raw_lView[T_HOST];
|
31937 | }
|
31938 | get decls() {
|
31939 | return toLViewRange(this.tView, this._raw_lView, HEADER_OFFSET, this.tView.bindingStartIndex);
|
31940 | }
|
31941 | get vars() {
|
31942 | return toLViewRange(this.tView, this._raw_lView, this.tView.bindingStartIndex, this.tView.expandoStartIndex);
|
31943 | }
|
31944 | get expando() {
|
31945 | return toLViewRange(this.tView, this._raw_lView, this.tView.expandoStartIndex, this._raw_lView.length);
|
31946 | }
|
31947 | /**
|
31948 | * Normalized view of child views (and containers) attached at this location.
|
31949 | */
|
31950 | get childViews() {
|
31951 | const childViews = [];
|
31952 | let child = this.childHead;
|
31953 | while (child) {
|
31954 | childViews.push(child);
|
31955 | child = child.next;
|
31956 | }
|
31957 | return childViews;
|
31958 | }
|
31959 | }
|
31960 | function mapToHTML(node) {
|
31961 | if (node.type === 'ElementContainer') {
|
31962 | return (node.children || []).map(mapToHTML).join('');
|
31963 | }
|
31964 | else if (node.type === 'IcuContainer') {
|
31965 | throw new Error('Not implemented');
|
31966 | }
|
31967 | else {
|
31968 | return toHtml(node.native, true) || '';
|
31969 | }
|
31970 | }
|
31971 | function toLViewRange(tView, lView, start, end) {
|
31972 | let content = [];
|
31973 | for (let index = start; index < end; index++) {
|
31974 | content.push({ index: index, t: tView.data[index], l: lView[index] });
|
31975 | }
|
31976 | return { start: start, end: end, length: end - start, content: content };
|
31977 | }
|
31978 | /**
|
31979 | * Turns a flat list of nodes into a tree by walking the associated `TNode` tree.
|
31980 | *
|
31981 | * @param tNode
|
31982 | * @param lView
|
31983 | */
|
31984 | function toDebugNodes(tNode, lView) {
|
31985 | if (tNode) {
|
31986 | const debugNodes = [];
|
31987 | let tNodeCursor = tNode;
|
31988 | while (tNodeCursor) {
|
31989 | debugNodes.push(buildDebugNode(tNodeCursor, lView));
|
31990 | tNodeCursor = tNodeCursor.next;
|
31991 | }
|
31992 | return debugNodes;
|
31993 | }
|
31994 | else {
|
31995 | return [];
|
31996 | }
|
31997 | }
|
31998 | function buildDebugNode(tNode, lView) {
|
31999 | const rawValue = lView[tNode.index];
|
32000 | const native = unwrapRNode(rawValue);
|
32001 | const factories = [];
|
32002 | const instances = [];
|
32003 | const tView = lView[TVIEW];
|
32004 | for (let i = tNode.directiveStart; i < tNode.directiveEnd; i++) {
|
32005 | const def = tView.data[i];
|
32006 | factories.push(def.type);
|
32007 | instances.push(lView[i]);
|
32008 | }
|
32009 | return {
|
32010 | html: toHtml(native),
|
32011 | type: toTNodeTypeAsString(tNode.type),
|
32012 | tNode,
|
32013 | native: native,
|
32014 | children: toDebugNodes(tNode.child, lView),
|
32015 | factories,
|
32016 | instances,
|
32017 | injector: buildNodeInjectorDebug(tNode, tView, lView),
|
32018 | get injectorResolutionPath() {
|
32019 | return tNode.debugNodeInjectorPath(lView);
|
32020 | },
|
32021 | };
|
32022 | }
|
32023 | function buildNodeInjectorDebug(tNode, tView, lView) {
|
32024 | const viewProviders = [];
|
32025 | for (let i = tNode.providerIndexStart_; i < tNode.providerIndexEnd_; i++) {
|
32026 | viewProviders.push(tView.data[i]);
|
32027 | }
|
32028 | const providers = [];
|
32029 | for (let i = tNode.providerIndexEnd_; i < tNode.directiveEnd; i++) {
|
32030 | providers.push(tView.data[i]);
|
32031 | }
|
32032 | const nodeInjectorDebug = {
|
32033 | bloom: toBloom(lView, tNode.injectorIndex),
|
32034 | cumulativeBloom: toBloom(tView.data, tNode.injectorIndex),
|
32035 | providers,
|
32036 | viewProviders,
|
32037 | parentInjectorIndex: lView[tNode.providerIndexStart_ - 1],
|
32038 | };
|
32039 | return nodeInjectorDebug;
|
32040 | }
|
32041 | /**
|
32042 | * Convert a number at `idx` location in `array` into binary representation.
|
32043 | *
|
32044 | * @param array
|
32045 | * @param idx
|
32046 | */
|
32047 | function binary(array, idx) {
|
32048 | const value = array[idx];
|
32049 | // If not a number we print 8 `?` to retain alignment but let user know that it was called on
|
32050 | // wrong type.
|
32051 | if (typeof value !== 'number')
|
32052 | return '????????';
|
32053 | // We prefix 0s so that we have constant length number
|
32054 | const text = '00000000' + value.toString(2);
|
32055 | return text.substring(text.length - 8);
|
32056 | }
|
32057 | /**
|
32058 | * Convert a bloom filter at location `idx` in `array` into binary representation.
|
32059 | *
|
32060 | * @param array
|
32061 | * @param idx
|
32062 | */
|
32063 | function toBloom(array, idx) {
|
32064 | if (idx < 0) {
|
32065 | return 'NO_NODE_INJECTOR';
|
32066 | }
|
32067 | 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)}`;
|
32068 | }
|
32069 |
|
32070 | const ɵ0$5 = () => Promise.resolve(null);
|
32071 | /**
|
32072 | * A permanent marker promise which signifies that the current CD tree is
|
32073 | * clean.
|
32074 | */
|
32075 | const _CLEAN_PROMISE = (ɵ0$5)();
|
32076 | /**
|
32077 | * Invoke `HostBindingsFunction`s for view.
|
32078 | *
|
32079 | * This methods executes `TView.hostBindingOpCodes`. It is used to execute the
|
32080 | * `HostBindingsFunction`s associated with the current `LView`.
|
32081 | *
|
32082 | * @param tView Current `TView`.
|
32083 | * @param lView Current `LView`.
|
32084 | */
|
32085 | function processHostBindingOpCodes(tView, lView) {
|
32086 | const hostBindingOpCodes = tView.hostBindingOpCodes;
|
32087 | if (hostBindingOpCodes === null)
|
32088 | return;
|
32089 | try {
|
32090 | for (let i = 0; i < hostBindingOpCodes.length; i++) {
|
32091 | const opCode = hostBindingOpCodes[i];
|
32092 | if (opCode < 0) {
|
32093 | // Negative numbers are element indexes.
|
32094 | setSelectedIndex(~opCode);
|
32095 | }
|
32096 | else {
|
32097 | // Positive numbers are NumberTuple which store bindingRootIndex and directiveIndex.
|
32098 | const directiveIdx = opCode;
|
32099 | const bindingRootIndx = hostBindingOpCodes[++i];
|
32100 | const hostBindingFn = hostBindingOpCodes[++i];
|
32101 | setBindingRootForHostBindings(bindingRootIndx, directiveIdx);
|
32102 | const context = lView[directiveIdx];
|
32103 | hostBindingFn(2 /* Update */, context);
|
32104 | }
|
32105 | }
|
32106 | }
|
32107 | finally {
|
32108 | setSelectedIndex(-1);
|
32109 | }
|
32110 | }
|
32111 | /** Refreshes all content queries declared by directives in a given view */
|
32112 | function refreshContentQueries(tView, lView) {
|
32113 | const contentQueries = tView.contentQueries;
|
32114 | if (contentQueries !== null) {
|
32115 | for (let i = 0; i < contentQueries.length; i += 2) {
|
32116 | const queryStartIdx = contentQueries[i];
|
32117 | const directiveDefIdx = contentQueries[i + 1];
|
32118 | if (directiveDefIdx !== -1) {
|
32119 | const directiveDef = tView.data[directiveDefIdx];
|
32120 | ngDevMode && assertDefined(directiveDef, 'DirectiveDef not found.');
|
32121 | ngDevMode &&
|
32122 | assertDefined(directiveDef.contentQueries, 'contentQueries function should be defined');
|
32123 | setCurrentQueryIndex(queryStartIdx);
|
32124 | directiveDef.contentQueries(2 /* Update */, lView[directiveDefIdx], directiveDefIdx);
|
32125 | }
|
32126 | }
|
32127 | }
|
32128 | }
|
32129 | /** Refreshes child components in the current view (update mode). */
|
32130 | function refreshChildComponents(hostLView, components) {
|
32131 | for (let i = 0; i < components.length; i++) {
|
32132 | refreshComponent(hostLView, components[i]);
|
32133 | }
|
32134 | }
|
32135 | /** Renders child components in the current view (creation mode). */
|
32136 | function renderChildComponents(hostLView, components) {
|
32137 | for (let i = 0; i < components.length; i++) {
|
32138 | renderComponent(hostLView, components[i]);
|
32139 | }
|
32140 | }
|
32141 | function createLView(parentLView, tView, context, flags, host, tHostNode, rendererFactory, renderer, sanitizer, injector) {
|
32142 | const lView = ngDevMode ? cloneToLViewFromTViewBlueprint(tView) : tView.blueprint.slice();
|
32143 | lView[HOST] = host;
|
32144 | lView[FLAGS] = flags | 4 /* CreationMode */ | 128 /* Attached */ | 8 /* FirstLViewPass */;
|
32145 | resetPreOrderHookFlags(lView);
|
32146 | ngDevMode && tView.declTNode && parentLView && assertTNodeForLView(tView.declTNode, parentLView);
|
32147 | lView[PARENT] = lView[DECLARATION_VIEW] = parentLView;
|
32148 | lView[CONTEXT] = context;
|
32149 | lView[RENDERER_FACTORY] = (rendererFactory || parentLView && parentLView[RENDERER_FACTORY]);
|
32150 | ngDevMode && assertDefined(lView[RENDERER_FACTORY], 'RendererFactory is required');
|
32151 | lView[RENDERER] = (renderer || parentLView && parentLView[RENDERER]);
|
32152 | ngDevMode && assertDefined(lView[RENDERER], 'Renderer is required');
|
32153 | lView[SANITIZER] = sanitizer || parentLView && parentLView[SANITIZER] || null;
|
32154 | lView[INJECTOR] = injector || parentLView && parentLView[INJECTOR] || null;
|
32155 | lView[T_HOST] = tHostNode;
|
32156 | ngDevMode &&
|
32157 | assertEqual(tView.type == 2 /* Embedded */ ? parentLView !== null : true, true, 'Embedded views must have parentLView');
|
32158 | lView[DECLARATION_COMPONENT_VIEW] =
|
32159 | tView.type == 2 /* Embedded */ ? parentLView[DECLARATION_COMPONENT_VIEW] : lView;
|
32160 | ngDevMode && attachLViewDebug(lView);
|
32161 | return lView;
|
32162 | }
|
32163 | function getOrCreateTNode(tView, index, type, name, attrs) {
|
32164 | ngDevMode && index !== 0 && // 0 are bogus nodes and they are OK. See `createContainerRef` in
|
32165 | // `view_engine_compatibility` for additional context.
|
32166 | assertGreaterThanOrEqual(index, HEADER_OFFSET, 'TNodes can\'t be in the LView header.');
|
32167 | // Keep this function short, so that the VM will inline it.
|
32168 | ngDevMode && assertPureTNodeType(type);
|
32169 | let tNode = tView.data[index];
|
32170 | if (tNode === null) {
|
32171 | tNode = createTNodeAtIndex(tView, index, type, name, attrs);
|
32172 | if (isInI18nBlock()) {
|
32173 | // If we are in i18n block then all elements should be pre declared through `Placeholder`
|
32174 | // See `TNodeType.Placeholder` and `LFrame.inI18n` for more context.
|
32175 | // If the `TNode` was not pre-declared than it means it was not mentioned which means it was
|
32176 | // removed, so we mark it as detached.
|
32177 | tNode.flags |= 64 /* isDetached */;
|
32178 | }
|
32179 | }
|
32180 | else if (tNode.type & 64 /* Placeholder */) {
|
32181 | tNode.type = type;
|
32182 | tNode.value = name;
|
32183 | tNode.attrs = attrs;
|
32184 | const parent = getCurrentParentTNode();
|
32185 | tNode.injectorIndex = parent === null ? -1 : parent.injectorIndex;
|
32186 | ngDevMode && assertTNodeForTView(tNode, tView);
|
32187 | ngDevMode && assertEqual(index, tNode.index, 'Expecting same index');
|
32188 | }
|
32189 | setCurrentTNode(tNode, true);
|
32190 | return tNode;
|
32191 | }
|
32192 | function createTNodeAtIndex(tView, index, type, name, attrs) {
|
32193 | const currentTNode = getCurrentTNodePlaceholderOk();
|
32194 | const isParent = isCurrentTNodeParent();
|
32195 | const parent = isParent ? currentTNode : currentTNode && currentTNode.parent;
|
32196 | // Parents cannot cross component boundaries because components will be used in multiple places.
|
32197 | const tNode = tView.data[index] =
|
32198 | createTNode(tView, parent, type, index, name, attrs);
|
32199 | // Assign a pointer to the first child node of a given view. The first node is not always the one
|
32200 | // at index 0, in case of i18n, index 0 can be the instruction `i18nStart` and the first node has
|
32201 | // the index 1 or more, so we can't just check node index.
|
32202 | if (tView.firstChild === null) {
|
32203 | tView.firstChild = tNode;
|
32204 | }
|
32205 | if (currentTNode !== null) {
|
32206 | if (isParent) {
|
32207 | // FIXME(misko): This logic looks unnecessarily complicated. Could we simplify?
|
32208 | if (currentTNode.child == null && tNode.parent !== null) {
|
32209 | // We are in the same view, which means we are adding content node to the parent view.
|
32210 | currentTNode.child = tNode;
|
32211 | }
|
32212 | }
|
32213 | else {
|
32214 | if (currentTNode.next === null) {
|
32215 | // In the case of i18n the `currentTNode` may already be linked, in which case we don't want
|
32216 | // to break the links which i18n created.
|
32217 | currentTNode.next = tNode;
|
32218 | }
|
32219 | }
|
32220 | }
|
32221 | return tNode;
|
32222 | }
|
32223 | /**
|
32224 | * When elements are created dynamically after a view blueprint is created (e.g. through
|
32225 | * i18nApply()), we need to adjust the blueprint for future
|
32226 | * template passes.
|
32227 | *
|
32228 | * @param tView `TView` associated with `LView`
|
32229 | * @param lView The `LView` containing the blueprint to adjust
|
32230 | * @param numSlotsToAlloc The number of slots to alloc in the LView, should be >0
|
32231 | * @param initialValue Initial value to store in blueprint
|
32232 | */
|
32233 | function allocExpando(tView, lView, numSlotsToAlloc, initialValue) {
|
32234 | if (numSlotsToAlloc === 0)
|
32235 | return -1;
|
32236 | if (ngDevMode) {
|
32237 | assertFirstCreatePass(tView);
|
32238 | assertSame(tView, lView[TVIEW], '`LView` must be associated with `TView`!');
|
32239 | assertEqual(tView.data.length, lView.length, 'Expecting LView to be same size as TView');
|
32240 | assertEqual(tView.data.length, tView.blueprint.length, 'Expecting Blueprint to be same size as TView');
|
32241 | assertFirstUpdatePass(tView);
|
32242 | }
|
32243 | const allocIdx = lView.length;
|
32244 | for (let i = 0; i < numSlotsToAlloc; i++) {
|
32245 | lView.push(initialValue);
|
32246 | tView.blueprint.push(initialValue);
|
32247 | tView.data.push(null);
|
32248 | }
|
32249 | return allocIdx;
|
32250 | }
|
32251 | //////////////////////////
|
32252 | //// Render
|
32253 | //////////////////////////
|
32254 | /**
|
32255 | * Processes a view in the creation mode. This includes a number of steps in a specific order:
|
32256 | * - creating view query functions (if any);
|
32257 | * - executing a template function in the creation mode;
|
32258 | * - updating static queries (if any);
|
32259 | * - creating child components defined in a given view.
|
32260 | */
|
32261 | function renderView(tView, lView, context) {
|
32262 | ngDevMode && assertEqual(isCreationMode(lView), true, 'Should be run in creation mode');
|
32263 | enterView(lView);
|
32264 | try {
|
32265 | const viewQuery = tView.viewQuery;
|
32266 | if (viewQuery !== null) {
|
32267 | executeViewQueryFn(1 /* Create */, viewQuery, context);
|
32268 | }
|
32269 | // Execute a template associated with this view, if it exists. A template function might not be
|
32270 | // defined for the root component views.
|
32271 | const templateFn = tView.template;
|
32272 | if (templateFn !== null) {
|
32273 | executeTemplate(tView, lView, templateFn, 1 /* Create */, context);
|
32274 | }
|
32275 | // This needs to be set before children are processed to support recursive components.
|
32276 | // This must be set to false immediately after the first creation run because in an
|
32277 | // ngFor loop, all the views will be created together before update mode runs and turns
|
32278 | // off firstCreatePass. If we don't set it here, instances will perform directive
|
32279 | // matching, etc again and again.
|
32280 | if (tView.firstCreatePass) {
|
32281 | tView.firstCreatePass = false;
|
32282 | }
|
32283 | // We resolve content queries specifically marked as `static` in creation mode. Dynamic
|
32284 | // content queries are resolved during change detection (i.e. update mode), after embedded
|
32285 | // views are refreshed (see block above).
|
32286 | if (tView.staticContentQueries) {
|
32287 | refreshContentQueries(tView, lView);
|
32288 | }
|
32289 | // We must materialize query results before child components are processed
|
32290 | // in case a child component has projected a container. The LContainer needs
|
32291 | // to exist so the embedded views are properly attached by the container.
|
32292 | if (tView.staticViewQueries) {
|
32293 | executeViewQueryFn(2 /* Update */, tView.viewQuery, context);
|
32294 | }
|
32295 | // Render child component views.
|
32296 | const components = tView.components;
|
32297 | if (components !== null) {
|
32298 | renderChildComponents(lView, components);
|
32299 | }
|
32300 | }
|
32301 | catch (error) {
|
32302 | // If we didn't manage to get past the first template pass due to
|
32303 | // an error, mark the view as corrupted so we can try to recover.
|
32304 | if (tView.firstCreatePass) {
|
32305 | tView.incompleteFirstPass = true;
|
32306 | }
|
32307 | throw error;
|
32308 | }
|
32309 | finally {
|
32310 | lView[FLAGS] &= ~4 /* CreationMode */;
|
32311 | leaveView();
|
32312 | }
|
32313 | }
|
32314 | /**
|
32315 | * Processes a view in update mode. This includes a number of steps in a specific order:
|
32316 | * - executing a template function in update mode;
|
32317 | * - executing hooks;
|
32318 | * - refreshing queries;
|
32319 | * - setting host bindings;
|
32320 | * - refreshing child (embedded and component) views.
|
32321 | */
|
32322 | function refreshView(tView, lView, templateFn, context) {
|
32323 | ngDevMode && assertEqual(isCreationMode(lView), false, 'Should be run in update mode');
|
32324 | const flags = lView[FLAGS];
|
32325 | if ((flags & 256 /* Destroyed */) === 256 /* Destroyed */)
|
32326 | return;
|
32327 | enterView(lView);
|
32328 | // Check no changes mode is a dev only mode used to verify that bindings have not changed
|
32329 | // since they were assigned. We do not want to execute lifecycle hooks in that mode.
|
32330 | const isInCheckNoChangesPass = isInCheckNoChangesMode();
|
32331 | try {
|
32332 | resetPreOrderHookFlags(lView);
|
32333 | setBindingIndex(tView.bindingStartIndex);
|
32334 | if (templateFn !== null) {
|
32335 | executeTemplate(tView, lView, templateFn, 2 /* Update */, context);
|
32336 | }
|
32337 | const hooksInitPhaseCompleted = (flags & 3 /* InitPhaseStateMask */) === 3 /* InitPhaseCompleted */;
|
32338 | // execute pre-order hooks (OnInit, OnChanges, DoCheck)
|
32339 | // PERF WARNING: do NOT extract this to a separate function without running benchmarks
|
32340 | if (!isInCheckNoChangesPass) {
|
32341 | if (hooksInitPhaseCompleted) {
|
32342 | const preOrderCheckHooks = tView.preOrderCheckHooks;
|
32343 | if (preOrderCheckHooks !== null) {
|
32344 | executeCheckHooks(lView, preOrderCheckHooks, null);
|
32345 | }
|
32346 | }
|
32347 | else {
|
32348 | const preOrderHooks = tView.preOrderHooks;
|
32349 | if (preOrderHooks !== null) {
|
32350 | executeInitAndCheckHooks(lView, preOrderHooks, 0 /* OnInitHooksToBeRun */, null);
|
32351 | }
|
32352 | incrementInitPhaseFlags(lView, 0 /* OnInitHooksToBeRun */);
|
32353 | }
|
32354 | }
|
32355 | // First mark transplanted views that are declared in this lView as needing a refresh at their
|
32356 | // insertion points. This is needed to avoid the situation where the template is defined in this
|
32357 | // `LView` but its declaration appears after the insertion component.
|
32358 | markTransplantedViewsForRefresh(lView);
|
32359 | refreshEmbeddedViews(lView);
|
32360 | // Content query results must be refreshed before content hooks are called.
|
32361 | if (tView.contentQueries !== null) {
|
32362 | refreshContentQueries(tView, lView);
|
32363 | }
|
32364 | // execute content hooks (AfterContentInit, AfterContentChecked)
|
32365 | // PERF WARNING: do NOT extract this to a separate function without running benchmarks
|
32366 | if (!isInCheckNoChangesPass) {
|
32367 | if (hooksInitPhaseCompleted) {
|
32368 | const contentCheckHooks = tView.contentCheckHooks;
|
32369 | if (contentCheckHooks !== null) {
|
32370 | executeCheckHooks(lView, contentCheckHooks);
|
32371 | }
|
32372 | }
|
32373 | else {
|
32374 | const contentHooks = tView.contentHooks;
|
32375 | if (contentHooks !== null) {
|
32376 | executeInitAndCheckHooks(lView, contentHooks, 1 /* AfterContentInitHooksToBeRun */);
|
32377 | }
|
32378 | incrementInitPhaseFlags(lView, 1 /* AfterContentInitHooksToBeRun */);
|
32379 | }
|
32380 | }
|
32381 | processHostBindingOpCodes(tView, lView);
|
32382 | // Refresh child component views.
|
32383 | const components = tView.components;
|
32384 | if (components !== null) {
|
32385 | refreshChildComponents(lView, components);
|
32386 | }
|
32387 | // View queries must execute after refreshing child components because a template in this view
|
32388 | // could be inserted in a child component. If the view query executes before child component
|
32389 | // refresh, the template might not yet be inserted.
|
32390 | const viewQuery = tView.viewQuery;
|
32391 | if (viewQuery !== null) {
|
32392 | executeViewQueryFn(2 /* Update */, viewQuery, context);
|
32393 | }
|
32394 | // execute view hooks (AfterViewInit, AfterViewChecked)
|
32395 | // PERF WARNING: do NOT extract this to a separate function without running benchmarks
|
32396 | if (!isInCheckNoChangesPass) {
|
32397 | if (hooksInitPhaseCompleted) {
|
32398 | const viewCheckHooks = tView.viewCheckHooks;
|
32399 | if (viewCheckHooks !== null) {
|
32400 | executeCheckHooks(lView, viewCheckHooks);
|
32401 | }
|
32402 | }
|
32403 | else {
|
32404 | const viewHooks = tView.viewHooks;
|
32405 | if (viewHooks !== null) {
|
32406 | executeInitAndCheckHooks(lView, viewHooks, 2 /* AfterViewInitHooksToBeRun */);
|
32407 | }
|
32408 | incrementInitPhaseFlags(lView, 2 /* AfterViewInitHooksToBeRun */);
|
32409 | }
|
32410 | }
|
32411 | if (tView.firstUpdatePass === true) {
|
32412 | // We need to make sure that we only flip the flag on successful `refreshView` only
|
32413 | // Don't do this in `finally` block.
|
32414 | // If we did this in `finally` block then an exception could block the execution of styling
|
32415 | // instructions which in turn would be unable to insert themselves into the styling linked
|
32416 | // list. The result of this would be that if the exception would not be throw on subsequent CD
|
32417 | // the styling would be unable to process it data and reflect to the DOM.
|
32418 | tView.firstUpdatePass = false;
|
32419 | }
|
32420 | // Do not reset the dirty state when running in check no changes mode. We don't want components
|
32421 | // to behave differently depending on whether check no changes is enabled or not. For example:
|
32422 | // Marking an OnPush component as dirty from within the `ngAfterViewInit` hook in order to
|
32423 | // refresh a `NgClass` binding should work. If we would reset the dirty state in the check
|
32424 | // no changes cycle, the component would be not be dirty for the next update pass. This would
|
32425 | // be different in production mode where the component dirty state is not reset.
|
32426 | if (!isInCheckNoChangesPass) {
|
32427 | lView[FLAGS] &= ~(64 /* Dirty */ | 8 /* FirstLViewPass */);
|
32428 | }
|
32429 | if (lView[FLAGS] & 1024 /* RefreshTransplantedView */) {
|
32430 | lView[FLAGS] &= ~1024 /* RefreshTransplantedView */;
|
32431 | updateTransplantedViewCount(lView[PARENT], -1);
|
32432 | }
|
32433 | }
|
32434 | finally {
|
32435 | leaveView();
|
32436 | }
|
32437 | }
|
32438 | function renderComponentOrTemplate(tView, lView, templateFn, context) {
|
32439 | const rendererFactory = lView[RENDERER_FACTORY];
|
32440 | const normalExecutionPath = !isInCheckNoChangesMode();
|
32441 | const creationModeIsActive = isCreationMode(lView);
|
32442 | try {
|
32443 | if (normalExecutionPath && !creationModeIsActive && rendererFactory.begin) {
|
32444 | rendererFactory.begin();
|
32445 | }
|
32446 | if (creationModeIsActive) {
|
32447 | renderView(tView, lView, context);
|
32448 | }
|
32449 | refreshView(tView, lView, templateFn, context);
|
32450 | }
|
32451 | finally {
|
32452 | if (normalExecutionPath && !creationModeIsActive && rendererFactory.end) {
|
32453 | rendererFactory.end();
|
32454 | }
|
32455 | }
|
32456 | }
|
32457 | function executeTemplate(tView, lView, templateFn, rf, context) {
|
32458 | const prevSelectedIndex = getSelectedIndex();
|
32459 | try {
|
32460 | setSelectedIndex(-1);
|
32461 | if (rf & 2 /* Update */ && lView.length > HEADER_OFFSET) {
|
32462 | // When we're updating, inherently select 0 so we don't
|
32463 | // have to generate that instruction for most update blocks.
|
32464 | selectIndexInternal(tView, lView, HEADER_OFFSET, isInCheckNoChangesMode());
|
32465 | }
|
32466 | templateFn(rf, context);
|
32467 | }
|
32468 | finally {
|
32469 | setSelectedIndex(prevSelectedIndex);
|
32470 | }
|
32471 | }
|
32472 | /**
|
32473 | * Gets TView from a template function or creates a new TView
|
32474 | * if it doesn't already exist.
|
32475 | *
|
32476 | * @param def ComponentDef
|
32477 | * @returns TView
|
32478 | */
|
32479 | function getOrCreateTComponentView(def) {
|
32480 | const tView = def.tView;
|
32481 | // Create a TView if there isn't one, or recreate it if the first create pass didn't
|
32482 | // complete successfully since we can't know for sure whether it's in a usable shape.
|
32483 | if (tView === null || tView.incompleteFirstPass) {
|
32484 | // Declaration node here is null since this function is called when we dynamically create a
|
32485 | // component and hence there is no declaration.
|
32486 | const declTNode = null;
|
32487 | return def.tView = createTView(1 /* Component */, declTNode, def.template, def.decls, def.vars, def.directiveDefs, def.pipeDefs, def.viewQuery, def.schemas, def.consts);
|
32488 | }
|
32489 | return tView;
|
32490 | }
|
32491 | /**
|
32492 | * Creates a TView instance
|
32493 | *
|
32494 | * @param type Type of `TView`.
|
32495 | * @param declTNode Declaration location of this `TView`.
|
32496 | * @param templateFn Template function
|
32497 | * @param decls The number of nodes, local refs, and pipes in this template
|
32498 | * @param directives Registry of directives for this view
|
32499 | * @param pipes Registry of pipes for this view
|
32500 | * @param viewQuery View queries for this view
|
32501 | * @param schemas Schemas for this view
|
32502 | * @param consts Constants for this view
|
32503 | */
|
32504 | function createTView(type, declTNode, templateFn, decls, vars, directives, pipes, viewQuery, schemas, constsOrFactory) {
|
32505 | ngDevMode && ngDevMode.tView++;
|
32506 | const bindingStartIndex = HEADER_OFFSET + decls;
|
32507 | // This length does not yet contain host bindings from child directives because at this point,
|
32508 | // we don't know which directives are active on this template. As soon as a directive is matched
|
32509 | // that has a host binding, we will update the blueprint with that def's hostVars count.
|
32510 | const initialViewLength = bindingStartIndex + vars;
|
32511 | const blueprint = createViewBlueprint(bindingStartIndex, initialViewLength);
|
32512 | const consts = typeof constsOrFactory === 'function' ? constsOrFactory() : constsOrFactory;
|
32513 | const tView = blueprint[TVIEW] = ngDevMode ?
|
32514 | new TViewConstructor(type, // type: TViewType,
|
32515 | blueprint, // blueprint: LView,
|
32516 | templateFn, // template: ComponentTemplate<{}>|null,
|
32517 | null, // queries: TQueries|null
|
32518 | viewQuery, // viewQuery: ViewQueriesFunction<{}>|null,
|
32519 | declTNode, // declTNode: TNode|null,
|
32520 | cloneToTViewData(blueprint).fill(null, bindingStartIndex), // data: TData,
|
32521 | bindingStartIndex, // bindingStartIndex: number,
|
32522 | initialViewLength, // expandoStartIndex: number,
|
32523 | null, // hostBindingOpCodes: HostBindingOpCodes,
|
32524 | true, // firstCreatePass: boolean,
|
32525 | true, // firstUpdatePass: boolean,
|
32526 | false, // staticViewQueries: boolean,
|
32527 | false, // staticContentQueries: boolean,
|
32528 | null, // preOrderHooks: HookData|null,
|
32529 | null, // preOrderCheckHooks: HookData|null,
|
32530 | null, // contentHooks: HookData|null,
|
32531 | null, // contentCheckHooks: HookData|null,
|
32532 | null, // viewHooks: HookData|null,
|
32533 | null, // viewCheckHooks: HookData|null,
|
32534 | null, // destroyHooks: DestroyHookData|null,
|
32535 | null, // cleanup: any[]|null,
|
32536 | null, // contentQueries: number[]|null,
|
32537 | null, // components: number[]|null,
|
32538 | typeof directives === 'function' ? //
|
32539 | directives() : //
|
32540 | directives, // directiveRegistry: DirectiveDefList|null,
|
32541 | typeof pipes === 'function' ? pipes() : pipes, // pipeRegistry: PipeDefList|null,
|
32542 | null, // firstChild: TNode|null,
|
32543 | schemas, // schemas: SchemaMetadata[]|null,
|
32544 | consts, // consts: TConstants|null
|
32545 | false, // incompleteFirstPass: boolean
|
32546 | decls, // ngDevMode only: decls
|
32547 | vars) :
|
32548 | {
|
32549 | type: type,
|
32550 | blueprint: blueprint,
|
32551 | template: templateFn,
|
32552 | queries: null,
|
32553 | viewQuery: viewQuery,
|
32554 | declTNode: declTNode,
|
32555 | data: blueprint.slice().fill(null, bindingStartIndex),
|
32556 | bindingStartIndex: bindingStartIndex,
|
32557 | expandoStartIndex: initialViewLength,
|
32558 | hostBindingOpCodes: null,
|
32559 | firstCreatePass: true,
|
32560 | firstUpdatePass: true,
|
32561 | staticViewQueries: false,
|
32562 | staticContentQueries: false,
|
32563 | preOrderHooks: null,
|
32564 | preOrderCheckHooks: null,
|
32565 | contentHooks: null,
|
32566 | contentCheckHooks: null,
|
32567 | viewHooks: null,
|
32568 | viewCheckHooks: null,
|
32569 | destroyHooks: null,
|
32570 | cleanup: null,
|
32571 | contentQueries: null,
|
32572 | components: null,
|
32573 | directiveRegistry: typeof directives === 'function' ? directives() : directives,
|
32574 | pipeRegistry: typeof pipes === 'function' ? pipes() : pipes,
|
32575 | firstChild: null,
|
32576 | schemas: schemas,
|
32577 | consts: consts,
|
32578 | incompleteFirstPass: false
|
32579 | };
|
32580 | if (ngDevMode) {
|
32581 | // For performance reasons it is important that the tView retains the same shape during runtime.
|
32582 | // (To make sure that all of the code is monomorphic.) For this reason we seal the object to
|
32583 | // prevent class transitions.
|
32584 | Object.seal(tView);
|
32585 | }
|
32586 | return tView;
|
32587 | }
|
32588 | function createViewBlueprint(bindingStartIndex, initialViewLength) {
|
32589 | const blueprint = ngDevMode ? new LViewBlueprint() : [];
|
32590 | for (let i = 0; i < initialViewLength; i++) {
|
32591 | blueprint.push(i < bindingStartIndex ? null : NO_CHANGE);
|
32592 | }
|
32593 | return blueprint;
|
32594 | }
|
32595 | function createError(text, token) {
|
32596 | return new Error(`Renderer: ${text} [${stringifyForError(token)}]`);
|
32597 | }
|
32598 | function assertHostNodeExists(rElement, elementOrSelector) {
|
32599 | if (!rElement) {
|
32600 | if (typeof elementOrSelector === 'string') {
|
32601 | throw createError('Host node with selector not found:', elementOrSelector);
|
32602 | }
|
32603 | else {
|
32604 | throw createError('Host node is required:', elementOrSelector);
|
32605 | }
|
32606 | }
|
32607 | }
|
32608 | /**
|
32609 | * Locates the host native element, used for bootstrapping existing nodes into rendering pipeline.
|
32610 | *
|
32611 | * @param rendererFactory Factory function to create renderer instance.
|
32612 | * @param elementOrSelector Render element or CSS selector to locate the element.
|
32613 | * @param encapsulation View Encapsulation defined for component that requests host element.
|
32614 | */
|
32615 | function locateHostElement(renderer, elementOrSelector, encapsulation) {
|
32616 | if (isProceduralRenderer(renderer)) {
|
32617 | // When using native Shadow DOM, do not clear host element to allow native slot projection
|
32618 | const preserveContent = encapsulation === ViewEncapsulation$1.ShadowDom;
|
32619 | return renderer.selectRootElement(elementOrSelector, preserveContent);
|
32620 | }
|
32621 | let rElement = typeof elementOrSelector === 'string' ?
|
32622 | renderer.querySelector(elementOrSelector) :
|
32623 | elementOrSelector;
|
32624 | ngDevMode && assertHostNodeExists(rElement, elementOrSelector);
|
32625 | // Always clear host element's content when Renderer3 is in use. For procedural renderer case we
|
32626 | // make it depend on whether ShadowDom encapsulation is used (in which case the content should be
|
32627 | // preserved to allow native slot projection). ShadowDom encapsulation requires procedural
|
32628 | // renderer, and procedural renderer case is handled above.
|
32629 | rElement.textContent = '';
|
32630 | return rElement;
|
32631 | }
|
32632 | /**
|
32633 | * Saves context for this cleanup function in LView.cleanupInstances.
|
32634 | *
|
32635 | * On the first template pass, saves in TView:
|
32636 | * - Cleanup function
|
32637 | * - Index of context we just saved in LView.cleanupInstances
|
32638 | *
|
32639 | * This function can also be used to store instance specific cleanup fns. In that case the `context`
|
32640 | * is `null` and the function is store in `LView` (rather than it `TView`).
|
32641 | */
|
32642 | function storeCleanupWithContext(tView, lView, context, cleanupFn) {
|
32643 | const lCleanup = getOrCreateLViewCleanup(lView);
|
32644 | if (context === null) {
|
32645 | // If context is null that this is instance specific callback. These callbacks can only be
|
32646 | // inserted after template shared instances. For this reason in ngDevMode we freeze the TView.
|
32647 | if (ngDevMode) {
|
32648 | Object.freeze(getOrCreateTViewCleanup(tView));
|
32649 | }
|
32650 | lCleanup.push(cleanupFn);
|
32651 | }
|
32652 | else {
|
32653 | lCleanup.push(context);
|
32654 | if (tView.firstCreatePass) {
|
32655 | getOrCreateTViewCleanup(tView).push(cleanupFn, lCleanup.length - 1);
|
32656 | }
|
32657 | }
|
32658 | }
|
32659 | function createTNode(tView, tParent, type, index, value, attrs) {
|
32660 | ngDevMode && index !== 0 && // 0 are bogus nodes and they are OK. See `createContainerRef` in
|
32661 | // `view_engine_compatibility` for additional context.
|
32662 | assertGreaterThanOrEqual(index, HEADER_OFFSET, 'TNodes can\'t be in the LView header.');
|
32663 | ngDevMode && assertNotSame(attrs, undefined, '\'undefined\' is not valid value for \'attrs\'');
|
32664 | ngDevMode && ngDevMode.tNode++;
|
32665 | ngDevMode && tParent && assertTNodeForTView(tParent, tView);
|
32666 | let injectorIndex = tParent ? tParent.injectorIndex : -1;
|
32667 | const tNode = ngDevMode ?
|
32668 | new TNodeDebug(tView, // tView_: TView
|
32669 | type, // type: TNodeType
|
32670 | index, // index: number
|
32671 | null, // insertBeforeIndex: null|-1|number|number[]
|
32672 | injectorIndex, // injectorIndex: number
|
32673 | -1, // directiveStart: number
|
32674 | -1, // directiveEnd: number
|
32675 | -1, // directiveStylingLast: number
|
32676 | null, // propertyBindings: number[]|null
|
32677 | 0, // flags: TNodeFlags
|
32678 | 0, // providerIndexes: TNodeProviderIndexes
|
32679 | value, // value: string|null
|
32680 | attrs, // attrs: (string|AttributeMarker|(string|SelectorFlags)[])[]|null
|
32681 | null, // mergedAttrs
|
32682 | null, // localNames: (string|number)[]|null
|
32683 | undefined, // initialInputs: (string[]|null)[]|null|undefined
|
32684 | null, // inputs: PropertyAliases|null
|
32685 | null, // outputs: PropertyAliases|null
|
32686 | null, // tViews: ITView|ITView[]|null
|
32687 | null, // next: ITNode|null
|
32688 | null, // projectionNext: ITNode|null
|
32689 | null, // child: ITNode|null
|
32690 | tParent, // parent: TElementNode|TContainerNode|null
|
32691 | null, // projection: number|(ITNode|RNode[])[]|null
|
32692 | null, // styles: string|null
|
32693 | null, // stylesWithoutHost: string|null
|
32694 | undefined, // residualStyles: string|null
|
32695 | null, // classes: string|null
|
32696 | null, // classesWithoutHost: string|null
|
32697 | undefined, // residualClasses: string|null
|
32698 | 0, // classBindings: TStylingRange;
|
32699 | 0) :
|
32700 | {
|
32701 | type,
|
32702 | index,
|
32703 | insertBeforeIndex: null,
|
32704 | injectorIndex,
|
32705 | directiveStart: -1,
|
32706 | directiveEnd: -1,
|
32707 | directiveStylingLast: -1,
|
32708 | propertyBindings: null,
|
32709 | flags: 0,
|
32710 | providerIndexes: 0,
|
32711 | value: value,
|
32712 | attrs: attrs,
|
32713 | mergedAttrs: null,
|
32714 | localNames: null,
|
32715 | initialInputs: undefined,
|
32716 | inputs: null,
|
32717 | outputs: null,
|
32718 | tViews: null,
|
32719 | next: null,
|
32720 | projectionNext: null,
|
32721 | child: null,
|
32722 | parent: tParent,
|
32723 | projection: null,
|
32724 | styles: null,
|
32725 | stylesWithoutHost: null,
|
32726 | residualStyles: undefined,
|
32727 | classes: null,
|
32728 | classesWithoutHost: null,
|
32729 | residualClasses: undefined,
|
32730 | classBindings: 0,
|
32731 | styleBindings: 0,
|
32732 | };
|
32733 | if (ngDevMode) {
|
32734 | // For performance reasons it is important that the tNode retains the same shape during runtime.
|
32735 | // (To make sure that all of the code is monomorphic.) For this reason we seal the object to
|
32736 | // prevent class transitions.
|
32737 | Object.seal(tNode);
|
32738 | }
|
32739 | return tNode;
|
32740 | }
|
32741 | /**
|
32742 | * Instantiate a root component.
|
32743 | */
|
32744 | function instantiateRootComponent(tView, lView, def) {
|
32745 | const rootTNode = getCurrentTNode();
|
32746 | if (tView.firstCreatePass) {
|
32747 | if (def.providersResolver)
|
32748 | def.providersResolver(def);
|
32749 | const directiveIndex = allocExpando(tView, lView, 1, null);
|
32750 | ngDevMode &&
|
32751 | assertEqual(directiveIndex, rootTNode.directiveStart, 'Because this is a root component the allocated expando should match the TNode component.');
|
32752 | configureViewWithDirective(tView, rootTNode, lView, directiveIndex, def);
|
32753 | }
|
32754 | const directive = getNodeInjectable(lView, tView, rootTNode.directiveStart, rootTNode);
|
32755 | attachPatchData(directive, lView);
|
32756 | const native = getNativeByTNode(rootTNode, lView);
|
32757 | if (native) {
|
32758 | attachPatchData(native, lView);
|
32759 | }
|
32760 | return directive;
|
32761 | }
|
32762 | /**
|
32763 | * Add `hostBindings` to the `TView.hostBindingOpCodes`.
|
32764 | *
|
32765 | * @param tView `TView` to which the `hostBindings` should be added.
|
32766 | * @param tNode `TNode` the element which contains the directive
|
32767 | * @param lView `LView` current `LView`
|
32768 | * @param directiveIdx Directive index in view.
|
32769 | * @param directiveVarsIdx Where will the directive's vars be stored
|
32770 | * @param def `ComponentDef`/`DirectiveDef`, which contains the `hostVars`/`hostBindings` to add.
|
32771 | */
|
32772 | function registerHostBindingOpCodes(tView, tNode, lView, directiveIdx, directiveVarsIdx, def) {
|
32773 | ngDevMode && assertFirstCreatePass(tView);
|
32774 | const hostBindings = def.hostBindings;
|
32775 | if (hostBindings) {
|
32776 | let hostBindingOpCodes = tView.hostBindingOpCodes;
|
32777 | if (hostBindingOpCodes === null) {
|
32778 | hostBindingOpCodes = tView.hostBindingOpCodes = [];
|
32779 | }
|
32780 | const elementIndx = ~tNode.index;
|
32781 | if (lastSelectedElementIdx(hostBindingOpCodes) != elementIndx) {
|
32782 | // Conditionally add select element so that we are more efficient in execution.
|
32783 | // NOTE: this is strictly not necessary and it trades code size for runtime perf.
|
32784 | // (We could just always add it.)
|
32785 | hostBindingOpCodes.push(elementIndx);
|
32786 | }
|
32787 | hostBindingOpCodes.push(directiveIdx, directiveVarsIdx, hostBindings);
|
32788 | }
|
32789 | }
|
32790 | /**
|
32791 | * Returns the last selected element index in the `HostBindingOpCodes`
|
32792 | *
|
32793 | * For perf reasons we don't need to update the selected element index in `HostBindingOpCodes` only
|
32794 | * if it changes. This method returns the last index (or '0' if not found.)
|
32795 | *
|
32796 | * Selected element index are only the ones which are negative.
|
32797 | */
|
32798 | function lastSelectedElementIdx(hostBindingOpCodes) {
|
32799 | let i = hostBindingOpCodes.length;
|
32800 | while (i > 0) {
|
32801 | const value = hostBindingOpCodes[--i];
|
32802 | if (typeof value === 'number' && value < 0) {
|
32803 | return value;
|
32804 | }
|
32805 | }
|
32806 | return 0;
|
32807 | }
|
32808 | /**
|
32809 | * Invoke the host bindings in creation mode.
|
32810 | *
|
32811 | * @param def `DirectiveDef` which may contain the `hostBindings` function.
|
32812 | * @param directive Instance of directive.
|
32813 | */
|
32814 | function invokeHostBindingsInCreationMode(def, directive) {
|
32815 | if (def.hostBindings !== null) {
|
32816 | def.hostBindings(1 /* Create */, directive);
|
32817 | }
|
32818 | }
|
32819 | /**
|
32820 | * Marks a given TNode as a component's host. This consists of:
|
32821 | * - setting appropriate TNode flags;
|
32822 | * - storing index of component's host element so it will be queued for view refresh during CD.
|
32823 | */
|
32824 | function markAsComponentHost(tView, hostTNode) {
|
32825 | ngDevMode && assertFirstCreatePass(tView);
|
32826 | hostTNode.flags |= 2 /* isComponentHost */;
|
32827 | (tView.components || (tView.components = ngDevMode ? new TViewComponents() : []))
|
32828 | .push(hostTNode.index);
|
32829 | }
|
32830 | /**
|
32831 | * Initializes the flags on the current node, setting all indices to the initial index,
|
32832 | * the directive count to 0, and adding the isComponent flag.
|
32833 | * @param index the initial index
|
32834 | */
|
32835 | function initTNodeFlags(tNode, index, numberOfDirectives) {
|
32836 | ngDevMode &&
|
32837 | assertNotEqual(numberOfDirectives, tNode.directiveEnd - tNode.directiveStart, 'Reached the max number of directives');
|
32838 | tNode.flags |= 1 /* isDirectiveHost */;
|
32839 | // When the first directive is created on a node, save the index
|
32840 | tNode.directiveStart = index;
|
32841 | tNode.directiveEnd = index + numberOfDirectives;
|
32842 | tNode.providerIndexes = index;
|
32843 | }
|
32844 | /**
|
32845 | * Setup directive for instantiation.
|
32846 | *
|
32847 | * We need to create a `NodeInjectorFactory` which is then inserted in both the `Blueprint` as well
|
32848 | * as `LView`. `TView` gets the `DirectiveDef`.
|
32849 | *
|
32850 | * @param tView `TView`
|
32851 | * @param tNode `TNode`
|
32852 | * @param lView `LView`
|
32853 | * @param directiveIndex Index where the directive will be stored in the Expando.
|
32854 | * @param def `DirectiveDef`
|
32855 | */
|
32856 | function configureViewWithDirective(tView, tNode, lView, directiveIndex, def) {
|
32857 | ngDevMode &&
|
32858 | assertGreaterThanOrEqual(directiveIndex, HEADER_OFFSET, 'Must be in Expando section');
|
32859 | tView.data[directiveIndex] = def;
|
32860 | const directiveFactory = def.factory || (def.factory = getFactoryDef(def.type, true));
|
32861 | const nodeInjectorFactory = new NodeInjectorFactory(directiveFactory, isComponentDef(def), null);
|
32862 | tView.blueprint[directiveIndex] = nodeInjectorFactory;
|
32863 | lView[directiveIndex] = nodeInjectorFactory;
|
32864 | registerHostBindingOpCodes(tView, tNode, lView, directiveIndex, allocExpando(tView, lView, def.hostVars, NO_CHANGE), def);
|
32865 | }
|
32866 | //////////////////////////
|
32867 | //// ViewContainer & View
|
32868 | //////////////////////////
|
32869 | // Not sure why I need to do `any` here but TS complains later.
|
32870 | const LContainerArray = ((typeof ngDevMode === 'undefined' || ngDevMode) && initNgDevMode()) &&
|
32871 | createNamedArrayType('LContainer');
|
32872 | /**
|
32873 | * Goes over embedded views (ones created through ViewContainerRef APIs) and refreshes
|
32874 | * them by executing an associated template function.
|
32875 | */
|
32876 | function refreshEmbeddedViews(lView) {
|
32877 | for (let lContainer = getFirstLContainer(lView); lContainer !== null; lContainer = getNextLContainer(lContainer)) {
|
32878 | for (let i = CONTAINER_HEADER_OFFSET; i < lContainer.length; i++) {
|
32879 | const embeddedLView = lContainer[i];
|
32880 | const embeddedTView = embeddedLView[TVIEW];
|
32881 | ngDevMode && assertDefined(embeddedTView, 'TView must be allocated');
|
32882 | if (viewAttachedToChangeDetector(embeddedLView)) {
|
32883 | refreshView(embeddedTView, embeddedLView, embeddedTView.template, embeddedLView[CONTEXT]);
|
32884 | }
|
32885 | }
|
32886 | }
|
32887 | }
|
32888 | /**
|
32889 | * Mark transplanted views as needing to be refreshed at their insertion points.
|
32890 | *
|
32891 | * @param lView The `LView` that may have transplanted views.
|
32892 | */
|
32893 | function markTransplantedViewsForRefresh(lView) {
|
32894 | for (let lContainer = getFirstLContainer(lView); lContainer !== null; lContainer = getNextLContainer(lContainer)) {
|
32895 | if (!lContainer[HAS_TRANSPLANTED_VIEWS])
|
32896 | continue;
|
32897 | const movedViews = lContainer[MOVED_VIEWS];
|
32898 | ngDevMode && assertDefined(movedViews, 'Transplanted View flags set but missing MOVED_VIEWS');
|
32899 | for (let i = 0; i < movedViews.length; i++) {
|
32900 | const movedLView = movedViews[i];
|
32901 | const insertionLContainer = movedLView[PARENT];
|
32902 | ngDevMode && assertLContainer(insertionLContainer);
|
32903 | // We don't want to increment the counter if the moved LView was already marked for
|
32904 | // refresh.
|
32905 | if ((movedLView[FLAGS] & 1024 /* RefreshTransplantedView */) === 0) {
|
32906 | updateTransplantedViewCount(insertionLContainer, 1);
|
32907 | }
|
32908 | // Note, it is possible that the `movedViews` is tracking views that are transplanted *and*
|
32909 | // those that aren't (declaration component === insertion component). In the latter case,
|
32910 | // it's fine to add the flag, as we will clear it immediately in
|
32911 | // `refreshEmbeddedViews` for the view currently being refreshed.
|
32912 | movedLView[FLAGS] |= 1024 /* RefreshTransplantedView */;
|
32913 | }
|
32914 | }
|
32915 | }
|
32916 | /////////////
|
32917 | /**
|
32918 | * Refreshes components by entering the component view and processing its bindings, queries, etc.
|
32919 | *
|
32920 | * @param componentHostIdx Element index in LView[] (adjusted for HEADER_OFFSET)
|
32921 | */
|
32922 | function refreshComponent(hostLView, componentHostIdx) {
|
32923 | ngDevMode && assertEqual(isCreationMode(hostLView), false, 'Should be run in update mode');
|
32924 | const componentView = getComponentLViewByIndex(componentHostIdx, hostLView);
|
32925 | // Only attached components that are CheckAlways or OnPush and dirty should be refreshed
|
32926 | if (viewAttachedToChangeDetector(componentView)) {
|
32927 | const tView = componentView[TVIEW];
|
32928 | if (componentView[FLAGS] & (16 /* CheckAlways */ | 64 /* Dirty */)) {
|
32929 | refreshView(tView, componentView, tView.template, componentView[CONTEXT]);
|
32930 | }
|
32931 | else if (componentView[TRANSPLANTED_VIEWS_TO_REFRESH] > 0) {
|
32932 | // Only attached components that are CheckAlways or OnPush and dirty should be refreshed
|
32933 | refreshContainsDirtyView(componentView);
|
32934 | }
|
32935 | }
|
32936 | }
|
32937 | /**
|
32938 | * Refreshes all transplanted views marked with `LViewFlags.RefreshTransplantedView` that are
|
32939 | * children or descendants of the given lView.
|
32940 | *
|
32941 | * @param lView The lView which contains descendant transplanted views that need to be refreshed.
|
32942 | */
|
32943 | function refreshContainsDirtyView(lView) {
|
32944 | for (let lContainer = getFirstLContainer(lView); lContainer !== null; lContainer = getNextLContainer(lContainer)) {
|
32945 | for (let i = CONTAINER_HEADER_OFFSET; i < lContainer.length; i++) {
|
32946 | const embeddedLView = lContainer[i];
|
32947 | if (embeddedLView[FLAGS] & 1024 /* RefreshTransplantedView */) {
|
32948 | const embeddedTView = embeddedLView[TVIEW];
|
32949 | ngDevMode && assertDefined(embeddedTView, 'TView must be allocated');
|
32950 | refreshView(embeddedTView, embeddedLView, embeddedTView.template, embeddedLView[CONTEXT]);
|
32951 | }
|
32952 | else if (embeddedLView[TRANSPLANTED_VIEWS_TO_REFRESH] > 0) {
|
32953 | refreshContainsDirtyView(embeddedLView);
|
32954 | }
|
32955 | }
|
32956 | }
|
32957 | const tView = lView[TVIEW];
|
32958 | // Refresh child component views.
|
32959 | const components = tView.components;
|
32960 | if (components !== null) {
|
32961 | for (let i = 0; i < components.length; i++) {
|
32962 | const componentView = getComponentLViewByIndex(components[i], lView);
|
32963 | // Only attached components that are CheckAlways or OnPush and dirty should be refreshed
|
32964 | if (viewAttachedToChangeDetector(componentView) &&
|
32965 | componentView[TRANSPLANTED_VIEWS_TO_REFRESH] > 0) {
|
32966 | refreshContainsDirtyView(componentView);
|
32967 | }
|
32968 | }
|
32969 | }
|
32970 | }
|
32971 | function renderComponent(hostLView, componentHostIdx) {
|
32972 | ngDevMode && assertEqual(isCreationMode(hostLView), true, 'Should be run in creation mode');
|
32973 | const componentView = getComponentLViewByIndex(componentHostIdx, hostLView);
|
32974 | const componentTView = componentView[TVIEW];
|
32975 | syncViewWithBlueprint(componentTView, componentView);
|
32976 | renderView(componentTView, componentView, componentView[CONTEXT]);
|
32977 | }
|
32978 | /**
|
32979 | * Syncs an LView instance with its blueprint if they have gotten out of sync.
|
32980 | *
|
32981 | * Typically, blueprints and their view instances should always be in sync, so the loop here
|
32982 | * will be skipped. However, consider this case of two components side-by-side:
|
32983 | *
|
32984 | * App template:
|
32985 | * ```
|
32986 | * <comp></comp>
|
32987 | * <comp></comp>
|
32988 | * ```
|
32989 | *
|
32990 | * The following will happen:
|
32991 | * 1. App template begins processing.
|
32992 | * 2. First <comp> is matched as a component and its LView is created.
|
32993 | * 3. Second <comp> is matched as a component and its LView is created.
|
32994 | * 4. App template completes processing, so it's time to check child templates.
|
32995 | * 5. First <comp> template is checked. It has a directive, so its def is pushed to blueprint.
|
32996 | * 6. Second <comp> template is checked. Its blueprint has been updated by the first
|
32997 | * <comp> template, but its LView was created before this update, so it is out of sync.
|
32998 | *
|
32999 | * Note that embedded views inside ngFor loops will never be out of sync because these views
|
33000 | * are processed as soon as they are created.
|
33001 | *
|
33002 | * @param tView The `TView` that contains the blueprint for syncing
|
33003 | * @param lView The view to sync
|
33004 | */
|
33005 | function syncViewWithBlueprint(tView, lView) {
|
33006 | for (let i = lView.length; i < tView.blueprint.length; i++) {
|
33007 | lView.push(tView.blueprint[i]);
|
33008 | }
|
33009 | }
|
33010 | /**
|
33011 | * Adds LView or LContainer to the end of the current view tree.
|
33012 | *
|
33013 | * This structure will be used to traverse through nested views to remove listeners
|
33014 | * and call onDestroy callbacks.
|
33015 | *
|
33016 | * @param lView The view where LView or LContainer should be added
|
33017 | * @param adjustedHostIndex Index of the view's host node in LView[], adjusted for header
|
33018 | * @param lViewOrLContainer The LView or LContainer to add to the view tree
|
33019 | * @returns The state passed in
|
33020 | */
|
33021 | function addToViewTree(lView, lViewOrLContainer) {
|
33022 | // TODO(benlesh/misko): This implementation is incorrect, because it always adds the LContainer
|
33023 | // to the end of the queue, which means if the developer retrieves the LContainers from RNodes out
|
33024 | // of order, the change detection will run out of order, as the act of retrieving the the
|
33025 | // LContainer from the RNode is what adds it to the queue.
|
33026 | if (lView[CHILD_HEAD]) {
|
33027 | lView[CHILD_TAIL][NEXT] = lViewOrLContainer;
|
33028 | }
|
33029 | else {
|
33030 | lView[CHILD_HEAD] = lViewOrLContainer;
|
33031 | }
|
33032 | lView[CHILD_TAIL] = lViewOrLContainer;
|
33033 | return lViewOrLContainer;
|
33034 | }
|
33035 | ///////////////////////////////
|
33036 | //// Change detection
|
33037 | ///////////////////////////////
|
33038 | /**
|
33039 | * Marks current view and all ancestors dirty.
|
33040 | *
|
33041 | * Returns the root view because it is found as a byproduct of marking the view tree
|
33042 | * dirty, and can be used by methods that consume markViewDirty() to easily schedule
|
33043 | * change detection. Otherwise, such methods would need to traverse up the view tree
|
33044 | * an additional time to get the root view and schedule a tick on it.
|
33045 | *
|
33046 | * @param lView The starting LView to mark dirty
|
33047 | * @returns the root LView
|
33048 | */
|
33049 | function markViewDirty(lView) {
|
33050 | while (lView) {
|
33051 | lView[FLAGS] |= 64 /* Dirty */;
|
33052 | const parent = getLViewParent(lView);
|
33053 | // Stop traversing up as soon as you find a root view that wasn't attached to any container
|
33054 | if (isRootView(lView) && !parent) {
|
33055 | return lView;
|
33056 | }
|
33057 | // continue otherwise
|
33058 | lView = parent;
|
33059 | }
|
33060 | return null;
|
33061 | }
|
33062 | function tickRootContext(rootContext) {
|
33063 | for (let i = 0; i < rootContext.components.length; i++) {
|
33064 | const rootComponent = rootContext.components[i];
|
33065 | const lView = readPatchedLView(rootComponent);
|
33066 | const tView = lView[TVIEW];
|
33067 | renderComponentOrTemplate(tView, lView, tView.template, rootComponent);
|
33068 | }
|
33069 | }
|
33070 | function detectChangesInternal(tView, lView, context) {
|
33071 | const rendererFactory = lView[RENDERER_FACTORY];
|
33072 | if (rendererFactory.begin)
|
33073 | rendererFactory.begin();
|
33074 | try {
|
33075 | refreshView(tView, lView, tView.template, context);
|
33076 | }
|
33077 | catch (error) {
|
33078 | handleError(lView, error);
|
33079 | throw error;
|
33080 | }
|
33081 | finally {
|
33082 | if (rendererFactory.end)
|
33083 | rendererFactory.end();
|
33084 | }
|
33085 | }
|
33086 | /**
|
33087 | * Synchronously perform change detection on a root view and its components.
|
33088 | *
|
33089 | * @param lView The view which the change detection should be performed on.
|
33090 | */
|
33091 | function detectChangesInRootView(lView) {
|
33092 | tickRootContext(lView[CONTEXT]);
|
33093 | }
|
33094 | function checkNoChangesInternal(tView, view, context) {
|
33095 | setIsInCheckNoChangesMode(true);
|
33096 | try {
|
33097 | detectChangesInternal(tView, view, context);
|
33098 | }
|
33099 | finally {
|
33100 | setIsInCheckNoChangesMode(false);
|
33101 | }
|
33102 | }
|
33103 | /**
|
33104 | * Checks the change detector on a root view and its components, and throws if any changes are
|
33105 | * detected.
|
33106 | *
|
33107 | * This is used in development mode to verify that running change detection doesn't
|
33108 | * introduce other changes.
|
33109 | *
|
33110 | * @param lView The view which the change detection should be checked on.
|
33111 | */
|
33112 | function checkNoChangesInRootView(lView) {
|
33113 | setIsInCheckNoChangesMode(true);
|
33114 | try {
|
33115 | detectChangesInRootView(lView);
|
33116 | }
|
33117 | finally {
|
33118 | setIsInCheckNoChangesMode(false);
|
33119 | }
|
33120 | }
|
33121 | function executeViewQueryFn(flags, viewQueryFn, component) {
|
33122 | ngDevMode && assertDefined(viewQueryFn, 'View queries function to execute must be defined.');
|
33123 | setCurrentQueryIndex(0);
|
33124 | viewQueryFn(flags, component);
|
33125 | }
|
33126 | const CLEAN_PROMISE = _CLEAN_PROMISE;
|
33127 | function getOrCreateLViewCleanup(view) {
|
33128 | // top level variables should not be exported for performance reasons (PERF_NOTES.md)
|
33129 | return view[CLEANUP] || (view[CLEANUP] = ngDevMode ? new LCleanup() : []);
|
33130 | }
|
33131 | function getOrCreateTViewCleanup(tView) {
|
33132 | return tView.cleanup || (tView.cleanup = ngDevMode ? new TCleanup() : []);
|
33133 | }
|
33134 | /** Handles an error thrown in an LView. */
|
33135 | function handleError(lView, error) {
|
33136 | const injector = lView[INJECTOR];
|
33137 | const errorHandler = injector ? injector.get(ErrorHandler, null) : null;
|
33138 | errorHandler && errorHandler.handleError(error);
|
33139 | }
|
33140 |
|
33141 | /**
|
33142 | * @license
|
33143 | * Copyright Google LLC All Rights Reserved.
|
33144 | *
|
33145 | * Use of this source code is governed by an MIT-style license that can be
|
33146 | * found in the LICENSE file at https://angular.io/license
|
33147 | */
|
33148 | /**
|
33149 | * Compute the static styling (class/style) from `TAttributes`.
|
33150 | *
|
33151 | * This function should be called during `firstCreatePass` only.
|
33152 | *
|
33153 | * @param tNode The `TNode` into which the styling information should be loaded.
|
33154 | * @param attrs `TAttributes` containing the styling information.
|
33155 | * @param writeToHost Where should the resulting static styles be written?
|
33156 | * - `false` Write to `TNode.stylesWithoutHost` / `TNode.classesWithoutHost`
|
33157 | * - `true` Write to `TNode.styles` / `TNode.classes`
|
33158 | */
|
33159 | function computeStaticStyling(tNode, attrs, writeToHost) {
|
33160 | ngDevMode &&
|
33161 | assertFirstCreatePass(getTView(), 'Expecting to be called in first template pass only');
|
33162 | let styles = writeToHost ? tNode.styles : null;
|
33163 | let classes = writeToHost ? tNode.classes : null;
|
33164 | let mode = 0;
|
33165 | if (attrs !== null) {
|
33166 | for (let i = 0; i < attrs.length; i++) {
|
33167 | const value = attrs[i];
|
33168 | if (typeof value === 'number') {
|
33169 | mode = value;
|
33170 | }
|
33171 | else if (mode == 1 /* Classes */) {
|
33172 | classes = concatStringsWithSpace(classes, value);
|
33173 | }
|
33174 | else if (mode == 2 /* Styles */) {
|
33175 | const style = value;
|
33176 | const styleValue = attrs[++i];
|
33177 | styles = concatStringsWithSpace(styles, style + ': ' + styleValue + ';');
|
33178 | }
|
33179 | }
|
33180 | }
|
33181 | writeToHost ? tNode.styles = styles : tNode.stylesWithoutHost = styles;
|
33182 | writeToHost ? tNode.classes = classes : tNode.classesWithoutHost = classes;
|
33183 | }
|
33184 |
|
33185 | /**
|
33186 | * @license
|
33187 | * Copyright Google LLC All Rights Reserved.
|
33188 | *
|
33189 | * Use of this source code is governed by an MIT-style license that can be
|
33190 | * found in the LICENSE file at https://angular.io/license
|
33191 | */
|
33192 | /**
|
33193 | * An InjectionToken that gets the current `Injector` for `createInjector()`-style injectors.
|
33194 | *
|
33195 | * Requesting this token instead of `Injector` allows `StaticInjector` to be tree-shaken from a
|
33196 | * project.
|
33197 | *
|
33198 | * @publicApi
|
33199 | */
|
33200 | const INJECTOR$1 = new InjectionToken('INJECTOR',
|
33201 | // Dissable tslint because this is const enum which gets inlined not top level prop access.
|
33202 | // tslint:disable-next-line: no-toplevel-property-access
|
33203 | -1 /* Injector */);
|
33204 |
|
33205 | /**
|
33206 | * @license
|
33207 | * Copyright Google LLC All Rights Reserved.
|
33208 | *
|
33209 | * Use of this source code is governed by an MIT-style license that can be
|
33210 | * found in the LICENSE file at https://angular.io/license
|
33211 | */
|
33212 | class NullInjector {
|
33213 | get(token, notFoundValue = THROW_IF_NOT_FOUND) {
|
33214 | if (notFoundValue === THROW_IF_NOT_FOUND) {
|
33215 | const error = new Error(`NullInjectorError: No provider for ${stringify$1(token)}!`);
|
33216 | error.name = 'NullInjectorError';
|
33217 | throw error;
|
33218 | }
|
33219 | return notFoundValue;
|
33220 | }
|
33221 | }
|
33222 |
|
33223 | /**
|
33224 | * @license
|
33225 | * Copyright Google LLC All Rights Reserved.
|
33226 | *
|
33227 | * Use of this source code is governed by an MIT-style license that can be
|
33228 | * found in the LICENSE file at https://angular.io/license
|
33229 | */
|
33230 | /**
|
33231 | * An internal token whose presence in an injector indicates that the injector should treat itself
|
33232 | * as a root scoped injector when processing requests for unknown tokens which may indicate
|
33233 | * they are provided in the root scope.
|
33234 | */
|
33235 | const INJECTOR_SCOPE = new InjectionToken('Set Injector scope.');
|
33236 |
|
33237 | /**
|
33238 | * @license
|
33239 | * Copyright Google LLC All Rights Reserved.
|
33240 | *
|
33241 | * Use of this source code is governed by an MIT-style license that can be
|
33242 | * found in the LICENSE file at https://angular.io/license
|
33243 | */
|
33244 | function INJECTOR_IMPL__PRE_R3__(providers, parent, name) {
|
33245 | return new StaticInjector(providers, parent, name);
|
33246 | }
|
33247 | const INJECTOR_IMPL = INJECTOR_IMPL__PRE_R3__;
|
33248 | /**
|
33249 | * Concrete injectors implement this interface. Injectors are configured
|
33250 | * with [providers](guide/glossary#provider) that associate
|
33251 | * dependencies of various types with [injection tokens](guide/glossary#di-token).
|
33252 | *
|
33253 | * @see ["DI Providers"](guide/dependency-injection-providers).
|
33254 | * @see `StaticProvider`
|
33255 | *
|
33256 | * @usageNotes
|
33257 | *
|
33258 | * The following example creates a service injector instance.
|
33259 | *
|
33260 | * {@example core/di/ts/provider_spec.ts region='ConstructorProvider'}
|
33261 | *
|
33262 | * ### Usage example
|
33263 | *
|
33264 | * {@example core/di/ts/injector_spec.ts region='Injector'}
|
33265 | *
|
33266 | * `Injector` returns itself when given `Injector` as a token:
|
33267 | *
|
33268 | * {@example core/di/ts/injector_spec.ts region='injectInjector'}
|
33269 | *
|
33270 | * @publicApi
|
33271 | */
|
33272 | class Injector {
|
33273 | static create(options, parent) {
|
33274 | if (Array.isArray(options)) {
|
33275 | return INJECTOR_IMPL(options, parent, '');
|
33276 | }
|
33277 | else {
|
33278 | return INJECTOR_IMPL(options.providers, options.parent, options.name || '');
|
33279 | }
|
33280 | }
|
33281 | }
|
33282 | Injector.THROW_IF_NOT_FOUND = THROW_IF_NOT_FOUND;
|
33283 | Injector.NULL = new NullInjector();
|
33284 | /** @nocollapse */
|
33285 | Injector.ɵprov = ɵɵdefineInjectable({
|
33286 | token: Injector,
|
33287 | providedIn: 'any',
|
33288 | factory: () => ɵɵinject(INJECTOR$1),
|
33289 | });
|
33290 | /**
|
33291 | * @internal
|
33292 | * @nocollapse
|
33293 | */
|
33294 | Injector.__NG_ELEMENT_ID__ = -1 /* Injector */;
|
33295 | const IDENT = function (value) {
|
33296 | return value;
|
33297 | };
|
33298 | const EMPTY = [];
|
33299 | const CIRCULAR = IDENT;
|
33300 | const MULTI_PROVIDER_FN = function () {
|
33301 | return Array.prototype.slice.call(arguments);
|
33302 | };
|
33303 | const NO_NEW_LINE$1 = 'ɵ';
|
33304 | class StaticInjector {
|
33305 | constructor(providers, parent = Injector.NULL, source = null) {
|
33306 | this.parent = parent;
|
33307 | this.source = source;
|
33308 | const records = this._records = new Map();
|
33309 | records.set(Injector, { token: Injector, fn: IDENT, deps: EMPTY, value: this, useNew: false });
|
33310 | records.set(INJECTOR$1, { token: INJECTOR$1, fn: IDENT, deps: EMPTY, value: this, useNew: false });
|
33311 | this.scope = recursivelyProcessProviders(records, providers);
|
33312 | }
|
33313 | get(token, notFoundValue, flags = InjectFlags.Default) {
|
33314 | const records = this._records;
|
33315 | let record = records.get(token);
|
33316 | if (record === undefined) {
|
33317 | // This means we have never seen this record, see if it is tree shakable provider.
|
33318 | const injectableDef = getInjectableDef(token);
|
33319 | if (injectableDef) {
|
33320 | const providedIn = injectableDef && injectableDef.providedIn;
|
33321 | if (providedIn === 'any' || providedIn != null && providedIn === this.scope) {
|
33322 | records.set(token, record = resolveProvider({ provide: token, useFactory: injectableDef.factory, deps: EMPTY }));
|
33323 | }
|
33324 | }
|
33325 | if (record === undefined) {
|
33326 | // Set record to null to make sure that we don't go through expensive lookup above again.
|
33327 | records.set(token, null);
|
33328 | }
|
33329 | }
|
33330 | let lastInjector = setCurrentInjector(this);
|
33331 | try {
|
33332 | return tryResolveToken(token, record, records, this.parent, notFoundValue, flags);
|
33333 | }
|
33334 | catch (e) {
|
33335 | return catchInjectorError(e, token, 'StaticInjectorError', this.source);
|
33336 | }
|
33337 | finally {
|
33338 | setCurrentInjector(lastInjector);
|
33339 | }
|
33340 | }
|
33341 | toString() {
|
33342 | const tokens = [], records = this._records;
|
33343 | records.forEach((v, token) => tokens.push(stringify$1(token)));
|
33344 | return `StaticInjector[${tokens.join(', ')}]`;
|
33345 | }
|
33346 | }
|
33347 | function resolveProvider(provider) {
|
33348 | const deps = computeDeps(provider);
|
33349 | let fn = IDENT;
|
33350 | let value = EMPTY;
|
33351 | let useNew = false;
|
33352 | let provide = resolveForwardRef$1(provider.provide);
|
33353 | if (USE_VALUE$2 in provider) {
|
33354 | // We need to use USE_VALUE in provider since provider.useValue could be defined as undefined.
|
33355 | value = provider.useValue;
|
33356 | }
|
33357 | else if (provider.useFactory) {
|
33358 | fn = provider.useFactory;
|
33359 | }
|
33360 | else if (provider.useExisting) ;
|
33361 | else if (provider.useClass) {
|
33362 | useNew = true;
|
33363 | fn = resolveForwardRef$1(provider.useClass);
|
33364 | }
|
33365 | else if (typeof provide == 'function') {
|
33366 | useNew = true;
|
33367 | fn = provide;
|
33368 | }
|
33369 | else {
|
33370 | throw staticError('StaticProvider does not have [useValue|useFactory|useExisting|useClass] or [provide] is not newable', provider);
|
33371 | }
|
33372 | return { deps, fn, useNew, value };
|
33373 | }
|
33374 | function multiProviderMixError(token) {
|
33375 | return staticError('Cannot mix multi providers and regular providers', token);
|
33376 | }
|
33377 | function recursivelyProcessProviders(records, provider) {
|
33378 | let scope = null;
|
33379 | if (provider) {
|
33380 | provider = resolveForwardRef$1(provider);
|
33381 | if (Array.isArray(provider)) {
|
33382 | // if we have an array recurse into the array
|
33383 | for (let i = 0; i < provider.length; i++) {
|
33384 | scope = recursivelyProcessProviders(records, provider[i]) || scope;
|
33385 | }
|
33386 | }
|
33387 | else if (typeof provider === 'function') {
|
33388 | // Functions were supported in ReflectiveInjector, but are not here. For safety give useful
|
33389 | // error messages
|
33390 | throw staticError('Function/Class not supported', provider);
|
33391 | }
|
33392 | else if (provider && typeof provider === 'object' && provider.provide) {
|
33393 | // At this point we have what looks like a provider: {provide: ?, ....}
|
33394 | let token = resolveForwardRef$1(provider.provide);
|
33395 | const resolvedProvider = resolveProvider(provider);
|
33396 | if (provider.multi === true) {
|
33397 | // This is a multi provider.
|
33398 | let multiProvider = records.get(token);
|
33399 | if (multiProvider) {
|
33400 | if (multiProvider.fn !== MULTI_PROVIDER_FN) {
|
33401 | throw multiProviderMixError(token);
|
33402 | }
|
33403 | }
|
33404 | else {
|
33405 | // Create a placeholder factory which will look up the constituents of the multi provider.
|
33406 | records.set(token, multiProvider = {
|
33407 | token: provider.provide,
|
33408 | deps: [],
|
33409 | useNew: false,
|
33410 | fn: MULTI_PROVIDER_FN,
|
33411 | value: EMPTY
|
33412 | });
|
33413 | }
|
33414 | // Treat the provider as the token.
|
33415 | token = provider;
|
33416 | multiProvider.deps.push({ token, options: 6 /* Default */ });
|
33417 | }
|
33418 | const record = records.get(token);
|
33419 | if (record && record.fn == MULTI_PROVIDER_FN) {
|
33420 | throw multiProviderMixError(token);
|
33421 | }
|
33422 | if (token === INJECTOR_SCOPE) {
|
33423 | scope = resolvedProvider.value;
|
33424 | }
|
33425 | records.set(token, resolvedProvider);
|
33426 | }
|
33427 | else {
|
33428 | throw staticError('Unexpected provider', provider);
|
33429 | }
|
33430 | }
|
33431 | return scope;
|
33432 | }
|
33433 | function tryResolveToken(token, record, records, parent, notFoundValue, flags) {
|
33434 | try {
|
33435 | return resolveToken(token, record, records, parent, notFoundValue, flags);
|
33436 | }
|
33437 | catch (e) {
|
33438 | // ensure that 'e' is of type Error.
|
33439 | if (!(e instanceof Error)) {
|
33440 | e = new Error(e);
|
33441 | }
|
33442 | const path = e[NG_TEMP_TOKEN_PATH] = e[NG_TEMP_TOKEN_PATH] || [];
|
33443 | path.unshift(token);
|
33444 | if (record && record.value == CIRCULAR) {
|
33445 | // Reset the Circular flag.
|
33446 | record.value = EMPTY;
|
33447 | }
|
33448 | throw e;
|
33449 | }
|
33450 | }
|
33451 | function resolveToken(token, record, records, parent, notFoundValue, flags) {
|
33452 | let value;
|
33453 | if (record && !(flags & InjectFlags.SkipSelf)) {
|
33454 | // If we don't have a record, this implies that we don't own the provider hence don't know how
|
33455 | // to resolve it.
|
33456 | value = record.value;
|
33457 | if (value == CIRCULAR) {
|
33458 | throw Error(NO_NEW_LINE$1 + 'Circular dependency');
|
33459 | }
|
33460 | else if (value === EMPTY) {
|
33461 | record.value = CIRCULAR;
|
33462 | let obj = undefined;
|
33463 | let useNew = record.useNew;
|
33464 | let fn = record.fn;
|
33465 | let depRecords = record.deps;
|
33466 | let deps = EMPTY;
|
33467 | if (depRecords.length) {
|
33468 | deps = [];
|
33469 | for (let i = 0; i < depRecords.length; i++) {
|
33470 | const depRecord = depRecords[i];
|
33471 | const options = depRecord.options;
|
33472 | const childRecord = options & 2 /* CheckSelf */ ? records.get(depRecord.token) : undefined;
|
33473 | deps.push(tryResolveToken(
|
33474 | // Current Token to resolve
|
33475 | depRecord.token,
|
33476 | // A record which describes how to resolve the token.
|
33477 | // If undefined, this means we don't have such a record
|
33478 | childRecord,
|
33479 | // Other records we know about.
|
33480 | records,
|
33481 | // If we don't know how to resolve dependency and we should not check parent for it,
|
33482 | // than pass in Null injector.
|
33483 | !childRecord && !(options & 4 /* CheckParent */) ? Injector.NULL : parent, options & 1 /* Optional */ ? null : Injector.THROW_IF_NOT_FOUND, InjectFlags.Default));
|
33484 | }
|
33485 | }
|
33486 | record.value = value = useNew ? new fn(...deps) : fn.apply(obj, deps);
|
33487 | }
|
33488 | }
|
33489 | else if (!(flags & InjectFlags.Self)) {
|
33490 | value = parent.get(token, notFoundValue, InjectFlags.Default);
|
33491 | }
|
33492 | else if (!(flags & InjectFlags.Optional)) {
|
33493 | value = Injector.NULL.get(token, notFoundValue);
|
33494 | }
|
33495 | else {
|
33496 | value = Injector.NULL.get(token, typeof notFoundValue !== 'undefined' ? notFoundValue : null);
|
33497 | }
|
33498 | return value;
|
33499 | }
|
33500 | function computeDeps(provider) {
|
33501 | let deps = EMPTY;
|
33502 | const providerDeps = provider.deps;
|
33503 | if (providerDeps && providerDeps.length) {
|
33504 | deps = [];
|
33505 | for (let i = 0; i < providerDeps.length; i++) {
|
33506 | let options = 6 /* Default */;
|
33507 | let token = resolveForwardRef$1(providerDeps[i]);
|
33508 | if (Array.isArray(token)) {
|
33509 | for (let j = 0, annotations = token; j < annotations.length; j++) {
|
33510 | const annotation = annotations[j];
|
33511 | if (annotation instanceof Optional || annotation == Optional) {
|
33512 | options = options | 1 /* Optional */;
|
33513 | }
|
33514 | else if (annotation instanceof SkipSelf || annotation == SkipSelf) {
|
33515 | options = options & ~2 /* CheckSelf */;
|
33516 | }
|
33517 | else if (annotation instanceof Self || annotation == Self) {
|
33518 | options = options & ~4 /* CheckParent */;
|
33519 | }
|
33520 | else if (annotation instanceof Inject) {
|
33521 | token = annotation.token;
|
33522 | }
|
33523 | else {
|
33524 | token = resolveForwardRef$1(annotation);
|
33525 | }
|
33526 | }
|
33527 | }
|
33528 | deps.push({ token, options });
|
33529 | }
|
33530 | }
|
33531 | else if (provider.useExisting) {
|
33532 | const token = resolveForwardRef$1(provider.useExisting);
|
33533 | deps = [{ token, options: 6 /* Default */ }];
|
33534 | }
|
33535 | else if (!providerDeps && !(USE_VALUE$2 in provider)) {
|
33536 | // useValue & useExisting are the only ones which are exempt from deps all others need it.
|
33537 | throw staticError('\'deps\' required', provider);
|
33538 | }
|
33539 | return deps;
|
33540 | }
|
33541 | function staticError(text, obj) {
|
33542 | return new Error(formatError(text, obj, 'StaticInjectorError'));
|
33543 | }
|
33544 |
|
33545 | /**
|
33546 | * @license
|
33547 | * Copyright Google LLC All Rights Reserved.
|
33548 | *
|
33549 | * Use of this source code is governed by an MIT-style license that can be
|
33550 | * found in the LICENSE file at https://angular.io/license
|
33551 | */
|
33552 | /**
|
33553 | * Creates the root component view and the root component node.
|
33554 | *
|
33555 | * @param rNode Render host element.
|
33556 | * @param def ComponentDef
|
33557 | * @param rootView The parent view where the host node is stored
|
33558 | * @param rendererFactory Factory to be used for creating child renderers.
|
33559 | * @param hostRenderer The current renderer
|
33560 | * @param sanitizer The sanitizer, if provided
|
33561 | *
|
33562 | * @returns Component view created
|
33563 | */
|
33564 | function createRootComponentView(rNode, def, rootView, rendererFactory, hostRenderer, sanitizer) {
|
33565 | const tView = rootView[TVIEW];
|
33566 | const index = HEADER_OFFSET;
|
33567 | ngDevMode && assertIndexInRange(rootView, index);
|
33568 | rootView[index] = rNode;
|
33569 | // '#host' is added here as we don't know the real host DOM name (we don't want to read it) and at
|
33570 | // the same time we want to communicate the debug `TNode` that this is a special `TNode`
|
33571 | // representing a host element.
|
33572 | const tNode = getOrCreateTNode(tView, index, 2 /* Element */, '#host', null);
|
33573 | const mergedAttrs = tNode.mergedAttrs = def.hostAttrs;
|
33574 | if (mergedAttrs !== null) {
|
33575 | computeStaticStyling(tNode, mergedAttrs, true);
|
33576 | if (rNode !== null) {
|
33577 | setUpAttributes(hostRenderer, rNode, mergedAttrs);
|
33578 | if (tNode.classes !== null) {
|
33579 | writeDirectClass(hostRenderer, rNode, tNode.classes);
|
33580 | }
|
33581 | if (tNode.styles !== null) {
|
33582 | writeDirectStyle(hostRenderer, rNode, tNode.styles);
|
33583 | }
|
33584 | }
|
33585 | }
|
33586 | const viewRenderer = rendererFactory.createRenderer(rNode, def);
|
33587 | const componentView = createLView(rootView, getOrCreateTComponentView(def), null, def.onPush ? 64 /* Dirty */ : 16 /* CheckAlways */, rootView[index], tNode, rendererFactory, viewRenderer, sanitizer || null, null);
|
33588 | if (tView.firstCreatePass) {
|
33589 | diPublicInInjector(getOrCreateNodeInjectorForNode(tNode, rootView), tView, def.type);
|
33590 | markAsComponentHost(tView, tNode);
|
33591 | initTNodeFlags(tNode, rootView.length, 1);
|
33592 | }
|
33593 | addToViewTree(rootView, componentView);
|
33594 | // Store component view at node index, with node as the HOST
|
33595 | return rootView[index] = componentView;
|
33596 | }
|
33597 | /**
|
33598 | * Creates a root component and sets it up with features and host bindings. Shared by
|
33599 | * renderComponent() and ViewContainerRef.createComponent().
|
33600 | */
|
33601 | function createRootComponent(componentView, componentDef, rootLView, rootContext, hostFeatures) {
|
33602 | const tView = rootLView[TVIEW];
|
33603 | // Create directive instance with factory() and store at next index in viewData
|
33604 | const component = instantiateRootComponent(tView, rootLView, componentDef);
|
33605 | rootContext.components.push(component);
|
33606 | componentView[CONTEXT] = component;
|
33607 | hostFeatures && hostFeatures.forEach((feature) => feature(component, componentDef));
|
33608 | // We want to generate an empty QueryList for root content queries for backwards
|
33609 | // compatibility with ViewEngine.
|
33610 | if (componentDef.contentQueries) {
|
33611 | const tNode = getCurrentTNode();
|
33612 | ngDevMode && assertDefined(tNode, 'TNode expected');
|
33613 | componentDef.contentQueries(1 /* Create */, component, tNode.directiveStart);
|
33614 | }
|
33615 | const rootTNode = getCurrentTNode();
|
33616 | ngDevMode && assertDefined(rootTNode, 'tNode should have been already created');
|
33617 | if (tView.firstCreatePass &&
|
33618 | (componentDef.hostBindings !== null || componentDef.hostAttrs !== null)) {
|
33619 | setSelectedIndex(rootTNode.index);
|
33620 | const rootTView = rootLView[TVIEW];
|
33621 | registerHostBindingOpCodes(rootTView, rootTNode, rootLView, rootTNode.directiveStart, rootTNode.directiveEnd, componentDef);
|
33622 | invokeHostBindingsInCreationMode(componentDef, component);
|
33623 | }
|
33624 | return component;
|
33625 | }
|
33626 | function createRootContext(scheduler, playerHandler) {
|
33627 | return {
|
33628 | components: [],
|
33629 | scheduler: scheduler || defaultScheduler,
|
33630 | clean: CLEAN_PROMISE,
|
33631 | playerHandler: playerHandler || null,
|
33632 | flags: 0 /* Empty */
|
33633 | };
|
33634 | }
|
33635 | /**
|
33636 | * Used to enable lifecycle hooks on the root component.
|
33637 | *
|
33638 | * Include this feature when calling `renderComponent` if the root component
|
33639 | * you are rendering has lifecycle hooks defined. Otherwise, the hooks won't
|
33640 | * be called properly.
|
33641 | *
|
33642 | * Example:
|
33643 | *
|
33644 | * ```
|
33645 | * renderComponent(AppComponent, {hostFeatures: [LifecycleHooksFeature]});
|
33646 | * ```
|
33647 | */
|
33648 | function LifecycleHooksFeature(component, def) {
|
33649 | const lView = readPatchedLView(component);
|
33650 | ngDevMode && assertDefined(lView, 'LView is required');
|
33651 | const tView = lView[TVIEW];
|
33652 | const tNode = getCurrentTNode();
|
33653 | ngDevMode && assertDefined(tNode, 'TNode is required');
|
33654 | registerPostOrderHooks(tView, tNode);
|
33655 | }
|
33656 |
|
33657 | /**
|
33658 | * @license
|
33659 | * Copyright Google LLC All Rights Reserved.
|
33660 | *
|
33661 | * Use of this source code is governed by an MIT-style license that can be
|
33662 | * found in the LICENSE file at https://angular.io/license
|
33663 | */
|
33664 | let _symbolIterator = null;
|
33665 | function getSymbolIterator() {
|
33666 | if (!_symbolIterator) {
|
33667 | const Symbol = _global$1['Symbol'];
|
33668 | if (Symbol && Symbol.iterator) {
|
33669 | _symbolIterator = Symbol.iterator;
|
33670 | }
|
33671 | else {
|
33672 | // es6-shim specific logic
|
33673 | const keys = Object.getOwnPropertyNames(Map.prototype);
|
33674 | for (let i = 0; i < keys.length; ++i) {
|
33675 | const key = keys[i];
|
33676 | if (key !== 'entries' && key !== 'size' &&
|
33677 | Map.prototype[key] === Map.prototype['entries']) {
|
33678 | _symbolIterator = key;
|
33679 | }
|
33680 | }
|
33681 | }
|
33682 | }
|
33683 | return _symbolIterator;
|
33684 | }
|
33685 |
|
33686 | /**
|
33687 | * @license
|
33688 | * Copyright Google LLC All Rights Reserved.
|
33689 | *
|
33690 | * Use of this source code is governed by an MIT-style license that can be
|
33691 | * found in the LICENSE file at https://angular.io/license
|
33692 | */
|
33693 | function isListLikeIterable(obj) {
|
33694 | if (!isJsObject(obj))
|
33695 | return false;
|
33696 | return Array.isArray(obj) ||
|
33697 | (!(obj instanceof Map) && // JS Map are iterables but return entries as [k, v]
|
33698 | getSymbolIterator() in obj); // JS Iterable have a Symbol.iterator prop
|
33699 | }
|
33700 | function iterateListLike(obj, fn) {
|
33701 | if (Array.isArray(obj)) {
|
33702 | for (let i = 0; i < obj.length; i++) {
|
33703 | fn(obj[i]);
|
33704 | }
|
33705 | }
|
33706 | else {
|
33707 | const iterator = obj[getSymbolIterator()]();
|
33708 | let item;
|
33709 | while (!((item = iterator.next()).done)) {
|
33710 | fn(item.value);
|
33711 | }
|
33712 | }
|
33713 | }
|
33714 | function isJsObject(o) {
|
33715 | return o !== null && (typeof o === 'function' || typeof o === 'object');
|
33716 | }
|
33717 |
|
33718 | /**
|
33719 | * @license
|
33720 | * Copyright Google LLC All Rights Reserved.
|
33721 | *
|
33722 | * Use of this source code is governed by an MIT-style license that can be
|
33723 | * found in the LICENSE file at https://angular.io/license
|
33724 | */
|
33725 | const ɵ0$6 = getClosureSafeProperty;
|
33726 | const USE_VALUE$3 = getClosureSafeProperty({ provide: String, useValue: ɵ0$6 });
|
33727 |
|
33728 | /**
|
33729 | * @license
|
33730 | * Copyright Google LLC All Rights Reserved.
|
33731 | *
|
33732 | * Use of this source code is governed by an MIT-style license that can be
|
33733 | * found in the LICENSE file at https://angular.io/license
|
33734 | */
|
33735 | const ɵ0$7 = getClosureSafeProperty;
|
33736 | const USE_VALUE$4 = getClosureSafeProperty({ provide: String, useValue: ɵ0$7 });
|
33737 | const EMPTY_ARRAY$1 = [];
|
33738 | function convertInjectableProviderToFactory(type, provider) {
|
33739 | if (!provider) {
|
33740 | const reflectionCapabilities = new ReflectionCapabilities();
|
33741 | const deps = reflectionCapabilities.parameters(type);
|
33742 | // TODO - convert to flags.
|
33743 | return () => new type(...injectArgs(deps));
|
33744 | }
|
33745 | if (USE_VALUE$4 in provider) {
|
33746 | const valueProvider = provider;
|
33747 | return () => valueProvider.useValue;
|
33748 | }
|
33749 | else if (provider.useExisting) {
|
33750 | const existingProvider = provider;
|
33751 | return () => ɵɵinject(resolveForwardRef$1(existingProvider.useExisting));
|
33752 | }
|
33753 | else if (provider.useFactory) {
|
33754 | const factoryProvider = provider;
|
33755 | return () => factoryProvider.useFactory(...injectArgs(factoryProvider.deps || EMPTY_ARRAY$1));
|
33756 | }
|
33757 | else if (provider.useClass) {
|
33758 | const classProvider = provider;
|
33759 | let deps = provider.deps;
|
33760 | if (!deps) {
|
33761 | const reflectionCapabilities = new ReflectionCapabilities();
|
33762 | deps = reflectionCapabilities.parameters(type);
|
33763 | }
|
33764 | return () => new (resolveForwardRef$1(classProvider.useClass))(...injectArgs(deps));
|
33765 | }
|
33766 | else {
|
33767 | let deps = provider.deps;
|
33768 | if (!deps) {
|
33769 | const reflectionCapabilities = new ReflectionCapabilities();
|
33770 | deps = reflectionCapabilities.parameters(type);
|
33771 | }
|
33772 | return () => new type(...injectArgs(deps));
|
33773 | }
|
33774 | }
|
33775 |
|
33776 | /**
|
33777 | * @license
|
33778 | * Copyright Google LLC All Rights Reserved.
|
33779 | *
|
33780 | * Use of this source code is governed by an MIT-style license that can be
|
33781 | * found in the LICENSE file at https://angular.io/license
|
33782 | */
|
33783 | const ɵ0$8 = (type, meta) => SWITCH_COMPILE_INJECTABLE(type, meta);
|
33784 | /**
|
33785 | * Injectable decorator and metadata.
|
33786 | *
|
33787 | * @Annotation
|
33788 | * @publicApi
|
33789 | */
|
33790 | const Injectable = makeDecorator('Injectable', undefined, undefined, undefined, ɵ0$8);
|
33791 | /**
|
33792 | * Supports @Injectable() in JIT mode for Render2.
|
33793 | */
|
33794 | function render2CompileInjectable(injectableType, options) {
|
33795 | if (options && options.providedIn !== undefined && !getInjectableDef(injectableType)) {
|
33796 | injectableType.ɵprov = ɵɵdefineInjectable({
|
33797 | token: injectableType,
|
33798 | providedIn: options.providedIn,
|
33799 | factory: convertInjectableProviderToFactory(injectableType, options),
|
33800 | });
|
33801 | }
|
33802 | }
|
33803 | const SWITCH_COMPILE_INJECTABLE__PRE_R3__ = render2CompileInjectable;
|
33804 | const SWITCH_COMPILE_INJECTABLE = SWITCH_COMPILE_INJECTABLE__PRE_R3__;
|
33805 |
|
33806 | /**
|
33807 | * @license
|
33808 | * Copyright Google LLC All Rights Reserved.
|
33809 | *
|
33810 | * Use of this source code is governed by an MIT-style license that can be
|
33811 | * found in the LICENSE file at https://angular.io/license
|
33812 | */
|
33813 | function findFirstClosedCycle(keys) {
|
33814 | const res = [];
|
33815 | for (let i = 0; i < keys.length; ++i) {
|
33816 | if (res.indexOf(keys[i]) > -1) {
|
33817 | res.push(keys[i]);
|
33818 | return res;
|
33819 | }
|
33820 | res.push(keys[i]);
|
33821 | }
|
33822 | return res;
|
33823 | }
|
33824 | function constructResolvingPath(keys) {
|
33825 | if (keys.length > 1) {
|
33826 | const reversed = findFirstClosedCycle(keys.slice().reverse());
|
33827 | const tokenStrs = reversed.map(k => stringify$1(k.token));
|
33828 | return ' (' + tokenStrs.join(' -> ') + ')';
|
33829 | }
|
33830 | return '';
|
33831 | }
|
33832 | function injectionError(injector, key, constructResolvingMessage, originalError) {
|
33833 | const keys = [key];
|
33834 | const errMsg = constructResolvingMessage(keys);
|
33835 | const error = (originalError ? wrappedError(errMsg, originalError) : Error(errMsg));
|
33836 | error.addKey = addKey;
|
33837 | error.keys = keys;
|
33838 | error.injectors = [injector];
|
33839 | error.constructResolvingMessage = constructResolvingMessage;
|
33840 | error[ERROR_ORIGINAL_ERROR] = originalError;
|
33841 | return error;
|
33842 | }
|
33843 | function addKey(injector, key) {
|
33844 | this.injectors.push(injector);
|
33845 | this.keys.push(key);
|
33846 | // Note: This updated message won't be reflected in the `.stack` property
|
33847 | this.message = this.constructResolvingMessage(this.keys);
|
33848 | }
|
33849 | /**
|
33850 | * Thrown when trying to retrieve a dependency by key from {@link Injector}, but the
|
33851 | * {@link Injector} does not have a {@link Provider} for the given key.
|
33852 | *
|
33853 | * @usageNotes
|
33854 | * ### Example
|
33855 | *
|
33856 | * ```typescript
|
33857 | * class A {
|
33858 | * constructor(b:B) {}
|
33859 | * }
|
33860 | *
|
33861 | * expect(() => Injector.resolveAndCreate([A])).toThrowError();
|
33862 | * ```
|
33863 | */
|
33864 | function noProviderError(injector, key) {
|
33865 | return injectionError(injector, key, function (keys) {
|
33866 | const first = stringify$1(keys[0].token);
|
33867 | return `No provider for ${first}!${constructResolvingPath(keys)}`;
|
33868 | });
|
33869 | }
|
33870 | /**
|
33871 | * Thrown when dependencies form a cycle.
|
33872 | *
|
33873 | * @usageNotes
|
33874 | * ### Example
|
33875 | *
|
33876 | * ```typescript
|
33877 | * var injector = Injector.resolveAndCreate([
|
33878 | * {provide: "one", useFactory: (two) => "two", deps: [[new Inject("two")]]},
|
33879 | * {provide: "two", useFactory: (one) => "one", deps: [[new Inject("one")]]}
|
33880 | * ]);
|
33881 | *
|
33882 | * expect(() => injector.get("one")).toThrowError();
|
33883 | * ```
|
33884 | *
|
33885 | * Retrieving `A` or `B` throws a `CyclicDependencyError` as the graph above cannot be constructed.
|
33886 | */
|
33887 | function cyclicDependencyError(injector, key) {
|
33888 | return injectionError(injector, key, function (keys) {
|
33889 | return `Cannot instantiate cyclic dependency!${constructResolvingPath(keys)}`;
|
33890 | });
|
33891 | }
|
33892 | /**
|
33893 | * Thrown when a constructing type returns with an Error.
|
33894 | *
|
33895 | * The `InstantiationError` class contains the original error plus the dependency graph which caused
|
33896 | * this object to be instantiated.
|
33897 | *
|
33898 | * @usageNotes
|
33899 | * ### Example
|
33900 | *
|
33901 | * ```typescript
|
33902 | * class A {
|
33903 | * constructor() {
|
33904 | * throw new Error('message');
|
33905 | * }
|
33906 | * }
|
33907 | *
|
33908 | * var injector = Injector.resolveAndCreate([A]);
|
33909 |
|
33910 | * try {
|
33911 | * injector.get(A);
|
33912 | * } catch (e) {
|
33913 | * expect(e instanceof InstantiationError).toBe(true);
|
33914 | * expect(e.originalException.message).toEqual("message");
|
33915 | * expect(e.originalStack).toBeDefined();
|
33916 | * }
|
33917 | * ```
|
33918 | */
|
33919 | function instantiationError(injector, originalException, originalStack, key) {
|
33920 | return injectionError(injector, key, function (keys) {
|
33921 | const first = stringify$1(keys[0].token);
|
33922 | return `${originalException.message}: Error during instantiation of ${first}!${constructResolvingPath(keys)}.`;
|
33923 | }, originalException);
|
33924 | }
|
33925 | /**
|
33926 | * Thrown when an object other then {@link Provider} (or `Type`) is passed to {@link Injector}
|
33927 | * creation.
|
33928 | *
|
33929 | * @usageNotes
|
33930 | * ### Example
|
33931 | *
|
33932 | * ```typescript
|
33933 | * expect(() => Injector.resolveAndCreate(["not a type"])).toThrowError();
|
33934 | * ```
|
33935 | */
|
33936 | function invalidProviderError(provider) {
|
33937 | return Error(`Invalid provider - only instances of Provider and Type are allowed, got: ${provider}`);
|
33938 | }
|
33939 | /**
|
33940 | * Thrown when the class has no annotation information.
|
33941 | *
|
33942 | * Lack of annotation information prevents the {@link Injector} from determining which dependencies
|
33943 | * need to be injected into the constructor.
|
33944 | *
|
33945 | * @usageNotes
|
33946 | * ### Example
|
33947 | *
|
33948 | * ```typescript
|
33949 | * class A {
|
33950 | * constructor(b) {}
|
33951 | * }
|
33952 | *
|
33953 | * expect(() => Injector.resolveAndCreate([A])).toThrowError();
|
33954 | * ```
|
33955 | *
|
33956 | * This error is also thrown when the class not marked with {@link Injectable} has parameter types.
|
33957 | *
|
33958 | * ```typescript
|
33959 | * class B {}
|
33960 | *
|
33961 | * class A {
|
33962 | * constructor(b:B) {} // no information about the parameter types of A is available at runtime.
|
33963 | * }
|
33964 | *
|
33965 | * expect(() => Injector.resolveAndCreate([A,B])).toThrowError();
|
33966 | * ```
|
33967 | *
|
33968 | */
|
33969 | function noAnnotationError(typeOrFunc, params) {
|
33970 | const signature = [];
|
33971 | for (let i = 0, ii = params.length; i < ii; i++) {
|
33972 | const parameter = params[i];
|
33973 | if (!parameter || parameter.length == 0) {
|
33974 | signature.push('?');
|
33975 | }
|
33976 | else {
|
33977 | signature.push(parameter.map(stringify$1).join(' '));
|
33978 | }
|
33979 | }
|
33980 | return Error('Cannot resolve all parameters for \'' + stringify$1(typeOrFunc) + '\'(' +
|
33981 | signature.join(', ') + '). ' +
|
33982 | 'Make sure that all the parameters are decorated with Inject or have valid type annotations and that \'' +
|
33983 | stringify$1(typeOrFunc) + '\' is decorated with Injectable.');
|
33984 | }
|
33985 | /**
|
33986 | * Thrown when getting an object by index.
|
33987 | *
|
33988 | * @usageNotes
|
33989 | * ### Example
|
33990 | *
|
33991 | * ```typescript
|
33992 | * class A {}
|
33993 | *
|
33994 | * var injector = Injector.resolveAndCreate([A]);
|
33995 | *
|
33996 | * expect(() => injector.getAt(100)).toThrowError();
|
33997 | * ```
|
33998 | *
|
33999 | */
|
34000 | function outOfBoundsError(index) {
|
34001 | return Error(`Index ${index} is out-of-bounds.`);
|
34002 | }
|
34003 | // TODO: add a working example after alpha38 is released
|
34004 | /**
|
34005 | * Thrown when a multi provider and a regular provider are bound to the same token.
|
34006 | *
|
34007 | * @usageNotes
|
34008 | * ### Example
|
34009 | *
|
34010 | * ```typescript
|
34011 | * expect(() => Injector.resolveAndCreate([
|
34012 | * { provide: "Strings", useValue: "string1", multi: true},
|
34013 | * { provide: "Strings", useValue: "string2", multi: false}
|
34014 | * ])).toThrowError();
|
34015 | * ```
|
34016 | */
|
34017 | function mixingMultiProvidersWithRegularProvidersError(provider1, provider2) {
|
34018 | return Error(`Cannot mix multi providers and regular providers, got: ${provider1} ${provider2}`);
|
34019 | }
|
34020 |
|
34021 | /**
|
34022 | * @license
|
34023 | * Copyright Google LLC All Rights Reserved.
|
34024 | *
|
34025 | * Use of this source code is governed by an MIT-style license that can be
|
34026 | * found in the LICENSE file at https://angular.io/license
|
34027 | */
|
34028 | /**
|
34029 | * A unique object used for retrieving items from the {@link ReflectiveInjector}.
|
34030 | *
|
34031 | * Keys have:
|
34032 | * - a system-wide unique `id`.
|
34033 | * - a `token`.
|
34034 | *
|
34035 | * `Key` is used internally by {@link ReflectiveInjector} because its system-wide unique `id` allows
|
34036 | * the
|
34037 | * injector to store created objects in a more efficient way.
|
34038 | *
|
34039 | * `Key` should not be created directly. {@link ReflectiveInjector} creates keys automatically when
|
34040 | * resolving
|
34041 | * providers.
|
34042 | *
|
34043 | * @deprecated No replacement
|
34044 | * @publicApi
|
34045 | */
|
34046 | class ReflectiveKey {
|
34047 | /**
|
34048 | * Private
|
34049 | */
|
34050 | constructor(token, id) {
|
34051 | this.token = token;
|
34052 | this.id = id;
|
34053 | if (!token) {
|
34054 | throw new Error('Token must be defined!');
|
34055 | }
|
34056 | this.displayName = stringify$1(this.token);
|
34057 | }
|
34058 | /**
|
34059 | * Retrieves a `Key` for a token.
|
34060 | */
|
34061 | static get(token) {
|
34062 | return _globalKeyRegistry.get(resolveForwardRef$1(token));
|
34063 | }
|
34064 | /**
|
34065 | * @returns the number of keys registered in the system.
|
34066 | */
|
34067 | static get numberOfKeys() {
|
34068 | return _globalKeyRegistry.numberOfKeys;
|
34069 | }
|
34070 | }
|
34071 | class KeyRegistry {
|
34072 | constructor() {
|
34073 | this._allKeys = new Map();
|
34074 | }
|
34075 | get(token) {
|
34076 | if (token instanceof ReflectiveKey)
|
34077 | return token;
|
34078 | if (this._allKeys.has(token)) {
|
34079 | return this._allKeys.get(token);
|
34080 | }
|
34081 | const newKey = new ReflectiveKey(token, ReflectiveKey.numberOfKeys);
|
34082 | this._allKeys.set(token, newKey);
|
34083 | return newKey;
|
34084 | }
|
34085 | get numberOfKeys() {
|
34086 | return this._allKeys.size;
|
34087 | }
|
34088 | }
|
34089 | const _globalKeyRegistry = new KeyRegistry();
|
34090 |
|
34091 | /**
|
34092 | * @license
|
34093 | * Copyright Google LLC All Rights Reserved.
|
34094 | *
|
34095 | * Use of this source code is governed by an MIT-style license that can be
|
34096 | * found in the LICENSE file at https://angular.io/license
|
34097 | */
|
34098 | /**
|
34099 | * Provides access to reflection data about symbols. Used internally by Angular
|
34100 | * to power dependency injection and compilation.
|
34101 | */
|
34102 | class Reflector {
|
34103 | constructor(reflectionCapabilities) {
|
34104 | this.reflectionCapabilities = reflectionCapabilities;
|
34105 | }
|
34106 | updateCapabilities(caps) {
|
34107 | this.reflectionCapabilities = caps;
|
34108 | }
|
34109 | factory(type) {
|
34110 | return this.reflectionCapabilities.factory(type);
|
34111 | }
|
34112 | parameters(typeOrFunc) {
|
34113 | return this.reflectionCapabilities.parameters(typeOrFunc);
|
34114 | }
|
34115 | annotations(typeOrFunc) {
|
34116 | return this.reflectionCapabilities.annotations(typeOrFunc);
|
34117 | }
|
34118 | propMetadata(typeOrFunc) {
|
34119 | return this.reflectionCapabilities.propMetadata(typeOrFunc);
|
34120 | }
|
34121 | hasLifecycleHook(type, lcProperty) {
|
34122 | return this.reflectionCapabilities.hasLifecycleHook(type, lcProperty);
|
34123 | }
|
34124 | getter(name) {
|
34125 | return this.reflectionCapabilities.getter(name);
|
34126 | }
|
34127 | setter(name) {
|
34128 | return this.reflectionCapabilities.setter(name);
|
34129 | }
|
34130 | method(name) {
|
34131 | return this.reflectionCapabilities.method(name);
|
34132 | }
|
34133 | importUri(type) {
|
34134 | return this.reflectionCapabilities.importUri(type);
|
34135 | }
|
34136 | resourceUri(type) {
|
34137 | return this.reflectionCapabilities.resourceUri(type);
|
34138 | }
|
34139 | resolveIdentifier(name, moduleUrl, members, runtime) {
|
34140 | return this.reflectionCapabilities.resolveIdentifier(name, moduleUrl, members, runtime);
|
34141 | }
|
34142 | resolveEnum(identifier, name) {
|
34143 | return this.reflectionCapabilities.resolveEnum(identifier, name);
|
34144 | }
|
34145 | }
|
34146 |
|
34147 | /**
|
34148 | * @license
|
34149 | * Copyright Google LLC All Rights Reserved.
|
34150 | *
|
34151 | * Use of this source code is governed by an MIT-style license that can be
|
34152 | * found in the LICENSE file at https://angular.io/license
|
34153 | */
|
34154 | /**
|
34155 | * The {@link Reflector} used internally in Angular to access metadata
|
34156 | * about symbols.
|
34157 | */
|
34158 | const reflector = new Reflector(new ReflectionCapabilities());
|
34159 |
|
34160 | /**
|
34161 | * @license
|
34162 | * Copyright Google LLC All Rights Reserved.
|
34163 | *
|
34164 | * Use of this source code is governed by an MIT-style license that can be
|
34165 | * found in the LICENSE file at https://angular.io/license
|
34166 | */
|
34167 | /**
|
34168 | * `Dependency` is used by the framework to extend DI.
|
34169 | * This is internal to Angular and should not be used directly.
|
34170 | */
|
34171 | class ReflectiveDependency {
|
34172 | constructor(key, optional, visibility) {
|
34173 | this.key = key;
|
34174 | this.optional = optional;
|
34175 | this.visibility = visibility;
|
34176 | }
|
34177 | static fromKey(key) {
|
34178 | return new ReflectiveDependency(key, false, null);
|
34179 | }
|
34180 | }
|
34181 | const _EMPTY_LIST = [];
|
34182 | class ResolvedReflectiveProvider_ {
|
34183 | constructor(key, resolvedFactories, multiProvider) {
|
34184 | this.key = key;
|
34185 | this.resolvedFactories = resolvedFactories;
|
34186 | this.multiProvider = multiProvider;
|
34187 | this.resolvedFactory = this.resolvedFactories[0];
|
34188 | }
|
34189 | }
|
34190 | /**
|
34191 | * An internal resolved representation of a factory function created by resolving `Provider`.
|
34192 | * @publicApi
|
34193 | */
|
34194 | class ResolvedReflectiveFactory {
|
34195 | constructor(
|
34196 | /**
|
34197 | * Factory function which can return an instance of an object represented by a key.
|
34198 | */
|
34199 | factory,
|
34200 | /**
|
34201 | * Arguments (dependencies) to the `factory` function.
|
34202 | */
|
34203 | dependencies) {
|
34204 | this.factory = factory;
|
34205 | this.dependencies = dependencies;
|
34206 | }
|
34207 | }
|
34208 | /**
|
34209 | * Resolve a single provider.
|
34210 | */
|
34211 | function resolveReflectiveFactory(provider) {
|
34212 | let factoryFn;
|
34213 | let resolvedDeps;
|
34214 | if (provider.useClass) {
|
34215 | const useClass = resolveForwardRef$1(provider.useClass);
|
34216 | factoryFn = reflector.factory(useClass);
|
34217 | resolvedDeps = _dependenciesFor(useClass);
|
34218 | }
|
34219 | else if (provider.useExisting) {
|
34220 | factoryFn = (aliasInstance) => aliasInstance;
|
34221 | resolvedDeps = [ReflectiveDependency.fromKey(ReflectiveKey.get(provider.useExisting))];
|
34222 | }
|
34223 | else if (provider.useFactory) {
|
34224 | factoryFn = provider.useFactory;
|
34225 | resolvedDeps = constructDependencies(provider.useFactory, provider.deps);
|
34226 | }
|
34227 | else {
|
34228 | factoryFn = () => provider.useValue;
|
34229 | resolvedDeps = _EMPTY_LIST;
|
34230 | }
|
34231 | return new ResolvedReflectiveFactory(factoryFn, resolvedDeps);
|
34232 | }
|
34233 | /**
|
34234 | * Converts the `Provider` into `ResolvedProvider`.
|
34235 | *
|
34236 | * `Injector` internally only uses `ResolvedProvider`, `Provider` contains convenience provider
|
34237 | * syntax.
|
34238 | */
|
34239 | function resolveReflectiveProvider(provider) {
|
34240 | return new ResolvedReflectiveProvider_(ReflectiveKey.get(provider.provide), [resolveReflectiveFactory(provider)], provider.multi || false);
|
34241 | }
|
34242 | /**
|
34243 | * Resolve a list of Providers.
|
34244 | */
|
34245 | function resolveReflectiveProviders(providers) {
|
34246 | const normalized = _normalizeProviders(providers, []);
|
34247 | const resolved = normalized.map(resolveReflectiveProvider);
|
34248 | const resolvedProviderMap = mergeResolvedReflectiveProviders(resolved, new Map());
|
34249 | return Array.from(resolvedProviderMap.values());
|
34250 | }
|
34251 | /**
|
34252 | * Merges a list of ResolvedProviders into a list where each key is contained exactly once and
|
34253 | * multi providers have been merged.
|
34254 | */
|
34255 | function mergeResolvedReflectiveProviders(providers, normalizedProvidersMap) {
|
34256 | for (let i = 0; i < providers.length; i++) {
|
34257 | const provider = providers[i];
|
34258 | const existing = normalizedProvidersMap.get(provider.key.id);
|
34259 | if (existing) {
|
34260 | if (provider.multiProvider !== existing.multiProvider) {
|
34261 | throw mixingMultiProvidersWithRegularProvidersError(existing, provider);
|
34262 | }
|
34263 | if (provider.multiProvider) {
|
34264 | for (let j = 0; j < provider.resolvedFactories.length; j++) {
|
34265 | existing.resolvedFactories.push(provider.resolvedFactories[j]);
|
34266 | }
|
34267 | }
|
34268 | else {
|
34269 | normalizedProvidersMap.set(provider.key.id, provider);
|
34270 | }
|
34271 | }
|
34272 | else {
|
34273 | let resolvedProvider;
|
34274 | if (provider.multiProvider) {
|
34275 | resolvedProvider = new ResolvedReflectiveProvider_(provider.key, provider.resolvedFactories.slice(), provider.multiProvider);
|
34276 | }
|
34277 | else {
|
34278 | resolvedProvider = provider;
|
34279 | }
|
34280 | normalizedProvidersMap.set(provider.key.id, resolvedProvider);
|
34281 | }
|
34282 | }
|
34283 | return normalizedProvidersMap;
|
34284 | }
|
34285 | function _normalizeProviders(providers, res) {
|
34286 | providers.forEach(b => {
|
34287 | if (b instanceof Type$2) {
|
34288 | res.push({ provide: b, useClass: b });
|
34289 | }
|
34290 | else if (b && typeof b == 'object' && b.provide !== undefined) {
|
34291 | res.push(b);
|
34292 | }
|
34293 | else if (Array.isArray(b)) {
|
34294 | _normalizeProviders(b, res);
|
34295 | }
|
34296 | else {
|
34297 | throw invalidProviderError(b);
|
34298 | }
|
34299 | });
|
34300 | return res;
|
34301 | }
|
34302 | function constructDependencies(typeOrFunc, dependencies) {
|
34303 | if (!dependencies) {
|
34304 | return _dependenciesFor(typeOrFunc);
|
34305 | }
|
34306 | else {
|
34307 | const params = dependencies.map(t => [t]);
|
34308 | return dependencies.map(t => _extractToken(typeOrFunc, t, params));
|
34309 | }
|
34310 | }
|
34311 | function _dependenciesFor(typeOrFunc) {
|
34312 | const params = reflector.parameters(typeOrFunc);
|
34313 | if (!params)
|
34314 | return [];
|
34315 | if (params.some(p => p == null)) {
|
34316 | throw noAnnotationError(typeOrFunc, params);
|
34317 | }
|
34318 | return params.map(p => _extractToken(typeOrFunc, p, params));
|
34319 | }
|
34320 | function _extractToken(typeOrFunc, metadata, params) {
|
34321 | let token = null;
|
34322 | let optional = false;
|
34323 | if (!Array.isArray(metadata)) {
|
34324 | if (metadata instanceof Inject) {
|
34325 | return _createDependency(metadata.token, optional, null);
|
34326 | }
|
34327 | else {
|
34328 | return _createDependency(metadata, optional, null);
|
34329 | }
|
34330 | }
|
34331 | let visibility = null;
|
34332 | for (let i = 0; i < metadata.length; ++i) {
|
34333 | const paramMetadata = metadata[i];
|
34334 | if (paramMetadata instanceof Type$2) {
|
34335 | token = paramMetadata;
|
34336 | }
|
34337 | else if (paramMetadata instanceof Inject) {
|
34338 | token = paramMetadata.token;
|
34339 | }
|
34340 | else if (paramMetadata instanceof Optional) {
|
34341 | optional = true;
|
34342 | }
|
34343 | else if (paramMetadata instanceof Self || paramMetadata instanceof SkipSelf) {
|
34344 | visibility = paramMetadata;
|
34345 | }
|
34346 | else if (paramMetadata instanceof InjectionToken) {
|
34347 | token = paramMetadata;
|
34348 | }
|
34349 | }
|
34350 | token = resolveForwardRef$1(token);
|
34351 | if (token != null) {
|
34352 | return _createDependency(token, optional, visibility);
|
34353 | }
|
34354 | else {
|
34355 | throw noAnnotationError(typeOrFunc, params);
|
34356 | }
|
34357 | }
|
34358 | function _createDependency(token, optional, visibility) {
|
34359 | return new ReflectiveDependency(ReflectiveKey.get(token), optional, visibility);
|
34360 | }
|
34361 |
|
34362 | /**
|
34363 | * @license
|
34364 | * Copyright Google LLC All Rights Reserved.
|
34365 | *
|
34366 | * Use of this source code is governed by an MIT-style license that can be
|
34367 | * found in the LICENSE file at https://angular.io/license
|
34368 | */
|
34369 | // Threshold for the dynamic version
|
34370 | const UNDEFINED = {};
|
34371 | /**
|
34372 | * A ReflectiveDependency injection container used for instantiating objects and resolving
|
34373 | * dependencies.
|
34374 | *
|
34375 | * An `Injector` is a replacement for a `new` operator, which can automatically resolve the
|
34376 | * constructor dependencies.
|
34377 | *
|
34378 | * In typical use, application code asks for the dependencies in the constructor and they are
|
34379 | * resolved by the `Injector`.
|
34380 | *
|
34381 | * @usageNotes
|
34382 | * ### Example
|
34383 | *
|
34384 | * The following example creates an `Injector` configured to create `Engine` and `Car`.
|
34385 | *
|
34386 | * ```typescript
|
34387 | * @Injectable()
|
34388 | * class Engine {
|
34389 | * }
|
34390 | *
|
34391 | * @Injectable()
|
34392 | * class Car {
|
34393 | * constructor(public engine:Engine) {}
|
34394 | * }
|
34395 | *
|
34396 | * var injector = ReflectiveInjector.resolveAndCreate([Car, Engine]);
|
34397 | * var car = injector.get(Car);
|
34398 | * expect(car instanceof Car).toBe(true);
|
34399 | * expect(car.engine instanceof Engine).toBe(true);
|
34400 | * ```
|
34401 | *
|
34402 | * Notice, we don't use the `new` operator because we explicitly want to have the `Injector`
|
34403 | * resolve all of the object's dependencies automatically.
|
34404 | *
|
34405 | * @deprecated from v5 - slow and brings in a lot of code, Use `Injector.create` instead.
|
34406 | * @publicApi
|
34407 | */
|
34408 | class ReflectiveInjector {
|
34409 | /**
|
34410 | * Turns an array of provider definitions into an array of resolved providers.
|
34411 | *
|
34412 | * A resolution is a process of flattening multiple nested arrays and converting individual
|
34413 | * providers into an array of `ResolvedReflectiveProvider`s.
|
34414 | *
|
34415 | * @usageNotes
|
34416 | * ### Example
|
34417 | *
|
34418 | * ```typescript
|
34419 | * @Injectable()
|
34420 | * class Engine {
|
34421 | * }
|
34422 | *
|
34423 | * @Injectable()
|
34424 | * class Car {
|
34425 | * constructor(public engine:Engine) {}
|
34426 | * }
|
34427 | *
|
34428 | * var providers = ReflectiveInjector.resolve([Car, [[Engine]]]);
|
34429 | *
|
34430 | * expect(providers.length).toEqual(2);
|
34431 | *
|
34432 | * expect(providers[0] instanceof ResolvedReflectiveProvider).toBe(true);
|
34433 | * expect(providers[0].key.displayName).toBe("Car");
|
34434 | * expect(providers[0].dependencies.length).toEqual(1);
|
34435 | * expect(providers[0].factory).toBeDefined();
|
34436 | *
|
34437 | * expect(providers[1].key.displayName).toBe("Engine");
|
34438 | * });
|
34439 | * ```
|
34440 | *
|
34441 | */
|
34442 | static resolve(providers) {
|
34443 | return resolveReflectiveProviders(providers);
|
34444 | }
|
34445 | /**
|
34446 | * Resolves an array of providers and creates an injector from those providers.
|
34447 | *
|
34448 | * The passed-in providers can be an array of `Type`, `Provider`,
|
34449 | * or a recursive array of more providers.
|
34450 | *
|
34451 | * @usageNotes
|
34452 | * ### Example
|
34453 | *
|
34454 | * ```typescript
|
34455 | * @Injectable()
|
34456 | * class Engine {
|
34457 | * }
|
34458 | *
|
34459 | * @Injectable()
|
34460 | * class Car {
|
34461 | * constructor(public engine:Engine) {}
|
34462 | * }
|
34463 | *
|
34464 | * var injector = ReflectiveInjector.resolveAndCreate([Car, Engine]);
|
34465 | * expect(injector.get(Car) instanceof Car).toBe(true);
|
34466 | * ```
|
34467 | */
|
34468 | static resolveAndCreate(providers, parent) {
|
34469 | const ResolvedReflectiveProviders = ReflectiveInjector.resolve(providers);
|
34470 | return ReflectiveInjector.fromResolvedProviders(ResolvedReflectiveProviders, parent);
|
34471 | }
|
34472 | /**
|
34473 | * Creates an injector from previously resolved providers.
|
34474 | *
|
34475 | * This API is the recommended way to construct injectors in performance-sensitive parts.
|
34476 | *
|
34477 | * @usageNotes
|
34478 | * ### Example
|
34479 | *
|
34480 | * ```typescript
|
34481 | * @Injectable()
|
34482 | * class Engine {
|
34483 | * }
|
34484 | *
|
34485 | * @Injectable()
|
34486 | * class Car {
|
34487 | * constructor(public engine:Engine) {}
|
34488 | * }
|
34489 | *
|
34490 | * var providers = ReflectiveInjector.resolve([Car, Engine]);
|
34491 | * var injector = ReflectiveInjector.fromResolvedProviders(providers);
|
34492 | * expect(injector.get(Car) instanceof Car).toBe(true);
|
34493 | * ```
|
34494 | */
|
34495 | static fromResolvedProviders(providers, parent) {
|
34496 | return new ReflectiveInjector_(providers, parent);
|
34497 | }
|
34498 | }
|
34499 | class ReflectiveInjector_ {
|
34500 | /**
|
34501 | * Private
|
34502 | */
|
34503 | constructor(_providers, _parent) {
|
34504 | /** @internal */
|
34505 | this._constructionCounter = 0;
|
34506 | this._providers = _providers;
|
34507 | this.parent = _parent || null;
|
34508 | const len = _providers.length;
|
34509 | this.keyIds = [];
|
34510 | this.objs = [];
|
34511 | for (let i = 0; i < len; i++) {
|
34512 | this.keyIds[i] = _providers[i].key.id;
|
34513 | this.objs[i] = UNDEFINED;
|
34514 | }
|
34515 | }
|
34516 | get(token, notFoundValue = THROW_IF_NOT_FOUND) {
|
34517 | return this._getByKey(ReflectiveKey.get(token), null, notFoundValue);
|
34518 | }
|
34519 | resolveAndCreateChild(providers) {
|
34520 | const ResolvedReflectiveProviders = ReflectiveInjector.resolve(providers);
|
34521 | return this.createChildFromResolved(ResolvedReflectiveProviders);
|
34522 | }
|
34523 | createChildFromResolved(providers) {
|
34524 | const inj = new ReflectiveInjector_(providers);
|
34525 | inj.parent = this;
|
34526 | return inj;
|
34527 | }
|
34528 | resolveAndInstantiate(provider) {
|
34529 | return this.instantiateResolved(ReflectiveInjector.resolve([provider])[0]);
|
34530 | }
|
34531 | instantiateResolved(provider) {
|
34532 | return this._instantiateProvider(provider);
|
34533 | }
|
34534 | getProviderAtIndex(index) {
|
34535 | if (index < 0 || index >= this._providers.length) {
|
34536 | throw outOfBoundsError(index);
|
34537 | }
|
34538 | return this._providers[index];
|
34539 | }
|
34540 | /** @internal */
|
34541 | _new(provider) {
|
34542 | if (this._constructionCounter++ > this._getMaxNumberOfObjects()) {
|
34543 | throw cyclicDependencyError(this, provider.key);
|
34544 | }
|
34545 | return this._instantiateProvider(provider);
|
34546 | }
|
34547 | _getMaxNumberOfObjects() {
|
34548 | return this.objs.length;
|
34549 | }
|
34550 | _instantiateProvider(provider) {
|
34551 | if (provider.multiProvider) {
|
34552 | const res = [];
|
34553 | for (let i = 0; i < provider.resolvedFactories.length; ++i) {
|
34554 | res[i] = this._instantiate(provider, provider.resolvedFactories[i]);
|
34555 | }
|
34556 | return res;
|
34557 | }
|
34558 | else {
|
34559 | return this._instantiate(provider, provider.resolvedFactories[0]);
|
34560 | }
|
34561 | }
|
34562 | _instantiate(provider, ResolvedReflectiveFactory) {
|
34563 | const factory = ResolvedReflectiveFactory.factory;
|
34564 | let deps;
|
34565 | try {
|
34566 | deps =
|
34567 | ResolvedReflectiveFactory.dependencies.map(dep => this._getByReflectiveDependency(dep));
|
34568 | }
|
34569 | catch (e) {
|
34570 | if (e.addKey) {
|
34571 | e.addKey(this, provider.key);
|
34572 | }
|
34573 | throw e;
|
34574 | }
|
34575 | let obj;
|
34576 | try {
|
34577 | obj = factory(...deps);
|
34578 | }
|
34579 | catch (e) {
|
34580 | throw instantiationError(this, e, e.stack, provider.key);
|
34581 | }
|
34582 | return obj;
|
34583 | }
|
34584 | _getByReflectiveDependency(dep) {
|
34585 | return this._getByKey(dep.key, dep.visibility, dep.optional ? null : THROW_IF_NOT_FOUND);
|
34586 | }
|
34587 | _getByKey(key, visibility, notFoundValue) {
|
34588 | if (key === ReflectiveInjector_.INJECTOR_KEY) {
|
34589 | return this;
|
34590 | }
|
34591 | if (visibility instanceof Self) {
|
34592 | return this._getByKeySelf(key, notFoundValue);
|
34593 | }
|
34594 | else {
|
34595 | return this._getByKeyDefault(key, notFoundValue, visibility);
|
34596 | }
|
34597 | }
|
34598 | _getObjByKeyId(keyId) {
|
34599 | for (let i = 0; i < this.keyIds.length; i++) {
|
34600 | if (this.keyIds[i] === keyId) {
|
34601 | if (this.objs[i] === UNDEFINED) {
|
34602 | this.objs[i] = this._new(this._providers[i]);
|
34603 | }
|
34604 | return this.objs[i];
|
34605 | }
|
34606 | }
|
34607 | return UNDEFINED;
|
34608 | }
|
34609 | /** @internal */
|
34610 | _throwOrNull(key, notFoundValue) {
|
34611 | if (notFoundValue !== THROW_IF_NOT_FOUND) {
|
34612 | return notFoundValue;
|
34613 | }
|
34614 | else {
|
34615 | throw noProviderError(this, key);
|
34616 | }
|
34617 | }
|
34618 | /** @internal */
|
34619 | _getByKeySelf(key, notFoundValue) {
|
34620 | const obj = this._getObjByKeyId(key.id);
|
34621 | return (obj !== UNDEFINED) ? obj : this._throwOrNull(key, notFoundValue);
|
34622 | }
|
34623 | /** @internal */
|
34624 | _getByKeyDefault(key, notFoundValue, visibility) {
|
34625 | let inj;
|
34626 | if (visibility instanceof SkipSelf) {
|
34627 | inj = this.parent;
|
34628 | }
|
34629 | else {
|
34630 | inj = this;
|
34631 | }
|
34632 | while (inj instanceof ReflectiveInjector_) {
|
34633 | const inj_ = inj;
|
34634 | const obj = inj_._getObjByKeyId(key.id);
|
34635 | if (obj !== UNDEFINED)
|
34636 | return obj;
|
34637 | inj = inj_.parent;
|
34638 | }
|
34639 | if (inj !== null) {
|
34640 | return inj.get(key.token, notFoundValue);
|
34641 | }
|
34642 | else {
|
34643 | return this._throwOrNull(key, notFoundValue);
|
34644 | }
|
34645 | }
|
34646 | get displayName() {
|
34647 | const providers = _mapProviders(this, (b) => ' "' + b.key.displayName + '" ')
|
34648 | .join(', ');
|
34649 | return `ReflectiveInjector(providers: [${providers}])`;
|
34650 | }
|
34651 | toString() {
|
34652 | return this.displayName;
|
34653 | }
|
34654 | }
|
34655 | ReflectiveInjector_.INJECTOR_KEY = ReflectiveKey.get(Injector);
|
34656 | function _mapProviders(injector, fn) {
|
34657 | const res = [];
|
34658 | for (let i = 0; i < injector._providers.length; ++i) {
|
34659 | res[i] = fn(injector.getProviderAtIndex(i));
|
34660 | }
|
34661 | return res;
|
34662 | }
|
34663 |
|
34664 | /**
|
34665 | * @license
|
34666 | * Copyright Google LLC All Rights Reserved.
|
34667 | *
|
34668 | * Use of this source code is governed by an MIT-style license that can be
|
34669 | * found in the LICENSE file at https://angular.io/license
|
34670 | */
|
34671 | /**
|
34672 | * Determine if the argument is shaped like a Promise
|
34673 | */
|
34674 | function isPromise$1(obj) {
|
34675 | // allow any Promise/A+ compliant thenable.
|
34676 | // It's up to the caller to ensure that obj.then conforms to the spec
|
34677 | return !!obj && typeof obj.then === 'function';
|
34678 | }
|
34679 |
|
34680 | /**
|
34681 | * @license
|
34682 | * Copyright Google LLC All Rights Reserved.
|
34683 | *
|
34684 | * Use of this source code is governed by an MIT-style license that can be
|
34685 | * found in the LICENSE file at https://angular.io/license
|
34686 | */
|
34687 | /**
|
34688 | * This file contains reuseable "empty" symbols that can be used as default return values
|
34689 | * in different parts of the rendering code. Because the same symbols are returned, this
|
34690 | * allows for identity checks against these values to be consistently used by the framework
|
34691 | * code.
|
34692 | */
|
34693 | const EMPTY_OBJ$1 = {};
|
34694 | const EMPTY_ARRAY$2 = [];
|
34695 | // freezing the values prevents any code from accidentally inserting new values in
|
34696 | if ((typeof ngDevMode === 'undefined' || ngDevMode) && initNgDevMode()) {
|
34697 | // These property accesses can be ignored because ngDevMode will be set to false
|
34698 | // when optimizing code and the whole if statement will be dropped.
|
34699 | // tslint:disable-next-line:no-toplevel-property-access
|
34700 | Object.freeze(EMPTY_OBJ$1);
|
34701 | // tslint:disable-next-line:no-toplevel-property-access
|
34702 | Object.freeze(EMPTY_ARRAY$2);
|
34703 | }
|
34704 |
|
34705 | /**
|
34706 | * @license
|
34707 | * Copyright Google LLC All Rights Reserved.
|
34708 | *
|
34709 | * Use of this source code is governed by an MIT-style license that can be
|
34710 | * found in the LICENSE file at https://angular.io/license
|
34711 | */
|
34712 | /**
|
34713 | * NOTE: changes to the `ngI18nClosureMode` name must be synced with `compiler-cli/src/tooling.ts`.
|
34714 | */
|
34715 | if (typeof ngI18nClosureMode === 'undefined') {
|
34716 | // These property accesses can be ignored because ngI18nClosureMode will be set to false
|
34717 | // when optimizing code and the whole if statement will be dropped.
|
34718 | // Make sure to refer to ngI18nClosureMode as ['ngI18nClosureMode'] for closure.
|
34719 | // NOTE: we need to have it in IIFE so that the tree-shaker is happy.
|
34720 | (function () {
|
34721 | // tslint:disable-next-line:no-toplevel-property-access
|
34722 | _global$1['ngI18nClosureMode'] =
|
34723 | // TODO(FW-1250): validate that this actually, you know, works.
|
34724 | // tslint:disable-next-line:no-toplevel-property-access
|
34725 | typeof goog !== 'undefined' && typeof goog.getMsg === 'function';
|
34726 | })();
|
34727 | }
|
34728 |
|
34729 | /**
|
34730 | * @license
|
34731 | * Copyright Google LLC All Rights Reserved.
|
34732 | *
|
34733 | * Use of this source code is governed by an MIT-style license that can be
|
34734 | * found in the LICENSE file at https://angular.io/license
|
34735 | */
|
34736 | /**
|
34737 | * Index of each type of locale data from the locale data array
|
34738 | */
|
34739 | var LocaleDataIndex;
|
34740 | (function (LocaleDataIndex) {
|
34741 | LocaleDataIndex[LocaleDataIndex["LocaleId"] = 0] = "LocaleId";
|
34742 | LocaleDataIndex[LocaleDataIndex["DayPeriodsFormat"] = 1] = "DayPeriodsFormat";
|
34743 | LocaleDataIndex[LocaleDataIndex["DayPeriodsStandalone"] = 2] = "DayPeriodsStandalone";
|
34744 | LocaleDataIndex[LocaleDataIndex["DaysFormat"] = 3] = "DaysFormat";
|
34745 | LocaleDataIndex[LocaleDataIndex["DaysStandalone"] = 4] = "DaysStandalone";
|
34746 | LocaleDataIndex[LocaleDataIndex["MonthsFormat"] = 5] = "MonthsFormat";
|
34747 | LocaleDataIndex[LocaleDataIndex["MonthsStandalone"] = 6] = "MonthsStandalone";
|
34748 | LocaleDataIndex[LocaleDataIndex["Eras"] = 7] = "Eras";
|
34749 | LocaleDataIndex[LocaleDataIndex["FirstDayOfWeek"] = 8] = "FirstDayOfWeek";
|
34750 | LocaleDataIndex[LocaleDataIndex["WeekendRange"] = 9] = "WeekendRange";
|
34751 | LocaleDataIndex[LocaleDataIndex["DateFormat"] = 10] = "DateFormat";
|
34752 | LocaleDataIndex[LocaleDataIndex["TimeFormat"] = 11] = "TimeFormat";
|
34753 | LocaleDataIndex[LocaleDataIndex["DateTimeFormat"] = 12] = "DateTimeFormat";
|
34754 | LocaleDataIndex[LocaleDataIndex["NumberSymbols"] = 13] = "NumberSymbols";
|
34755 | LocaleDataIndex[LocaleDataIndex["NumberFormats"] = 14] = "NumberFormats";
|
34756 | LocaleDataIndex[LocaleDataIndex["CurrencyCode"] = 15] = "CurrencyCode";
|
34757 | LocaleDataIndex[LocaleDataIndex["CurrencySymbol"] = 16] = "CurrencySymbol";
|
34758 | LocaleDataIndex[LocaleDataIndex["CurrencyName"] = 17] = "CurrencyName";
|
34759 | LocaleDataIndex[LocaleDataIndex["Currencies"] = 18] = "Currencies";
|
34760 | LocaleDataIndex[LocaleDataIndex["Directionality"] = 19] = "Directionality";
|
34761 | LocaleDataIndex[LocaleDataIndex["PluralCase"] = 20] = "PluralCase";
|
34762 | LocaleDataIndex[LocaleDataIndex["ExtraData"] = 21] = "ExtraData";
|
34763 | })(LocaleDataIndex || (LocaleDataIndex = {}));
|
34764 |
|
34765 | /**
|
34766 | * @license
|
34767 | * Copyright Google LLC All Rights Reserved.
|
34768 | *
|
34769 | * Use of this source code is governed by an MIT-style license that can be
|
34770 | * found in the LICENSE file at https://angular.io/license
|
34771 | */
|
34772 | /**
|
34773 | * The locale id that the application is using by default (for translations and ICU expressions).
|
34774 | */
|
34775 | const DEFAULT_LOCALE_ID = 'en-US';
|
34776 | /**
|
34777 | * USD currency code that the application uses by default for CurrencyPipe when no
|
34778 | * DEFAULT_CURRENCY_CODE is provided.
|
34779 | */
|
34780 | const USD_CURRENCY_CODE = 'USD';
|
34781 |
|
34782 | /**
|
34783 | * @license
|
34784 | * Copyright Google LLC All Rights Reserved.
|
34785 | *
|
34786 | * Use of this source code is governed by an MIT-style license that can be
|
34787 | * found in the LICENSE file at https://angular.io/license
|
34788 | */
|
34789 | /**
|
34790 | * See `I18nCreateOpCodes`
|
34791 | */
|
34792 | var I18nCreateOpCode;
|
34793 | (function (I18nCreateOpCode) {
|
34794 | /**
|
34795 | * Number of bits to shift index so that it can be combined with the `APPEND_EAGERLY` and
|
34796 | * `COMMENT`.
|
34797 | */
|
34798 | I18nCreateOpCode[I18nCreateOpCode["SHIFT"] = 2] = "SHIFT";
|
34799 | /**
|
34800 | * Should the node be appended to parent imedditatly after creation.
|
34801 | */
|
34802 | I18nCreateOpCode[I18nCreateOpCode["APPEND_EAGERLY"] = 1] = "APPEND_EAGERLY";
|
34803 | /**
|
34804 | * If set the node should be comment (rather than a text) node.
|
34805 | */
|
34806 | I18nCreateOpCode[I18nCreateOpCode["COMMENT"] = 2] = "COMMENT";
|
34807 | })(I18nCreateOpCode || (I18nCreateOpCode = {}));
|
34808 |
|
34809 | /**
|
34810 | * @license
|
34811 | * Copyright Google LLC All Rights Reserved.
|
34812 | *
|
34813 | * Use of this source code is governed by an MIT-style license that can be
|
34814 | * found in the LICENSE file at https://angular.io/license
|
34815 | */
|
34816 | /**
|
34817 | * The locale id that the application is currently using (for translations and ICU expressions).
|
34818 | * This is the ivy version of `LOCALE_ID` that was defined as an injection token for the view engine
|
34819 | * but is now defined as a global value.
|
34820 | */
|
34821 | let LOCALE_ID = DEFAULT_LOCALE_ID;
|
34822 | /**
|
34823 | * Sets the locale id that will be used for translations and ICU expressions.
|
34824 | * This is the ivy version of `LOCALE_ID` that was defined as an injection token for the view engine
|
34825 | * but is now defined as a global value.
|
34826 | *
|
34827 | * @param localeId
|
34828 | */
|
34829 | function setLocaleId(localeId) {
|
34830 | assertDefined(localeId, `Expected localeId to be defined`);
|
34831 | if (typeof localeId === 'string') {
|
34832 | LOCALE_ID = localeId.toLowerCase().replace(/_/g, '-');
|
34833 | }
|
34834 | }
|
34835 |
|
34836 | /**
|
34837 | * @license
|
34838 | * Copyright Google LLC All Rights Reserved.
|
34839 | *
|
34840 | * Use of this source code is governed by an MIT-style license that can be
|
34841 | * found in the LICENSE file at https://angular.io/license
|
34842 | */
|
34843 | /**
|
34844 | * Represents a component created by a `ComponentFactory`.
|
34845 | * Provides access to the component instance and related objects,
|
34846 | * and provides the means of destroying the instance.
|
34847 | *
|
34848 | * @publicApi
|
34849 | */
|
34850 | class ComponentRef {
|
34851 | }
|
34852 | /**
|
34853 | * Base class for a factory that can create a component dynamically.
|
34854 | * Instantiate a factory for a given type of component with `resolveComponentFactory()`.
|
34855 | * Use the resulting `ComponentFactory.create()` method to create a component of that type.
|
34856 | *
|
34857 | * @see [Dynamic Components](guide/dynamic-component-loader)
|
34858 | *
|
34859 | * @publicApi
|
34860 | */
|
34861 | class ComponentFactory {
|
34862 | }
|
34863 |
|
34864 | /**
|
34865 | * @license
|
34866 | * Copyright Google LLC All Rights Reserved.
|
34867 | *
|
34868 | * Use of this source code is governed by an MIT-style license that can be
|
34869 | * found in the LICENSE file at https://angular.io/license
|
34870 | */
|
34871 | function noComponentFactoryError(component) {
|
34872 | const error = Error(`No component factory found for ${stringify$1(component)}. Did you add it to @NgModule.entryComponents?`);
|
34873 | error[ERROR_COMPONENT] = component;
|
34874 | return error;
|
34875 | }
|
34876 | const ERROR_COMPONENT = 'ngComponent';
|
34877 | class _NullComponentFactoryResolver {
|
34878 | resolveComponentFactory(component) {
|
34879 | throw noComponentFactoryError(component);
|
34880 | }
|
34881 | }
|
34882 | /**
|
34883 | * A simple registry that maps `Components` to generated `ComponentFactory` classes
|
34884 | * that can be used to create instances of components.
|
34885 | * Use to obtain the factory for a given component type,
|
34886 | * then use the factory's `create()` method to create a component of that type.
|
34887 | *
|
34888 | * @see [Dynamic Components](guide/dynamic-component-loader)
|
34889 | * @publicApi
|
34890 | */
|
34891 | class ComponentFactoryResolver {
|
34892 | }
|
34893 | ComponentFactoryResolver.NULL = new _NullComponentFactoryResolver();
|
34894 | class ComponentFactoryBoundToModule extends ComponentFactory {
|
34895 | constructor(factory, ngModule) {
|
34896 | super();
|
34897 | this.factory = factory;
|
34898 | this.ngModule = ngModule;
|
34899 | this.selector = factory.selector;
|
34900 | this.componentType = factory.componentType;
|
34901 | this.ngContentSelectors = factory.ngContentSelectors;
|
34902 | this.inputs = factory.inputs;
|
34903 | this.outputs = factory.outputs;
|
34904 | }
|
34905 | create(injector, projectableNodes, rootSelectorOrNode, ngModule) {
|
34906 | return this.factory.create(injector, projectableNodes, rootSelectorOrNode, ngModule || this.ngModule);
|
34907 | }
|
34908 | }
|
34909 |
|
34910 | /**
|
34911 | * @license
|
34912 | * Copyright Google LLC All Rights Reserved.
|
34913 | *
|
34914 | * Use of this source code is governed by an MIT-style license that can be
|
34915 | * found in the LICENSE file at https://angular.io/license
|
34916 | */
|
34917 | function noop(...args) {
|
34918 | // Do nothing.
|
34919 | }
|
34920 |
|
34921 | /**
|
34922 | * @license
|
34923 | * Copyright Google LLC All Rights Reserved.
|
34924 | *
|
34925 | * Use of this source code is governed by an MIT-style license that can be
|
34926 | * found in the LICENSE file at https://angular.io/license
|
34927 | */
|
34928 | /**
|
34929 | * Creates an ElementRef given a node.
|
34930 | *
|
34931 | * @param tNode The node for which you'd like an ElementRef
|
34932 | * @param lView The view to which the node belongs
|
34933 | * @returns The ElementRef instance to use
|
34934 | */
|
34935 | function createElementRef(tNode, lView) {
|
34936 | return new ElementRef(getNativeByTNode(tNode, lView));
|
34937 | }
|
34938 | const SWITCH_ELEMENT_REF_FACTORY__PRE_R3__ = noop;
|
34939 | const SWITCH_ELEMENT_REF_FACTORY = SWITCH_ELEMENT_REF_FACTORY__PRE_R3__;
|
34940 | /**
|
34941 | * A wrapper around a native element inside of a View.
|
34942 | *
|
34943 | * An `ElementRef` is backed by a render-specific element. In the browser, this is usually a DOM
|
34944 | * element.
|
34945 | *
|
34946 | * @security Permitting direct access to the DOM can make your application more vulnerable to
|
34947 | * XSS attacks. Carefully review any use of `ElementRef` in your code. For more detail, see the
|
34948 | * [Security Guide](https://g.co/ng/security).
|
34949 | *
|
34950 | * @publicApi
|
34951 | */
|
34952 | // Note: We don't expose things like `Injector`, `ViewContainer`, ... here,
|
34953 | // i.e. users have to ask for what they need. With that, we can build better analysis tools
|
34954 | // and could do better codegen in the future.
|
34955 | class ElementRef {
|
34956 | constructor(nativeElement) {
|
34957 | this.nativeElement = nativeElement;
|
34958 | }
|
34959 | }
|
34960 | /**
|
34961 | * @internal
|
34962 | * @nocollapse
|
34963 | */
|
34964 | ElementRef.__NG_ELEMENT_ID__ = SWITCH_ELEMENT_REF_FACTORY;
|
34965 |
|
34966 | /**
|
34967 | * @license
|
34968 | * Copyright Google LLC All Rights Reserved.
|
34969 | *
|
34970 | * Use of this source code is governed by an MIT-style license that can be
|
34971 | * found in the LICENSE file at https://angular.io/license
|
34972 | */
|
34973 | const Renderer2Interceptor = new InjectionToken('Renderer2Interceptor');
|
34974 | /**
|
34975 | * Creates and initializes a custom renderer that implements the `Renderer2` base class.
|
34976 | *
|
34977 | * @publicApi
|
34978 | */
|
34979 | class RendererFactory2 {
|
34980 | }
|
34981 | /**
|
34982 | * Extend this base class to implement custom rendering. By default, Angular
|
34983 | * renders a template into DOM. You can use custom rendering to intercept
|
34984 | * rendering calls, or to render to something other than DOM.
|
34985 | *
|
34986 | * Create your custom renderer using `RendererFactory2`.
|
34987 | *
|
34988 | * Use a custom renderer to bypass Angular's templating and
|
34989 | * make custom UI changes that can't be expressed declaratively.
|
34990 | * For example if you need to set a property or an attribute whose name is
|
34991 | * not statically known, use the `setProperty()` or
|
34992 | * `setAttribute()` method.
|
34993 | *
|
34994 | * @publicApi
|
34995 | */
|
34996 | class Renderer2 {
|
34997 | }
|
34998 | /**
|
34999 | * @internal
|
35000 | * @nocollapse
|
35001 | */
|
35002 | Renderer2.__NG_ELEMENT_ID__ = () => SWITCH_RENDERER2_FACTORY();
|
35003 | const SWITCH_RENDERER2_FACTORY__PRE_R3__ = noop;
|
35004 | const SWITCH_RENDERER2_FACTORY = SWITCH_RENDERER2_FACTORY__PRE_R3__;
|
35005 |
|
35006 | /**
|
35007 | * @license
|
35008 | * Copyright Google LLC All Rights Reserved.
|
35009 | *
|
35010 | * Use of this source code is governed by an MIT-style license that can be
|
35011 | * found in the LICENSE file at https://angular.io/license
|
35012 | */
|
35013 | /**
|
35014 | * Sanitizer is used by the views to sanitize potentially dangerous values.
|
35015 | *
|
35016 | * @publicApi
|
35017 | */
|
35018 | class Sanitizer {
|
35019 | }
|
35020 | /** @nocollapse */
|
35021 | Sanitizer.ɵprov = ɵɵdefineInjectable({
|
35022 | token: Sanitizer,
|
35023 | providedIn: 'root',
|
35024 | factory: () => null,
|
35025 | });
|
35026 |
|
35027 | /**
|
35028 | * @license
|
35029 | * Copyright Google LLC All Rights Reserved.
|
35030 | *
|
35031 | * Use of this source code is governed by an MIT-style license that can be
|
35032 | * found in the LICENSE file at https://angular.io/license
|
35033 | */
|
35034 | /**
|
35035 | * @description Represents the version of Angular
|
35036 | *
|
35037 | * @publicApi
|
35038 | */
|
35039 | class Version$1 {
|
35040 | constructor(full) {
|
35041 | this.full = full;
|
35042 | this.major = full.split('.')[0];
|
35043 | this.minor = full.split('.')[1];
|
35044 | this.patch = full.split('.').slice(2).join('.');
|
35045 | }
|
35046 | }
|
35047 | /**
|
35048 | * @publicApi
|
35049 | */
|
35050 | const VERSION$2 = new Version$1('11.2.0');
|
35051 |
|
35052 | /**
|
35053 | * @license
|
35054 | * Copyright Google LLC All Rights Reserved.
|
35055 | *
|
35056 | * Use of this source code is governed by an MIT-style license that can be
|
35057 | * found in the LICENSE file at https://angular.io/license
|
35058 | */
|
35059 | class DefaultIterableDifferFactory {
|
35060 | constructor() { }
|
35061 | supports(obj) {
|
35062 | return isListLikeIterable(obj);
|
35063 | }
|
35064 | create(trackByFn) {
|
35065 | return new DefaultIterableDiffer(trackByFn);
|
35066 | }
|
35067 | }
|
35068 | const trackByIdentity = (index, item) => item;
|
35069 | /**
|
35070 | * @deprecated v4.0.0 - Should not be part of public API.
|
35071 | * @publicApi
|
35072 | */
|
35073 | class DefaultIterableDiffer {
|
35074 | constructor(trackByFn) {
|
35075 | this.length = 0;
|
35076 | // Keeps track of the used records at any point in time (during & across `_check()` calls)
|
35077 | this._linkedRecords = null;
|
35078 | // Keeps track of the removed records at any point in time during `_check()` calls.
|
35079 | this._unlinkedRecords = null;
|
35080 | this._previousItHead = null;
|
35081 | this._itHead = null;
|
35082 | this._itTail = null;
|
35083 | this._additionsHead = null;
|
35084 | this._additionsTail = null;
|
35085 | this._movesHead = null;
|
35086 | this._movesTail = null;
|
35087 | this._removalsHead = null;
|
35088 | this._removalsTail = null;
|
35089 | // Keeps track of records where custom track by is the same, but item identity has changed
|
35090 | this._identityChangesHead = null;
|
35091 | this._identityChangesTail = null;
|
35092 | this._trackByFn = trackByFn || trackByIdentity;
|
35093 | }
|
35094 | forEachItem(fn) {
|
35095 | let record;
|
35096 | for (record = this._itHead; record !== null; record = record._next) {
|
35097 | fn(record);
|
35098 | }
|
35099 | }
|
35100 | forEachOperation(fn) {
|
35101 | let nextIt = this._itHead;
|
35102 | let nextRemove = this._removalsHead;
|
35103 | let addRemoveOffset = 0;
|
35104 | let moveOffsets = null;
|
35105 | while (nextIt || nextRemove) {
|
35106 | // Figure out which is the next record to process
|
35107 | // Order: remove, add, move
|
35108 | const record = !nextRemove ||
|
35109 | nextIt &&
|
35110 | nextIt.currentIndex <
|
35111 | getPreviousIndex(nextRemove, addRemoveOffset, moveOffsets) ?
|
35112 | nextIt :
|
35113 | nextRemove;
|
35114 | const adjPreviousIndex = getPreviousIndex(record, addRemoveOffset, moveOffsets);
|
35115 | const currentIndex = record.currentIndex;
|
35116 | // consume the item, and adjust the addRemoveOffset and update moveDistance if necessary
|
35117 | if (record === nextRemove) {
|
35118 | addRemoveOffset--;
|
35119 | nextRemove = nextRemove._nextRemoved;
|
35120 | }
|
35121 | else {
|
35122 | nextIt = nextIt._next;
|
35123 | if (record.previousIndex == null) {
|
35124 | addRemoveOffset++;
|
35125 | }
|
35126 | else {
|
35127 | // INVARIANT: currentIndex < previousIndex
|
35128 | if (!moveOffsets)
|
35129 | moveOffsets = [];
|
35130 | const localMovePreviousIndex = adjPreviousIndex - addRemoveOffset;
|
35131 | const localCurrentIndex = currentIndex - addRemoveOffset;
|
35132 | if (localMovePreviousIndex != localCurrentIndex) {
|
35133 | for (let i = 0; i < localMovePreviousIndex; i++) {
|
35134 | const offset = i < moveOffsets.length ? moveOffsets[i] : (moveOffsets[i] = 0);
|
35135 | const index = offset + i;
|
35136 | if (localCurrentIndex <= index && index < localMovePreviousIndex) {
|
35137 | moveOffsets[i] = offset + 1;
|
35138 | }
|
35139 | }
|
35140 | const previousIndex = record.previousIndex;
|
35141 | moveOffsets[previousIndex] = localCurrentIndex - localMovePreviousIndex;
|
35142 | }
|
35143 | }
|
35144 | }
|
35145 | if (adjPreviousIndex !== currentIndex) {
|
35146 | fn(record, adjPreviousIndex, currentIndex);
|
35147 | }
|
35148 | }
|
35149 | }
|
35150 | forEachPreviousItem(fn) {
|
35151 | let record;
|
35152 | for (record = this._previousItHead; record !== null; record = record._nextPrevious) {
|
35153 | fn(record);
|
35154 | }
|
35155 | }
|
35156 | forEachAddedItem(fn) {
|
35157 | let record;
|
35158 | for (record = this._additionsHead; record !== null; record = record._nextAdded) {
|
35159 | fn(record);
|
35160 | }
|
35161 | }
|
35162 | forEachMovedItem(fn) {
|
35163 | let record;
|
35164 | for (record = this._movesHead; record !== null; record = record._nextMoved) {
|
35165 | fn(record);
|
35166 | }
|
35167 | }
|
35168 | forEachRemovedItem(fn) {
|
35169 | let record;
|
35170 | for (record = this._removalsHead; record !== null; record = record._nextRemoved) {
|
35171 | fn(record);
|
35172 | }
|
35173 | }
|
35174 | forEachIdentityChange(fn) {
|
35175 | let record;
|
35176 | for (record = this._identityChangesHead; record !== null; record = record._nextIdentityChange) {
|
35177 | fn(record);
|
35178 | }
|
35179 | }
|
35180 | diff(collection) {
|
35181 | if (collection == null)
|
35182 | collection = [];
|
35183 | if (!isListLikeIterable(collection)) {
|
35184 | throw new Error(`Error trying to diff '${stringify$1(collection)}'. Only arrays and iterables are allowed`);
|
35185 | }
|
35186 | if (this.check(collection)) {
|
35187 | return this;
|
35188 | }
|
35189 | else {
|
35190 | return null;
|
35191 | }
|
35192 | }
|
35193 | onDestroy() { }
|
35194 | check(collection) {
|
35195 | this._reset();
|
35196 | let record = this._itHead;
|
35197 | let mayBeDirty = false;
|
35198 | let index;
|
35199 | let item;
|
35200 | let itemTrackBy;
|
35201 | if (Array.isArray(collection)) {
|
35202 | this.length = collection.length;
|
35203 | for (let index = 0; index < this.length; index++) {
|
35204 | item = collection[index];
|
35205 | itemTrackBy = this._trackByFn(index, item);
|
35206 | if (record === null || !Object.is(record.trackById, itemTrackBy)) {
|
35207 | record = this._mismatch(record, item, itemTrackBy, index);
|
35208 | mayBeDirty = true;
|
35209 | }
|
35210 | else {
|
35211 | if (mayBeDirty) {
|
35212 | // TODO(misko): can we limit this to duplicates only?
|
35213 | record = this._verifyReinsertion(record, item, itemTrackBy, index);
|
35214 | }
|
35215 | if (!Object.is(record.item, item))
|
35216 | this._addIdentityChange(record, item);
|
35217 | }
|
35218 | record = record._next;
|
35219 | }
|
35220 | }
|
35221 | else {
|
35222 | index = 0;
|
35223 | iterateListLike(collection, (item) => {
|
35224 | itemTrackBy = this._trackByFn(index, item);
|
35225 | if (record === null || !Object.is(record.trackById, itemTrackBy)) {
|
35226 | record = this._mismatch(record, item, itemTrackBy, index);
|
35227 | mayBeDirty = true;
|
35228 | }
|
35229 | else {
|
35230 | if (mayBeDirty) {
|
35231 | // TODO(misko): can we limit this to duplicates only?
|
35232 | record = this._verifyReinsertion(record, item, itemTrackBy, index);
|
35233 | }
|
35234 | if (!Object.is(record.item, item))
|
35235 | this._addIdentityChange(record, item);
|
35236 | }
|
35237 | record = record._next;
|
35238 | index++;
|
35239 | });
|
35240 | this.length = index;
|
35241 | }
|
35242 | this._truncate(record);
|
35243 | this.collection = collection;
|
35244 | return this.isDirty;
|
35245 | }
|
35246 | /* CollectionChanges is considered dirty if it has any additions, moves, removals, or identity
|
35247 | * changes.
|
35248 | */
|
35249 | get isDirty() {
|
35250 | return this._additionsHead !== null || this._movesHead !== null ||
|
35251 | this._removalsHead !== null || this._identityChangesHead !== null;
|
35252 | }
|
35253 | /**
|
35254 | * Reset the state of the change objects to show no changes. This means set previousKey to
|
35255 | * currentKey, and clear all of the queues (additions, moves, removals).
|
35256 | * Set the previousIndexes of moved and added items to their currentIndexes
|
35257 | * Reset the list of additions, moves and removals
|
35258 | *
|
35259 | * @internal
|
35260 | */
|
35261 | _reset() {
|
35262 | if (this.isDirty) {
|
35263 | let record;
|
35264 | for (record = this._previousItHead = this._itHead; record !== null; record = record._next) {
|
35265 | record._nextPrevious = record._next;
|
35266 | }
|
35267 | for (record = this._additionsHead; record !== null; record = record._nextAdded) {
|
35268 | record.previousIndex = record.currentIndex;
|
35269 | }
|
35270 | this._additionsHead = this._additionsTail = null;
|
35271 | for (record = this._movesHead; record !== null; record = record._nextMoved) {
|
35272 | record.previousIndex = record.currentIndex;
|
35273 | }
|
35274 | this._movesHead = this._movesTail = null;
|
35275 | this._removalsHead = this._removalsTail = null;
|
35276 | this._identityChangesHead = this._identityChangesTail = null;
|
35277 | // TODO(vicb): when assert gets supported
|
35278 | // assert(!this.isDirty);
|
35279 | }
|
35280 | }
|
35281 | /**
|
35282 | * This is the core function which handles differences between collections.
|
35283 | *
|
35284 | * - `record` is the record which we saw at this position last time. If null then it is a new
|
35285 | * item.
|
35286 | * - `item` is the current item in the collection
|
35287 | * - `index` is the position of the item in the collection
|
35288 | *
|
35289 | * @internal
|
35290 | */
|
35291 | _mismatch(record, item, itemTrackBy, index) {
|
35292 | // The previous record after which we will append the current one.
|
35293 | let previousRecord;
|
35294 | if (record === null) {
|
35295 | previousRecord = this._itTail;
|
35296 | }
|
35297 | else {
|
35298 | previousRecord = record._prev;
|
35299 | // Remove the record from the collection since we know it does not match the item.
|
35300 | this._remove(record);
|
35301 | }
|
35302 | // See if we have evicted the item, which used to be at some anterior position of _itHead list.
|
35303 | record = this._unlinkedRecords === null ? null : this._unlinkedRecords.get(itemTrackBy, null);
|
35304 | if (record !== null) {
|
35305 | // It is an item which we have evicted earlier: reinsert it back into the list.
|
35306 | // But first we need to check if identity changed, so we can update in view if necessary.
|
35307 | if (!Object.is(record.item, item))
|
35308 | this._addIdentityChange(record, item);
|
35309 | this._reinsertAfter(record, previousRecord, index);
|
35310 | }
|
35311 | else {
|
35312 | // Attempt to see if the item is at some posterior position of _itHead list.
|
35313 | record = this._linkedRecords === null ? null : this._linkedRecords.get(itemTrackBy, index);
|
35314 | if (record !== null) {
|
35315 | // We have the item in _itHead at/after `index` position. We need to move it forward in the
|
35316 | // collection.
|
35317 | // But first we need to check if identity changed, so we can update in view if necessary.
|
35318 | if (!Object.is(record.item, item))
|
35319 | this._addIdentityChange(record, item);
|
35320 | this._moveAfter(record, previousRecord, index);
|
35321 | }
|
35322 | else {
|
35323 | // It is a new item: add it.
|
35324 | record =
|
35325 | this._addAfter(new IterableChangeRecord_(item, itemTrackBy), previousRecord, index);
|
35326 | }
|
35327 | }
|
35328 | return record;
|
35329 | }
|
35330 | /**
|
35331 | * This check is only needed if an array contains duplicates. (Short circuit of nothing dirty)
|
35332 | *
|
35333 | * Use case: `[a, a]` => `[b, a, a]`
|
35334 | *
|
35335 | * If we did not have this check then the insertion of `b` would:
|
35336 | * 1) evict first `a`
|
35337 | * 2) insert `b` at `0` index.
|
35338 | * 3) leave `a` at index `1` as is. <-- this is wrong!
|
35339 | * 3) reinsert `a` at index 2. <-- this is wrong!
|
35340 | *
|
35341 | * The correct behavior is:
|
35342 | * 1) evict first `a`
|
35343 | * 2) insert `b` at `0` index.
|
35344 | * 3) reinsert `a` at index 1.
|
35345 | * 3) move `a` at from `1` to `2`.
|
35346 | *
|
35347 | *
|
35348 | * Double check that we have not evicted a duplicate item. We need to check if the item type may
|
35349 | * have already been removed:
|
35350 | * The insertion of b will evict the first 'a'. If we don't reinsert it now it will be reinserted
|
35351 | * at the end. Which will show up as the two 'a's switching position. This is incorrect, since a
|
35352 | * better way to think of it is as insert of 'b' rather then switch 'a' with 'b' and then add 'a'
|
35353 | * at the end.
|
35354 | *
|
35355 | * @internal
|
35356 | */
|
35357 | _verifyReinsertion(record, item, itemTrackBy, index) {
|
35358 | let reinsertRecord = this._unlinkedRecords === null ? null : this._unlinkedRecords.get(itemTrackBy, null);
|
35359 | if (reinsertRecord !== null) {
|
35360 | record = this._reinsertAfter(reinsertRecord, record._prev, index);
|
35361 | }
|
35362 | else if (record.currentIndex != index) {
|
35363 | record.currentIndex = index;
|
35364 | this._addToMoves(record, index);
|
35365 | }
|
35366 | return record;
|
35367 | }
|
35368 | /**
|
35369 | * Get rid of any excess {@link IterableChangeRecord_}s from the previous collection
|
35370 | *
|
35371 | * - `record` The first excess {@link IterableChangeRecord_}.
|
35372 | *
|
35373 | * @internal
|
35374 | */
|
35375 | _truncate(record) {
|
35376 | // Anything after that needs to be removed;
|
35377 | while (record !== null) {
|
35378 | const nextRecord = record._next;
|
35379 | this._addToRemovals(this._unlink(record));
|
35380 | record = nextRecord;
|
35381 | }
|
35382 | if (this._unlinkedRecords !== null) {
|
35383 | this._unlinkedRecords.clear();
|
35384 | }
|
35385 | if (this._additionsTail !== null) {
|
35386 | this._additionsTail._nextAdded = null;
|
35387 | }
|
35388 | if (this._movesTail !== null) {
|
35389 | this._movesTail._nextMoved = null;
|
35390 | }
|
35391 | if (this._itTail !== null) {
|
35392 | this._itTail._next = null;
|
35393 | }
|
35394 | if (this._removalsTail !== null) {
|
35395 | this._removalsTail._nextRemoved = null;
|
35396 | }
|
35397 | if (this._identityChangesTail !== null) {
|
35398 | this._identityChangesTail._nextIdentityChange = null;
|
35399 | }
|
35400 | }
|
35401 | /** @internal */
|
35402 | _reinsertAfter(record, prevRecord, index) {
|
35403 | if (this._unlinkedRecords !== null) {
|
35404 | this._unlinkedRecords.remove(record);
|
35405 | }
|
35406 | const prev = record._prevRemoved;
|
35407 | const next = record._nextRemoved;
|
35408 | if (prev === null) {
|
35409 | this._removalsHead = next;
|
35410 | }
|
35411 | else {
|
35412 | prev._nextRemoved = next;
|
35413 | }
|
35414 | if (next === null) {
|
35415 | this._removalsTail = prev;
|
35416 | }
|
35417 | else {
|
35418 | next._prevRemoved = prev;
|
35419 | }
|
35420 | this._insertAfter(record, prevRecord, index);
|
35421 | this._addToMoves(record, index);
|
35422 | return record;
|
35423 | }
|
35424 | /** @internal */
|
35425 | _moveAfter(record, prevRecord, index) {
|
35426 | this._unlink(record);
|
35427 | this._insertAfter(record, prevRecord, index);
|
35428 | this._addToMoves(record, index);
|
35429 | return record;
|
35430 | }
|
35431 | /** @internal */
|
35432 | _addAfter(record, prevRecord, index) {
|
35433 | this._insertAfter(record, prevRecord, index);
|
35434 | if (this._additionsTail === null) {
|
35435 | // TODO(vicb):
|
35436 | // assert(this._additionsHead === null);
|
35437 | this._additionsTail = this._additionsHead = record;
|
35438 | }
|
35439 | else {
|
35440 | // TODO(vicb):
|
35441 | // assert(_additionsTail._nextAdded === null);
|
35442 | // assert(record._nextAdded === null);
|
35443 | this._additionsTail = this._additionsTail._nextAdded = record;
|
35444 | }
|
35445 | return record;
|
35446 | }
|
35447 | /** @internal */
|
35448 | _insertAfter(record, prevRecord, index) {
|
35449 | // TODO(vicb):
|
35450 | // assert(record != prevRecord);
|
35451 | // assert(record._next === null);
|
35452 | // assert(record._prev === null);
|
35453 | const next = prevRecord === null ? this._itHead : prevRecord._next;
|
35454 | // TODO(vicb):
|
35455 | // assert(next != record);
|
35456 | // assert(prevRecord != record);
|
35457 | record._next = next;
|
35458 | record._prev = prevRecord;
|
35459 | if (next === null) {
|
35460 | this._itTail = record;
|
35461 | }
|
35462 | else {
|
35463 | next._prev = record;
|
35464 | }
|
35465 | if (prevRecord === null) {
|
35466 | this._itHead = record;
|
35467 | }
|
35468 | else {
|
35469 | prevRecord._next = record;
|
35470 | }
|
35471 | if (this._linkedRecords === null) {
|
35472 | this._linkedRecords = new _DuplicateMap();
|
35473 | }
|
35474 | this._linkedRecords.put(record);
|
35475 | record.currentIndex = index;
|
35476 | return record;
|
35477 | }
|
35478 | /** @internal */
|
35479 | _remove(record) {
|
35480 | return this._addToRemovals(this._unlink(record));
|
35481 | }
|
35482 | /** @internal */
|
35483 | _unlink(record) {
|
35484 | if (this._linkedRecords !== null) {
|
35485 | this._linkedRecords.remove(record);
|
35486 | }
|
35487 | const prev = record._prev;
|
35488 | const next = record._next;
|
35489 | // TODO(vicb):
|
35490 | // assert((record._prev = null) === null);
|
35491 | // assert((record._next = null) === null);
|
35492 | if (prev === null) {
|
35493 | this._itHead = next;
|
35494 | }
|
35495 | else {
|
35496 | prev._next = next;
|
35497 | }
|
35498 | if (next === null) {
|
35499 | this._itTail = prev;
|
35500 | }
|
35501 | else {
|
35502 | next._prev = prev;
|
35503 | }
|
35504 | return record;
|
35505 | }
|
35506 | /** @internal */
|
35507 | _addToMoves(record, toIndex) {
|
35508 | // TODO(vicb):
|
35509 | // assert(record._nextMoved === null);
|
35510 | if (record.previousIndex === toIndex) {
|
35511 | return record;
|
35512 | }
|
35513 | if (this._movesTail === null) {
|
35514 | // TODO(vicb):
|
35515 | // assert(_movesHead === null);
|
35516 | this._movesTail = this._movesHead = record;
|
35517 | }
|
35518 | else {
|
35519 | // TODO(vicb):
|
35520 | // assert(_movesTail._nextMoved === null);
|
35521 | this._movesTail = this._movesTail._nextMoved = record;
|
35522 | }
|
35523 | return record;
|
35524 | }
|
35525 | _addToRemovals(record) {
|
35526 | if (this._unlinkedRecords === null) {
|
35527 | this._unlinkedRecords = new _DuplicateMap();
|
35528 | }
|
35529 | this._unlinkedRecords.put(record);
|
35530 | record.currentIndex = null;
|
35531 | record._nextRemoved = null;
|
35532 | if (this._removalsTail === null) {
|
35533 | // TODO(vicb):
|
35534 | // assert(_removalsHead === null);
|
35535 | this._removalsTail = this._removalsHead = record;
|
35536 | record._prevRemoved = null;
|
35537 | }
|
35538 | else {
|
35539 | // TODO(vicb):
|
35540 | // assert(_removalsTail._nextRemoved === null);
|
35541 | // assert(record._nextRemoved === null);
|
35542 | record._prevRemoved = this._removalsTail;
|
35543 | this._removalsTail = this._removalsTail._nextRemoved = record;
|
35544 | }
|
35545 | return record;
|
35546 | }
|
35547 | /** @internal */
|
35548 | _addIdentityChange(record, item) {
|
35549 | record.item = item;
|
35550 | if (this._identityChangesTail === null) {
|
35551 | this._identityChangesTail = this._identityChangesHead = record;
|
35552 | }
|
35553 | else {
|
35554 | this._identityChangesTail = this._identityChangesTail._nextIdentityChange = record;
|
35555 | }
|
35556 | return record;
|
35557 | }
|
35558 | }
|
35559 | class IterableChangeRecord_ {
|
35560 | constructor(item, trackById) {
|
35561 | this.item = item;
|
35562 | this.trackById = trackById;
|
35563 | this.currentIndex = null;
|
35564 | this.previousIndex = null;
|
35565 | /** @internal */
|
35566 | this._nextPrevious = null;
|
35567 | /** @internal */
|
35568 | this._prev = null;
|
35569 | /** @internal */
|
35570 | this._next = null;
|
35571 | /** @internal */
|
35572 | this._prevDup = null;
|
35573 | /** @internal */
|
35574 | this._nextDup = null;
|
35575 | /** @internal */
|
35576 | this._prevRemoved = null;
|
35577 | /** @internal */
|
35578 | this._nextRemoved = null;
|
35579 | /** @internal */
|
35580 | this._nextAdded = null;
|
35581 | /** @internal */
|
35582 | this._nextMoved = null;
|
35583 | /** @internal */
|
35584 | this._nextIdentityChange = null;
|
35585 | }
|
35586 | }
|
35587 | // A linked list of IterableChangeRecords with the same IterableChangeRecord_.item
|
35588 | class _DuplicateItemRecordList {
|
35589 | constructor() {
|
35590 | /** @internal */
|
35591 | this._head = null;
|
35592 | /** @internal */
|
35593 | this._tail = null;
|
35594 | }
|
35595 | /**
|
35596 | * Append the record to the list of duplicates.
|
35597 | *
|
35598 | * Note: by design all records in the list of duplicates hold the same value in record.item.
|
35599 | */
|
35600 | add(record) {
|
35601 | if (this._head === null) {
|
35602 | this._head = this._tail = record;
|
35603 | record._nextDup = null;
|
35604 | record._prevDup = null;
|
35605 | }
|
35606 | else {
|
35607 | // TODO(vicb):
|
35608 | // assert(record.item == _head.item ||
|
35609 | // record.item is num && record.item.isNaN && _head.item is num && _head.item.isNaN);
|
35610 | this._tail._nextDup = record;
|
35611 | record._prevDup = this._tail;
|
35612 | record._nextDup = null;
|
35613 | this._tail = record;
|
35614 | }
|
35615 | }
|
35616 | // Returns a IterableChangeRecord_ having IterableChangeRecord_.trackById == trackById and
|
35617 | // IterableChangeRecord_.currentIndex >= atOrAfterIndex
|
35618 | get(trackById, atOrAfterIndex) {
|
35619 | let record;
|
35620 | for (record = this._head; record !== null; record = record._nextDup) {
|
35621 | if ((atOrAfterIndex === null || atOrAfterIndex <= record.currentIndex) &&
|
35622 | Object.is(record.trackById, trackById)) {
|
35623 | return record;
|
35624 | }
|
35625 | }
|
35626 | return null;
|
35627 | }
|
35628 | /**
|
35629 | * Remove one {@link IterableChangeRecord_} from the list of duplicates.
|
35630 | *
|
35631 | * Returns whether the list of duplicates is empty.
|
35632 | */
|
35633 | remove(record) {
|
35634 | // TODO(vicb):
|
35635 | // assert(() {
|
35636 | // // verify that the record being removed is in the list.
|
35637 | // for (IterableChangeRecord_ cursor = _head; cursor != null; cursor = cursor._nextDup) {
|
35638 | // if (identical(cursor, record)) return true;
|
35639 | // }
|
35640 | // return false;
|
35641 | //});
|
35642 | const prev = record._prevDup;
|
35643 | const next = record._nextDup;
|
35644 | if (prev === null) {
|
35645 | this._head = next;
|
35646 | }
|
35647 | else {
|
35648 | prev._nextDup = next;
|
35649 | }
|
35650 | if (next === null) {
|
35651 | this._tail = prev;
|
35652 | }
|
35653 | else {
|
35654 | next._prevDup = prev;
|
35655 | }
|
35656 | return this._head === null;
|
35657 | }
|
35658 | }
|
35659 | class _DuplicateMap {
|
35660 | constructor() {
|
35661 | this.map = new Map();
|
35662 | }
|
35663 | put(record) {
|
35664 | const key = record.trackById;
|
35665 | let duplicates = this.map.get(key);
|
35666 | if (!duplicates) {
|
35667 | duplicates = new _DuplicateItemRecordList();
|
35668 | this.map.set(key, duplicates);
|
35669 | }
|
35670 | duplicates.add(record);
|
35671 | }
|
35672 | /**
|
35673 | * Retrieve the `value` using key. Because the IterableChangeRecord_ value may be one which we
|
35674 | * have already iterated over, we use the `atOrAfterIndex` to pretend it is not there.
|
35675 | *
|
35676 | * Use case: `[a, b, c, a, a]` if we are at index `3` which is the second `a` then asking if we
|
35677 | * have any more `a`s needs to return the second `a`.
|
35678 | */
|
35679 | get(trackById, atOrAfterIndex) {
|
35680 | const key = trackById;
|
35681 | const recordList = this.map.get(key);
|
35682 | return recordList ? recordList.get(trackById, atOrAfterIndex) : null;
|
35683 | }
|
35684 | /**
|
35685 | * Removes a {@link IterableChangeRecord_} from the list of duplicates.
|
35686 | *
|
35687 | * The list of duplicates also is removed from the map if it gets empty.
|
35688 | */
|
35689 | remove(record) {
|
35690 | const key = record.trackById;
|
35691 | const recordList = this.map.get(key);
|
35692 | // Remove the list of duplicates when it gets empty
|
35693 | if (recordList.remove(record)) {
|
35694 | this.map.delete(key);
|
35695 | }
|
35696 | return record;
|
35697 | }
|
35698 | get isEmpty() {
|
35699 | return this.map.size === 0;
|
35700 | }
|
35701 | clear() {
|
35702 | this.map.clear();
|
35703 | }
|
35704 | }
|
35705 | function getPreviousIndex(item, addRemoveOffset, moveOffsets) {
|
35706 | const previousIndex = item.previousIndex;
|
35707 | if (previousIndex === null)
|
35708 | return previousIndex;
|
35709 | let moveOffset = 0;
|
35710 | if (moveOffsets && previousIndex < moveOffsets.length) {
|
35711 | moveOffset = moveOffsets[previousIndex];
|
35712 | }
|
35713 | return previousIndex + addRemoveOffset + moveOffset;
|
35714 | }
|
35715 |
|
35716 | /**
|
35717 | * @license
|
35718 | * Copyright Google LLC All Rights Reserved.
|
35719 | *
|
35720 | * Use of this source code is governed by an MIT-style license that can be
|
35721 | * found in the LICENSE file at https://angular.io/license
|
35722 | */
|
35723 | class DefaultKeyValueDifferFactory {
|
35724 | constructor() { }
|
35725 | supports(obj) {
|
35726 | return obj instanceof Map || isJsObject(obj);
|
35727 | }
|
35728 | create() {
|
35729 | return new DefaultKeyValueDiffer();
|
35730 | }
|
35731 | }
|
35732 | class DefaultKeyValueDiffer {
|
35733 | constructor() {
|
35734 | this._records = new Map();
|
35735 | this._mapHead = null;
|
35736 | // _appendAfter is used in the check loop
|
35737 | this._appendAfter = null;
|
35738 | this._previousMapHead = null;
|
35739 | this._changesHead = null;
|
35740 | this._changesTail = null;
|
35741 | this._additionsHead = null;
|
35742 | this._additionsTail = null;
|
35743 | this._removalsHead = null;
|
35744 | this._removalsTail = null;
|
35745 | }
|
35746 | get isDirty() {
|
35747 | return this._additionsHead !== null || this._changesHead !== null ||
|
35748 | this._removalsHead !== null;
|
35749 | }
|
35750 | forEachItem(fn) {
|
35751 | let record;
|
35752 | for (record = this._mapHead; record !== null; record = record._next) {
|
35753 | fn(record);
|
35754 | }
|
35755 | }
|
35756 | forEachPreviousItem(fn) {
|
35757 | let record;
|
35758 | for (record = this._previousMapHead; record !== null; record = record._nextPrevious) {
|
35759 | fn(record);
|
35760 | }
|
35761 | }
|
35762 | forEachChangedItem(fn) {
|
35763 | let record;
|
35764 | for (record = this._changesHead; record !== null; record = record._nextChanged) {
|
35765 | fn(record);
|
35766 | }
|
35767 | }
|
35768 | forEachAddedItem(fn) {
|
35769 | let record;
|
35770 | for (record = this._additionsHead; record !== null; record = record._nextAdded) {
|
35771 | fn(record);
|
35772 | }
|
35773 | }
|
35774 | forEachRemovedItem(fn) {
|
35775 | let record;
|
35776 | for (record = this._removalsHead; record !== null; record = record._nextRemoved) {
|
35777 | fn(record);
|
35778 | }
|
35779 | }
|
35780 | diff(map) {
|
35781 | if (!map) {
|
35782 | map = new Map();
|
35783 | }
|
35784 | else if (!(map instanceof Map || isJsObject(map))) {
|
35785 | throw new Error(`Error trying to diff '${stringify$1(map)}'. Only maps and objects are allowed`);
|
35786 | }
|
35787 | return this.check(map) ? this : null;
|
35788 | }
|
35789 | onDestroy() { }
|
35790 | /**
|
35791 | * Check the current state of the map vs the previous.
|
35792 | * The algorithm is optimised for when the keys do no change.
|
35793 | */
|
35794 | check(map) {
|
35795 | this._reset();
|
35796 | let insertBefore = this._mapHead;
|
35797 | this._appendAfter = null;
|
35798 | this._forEach(map, (value, key) => {
|
35799 | if (insertBefore && insertBefore.key === key) {
|
35800 | this._maybeAddToChanges(insertBefore, value);
|
35801 | this._appendAfter = insertBefore;
|
35802 | insertBefore = insertBefore._next;
|
35803 | }
|
35804 | else {
|
35805 | const record = this._getOrCreateRecordForKey(key, value);
|
35806 | insertBefore = this._insertBeforeOrAppend(insertBefore, record);
|
35807 | }
|
35808 | });
|
35809 | // Items remaining at the end of the list have been deleted
|
35810 | if (insertBefore) {
|
35811 | if (insertBefore._prev) {
|
35812 | insertBefore._prev._next = null;
|
35813 | }
|
35814 | this._removalsHead = insertBefore;
|
35815 | for (let record = insertBefore; record !== null; record = record._nextRemoved) {
|
35816 | if (record === this._mapHead) {
|
35817 | this._mapHead = null;
|
35818 | }
|
35819 | this._records.delete(record.key);
|
35820 | record._nextRemoved = record._next;
|
35821 | record.previousValue = record.currentValue;
|
35822 | record.currentValue = null;
|
35823 | record._prev = null;
|
35824 | record._next = null;
|
35825 | }
|
35826 | }
|
35827 | // Make sure tails have no next records from previous runs
|
35828 | if (this._changesTail)
|
35829 | this._changesTail._nextChanged = null;
|
35830 | if (this._additionsTail)
|
35831 | this._additionsTail._nextAdded = null;
|
35832 | return this.isDirty;
|
35833 | }
|
35834 | /**
|
35835 | * Inserts a record before `before` or append at the end of the list when `before` is null.
|
35836 | *
|
35837 | * Notes:
|
35838 | * - This method appends at `this._appendAfter`,
|
35839 | * - This method updates `this._appendAfter`,
|
35840 | * - The return value is the new value for the insertion pointer.
|
35841 | */
|
35842 | _insertBeforeOrAppend(before, record) {
|
35843 | if (before) {
|
35844 | const prev = before._prev;
|
35845 | record._next = before;
|
35846 | record._prev = prev;
|
35847 | before._prev = record;
|
35848 | if (prev) {
|
35849 | prev._next = record;
|
35850 | }
|
35851 | if (before === this._mapHead) {
|
35852 | this._mapHead = record;
|
35853 | }
|
35854 | this._appendAfter = before;
|
35855 | return before;
|
35856 | }
|
35857 | if (this._appendAfter) {
|
35858 | this._appendAfter._next = record;
|
35859 | record._prev = this._appendAfter;
|
35860 | }
|
35861 | else {
|
35862 | this._mapHead = record;
|
35863 | }
|
35864 | this._appendAfter = record;
|
35865 | return null;
|
35866 | }
|
35867 | _getOrCreateRecordForKey(key, value) {
|
35868 | if (this._records.has(key)) {
|
35869 | const record = this._records.get(key);
|
35870 | this._maybeAddToChanges(record, value);
|
35871 | const prev = record._prev;
|
35872 | const next = record._next;
|
35873 | if (prev) {
|
35874 | prev._next = next;
|
35875 | }
|
35876 | if (next) {
|
35877 | next._prev = prev;
|
35878 | }
|
35879 | record._next = null;
|
35880 | record._prev = null;
|
35881 | return record;
|
35882 | }
|
35883 | const record = new KeyValueChangeRecord_(key);
|
35884 | this._records.set(key, record);
|
35885 | record.currentValue = value;
|
35886 | this._addToAdditions(record);
|
35887 | return record;
|
35888 | }
|
35889 | /** @internal */
|
35890 | _reset() {
|
35891 | if (this.isDirty) {
|
35892 | let record;
|
35893 | // let `_previousMapHead` contain the state of the map before the changes
|
35894 | this._previousMapHead = this._mapHead;
|
35895 | for (record = this._previousMapHead; record !== null; record = record._next) {
|
35896 | record._nextPrevious = record._next;
|
35897 | }
|
35898 | // Update `record.previousValue` with the value of the item before the changes
|
35899 | // We need to update all changed items (that's those which have been added and changed)
|
35900 | for (record = this._changesHead; record !== null; record = record._nextChanged) {
|
35901 | record.previousValue = record.currentValue;
|
35902 | }
|
35903 | for (record = this._additionsHead; record != null; record = record._nextAdded) {
|
35904 | record.previousValue = record.currentValue;
|
35905 | }
|
35906 | this._changesHead = this._changesTail = null;
|
35907 | this._additionsHead = this._additionsTail = null;
|
35908 | this._removalsHead = null;
|
35909 | }
|
35910 | }
|
35911 | // Add the record or a given key to the list of changes only when the value has actually changed
|
35912 | _maybeAddToChanges(record, newValue) {
|
35913 | if (!Object.is(newValue, record.currentValue)) {
|
35914 | record.previousValue = record.currentValue;
|
35915 | record.currentValue = newValue;
|
35916 | this._addToChanges(record);
|
35917 | }
|
35918 | }
|
35919 | _addToAdditions(record) {
|
35920 | if (this._additionsHead === null) {
|
35921 | this._additionsHead = this._additionsTail = record;
|
35922 | }
|
35923 | else {
|
35924 | this._additionsTail._nextAdded = record;
|
35925 | this._additionsTail = record;
|
35926 | }
|
35927 | }
|
35928 | _addToChanges(record) {
|
35929 | if (this._changesHead === null) {
|
35930 | this._changesHead = this._changesTail = record;
|
35931 | }
|
35932 | else {
|
35933 | this._changesTail._nextChanged = record;
|
35934 | this._changesTail = record;
|
35935 | }
|
35936 | }
|
35937 | /** @internal */
|
35938 | _forEach(obj, fn) {
|
35939 | if (obj instanceof Map) {
|
35940 | obj.forEach(fn);
|
35941 | }
|
35942 | else {
|
35943 | Object.keys(obj).forEach(k => fn(obj[k], k));
|
35944 | }
|
35945 | }
|
35946 | }
|
35947 | class KeyValueChangeRecord_ {
|
35948 | constructor(key) {
|
35949 | this.key = key;
|
35950 | this.previousValue = null;
|
35951 | this.currentValue = null;
|
35952 | /** @internal */
|
35953 | this._nextPrevious = null;
|
35954 | /** @internal */
|
35955 | this._next = null;
|
35956 | /** @internal */
|
35957 | this._prev = null;
|
35958 | /** @internal */
|
35959 | this._nextAdded = null;
|
35960 | /** @internal */
|
35961 | this._nextRemoved = null;
|
35962 | /** @internal */
|
35963 | this._nextChanged = null;
|
35964 | }
|
35965 | }
|
35966 |
|
35967 | /**
|
35968 | * @license
|
35969 | * Copyright Google LLC All Rights Reserved.
|
35970 | *
|
35971 | * Use of this source code is governed by an MIT-style license that can be
|
35972 | * found in the LICENSE file at https://angular.io/license
|
35973 | */
|
35974 | function defaultIterableDiffersFactory() {
|
35975 | return new IterableDiffers([new DefaultIterableDifferFactory()]);
|
35976 | }
|
35977 | /**
|
35978 | * A repository of different iterable diffing strategies used by NgFor, NgClass, and others.
|
35979 | *
|
35980 | * @publicApi
|
35981 | */
|
35982 | class IterableDiffers {
|
35983 | constructor(factories) {
|
35984 | this.factories = factories;
|
35985 | }
|
35986 | static create(factories, parent) {
|
35987 | if (parent != null) {
|
35988 | const copied = parent.factories.slice();
|
35989 | factories = factories.concat(copied);
|
35990 | }
|
35991 | return new IterableDiffers(factories);
|
35992 | }
|
35993 | /**
|
35994 | * Takes an array of {@link IterableDifferFactory} and returns a provider used to extend the
|
35995 | * inherited {@link IterableDiffers} instance with the provided factories and return a new
|
35996 | * {@link IterableDiffers} instance.
|
35997 | *
|
35998 | * @usageNotes
|
35999 | * ### Example
|
36000 | *
|
36001 | * The following example shows how to extend an existing list of factories,
|
36002 | * which will only be applied to the injector for this component and its children.
|
36003 | * This step is all that's required to make a new {@link IterableDiffer} available.
|
36004 | *
|
36005 | * ```
|
36006 | * @Component({
|
36007 | * viewProviders: [
|
36008 | * IterableDiffers.extend([new ImmutableListDiffer()])
|
36009 | * ]
|
36010 | * })
|
36011 | * ```
|
36012 | */
|
36013 | static extend(factories) {
|
36014 | return {
|
36015 | provide: IterableDiffers,
|
36016 | useFactory: (parent) => {
|
36017 | // if parent is null, it means that we are in the root injector and we have just overridden
|
36018 | // the default injection mechanism for IterableDiffers, in such a case just assume
|
36019 | // `defaultIterableDiffersFactory`.
|
36020 | return IterableDiffers.create(factories, parent || defaultIterableDiffersFactory());
|
36021 | },
|
36022 | // Dependency technically isn't optional, but we can provide a better error message this way.
|
36023 | deps: [[IterableDiffers, new SkipSelf(), new Optional()]]
|
36024 | };
|
36025 | }
|
36026 | find(iterable) {
|
36027 | const factory = this.factories.find(f => f.supports(iterable));
|
36028 | if (factory != null) {
|
36029 | return factory;
|
36030 | }
|
36031 | else {
|
36032 | throw new Error(`Cannot find a differ supporting object '${iterable}' of type '${getTypeNameForDebugging(iterable)}'`);
|
36033 | }
|
36034 | }
|
36035 | }
|
36036 | /** @nocollapse */
|
36037 | IterableDiffers.ɵprov = ɵɵdefineInjectable({ token: IterableDiffers, providedIn: 'root', factory: defaultIterableDiffersFactory });
|
36038 | function getTypeNameForDebugging(type) {
|
36039 | return type['name'] || typeof type;
|
36040 | }
|
36041 |
|
36042 | /**
|
36043 | * @license
|
36044 | * Copyright Google LLC All Rights Reserved.
|
36045 | *
|
36046 | * Use of this source code is governed by an MIT-style license that can be
|
36047 | * found in the LICENSE file at https://angular.io/license
|
36048 | */
|
36049 | function defaultKeyValueDiffersFactory() {
|
36050 | return new KeyValueDiffers([new DefaultKeyValueDifferFactory()]);
|
36051 | }
|
36052 | /**
|
36053 | * A repository of different Map diffing strategies used by NgClass, NgStyle, and others.
|
36054 | *
|
36055 | * @publicApi
|
36056 | */
|
36057 | class KeyValueDiffers {
|
36058 | constructor(factories) {
|
36059 | this.factories = factories;
|
36060 | }
|
36061 | static create(factories, parent) {
|
36062 | if (parent) {
|
36063 | const copied = parent.factories.slice();
|
36064 | factories = factories.concat(copied);
|
36065 | }
|
36066 | return new KeyValueDiffers(factories);
|
36067 | }
|
36068 | /**
|
36069 | * Takes an array of {@link KeyValueDifferFactory} and returns a provider used to extend the
|
36070 | * inherited {@link KeyValueDiffers} instance with the provided factories and return a new
|
36071 | * {@link KeyValueDiffers} instance.
|
36072 | *
|
36073 | * @usageNotes
|
36074 | * ### Example
|
36075 | *
|
36076 | * The following example shows how to extend an existing list of factories,
|
36077 | * which will only be applied to the injector for this component and its children.
|
36078 | * This step is all that's required to make a new {@link KeyValueDiffer} available.
|
36079 | *
|
36080 | * ```
|
36081 | * @Component({
|
36082 | * viewProviders: [
|
36083 | * KeyValueDiffers.extend([new ImmutableMapDiffer()])
|
36084 | * ]
|
36085 | * })
|
36086 | * ```
|
36087 | */
|
36088 | static extend(factories) {
|
36089 | return {
|
36090 | provide: KeyValueDiffers,
|
36091 | useFactory: (parent) => {
|
36092 | // if parent is null, it means that we are in the root injector and we have just overridden
|
36093 | // the default injection mechanism for KeyValueDiffers, in such a case just assume
|
36094 | // `defaultKeyValueDiffersFactory`.
|
36095 | return KeyValueDiffers.create(factories, parent || defaultKeyValueDiffersFactory());
|
36096 | },
|
36097 | // Dependency technically isn't optional, but we can provide a better error message this way.
|
36098 | deps: [[KeyValueDiffers, new SkipSelf(), new Optional()]]
|
36099 | };
|
36100 | }
|
36101 | find(kv) {
|
36102 | const factory = this.factories.find(f => f.supports(kv));
|
36103 | if (factory) {
|
36104 | return factory;
|
36105 | }
|
36106 | throw new Error(`Cannot find a differ supporting object '${kv}'`);
|
36107 | }
|
36108 | }
|
36109 | /** @nocollapse */
|
36110 | KeyValueDiffers.ɵprov = ɵɵdefineInjectable({ token: KeyValueDiffers, providedIn: 'root', factory: defaultKeyValueDiffersFactory });
|
36111 |
|
36112 | /**
|
36113 | * @license
|
36114 | * Copyright Google LLC All Rights Reserved.
|
36115 | *
|
36116 | * Use of this source code is governed by an MIT-style license that can be
|
36117 | * found in the LICENSE file at https://angular.io/license
|
36118 | */
|
36119 | function collectNativeNodes(tView, lView, tNode, result, isProjection = false) {
|
36120 | while (tNode !== null) {
|
36121 | ngDevMode &&
|
36122 | assertTNodeType(tNode, 3 /* AnyRNode */ | 12 /* AnyContainer */ | 16 /* Projection */ | 32 /* Icu */);
|
36123 | const lNode = lView[tNode.index];
|
36124 | if (lNode !== null) {
|
36125 | result.push(unwrapRNode(lNode));
|
36126 | }
|
36127 | // A given lNode can represent either a native node or a LContainer (when it is a host of a
|
36128 | // ViewContainerRef). When we find a LContainer we need to descend into it to collect root nodes
|
36129 | // from the views in this container.
|
36130 | if (isLContainer(lNode)) {
|
36131 | for (let i = CONTAINER_HEADER_OFFSET; i < lNode.length; i++) {
|
36132 | const lViewInAContainer = lNode[i];
|
36133 | const lViewFirstChildTNode = lViewInAContainer[TVIEW].firstChild;
|
36134 | if (lViewFirstChildTNode !== null) {
|
36135 | collectNativeNodes(lViewInAContainer[TVIEW], lViewInAContainer, lViewFirstChildTNode, result);
|
36136 | }
|
36137 | }
|
36138 | }
|
36139 | const tNodeType = tNode.type;
|
36140 | if (tNodeType & 8 /* ElementContainer */) {
|
36141 | collectNativeNodes(tView, lView, tNode.child, result);
|
36142 | }
|
36143 | else if (tNodeType & 32 /* Icu */) {
|
36144 | const nextRNode = icuContainerIterate();
|
36145 | let rNode;
|
36146 | while (rNode = nextRNode()) {
|
36147 | result.push(rNode);
|
36148 | }
|
36149 | }
|
36150 | else if (tNodeType & 16 /* Projection */) {
|
36151 | const nodesInSlot = getProjectionNodes(lView, tNode);
|
36152 | if (Array.isArray(nodesInSlot)) {
|
36153 | result.push(...nodesInSlot);
|
36154 | }
|
36155 | else {
|
36156 | const parentView = getLViewParent(lView[DECLARATION_COMPONENT_VIEW]);
|
36157 | ngDevMode && assertParentView(parentView);
|
36158 | collectNativeNodes(parentView[TVIEW], parentView, nodesInSlot, result, true);
|
36159 | }
|
36160 | }
|
36161 | tNode = isProjection ? tNode.projectionNext : tNode.next;
|
36162 | }
|
36163 | return result;
|
36164 | }
|
36165 |
|
36166 | /**
|
36167 | * @license
|
36168 | * Copyright Google LLC All Rights Reserved.
|
36169 | *
|
36170 | * Use of this source code is governed by an MIT-style license that can be
|
36171 | * found in the LICENSE file at https://angular.io/license
|
36172 | */
|
36173 | class ViewRef {
|
36174 | constructor(
|
36175 | /**
|
36176 | * This represents `LView` associated with the component when ViewRef is a ChangeDetectorRef.
|
36177 | *
|
36178 | * When ViewRef is created for a dynamic component, this also represents the `LView` for the
|
36179 | * component.
|
36180 | *
|
36181 | * For a "regular" ViewRef created for an embedded view, this is the `LView` for the embedded
|
36182 | * view.
|
36183 | *
|
36184 | * @internal
|
36185 | */
|
36186 | _lView,
|
36187 | /**
|
36188 | * This represents the `LView` associated with the point where `ChangeDetectorRef` was
|
36189 | * requested.
|
36190 | *
|
36191 | * This may be different from `_lView` if the `_cdRefInjectingView` is an embedded view.
|
36192 | */
|
36193 | _cdRefInjectingView) {
|
36194 | this._lView = _lView;
|
36195 | this._cdRefInjectingView = _cdRefInjectingView;
|
36196 | this._appRef = null;
|
36197 | this._attachedToViewContainer = false;
|
36198 | }
|
36199 | get rootNodes() {
|
36200 | const lView = this._lView;
|
36201 | const tView = lView[TVIEW];
|
36202 | return collectNativeNodes(tView, lView, tView.firstChild, []);
|
36203 | }
|
36204 | get context() {
|
36205 | return this._lView[CONTEXT];
|
36206 | }
|
36207 | get destroyed() {
|
36208 | return (this._lView[FLAGS] & 256 /* Destroyed */) === 256 /* Destroyed */;
|
36209 | }
|
36210 | destroy() {
|
36211 | if (this._appRef) {
|
36212 | this._appRef.detachView(this);
|
36213 | }
|
36214 | else if (this._attachedToViewContainer) {
|
36215 | const parent = this._lView[PARENT];
|
36216 | if (isLContainer(parent)) {
|
36217 | const viewRefs = parent[VIEW_REFS];
|
36218 | const index = viewRefs ? viewRefs.indexOf(this) : -1;
|
36219 | if (index > -1) {
|
36220 | ngDevMode &&
|
36221 | 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.');
|
36222 | detachView(parent, index);
|
36223 | removeFromArray(viewRefs, index);
|
36224 | }
|
36225 | }
|
36226 | this._attachedToViewContainer = false;
|
36227 | }
|
36228 | destroyLView(this._lView[TVIEW], this._lView);
|
36229 | }
|
36230 | onDestroy(callback) {
|
36231 | storeCleanupWithContext(this._lView[TVIEW], this._lView, null, callback);
|
36232 | }
|
36233 | /**
|
36234 | * Marks a view and all of its ancestors dirty.
|
36235 | *
|
36236 | * It also triggers change detection by calling `scheduleTick` internally, which coalesces
|
36237 | * multiple `markForCheck` calls to into one change detection run.
|
36238 | *
|
36239 | * This can be used to ensure an {@link ChangeDetectionStrategy#OnPush OnPush} component is
|
36240 | * checked when it needs to be re-rendered but the two normal triggers haven't marked it
|
36241 | * dirty (i.e. inputs haven't changed and events haven't fired in the view).
|
36242 | *
|
36243 | * <!-- TODO: Add a link to a chapter on OnPush components -->
|
36244 | *
|
36245 | * @usageNotes
|
36246 | * ### Example
|
36247 | *
|
36248 | * ```typescript
|
36249 | * @Component({
|
36250 | * selector: 'my-app',
|
36251 | * template: `Number of ticks: {{numberOfTicks}}`
|
36252 | * changeDetection: ChangeDetectionStrategy.OnPush,
|
36253 | * })
|
36254 | * class AppComponent {
|
36255 | * numberOfTicks = 0;
|
36256 | *
|
36257 | * constructor(private ref: ChangeDetectorRef) {
|
36258 | * setInterval(() => {
|
36259 | * this.numberOfTicks++;
|
36260 | * // the following is required, otherwise the view will not be updated
|
36261 | * this.ref.markForCheck();
|
36262 | * }, 1000);
|
36263 | * }
|
36264 | * }
|
36265 | * ```
|
36266 | */
|
36267 | markForCheck() {
|
36268 | markViewDirty(this._cdRefInjectingView || this._lView);
|
36269 | }
|
36270 | /**
|
36271 | * Detaches the view from the change detection tree.
|
36272 | *
|
36273 | * Detached views will not be checked during change detection runs until they are
|
36274 | * re-attached, even if they are dirty. `detach` can be used in combination with
|
36275 | * {@link ChangeDetectorRef#detectChanges detectChanges} to implement local change
|
36276 | * detection checks.
|
36277 | *
|
36278 | * <!-- TODO: Add a link to a chapter on detach/reattach/local digest -->
|
36279 | * <!-- TODO: Add a live demo once ref.detectChanges is merged into master -->
|
36280 | *
|
36281 | * @usageNotes
|
36282 | * ### Example
|
36283 | *
|
36284 | * The following example defines a component with a large list of readonly data.
|
36285 | * Imagine the data changes constantly, many times per second. For performance reasons,
|
36286 | * we want to check and update the list every five seconds. We can do that by detaching
|
36287 | * the component's change detector and doing a local check every five seconds.
|
36288 | *
|
36289 | * ```typescript
|
36290 | * class DataProvider {
|
36291 | * // in a real application the returned data will be different every time
|
36292 | * get data() {
|
36293 | * return [1,2,3,4,5];
|
36294 | * }
|
36295 | * }
|
36296 | *
|
36297 | * @Component({
|
36298 | * selector: 'giant-list',
|
36299 | * template: `
|
36300 | * <li *ngFor="let d of dataProvider.data">Data {{d}}</li>
|
36301 | * `,
|
36302 | * })
|
36303 | * class GiantList {
|
36304 | * constructor(private ref: ChangeDetectorRef, private dataProvider: DataProvider) {
|
36305 | * ref.detach();
|
36306 | * setInterval(() => {
|
36307 | * this.ref.detectChanges();
|
36308 | * }, 5000);
|
36309 | * }
|
36310 | * }
|
36311 | *
|
36312 | * @Component({
|
36313 | * selector: 'app',
|
36314 | * providers: [DataProvider],
|
36315 | * template: `
|
36316 | * <giant-list><giant-list>
|
36317 | * `,
|
36318 | * })
|
36319 | * class App {
|
36320 | * }
|
36321 | * ```
|
36322 | */
|
36323 | detach() {
|
36324 | this._lView[FLAGS] &= ~128 /* Attached */;
|
36325 | }
|
36326 | /**
|
36327 | * Re-attaches a view to the change detection tree.
|
36328 | *
|
36329 | * This can be used to re-attach views that were previously detached from the tree
|
36330 | * using {@link ChangeDetectorRef#detach detach}. Views are attached to the tree by default.
|
36331 | *
|
36332 | * <!-- TODO: Add a link to a chapter on detach/reattach/local digest -->
|
36333 | *
|
36334 | * @usageNotes
|
36335 | * ### Example
|
36336 | *
|
36337 | * The following example creates a component displaying `live` data. The component will detach
|
36338 | * its change detector from the main change detector tree when the component's live property
|
36339 | * is set to false.
|
36340 | *
|
36341 | * ```typescript
|
36342 | * class DataProvider {
|
36343 | * data = 1;
|
36344 | *
|
36345 | * constructor() {
|
36346 | * setInterval(() => {
|
36347 | * this.data = this.data * 2;
|
36348 | * }, 500);
|
36349 | * }
|
36350 | * }
|
36351 | *
|
36352 | * @Component({
|
36353 | * selector: 'live-data',
|
36354 | * inputs: ['live'],
|
36355 | * template: 'Data: {{dataProvider.data}}'
|
36356 | * })
|
36357 | * class LiveData {
|
36358 | * constructor(private ref: ChangeDetectorRef, private dataProvider: DataProvider) {}
|
36359 | *
|
36360 | * set live(value) {
|
36361 | * if (value) {
|
36362 | * this.ref.reattach();
|
36363 | * } else {
|
36364 | * this.ref.detach();
|
36365 | * }
|
36366 | * }
|
36367 | * }
|
36368 | *
|
36369 | * @Component({
|
36370 | * selector: 'my-app',
|
36371 | * providers: [DataProvider],
|
36372 | * template: `
|
36373 | * Live Update: <input type="checkbox" [(ngModel)]="live">
|
36374 | * <live-data [live]="live"><live-data>
|
36375 | * `,
|
36376 | * })
|
36377 | * class AppComponent {
|
36378 | * live = true;
|
36379 | * }
|
36380 | * ```
|
36381 | */
|
36382 | reattach() {
|
36383 | this._lView[FLAGS] |= 128 /* Attached */;
|
36384 | }
|
36385 | /**
|
36386 | * Checks the view and its children.
|
36387 | *
|
36388 | * This can also be used in combination with {@link ChangeDetectorRef#detach detach} to implement
|
36389 | * local change detection checks.
|
36390 | *
|
36391 | * <!-- TODO: Add a link to a chapter on detach/reattach/local digest -->
|
36392 | * <!-- TODO: Add a live demo once ref.detectChanges is merged into master -->
|
36393 | *
|
36394 | * @usageNotes
|
36395 | * ### Example
|
36396 | *
|
36397 | * The following example defines a component with a large list of readonly data.
|
36398 | * Imagine, the data changes constantly, many times per second. For performance reasons,
|
36399 | * we want to check and update the list every five seconds.
|
36400 | *
|
36401 | * We can do that by detaching the component's change detector and doing a local change detection
|
36402 | * check every five seconds.
|
36403 | *
|
36404 | * See {@link ChangeDetectorRef#detach detach} for more information.
|
36405 | */
|
36406 | detectChanges() {
|
36407 | detectChangesInternal(this._lView[TVIEW], this._lView, this.context);
|
36408 | }
|
36409 | /**
|
36410 | * Checks the change detector and its children, and throws if any changes are detected.
|
36411 | *
|
36412 | * This is used in development mode to verify that running change detection doesn't
|
36413 | * introduce other changes.
|
36414 | */
|
36415 | checkNoChanges() {
|
36416 | checkNoChangesInternal(this._lView[TVIEW], this._lView, this.context);
|
36417 | }
|
36418 | attachToViewContainerRef() {
|
36419 | if (this._appRef) {
|
36420 | throw new Error('This view is already attached directly to the ApplicationRef!');
|
36421 | }
|
36422 | this._attachedToViewContainer = true;
|
36423 | }
|
36424 | detachFromAppRef() {
|
36425 | this._appRef = null;
|
36426 | renderDetachView(this._lView[TVIEW], this._lView);
|
36427 | }
|
36428 | attachToAppRef(appRef) {
|
36429 | if (this._attachedToViewContainer) {
|
36430 | throw new Error('This view is already attached to a ViewContainer!');
|
36431 | }
|
36432 | this._appRef = appRef;
|
36433 | }
|
36434 | }
|
36435 | /** @internal */
|
36436 | class RootViewRef extends ViewRef {
|
36437 | constructor(_view) {
|
36438 | super(_view);
|
36439 | this._view = _view;
|
36440 | }
|
36441 | detectChanges() {
|
36442 | detectChangesInRootView(this._view);
|
36443 | }
|
36444 | checkNoChanges() {
|
36445 | checkNoChangesInRootView(this._view);
|
36446 | }
|
36447 | get context() {
|
36448 | return null;
|
36449 | }
|
36450 | }
|
36451 |
|
36452 | /**
|
36453 | * @license
|
36454 | * Copyright Google LLC All Rights Reserved.
|
36455 | *
|
36456 | * Use of this source code is governed by an MIT-style license that can be
|
36457 | * found in the LICENSE file at https://angular.io/license
|
36458 | */
|
36459 | const SWITCH_CHANGE_DETECTOR_REF_FACTORY__PRE_R3__ = noop;
|
36460 | const SWITCH_CHANGE_DETECTOR_REF_FACTORY = SWITCH_CHANGE_DETECTOR_REF_FACTORY__PRE_R3__;
|
36461 | /**
|
36462 | * Base class that provides change detection functionality.
|
36463 | * A change-detection tree collects all views that are to be checked for changes.
|
36464 | * Use the methods to add and remove views from the tree, initiate change-detection,
|
36465 | * and explicitly mark views as _dirty_, meaning that they have changed and need to be re-rendered.
|
36466 | *
|
36467 | * @see [Using change detection hooks](guide/lifecycle-hooks#using-change-detection-hooks)
|
36468 | * @see [Defining custom change detection](guide/lifecycle-hooks#defining-custom-change-detection)
|
36469 | *
|
36470 | * @usageNotes
|
36471 | *
|
36472 | * The following examples demonstrate how to modify default change-detection behavior
|
36473 | * to perform explicit detection when needed.
|
36474 | *
|
36475 | * ### Use `markForCheck()` with `CheckOnce` strategy
|
36476 | *
|
36477 | * The following example sets the `OnPush` change-detection strategy for a component
|
36478 | * (`CheckOnce`, rather than the default `CheckAlways`), then forces a second check
|
36479 | * after an interval. See [live demo](https://plnkr.co/edit/GC512b?p=preview).
|
36480 | *
|
36481 | * <code-example path="core/ts/change_detect/change-detection.ts"
|
36482 | * region="mark-for-check"></code-example>
|
36483 | *
|
36484 | * ### Detach change detector to limit how often check occurs
|
36485 | *
|
36486 | * The following example defines a component with a large list of read-only data
|
36487 | * that is expected to change constantly, many times per second.
|
36488 | * To improve performance, we want to check and update the list
|
36489 | * less often than the changes actually occur. To do that, we detach
|
36490 | * the component's change detector and perform an explicit local check every five seconds.
|
36491 | *
|
36492 | * <code-example path="core/ts/change_detect/change-detection.ts" region="detach"></code-example>
|
36493 | *
|
36494 | *
|
36495 | * ### Reattaching a detached component
|
36496 | *
|
36497 | * The following example creates a component displaying live data.
|
36498 | * The component detaches its change detector from the main change detector tree
|
36499 | * when the `live` property is set to false, and reattaches it when the property
|
36500 | * becomes true.
|
36501 | *
|
36502 | * <code-example path="core/ts/change_detect/change-detection.ts" region="reattach"></code-example>
|
36503 | *
|
36504 | * @publicApi
|
36505 | */
|
36506 | class ChangeDetectorRef {
|
36507 | }
|
36508 | /**
|
36509 | * @internal
|
36510 | * @nocollapse
|
36511 | */
|
36512 | ChangeDetectorRef.__NG_ELEMENT_ID__ = SWITCH_CHANGE_DETECTOR_REF_FACTORY;
|
36513 | /**
|
36514 | * This marker is need so that the JIT compiler can correctly identify this class as special.
|
36515 | *
|
36516 | * @internal
|
36517 | * @nocollapse
|
36518 | */
|
36519 | ChangeDetectorRef.__ChangeDetectorRef__ = true;
|
36520 |
|
36521 | /**
|
36522 | * @license
|
36523 | * Copyright Google LLC All Rights Reserved.
|
36524 | *
|
36525 | * Use of this source code is governed by an MIT-style license that can be
|
36526 | * found in the LICENSE file at https://angular.io/license
|
36527 | */
|
36528 | /**
|
36529 | * Structural diffing for `Object`s and `Map`s.
|
36530 | */
|
36531 | const keyValDiff = [new DefaultKeyValueDifferFactory()];
|
36532 | /**
|
36533 | * Structural diffing for `Iterable` types such as `Array`s.
|
36534 | */
|
36535 | const iterableDiff = [new DefaultIterableDifferFactory()];
|
36536 | const defaultIterableDiffers = new IterableDiffers(iterableDiff);
|
36537 | const defaultKeyValueDiffers = new KeyValueDiffers(keyValDiff);
|
36538 |
|
36539 | /**
|
36540 | * @license
|
36541 | * Copyright Google LLC All Rights Reserved.
|
36542 | *
|
36543 | * Use of this source code is governed by an MIT-style license that can be
|
36544 | * found in the LICENSE file at https://angular.io/license
|
36545 | */
|
36546 | const SWITCH_TEMPLATE_REF_FACTORY__PRE_R3__ = noop;
|
36547 | const SWITCH_TEMPLATE_REF_FACTORY = SWITCH_TEMPLATE_REF_FACTORY__PRE_R3__;
|
36548 | /**
|
36549 | * Represents an embedded template that can be used to instantiate embedded views.
|
36550 | * To instantiate embedded views based on a template, use the `ViewContainerRef`
|
36551 | * method `createEmbeddedView()`.
|
36552 | *
|
36553 | * Access a `TemplateRef` instance by placing a directive on an `<ng-template>`
|
36554 | * element (or directive prefixed with `*`). The `TemplateRef` for the embedded view
|
36555 | * is injected into the constructor of the directive,
|
36556 | * using the `TemplateRef` token.
|
36557 | *
|
36558 | * You can also use a `Query` to find a `TemplateRef` associated with
|
36559 | * a component or a directive.
|
36560 | *
|
36561 | * @see `ViewContainerRef`
|
36562 | * @see [Navigate the Component Tree with DI](guide/dependency-injection-navtree)
|
36563 | *
|
36564 | * @publicApi
|
36565 | */
|
36566 | class TemplateRef {
|
36567 | }
|
36568 | /**
|
36569 | * @internal
|
36570 | * @nocollapse
|
36571 | */
|
36572 | TemplateRef.__NG_ELEMENT_ID__ = SWITCH_TEMPLATE_REF_FACTORY;
|
36573 |
|
36574 | /**
|
36575 | * @license
|
36576 | * Copyright Google LLC All Rights Reserved.
|
36577 | *
|
36578 | * Use of this source code is governed by an MIT-style license that can be
|
36579 | * found in the LICENSE file at https://angular.io/license
|
36580 | */
|
36581 | /**
|
36582 | * Represents an instance of an `NgModule` created by an `NgModuleFactory`.
|
36583 | * Provides access to the `NgModule` instance and related objects.
|
36584 | *
|
36585 | * @publicApi
|
36586 | */
|
36587 | class NgModuleRef {
|
36588 | }
|
36589 |
|
36590 | /**
|
36591 | * @license
|
36592 | * Copyright Google LLC All Rights Reserved.
|
36593 | *
|
36594 | * Use of this source code is governed by an MIT-style license that can be
|
36595 | * found in the LICENSE file at https://angular.io/license
|
36596 | */
|
36597 | const SWITCH_VIEW_CONTAINER_REF_FACTORY__PRE_R3__ = noop;
|
36598 | const SWITCH_VIEW_CONTAINER_REF_FACTORY = SWITCH_VIEW_CONTAINER_REF_FACTORY__PRE_R3__;
|
36599 | /**
|
36600 | * Represents a container where one or more views can be attached to a component.
|
36601 | *
|
36602 | * Can contain *host views* (created by instantiating a
|
36603 | * component with the `createComponent()` method), and *embedded views*
|
36604 | * (created by instantiating a `TemplateRef` with the `createEmbeddedView()` method).
|
36605 | *
|
36606 | * A view container instance can contain other view containers,
|
36607 | * creating a [view hierarchy](guide/glossary#view-tree).
|
36608 | *
|
36609 | * @see `ComponentRef`
|
36610 | * @see `EmbeddedViewRef`
|
36611 | *
|
36612 | * @publicApi
|
36613 | */
|
36614 | class ViewContainerRef {
|
36615 | }
|
36616 | /**
|
36617 | * @internal
|
36618 | * @nocollapse
|
36619 | */
|
36620 | ViewContainerRef.__NG_ELEMENT_ID__ = SWITCH_VIEW_CONTAINER_REF_FACTORY;
|
36621 |
|
36622 | /**
|
36623 | * @license
|
36624 | * Copyright Google LLC All Rights Reserved.
|
36625 | *
|
36626 | * Use of this source code is governed by an MIT-style license that can be
|
36627 | * found in the LICENSE file at https://angular.io/license
|
36628 | */
|
36629 | const _tokenKeyCache = new Map();
|
36630 | function tokenKey(token) {
|
36631 | let key = _tokenKeyCache.get(token);
|
36632 | if (!key) {
|
36633 | key = stringify$1(token) + '_' + _tokenKeyCache.size;
|
36634 | _tokenKeyCache.set(token, key);
|
36635 | }
|
36636 | return key;
|
36637 | }
|
36638 |
|
36639 | /**
|
36640 | * @license
|
36641 | * Copyright Google LLC All Rights Reserved.
|
36642 | *
|
36643 | * Use of this source code is governed by an MIT-style license that can be
|
36644 | * found in the LICENSE file at https://angular.io/license
|
36645 | */
|
36646 | const InjectorRefTokenKey = tokenKey(Injector);
|
36647 | const INJECTORRefTokenKey = tokenKey(INJECTOR$1);
|
36648 | const NgModuleRefTokenKey = tokenKey(NgModuleRef);
|
36649 |
|
36650 | /**
|
36651 | * @license
|
36652 | * Copyright Google LLC All Rights Reserved.
|
36653 | *
|
36654 | * Use of this source code is governed by an MIT-style license that can be
|
36655 | * found in the LICENSE file at https://angular.io/license
|
36656 | */
|
36657 | const Renderer2TokenKey = tokenKey(Renderer2);
|
36658 | const ElementRefTokenKey = tokenKey(ElementRef);
|
36659 | const ViewContainerRefTokenKey = tokenKey(ViewContainerRef);
|
36660 | const TemplateRefTokenKey = tokenKey(TemplateRef);
|
36661 | const ChangeDetectorRefTokenKey = tokenKey(ChangeDetectorRef);
|
36662 | const InjectorRefTokenKey$1 = tokenKey(Injector);
|
36663 | const INJECTORRefTokenKey$1 = tokenKey(INJECTOR$1);
|
36664 | // This default value is when checking the hierarchy for a token.
|
36665 | //
|
36666 | // It means both:
|
36667 | // - the token is not provided by the current injector,
|
36668 | // - only the element injectors should be checked (ie do not check module injectors
|
36669 | //
|
36670 | // mod1
|
36671 | // /
|
36672 | // el1 mod2
|
36673 | // \ /
|
36674 | // el2
|
36675 | //
|
36676 | // When requesting el2.injector.get(token), we should check in the following order and return the
|
36677 | // first found value:
|
36678 | // - el2.injector.get(token, default)
|
36679 | // - el1.injector.get(token, NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR) -> do not check the module
|
36680 | // - mod2.injector.get(token, default)
|
36681 | const NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR = {};
|
36682 |
|
36683 | /**
|
36684 | * @license
|
36685 | * Copyright Google LLC All Rights Reserved.
|
36686 | *
|
36687 | * Use of this source code is governed by an MIT-style license that can be
|
36688 | * found in the LICENSE file at https://angular.io/license
|
36689 | */
|
36690 | class ComponentFactoryResolver$1 extends ComponentFactoryResolver {
|
36691 | /**
|
36692 | * @param ngModule The NgModuleRef to which all resolved factories are bound.
|
36693 | */
|
36694 | constructor(ngModule) {
|
36695 | super();
|
36696 | this.ngModule = ngModule;
|
36697 | }
|
36698 | resolveComponentFactory(component) {
|
36699 | ngDevMode && assertComponentType(component);
|
36700 | const componentDef = getComponentDef(component);
|
36701 | return new ComponentFactory$1(componentDef, this.ngModule);
|
36702 | }
|
36703 | }
|
36704 | function toRefArray(map) {
|
36705 | const array = [];
|
36706 | for (let nonMinified in map) {
|
36707 | if (map.hasOwnProperty(nonMinified)) {
|
36708 | const minified = map[nonMinified];
|
36709 | array.push({ propName: minified, templateName: nonMinified });
|
36710 | }
|
36711 | }
|
36712 | return array;
|
36713 | }
|
36714 | function getNamespace(elementName) {
|
36715 | const name = elementName.toLowerCase();
|
36716 | return name === 'svg' ? SVG_NAMESPACE : (name === 'math' ? MATH_ML_NAMESPACE : null);
|
36717 | }
|
36718 | /**
|
36719 | * A change detection scheduler token for {@link RootContext}. This token is the default value used
|
36720 | * for the default `RootContext` found in the {@link ROOT_CONTEXT} token.
|
36721 | */
|
36722 | const SCHEDULER = new InjectionToken('SCHEDULER_TOKEN', {
|
36723 | providedIn: 'root',
|
36724 | factory: () => defaultScheduler,
|
36725 | });
|
36726 | function createChainedInjector(rootViewInjector, moduleInjector) {
|
36727 | return {
|
36728 | get: (token, notFoundValue, flags) => {
|
36729 | const value = rootViewInjector.get(token, NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR, flags);
|
36730 | if (value !== NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR ||
|
36731 | notFoundValue === NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR) {
|
36732 | // Return the value from the root element injector when
|
36733 | // - it provides it
|
36734 | // (value !== NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR)
|
36735 | // - the module injector should not be checked
|
36736 | // (notFoundValue === NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR)
|
36737 | return value;
|
36738 | }
|
36739 | return moduleInjector.get(token, notFoundValue, flags);
|
36740 | }
|
36741 | };
|
36742 | }
|
36743 | /**
|
36744 | * Render3 implementation of {@link viewEngine_ComponentFactory}.
|
36745 | */
|
36746 | class ComponentFactory$1 extends ComponentFactory {
|
36747 | /**
|
36748 | * @param componentDef The component definition.
|
36749 | * @param ngModule The NgModuleRef to which the factory is bound.
|
36750 | */
|
36751 | constructor(componentDef, ngModule) {
|
36752 | super();
|
36753 | this.componentDef = componentDef;
|
36754 | this.ngModule = ngModule;
|
36755 | this.componentType = componentDef.type;
|
36756 | this.selector = stringifyCSSSelectorList(componentDef.selectors);
|
36757 | this.ngContentSelectors =
|
36758 | componentDef.ngContentSelectors ? componentDef.ngContentSelectors : [];
|
36759 | this.isBoundToModule = !!ngModule;
|
36760 | }
|
36761 | get inputs() {
|
36762 | return toRefArray(this.componentDef.inputs);
|
36763 | }
|
36764 | get outputs() {
|
36765 | return toRefArray(this.componentDef.outputs);
|
36766 | }
|
36767 | create(injector, projectableNodes, rootSelectorOrNode, ngModule) {
|
36768 | ngModule = ngModule || this.ngModule;
|
36769 | const rootViewInjector = ngModule ? createChainedInjector(injector, ngModule.injector) : injector;
|
36770 | const rendererFactory = rootViewInjector.get(RendererFactory2, domRendererFactory3);
|
36771 | const sanitizer = rootViewInjector.get(Sanitizer, null);
|
36772 | const hostRenderer = rendererFactory.createRenderer(null, this.componentDef);
|
36773 | // Determine a tag name used for creating host elements when this component is created
|
36774 | // dynamically. Default to 'div' if this component did not specify any tag name in its selector.
|
36775 | const elementName = this.componentDef.selectors[0][0] || 'div';
|
36776 | const hostRNode = rootSelectorOrNode ?
|
36777 | locateHostElement(hostRenderer, rootSelectorOrNode, this.componentDef.encapsulation) :
|
36778 | createElementNode(rendererFactory.createRenderer(null, this.componentDef), elementName, getNamespace(elementName));
|
36779 | const rootFlags = this.componentDef.onPush ? 64 /* Dirty */ | 512 /* IsRoot */ :
|
36780 | 16 /* CheckAlways */ | 512 /* IsRoot */;
|
36781 | const rootContext = createRootContext();
|
36782 | // Create the root view. Uses empty TView and ContentTemplate.
|
36783 | const rootTView = createTView(0 /* Root */, null, null, 1, 0, null, null, null, null, null);
|
36784 | const rootLView = createLView(null, rootTView, rootContext, rootFlags, null, null, rendererFactory, hostRenderer, sanitizer, rootViewInjector);
|
36785 | // rootView is the parent when bootstrapping
|
36786 | // TODO(misko): it looks like we are entering view here but we don't really need to as
|
36787 | // `renderView` does that. However as the code is written it is needed because
|
36788 | // `createRootComponentView` and `createRootComponent` both read global state. Fixing those
|
36789 | // issues would allow us to drop this.
|
36790 | enterView(rootLView);
|
36791 | let component;
|
36792 | let tElementNode;
|
36793 | try {
|
36794 | const componentView = createRootComponentView(hostRNode, this.componentDef, rootLView, rendererFactory, hostRenderer);
|
36795 | if (hostRNode) {
|
36796 | if (rootSelectorOrNode) {
|
36797 | setUpAttributes(hostRenderer, hostRNode, ['ng-version', VERSION$2.full]);
|
36798 | }
|
36799 | else {
|
36800 | // If host element is created as a part of this function call (i.e. `rootSelectorOrNode`
|
36801 | // is not defined), also apply attributes and classes extracted from component selector.
|
36802 | // Extract attributes and classes from the first selector only to match VE behavior.
|
36803 | const { attrs, classes } = extractAttrsAndClassesFromSelector(this.componentDef.selectors[0]);
|
36804 | if (attrs) {
|
36805 | setUpAttributes(hostRenderer, hostRNode, attrs);
|
36806 | }
|
36807 | if (classes && classes.length > 0) {
|
36808 | writeDirectClass(hostRenderer, hostRNode, classes.join(' '));
|
36809 | }
|
36810 | }
|
36811 | }
|
36812 | tElementNode = getTNode(rootTView, HEADER_OFFSET);
|
36813 | if (projectableNodes !== undefined) {
|
36814 | const projection = tElementNode.projection = [];
|
36815 | for (let i = 0; i < this.ngContentSelectors.length; i++) {
|
36816 | const nodesforSlot = projectableNodes[i];
|
36817 | // Projectable nodes can be passed as array of arrays or an array of iterables (ngUpgrade
|
36818 | // case). Here we do normalize passed data structure to be an array of arrays to avoid
|
36819 | // complex checks down the line.
|
36820 | // We also normalize the length of the passed in projectable nodes (to match the number of
|
36821 | // <ng-container> slots defined by a component).
|
36822 | projection.push(nodesforSlot != null ? Array.from(nodesforSlot) : null);
|
36823 | }
|
36824 | }
|
36825 | // TODO: should LifecycleHooksFeature and other host features be generated by the compiler and
|
36826 | // executed here?
|
36827 | // Angular 5 reference: https://stackblitz.com/edit/lifecycle-hooks-vcref
|
36828 | component = createRootComponent(componentView, this.componentDef, rootLView, rootContext, [LifecycleHooksFeature]);
|
36829 | renderView(rootTView, rootLView, null);
|
36830 | }
|
36831 | finally {
|
36832 | leaveView();
|
36833 | }
|
36834 | return new ComponentRef$1(this.componentType, component, createElementRef(tElementNode, rootLView), rootLView, tElementNode);
|
36835 | }
|
36836 | }
|
36837 | const componentFactoryResolver = new ComponentFactoryResolver$1();
|
36838 | /**
|
36839 | * Represents an instance of a Component created via a {@link ComponentFactory}.
|
36840 | *
|
36841 | * `ComponentRef` provides access to the Component Instance as well other objects related to this
|
36842 | * Component Instance and allows you to destroy the Component Instance via the {@link #destroy}
|
36843 | * method.
|
36844 | *
|
36845 | */
|
36846 | class ComponentRef$1 extends ComponentRef {
|
36847 | constructor(componentType, instance, location, _rootLView, _tNode) {
|
36848 | super();
|
36849 | this.location = location;
|
36850 | this._rootLView = _rootLView;
|
36851 | this._tNode = _tNode;
|
36852 | this.instance = instance;
|
36853 | this.hostView = this.changeDetectorRef = new RootViewRef(_rootLView);
|
36854 | this.componentType = componentType;
|
36855 | }
|
36856 | get injector() {
|
36857 | return new NodeInjector(this._tNode, this._rootLView);
|
36858 | }
|
36859 | destroy() {
|
36860 | this.hostView.destroy();
|
36861 | }
|
36862 | onDestroy(callback) {
|
36863 | this.hostView.onDestroy(callback);
|
36864 | }
|
36865 | }
|
36866 |
|
36867 | /*! *****************************************************************************
|
36868 | Copyright (c) Microsoft Corporation. All rights reserved.
|
36869 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
36870 | this file except in compliance with the License. You may obtain a copy of the
|
36871 | License at http://www.apache.org/licenses/LICENSE-2.0
|
36872 |
|
36873 | THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
36874 | KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
|
36875 | WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
|
36876 | MERCHANTABLITY OR NON-INFRINGEMENT.
|
36877 |
|
36878 | See the Apache Version 2.0 License for specific language governing permissions
|
36879 | and limitations under the License.
|
36880 | ***************************************************************************** */
|
36881 | /* global Reflect, Promise */
|
36882 |
|
36883 | var extendStatics = function(d, b) {
|
36884 | extendStatics = Object.setPrototypeOf ||
|
36885 | ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
36886 | function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
|
36887 | return extendStatics(d, b);
|
36888 | };
|
36889 |
|
36890 | function __extends(d, b) {
|
36891 | extendStatics(d, b);
|
36892 | function __() { this.constructor = d; }
|
36893 | d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
36894 | }
|
36895 |
|
36896 | /** PURE_IMPORTS_START PURE_IMPORTS_END */
|
36897 | function isFunction(x) {
|
36898 | return typeof x === 'function';
|
36899 | }
|
36900 |
|
36901 | /** PURE_IMPORTS_START PURE_IMPORTS_END */
|
36902 | var _enable_super_gross_mode_that_will_cause_bad_things = false;
|
36903 | var config = {
|
36904 | Promise: undefined,
|
36905 | set useDeprecatedSynchronousErrorHandling(value) {
|
36906 | if (value) {
|
36907 | var error = /*@__PURE__*/ new Error();
|
36908 | /*@__PURE__*/ console.warn('DEPRECATED! RxJS was set to use deprecated synchronous error handling behavior by code at: \n' + error.stack);
|
36909 | }
|
36910 | _enable_super_gross_mode_that_will_cause_bad_things = value;
|
36911 | },
|
36912 | get useDeprecatedSynchronousErrorHandling() {
|
36913 | return _enable_super_gross_mode_that_will_cause_bad_things;
|
36914 | },
|
36915 | };
|
36916 |
|
36917 | /** PURE_IMPORTS_START PURE_IMPORTS_END */
|
36918 | function hostReportError(err) {
|
36919 | setTimeout(function () { throw err; }, 0);
|
36920 | }
|
36921 |
|
36922 | /** PURE_IMPORTS_START _config,_util_hostReportError PURE_IMPORTS_END */
|
36923 | var empty = {
|
36924 | closed: true,
|
36925 | next: function (value) { },
|
36926 | error: function (err) {
|
36927 | if (config.useDeprecatedSynchronousErrorHandling) {
|
36928 | throw err;
|
36929 | }
|
36930 | else {
|
36931 | hostReportError(err);
|
36932 | }
|
36933 | },
|
36934 | complete: function () { }
|
36935 | };
|
36936 |
|
36937 | /** PURE_IMPORTS_START PURE_IMPORTS_END */
|
36938 | var isArray = /*@__PURE__*/ (function () { return Array.isArray || (function (x) { return x && typeof x.length === 'number'; }); })();
|
36939 |
|
36940 | /** PURE_IMPORTS_START PURE_IMPORTS_END */
|
36941 | function isObject(x) {
|
36942 | return x !== null && typeof x === 'object';
|
36943 | }
|
36944 |
|
36945 | /** PURE_IMPORTS_START PURE_IMPORTS_END */
|
36946 | var UnsubscriptionErrorImpl = /*@__PURE__*/ (function () {
|
36947 | function UnsubscriptionErrorImpl(errors) {
|
36948 | Error.call(this);
|
36949 | this.message = errors ?
|
36950 | errors.length + " errors occurred during unsubscription:\n" + errors.map(function (err, i) { return i + 1 + ") " + err.toString(); }).join('\n ') : '';
|
36951 | this.name = 'UnsubscriptionError';
|
36952 | this.errors = errors;
|
36953 | return this;
|
36954 | }
|
36955 | UnsubscriptionErrorImpl.prototype = /*@__PURE__*/ Object.create(Error.prototype);
|
36956 | return UnsubscriptionErrorImpl;
|
36957 | })();
|
36958 | var UnsubscriptionError = UnsubscriptionErrorImpl;
|
36959 |
|
36960 | /** PURE_IMPORTS_START _util_isArray,_util_isObject,_util_isFunction,_util_UnsubscriptionError PURE_IMPORTS_END */
|
36961 | var Subscription = /*@__PURE__*/ (function () {
|
36962 | function Subscription(unsubscribe) {
|
36963 | this.closed = false;
|
36964 | this._parentOrParents = null;
|
36965 | this._subscriptions = null;
|
36966 | if (unsubscribe) {
|
36967 | this._unsubscribe = unsubscribe;
|
36968 | }
|
36969 | }
|
36970 | Subscription.prototype.unsubscribe = function () {
|
36971 | var errors;
|
36972 | if (this.closed) {
|
36973 | return;
|
36974 | }
|
36975 | var _a = this, _parentOrParents = _a._parentOrParents, _unsubscribe = _a._unsubscribe, _subscriptions = _a._subscriptions;
|
36976 | this.closed = true;
|
36977 | this._parentOrParents = null;
|
36978 | this._subscriptions = null;
|
36979 | if (_parentOrParents instanceof Subscription) {
|
36980 | _parentOrParents.remove(this);
|
36981 | }
|
36982 | else if (_parentOrParents !== null) {
|
36983 | for (var index = 0; index < _parentOrParents.length; ++index) {
|
36984 | var parent_1 = _parentOrParents[index];
|
36985 | parent_1.remove(this);
|
36986 | }
|
36987 | }
|
36988 | if (isFunction(_unsubscribe)) {
|
36989 | try {
|
36990 | _unsubscribe.call(this);
|
36991 | }
|
36992 | catch (e) {
|
36993 | errors = e instanceof UnsubscriptionError ? flattenUnsubscriptionErrors(e.errors) : [e];
|
36994 | }
|
36995 | }
|
36996 | if (isArray(_subscriptions)) {
|
36997 | var index = -1;
|
36998 | var len = _subscriptions.length;
|
36999 | while (++index < len) {
|
37000 | var sub = _subscriptions[index];
|
37001 | if (isObject(sub)) {
|
37002 | try {
|
37003 | sub.unsubscribe();
|
37004 | }
|
37005 | catch (e) {
|
37006 | errors = errors || [];
|
37007 | if (e instanceof UnsubscriptionError) {
|
37008 | errors = errors.concat(flattenUnsubscriptionErrors(e.errors));
|
37009 | }
|
37010 | else {
|
37011 | errors.push(e);
|
37012 | }
|
37013 | }
|
37014 | }
|
37015 | }
|
37016 | }
|
37017 | if (errors) {
|
37018 | throw new UnsubscriptionError(errors);
|
37019 | }
|
37020 | };
|
37021 | Subscription.prototype.add = function (teardown) {
|
37022 | var subscription = teardown;
|
37023 | if (!teardown) {
|
37024 | return Subscription.EMPTY;
|
37025 | }
|
37026 | switch (typeof teardown) {
|
37027 | case 'function':
|
37028 | subscription = new Subscription(teardown);
|
37029 | case 'object':
|
37030 | if (subscription === this || subscription.closed || typeof subscription.unsubscribe !== 'function') {
|
37031 | return subscription;
|
37032 | }
|
37033 | else if (this.closed) {
|
37034 | subscription.unsubscribe();
|
37035 | return subscription;
|
37036 | }
|
37037 | else if (!(subscription instanceof Subscription)) {
|
37038 | var tmp = subscription;
|
37039 | subscription = new Subscription();
|
37040 | subscription._subscriptions = [tmp];
|
37041 | }
|
37042 | break;
|
37043 | default: {
|
37044 | throw new Error('unrecognized teardown ' + teardown + ' added to Subscription.');
|
37045 | }
|
37046 | }
|
37047 | var _parentOrParents = subscription._parentOrParents;
|
37048 | if (_parentOrParents === null) {
|
37049 | subscription._parentOrParents = this;
|
37050 | }
|
37051 | else if (_parentOrParents instanceof Subscription) {
|
37052 | if (_parentOrParents === this) {
|
37053 | return subscription;
|
37054 | }
|
37055 | subscription._parentOrParents = [_parentOrParents, this];
|
37056 | }
|
37057 | else if (_parentOrParents.indexOf(this) === -1) {
|
37058 | _parentOrParents.push(this);
|
37059 | }
|
37060 | else {
|
37061 | return subscription;
|
37062 | }
|
37063 | var subscriptions = this._subscriptions;
|
37064 | if (subscriptions === null) {
|
37065 | this._subscriptions = [subscription];
|
37066 | }
|
37067 | else {
|
37068 | subscriptions.push(subscription);
|
37069 | }
|
37070 | return subscription;
|
37071 | };
|
37072 | Subscription.prototype.remove = function (subscription) {
|
37073 | var subscriptions = this._subscriptions;
|
37074 | if (subscriptions) {
|
37075 | var subscriptionIndex = subscriptions.indexOf(subscription);
|
37076 | if (subscriptionIndex !== -1) {
|
37077 | subscriptions.splice(subscriptionIndex, 1);
|
37078 | }
|
37079 | }
|
37080 | };
|
37081 | Subscription.EMPTY = (function (empty) {
|
37082 | empty.closed = true;
|
37083 | return empty;
|
37084 | }(new Subscription()));
|
37085 | return Subscription;
|
37086 | }());
|
37087 | function flattenUnsubscriptionErrors(errors) {
|
37088 | return errors.reduce(function (errs, err) { return errs.concat((err instanceof UnsubscriptionError) ? err.errors : err); }, []);
|
37089 | }
|
37090 |
|
37091 | /** PURE_IMPORTS_START PURE_IMPORTS_END */
|
37092 | var rxSubscriber = /*@__PURE__*/ (function () {
|
37093 | return typeof Symbol === 'function'
|
37094 | ? /*@__PURE__*/ Symbol('rxSubscriber')
|
37095 | : '@@rxSubscriber_' + /*@__PURE__*/ Math.random();
|
37096 | })();
|
37097 |
|
37098 | /** PURE_IMPORTS_START tslib,_util_isFunction,_Observer,_Subscription,_internal_symbol_rxSubscriber,_config,_util_hostReportError PURE_IMPORTS_END */
|
37099 | var Subscriber = /*@__PURE__*/ (function (_super) {
|
37100 | __extends(Subscriber, _super);
|
37101 | function Subscriber(destinationOrNext, error, complete) {
|
37102 | var _this = _super.call(this) || this;
|
37103 | _this.syncErrorValue = null;
|
37104 | _this.syncErrorThrown = false;
|
37105 | _this.syncErrorThrowable = false;
|
37106 | _this.isStopped = false;
|
37107 | switch (arguments.length) {
|
37108 | case 0:
|
37109 | _this.destination = empty;
|
37110 | break;
|
37111 | case 1:
|
37112 | if (!destinationOrNext) {
|
37113 | _this.destination = empty;
|
37114 | break;
|
37115 | }
|
37116 | if (typeof destinationOrNext === 'object') {
|
37117 | if (destinationOrNext instanceof Subscriber) {
|
37118 | _this.syncErrorThrowable = destinationOrNext.syncErrorThrowable;
|
37119 | _this.destination = destinationOrNext;
|
37120 | destinationOrNext.add(_this);
|
37121 | }
|
37122 | else {
|
37123 | _this.syncErrorThrowable = true;
|
37124 | _this.destination = new SafeSubscriber(_this, destinationOrNext);
|
37125 | }
|
37126 | break;
|
37127 | }
|
37128 | default:
|
37129 | _this.syncErrorThrowable = true;
|
37130 | _this.destination = new SafeSubscriber(_this, destinationOrNext, error, complete);
|
37131 | break;
|
37132 | }
|
37133 | return _this;
|
37134 | }
|
37135 | Subscriber.prototype[rxSubscriber] = function () { return this; };
|
37136 | Subscriber.create = function (next, error, complete) {
|
37137 | var subscriber = new Subscriber(next, error, complete);
|
37138 | subscriber.syncErrorThrowable = false;
|
37139 | return subscriber;
|
37140 | };
|
37141 | Subscriber.prototype.next = function (value) {
|
37142 | if (!this.isStopped) {
|
37143 | this._next(value);
|
37144 | }
|
37145 | };
|
37146 | Subscriber.prototype.error = function (err) {
|
37147 | if (!this.isStopped) {
|
37148 | this.isStopped = true;
|
37149 | this._error(err);
|
37150 | }
|
37151 | };
|
37152 | Subscriber.prototype.complete = function () {
|
37153 | if (!this.isStopped) {
|
37154 | this.isStopped = true;
|
37155 | this._complete();
|
37156 | }
|
37157 | };
|
37158 | Subscriber.prototype.unsubscribe = function () {
|
37159 | if (this.closed) {
|
37160 | return;
|
37161 | }
|
37162 | this.isStopped = true;
|
37163 | _super.prototype.unsubscribe.call(this);
|
37164 | };
|
37165 | Subscriber.prototype._next = function (value) {
|
37166 | this.destination.next(value);
|
37167 | };
|
37168 | Subscriber.prototype._error = function (err) {
|
37169 | this.destination.error(err);
|
37170 | this.unsubscribe();
|
37171 | };
|
37172 | Subscriber.prototype._complete = function () {
|
37173 | this.destination.complete();
|
37174 | this.unsubscribe();
|
37175 | };
|
37176 | Subscriber.prototype._unsubscribeAndRecycle = function () {
|
37177 | var _parentOrParents = this._parentOrParents;
|
37178 | this._parentOrParents = null;
|
37179 | this.unsubscribe();
|
37180 | this.closed = false;
|
37181 | this.isStopped = false;
|
37182 | this._parentOrParents = _parentOrParents;
|
37183 | return this;
|
37184 | };
|
37185 | return Subscriber;
|
37186 | }(Subscription));
|
37187 | var SafeSubscriber = /*@__PURE__*/ (function (_super) {
|
37188 | __extends(SafeSubscriber, _super);
|
37189 | function SafeSubscriber(_parentSubscriber, observerOrNext, error, complete) {
|
37190 | var _this = _super.call(this) || this;
|
37191 | _this._parentSubscriber = _parentSubscriber;
|
37192 | var next;
|
37193 | var context = _this;
|
37194 | if (isFunction(observerOrNext)) {
|
37195 | next = observerOrNext;
|
37196 | }
|
37197 | else if (observerOrNext) {
|
37198 | next = observerOrNext.next;
|
37199 | error = observerOrNext.error;
|
37200 | complete = observerOrNext.complete;
|
37201 | if (observerOrNext !== empty) {
|
37202 | context = Object.create(observerOrNext);
|
37203 | if (isFunction(context.unsubscribe)) {
|
37204 | _this.add(context.unsubscribe.bind(context));
|
37205 | }
|
37206 | context.unsubscribe = _this.unsubscribe.bind(_this);
|
37207 | }
|
37208 | }
|
37209 | _this._context = context;
|
37210 | _this._next = next;
|
37211 | _this._error = error;
|
37212 | _this._complete = complete;
|
37213 | return _this;
|
37214 | }
|
37215 | SafeSubscriber.prototype.next = function (value) {
|
37216 | if (!this.isStopped && this._next) {
|
37217 | var _parentSubscriber = this._parentSubscriber;
|
37218 | if (!config.useDeprecatedSynchronousErrorHandling || !_parentSubscriber.syncErrorThrowable) {
|
37219 | this.__tryOrUnsub(this._next, value);
|
37220 | }
|
37221 | else if (this.__tryOrSetError(_parentSubscriber, this._next, value)) {
|
37222 | this.unsubscribe();
|
37223 | }
|
37224 | }
|
37225 | };
|
37226 | SafeSubscriber.prototype.error = function (err) {
|
37227 | if (!this.isStopped) {
|
37228 | var _parentSubscriber = this._parentSubscriber;
|
37229 | var useDeprecatedSynchronousErrorHandling = config.useDeprecatedSynchronousErrorHandling;
|
37230 | if (this._error) {
|
37231 | if (!useDeprecatedSynchronousErrorHandling || !_parentSubscriber.syncErrorThrowable) {
|
37232 | this.__tryOrUnsub(this._error, err);
|
37233 | this.unsubscribe();
|
37234 | }
|
37235 | else {
|
37236 | this.__tryOrSetError(_parentSubscriber, this._error, err);
|
37237 | this.unsubscribe();
|
37238 | }
|
37239 | }
|
37240 | else if (!_parentSubscriber.syncErrorThrowable) {
|
37241 | this.unsubscribe();
|
37242 | if (useDeprecatedSynchronousErrorHandling) {
|
37243 | throw err;
|
37244 | }
|
37245 | hostReportError(err);
|
37246 | }
|
37247 | else {
|
37248 | if (useDeprecatedSynchronousErrorHandling) {
|
37249 | _parentSubscriber.syncErrorValue = err;
|
37250 | _parentSubscriber.syncErrorThrown = true;
|
37251 | }
|
37252 | else {
|
37253 | hostReportError(err);
|
37254 | }
|
37255 | this.unsubscribe();
|
37256 | }
|
37257 | }
|
37258 | };
|
37259 | SafeSubscriber.prototype.complete = function () {
|
37260 | var _this = this;
|
37261 | if (!this.isStopped) {
|
37262 | var _parentSubscriber = this._parentSubscriber;
|
37263 | if (this._complete) {
|
37264 | var wrappedComplete = function () { return _this._complete.call(_this._context); };
|
37265 | if (!config.useDeprecatedSynchronousErrorHandling || !_parentSubscriber.syncErrorThrowable) {
|
37266 | this.__tryOrUnsub(wrappedComplete);
|
37267 | this.unsubscribe();
|
37268 | }
|
37269 | else {
|
37270 | this.__tryOrSetError(_parentSubscriber, wrappedComplete);
|
37271 | this.unsubscribe();
|
37272 | }
|
37273 | }
|
37274 | else {
|
37275 | this.unsubscribe();
|
37276 | }
|
37277 | }
|
37278 | };
|
37279 | SafeSubscriber.prototype.__tryOrUnsub = function (fn, value) {
|
37280 | try {
|
37281 | fn.call(this._context, value);
|
37282 | }
|
37283 | catch (err) {
|
37284 | this.unsubscribe();
|
37285 | if (config.useDeprecatedSynchronousErrorHandling) {
|
37286 | throw err;
|
37287 | }
|
37288 | else {
|
37289 | hostReportError(err);
|
37290 | }
|
37291 | }
|
37292 | };
|
37293 | SafeSubscriber.prototype.__tryOrSetError = function (parent, fn, value) {
|
37294 | if (!config.useDeprecatedSynchronousErrorHandling) {
|
37295 | throw new Error('bad call');
|
37296 | }
|
37297 | try {
|
37298 | fn.call(this._context, value);
|
37299 | }
|
37300 | catch (err) {
|
37301 | if (config.useDeprecatedSynchronousErrorHandling) {
|
37302 | parent.syncErrorValue = err;
|
37303 | parent.syncErrorThrown = true;
|
37304 | return true;
|
37305 | }
|
37306 | else {
|
37307 | hostReportError(err);
|
37308 | return true;
|
37309 | }
|
37310 | }
|
37311 | return false;
|
37312 | };
|
37313 | SafeSubscriber.prototype._unsubscribe = function () {
|
37314 | var _parentSubscriber = this._parentSubscriber;
|
37315 | this._context = null;
|
37316 | this._parentSubscriber = null;
|
37317 | _parentSubscriber.unsubscribe();
|
37318 | };
|
37319 | return SafeSubscriber;
|
37320 | }(Subscriber));
|
37321 |
|
37322 | /** PURE_IMPORTS_START _Subscriber PURE_IMPORTS_END */
|
37323 | function canReportError(observer) {
|
37324 | while (observer) {
|
37325 | var _a = observer, closed_1 = _a.closed, destination = _a.destination, isStopped = _a.isStopped;
|
37326 | if (closed_1 || isStopped) {
|
37327 | return false;
|
37328 | }
|
37329 | else if (destination && destination instanceof Subscriber) {
|
37330 | observer = destination;
|
37331 | }
|
37332 | else {
|
37333 | observer = null;
|
37334 | }
|
37335 | }
|
37336 | return true;
|
37337 | }
|
37338 |
|
37339 | /** PURE_IMPORTS_START _Subscriber,_symbol_rxSubscriber,_Observer PURE_IMPORTS_END */
|
37340 | function toSubscriber(nextOrObserver, error, complete) {
|
37341 | if (nextOrObserver) {
|
37342 | if (nextOrObserver instanceof Subscriber) {
|
37343 | return nextOrObserver;
|
37344 | }
|
37345 | if (nextOrObserver[rxSubscriber]) {
|
37346 | return nextOrObserver[rxSubscriber]();
|
37347 | }
|
37348 | }
|
37349 | if (!nextOrObserver && !error && !complete) {
|
37350 | return new Subscriber(empty);
|
37351 | }
|
37352 | return new Subscriber(nextOrObserver, error, complete);
|
37353 | }
|
37354 |
|
37355 | /** PURE_IMPORTS_START PURE_IMPORTS_END */
|
37356 | var observable = /*@__PURE__*/ (function () { return typeof Symbol === 'function' && Symbol.observable || '@@observable'; })();
|
37357 |
|
37358 | /** PURE_IMPORTS_START PURE_IMPORTS_END */
|
37359 | function noop$1() { }
|
37360 |
|
37361 | /** PURE_IMPORTS_START _noop PURE_IMPORTS_END */
|
37362 | function pipeFromArray(fns) {
|
37363 | if (!fns) {
|
37364 | return noop$1;
|
37365 | }
|
37366 | if (fns.length === 1) {
|
37367 | return fns[0];
|
37368 | }
|
37369 | return function piped(input) {
|
37370 | return fns.reduce(function (prev, fn) { return fn(prev); }, input);
|
37371 | };
|
37372 | }
|
37373 |
|
37374 | /** PURE_IMPORTS_START _util_canReportError,_util_toSubscriber,_symbol_observable,_util_pipe,_config PURE_IMPORTS_END */
|
37375 | var Observable = /*@__PURE__*/ (function () {
|
37376 | function Observable(subscribe) {
|
37377 | this._isScalar = false;
|
37378 | if (subscribe) {
|
37379 | this._subscribe = subscribe;
|
37380 | }
|
37381 | }
|
37382 | Observable.prototype.lift = function (operator) {
|
37383 | var observable = new Observable();
|
37384 | observable.source = this;
|
37385 | observable.operator = operator;
|
37386 | return observable;
|
37387 | };
|
37388 | Observable.prototype.subscribe = function (observerOrNext, error, complete) {
|
37389 | var operator = this.operator;
|
37390 | var sink = toSubscriber(observerOrNext, error, complete);
|
37391 | if (operator) {
|
37392 | sink.add(operator.call(sink, this.source));
|
37393 | }
|
37394 | else {
|
37395 | sink.add(this.source || (config.useDeprecatedSynchronousErrorHandling && !sink.syncErrorThrowable) ?
|
37396 | this._subscribe(sink) :
|
37397 | this._trySubscribe(sink));
|
37398 | }
|
37399 | if (config.useDeprecatedSynchronousErrorHandling) {
|
37400 | if (sink.syncErrorThrowable) {
|
37401 | sink.syncErrorThrowable = false;
|
37402 | if (sink.syncErrorThrown) {
|
37403 | throw sink.syncErrorValue;
|
37404 | }
|
37405 | }
|
37406 | }
|
37407 | return sink;
|
37408 | };
|
37409 | Observable.prototype._trySubscribe = function (sink) {
|
37410 | try {
|
37411 | return this._subscribe(sink);
|
37412 | }
|
37413 | catch (err) {
|
37414 | if (config.useDeprecatedSynchronousErrorHandling) {
|
37415 | sink.syncErrorThrown = true;
|
37416 | sink.syncErrorValue = err;
|
37417 | }
|
37418 | if (canReportError(sink)) {
|
37419 | sink.error(err);
|
37420 | }
|
37421 | else {
|
37422 | console.warn(err);
|
37423 | }
|
37424 | }
|
37425 | };
|
37426 | Observable.prototype.forEach = function (next, promiseCtor) {
|
37427 | var _this = this;
|
37428 | promiseCtor = getPromiseCtor(promiseCtor);
|
37429 | return new promiseCtor(function (resolve, reject) {
|
37430 | var subscription;
|
37431 | subscription = _this.subscribe(function (value) {
|
37432 | try {
|
37433 | next(value);
|
37434 | }
|
37435 | catch (err) {
|
37436 | reject(err);
|
37437 | if (subscription) {
|
37438 | subscription.unsubscribe();
|
37439 | }
|
37440 | }
|
37441 | }, reject, resolve);
|
37442 | });
|
37443 | };
|
37444 | Observable.prototype._subscribe = function (subscriber) {
|
37445 | var source = this.source;
|
37446 | return source && source.subscribe(subscriber);
|
37447 | };
|
37448 | Observable.prototype[observable] = function () {
|
37449 | return this;
|
37450 | };
|
37451 | Observable.prototype.pipe = function () {
|
37452 | var operations = [];
|
37453 | for (var _i = 0; _i < arguments.length; _i++) {
|
37454 | operations[_i] = arguments[_i];
|
37455 | }
|
37456 | if (operations.length === 0) {
|
37457 | return this;
|
37458 | }
|
37459 | return pipeFromArray(operations)(this);
|
37460 | };
|
37461 | Observable.prototype.toPromise = function (promiseCtor) {
|
37462 | var _this = this;
|
37463 | promiseCtor = getPromiseCtor(promiseCtor);
|
37464 | return new promiseCtor(function (resolve, reject) {
|
37465 | var value;
|
37466 | _this.subscribe(function (x) { return value = x; }, function (err) { return reject(err); }, function () { return resolve(value); });
|
37467 | });
|
37468 | };
|
37469 | Observable.create = function (subscribe) {
|
37470 | return new Observable(subscribe);
|
37471 | };
|
37472 | return Observable;
|
37473 | }());
|
37474 | function getPromiseCtor(promiseCtor) {
|
37475 | if (!promiseCtor) {
|
37476 | promiseCtor = Promise;
|
37477 | }
|
37478 | if (!promiseCtor) {
|
37479 | throw new Error('no Promise impl found');
|
37480 | }
|
37481 | return promiseCtor;
|
37482 | }
|
37483 |
|
37484 | /** PURE_IMPORTS_START PURE_IMPORTS_END */
|
37485 | var ObjectUnsubscribedErrorImpl = /*@__PURE__*/ (function () {
|
37486 | function ObjectUnsubscribedErrorImpl() {
|
37487 | Error.call(this);
|
37488 | this.message = 'object unsubscribed';
|
37489 | this.name = 'ObjectUnsubscribedError';
|
37490 | return this;
|
37491 | }
|
37492 | ObjectUnsubscribedErrorImpl.prototype = /*@__PURE__*/ Object.create(Error.prototype);
|
37493 | return ObjectUnsubscribedErrorImpl;
|
37494 | })();
|
37495 | var ObjectUnsubscribedError = ObjectUnsubscribedErrorImpl;
|
37496 |
|
37497 | /** PURE_IMPORTS_START tslib,_Subscription PURE_IMPORTS_END */
|
37498 | var SubjectSubscription = /*@__PURE__*/ (function (_super) {
|
37499 | __extends(SubjectSubscription, _super);
|
37500 | function SubjectSubscription(subject, subscriber) {
|
37501 | var _this = _super.call(this) || this;
|
37502 | _this.subject = subject;
|
37503 | _this.subscriber = subscriber;
|
37504 | _this.closed = false;
|
37505 | return _this;
|
37506 | }
|
37507 | SubjectSubscription.prototype.unsubscribe = function () {
|
37508 | if (this.closed) {
|
37509 | return;
|
37510 | }
|
37511 | this.closed = true;
|
37512 | var subject = this.subject;
|
37513 | var observers = subject.observers;
|
37514 | this.subject = null;
|
37515 | if (!observers || observers.length === 0 || subject.isStopped || subject.closed) {
|
37516 | return;
|
37517 | }
|
37518 | var subscriberIndex = observers.indexOf(this.subscriber);
|
37519 | if (subscriberIndex !== -1) {
|
37520 | observers.splice(subscriberIndex, 1);
|
37521 | }
|
37522 | };
|
37523 | return SubjectSubscription;
|
37524 | }(Subscription));
|
37525 |
|
37526 | /** PURE_IMPORTS_START tslib,_Observable,_Subscriber,_Subscription,_util_ObjectUnsubscribedError,_SubjectSubscription,_internal_symbol_rxSubscriber PURE_IMPORTS_END */
|
37527 | var SubjectSubscriber = /*@__PURE__*/ (function (_super) {
|
37528 | __extends(SubjectSubscriber, _super);
|
37529 | function SubjectSubscriber(destination) {
|
37530 | var _this = _super.call(this, destination) || this;
|
37531 | _this.destination = destination;
|
37532 | return _this;
|
37533 | }
|
37534 | return SubjectSubscriber;
|
37535 | }(Subscriber));
|
37536 | var Subject = /*@__PURE__*/ (function (_super) {
|
37537 | __extends(Subject, _super);
|
37538 | function Subject() {
|
37539 | var _this = _super.call(this) || this;
|
37540 | _this.observers = [];
|
37541 | _this.closed = false;
|
37542 | _this.isStopped = false;
|
37543 | _this.hasError = false;
|
37544 | _this.thrownError = null;
|
37545 | return _this;
|
37546 | }
|
37547 | Subject.prototype[rxSubscriber] = function () {
|
37548 | return new SubjectSubscriber(this);
|
37549 | };
|
37550 | Subject.prototype.lift = function (operator) {
|
37551 | var subject = new AnonymousSubject(this, this);
|
37552 | subject.operator = operator;
|
37553 | return subject;
|
37554 | };
|
37555 | Subject.prototype.next = function (value) {
|
37556 | if (this.closed) {
|
37557 | throw new ObjectUnsubscribedError();
|
37558 | }
|
37559 | if (!this.isStopped) {
|
37560 | var observers = this.observers;
|
37561 | var len = observers.length;
|
37562 | var copy = observers.slice();
|
37563 | for (var i = 0; i < len; i++) {
|
37564 | copy[i].next(value);
|
37565 | }
|
37566 | }
|
37567 | };
|
37568 | Subject.prototype.error = function (err) {
|
37569 | if (this.closed) {
|
37570 | throw new ObjectUnsubscribedError();
|
37571 | }
|
37572 | this.hasError = true;
|
37573 | this.thrownError = err;
|
37574 | this.isStopped = true;
|
37575 | var observers = this.observers;
|
37576 | var len = observers.length;
|
37577 | var copy = observers.slice();
|
37578 | for (var i = 0; i < len; i++) {
|
37579 | copy[i].error(err);
|
37580 | }
|
37581 | this.observers.length = 0;
|
37582 | };
|
37583 | Subject.prototype.complete = function () {
|
37584 | if (this.closed) {
|
37585 | throw new ObjectUnsubscribedError();
|
37586 | }
|
37587 | this.isStopped = true;
|
37588 | var observers = this.observers;
|
37589 | var len = observers.length;
|
37590 | var copy = observers.slice();
|
37591 | for (var i = 0; i < len; i++) {
|
37592 | copy[i].complete();
|
37593 | }
|
37594 | this.observers.length = 0;
|
37595 | };
|
37596 | Subject.prototype.unsubscribe = function () {
|
37597 | this.isStopped = true;
|
37598 | this.closed = true;
|
37599 | this.observers = null;
|
37600 | };
|
37601 | Subject.prototype._trySubscribe = function (subscriber) {
|
37602 | if (this.closed) {
|
37603 | throw new ObjectUnsubscribedError();
|
37604 | }
|
37605 | else {
|
37606 | return _super.prototype._trySubscribe.call(this, subscriber);
|
37607 | }
|
37608 | };
|
37609 | Subject.prototype._subscribe = function (subscriber) {
|
37610 | if (this.closed) {
|
37611 | throw new ObjectUnsubscribedError();
|
37612 | }
|
37613 | else if (this.hasError) {
|
37614 | subscriber.error(this.thrownError);
|
37615 | return Subscription.EMPTY;
|
37616 | }
|
37617 | else if (this.isStopped) {
|
37618 | subscriber.complete();
|
37619 | return Subscription.EMPTY;
|
37620 | }
|
37621 | else {
|
37622 | this.observers.push(subscriber);
|
37623 | return new SubjectSubscription(this, subscriber);
|
37624 | }
|
37625 | };
|
37626 | Subject.prototype.asObservable = function () {
|
37627 | var observable = new Observable();
|
37628 | observable.source = this;
|
37629 | return observable;
|
37630 | };
|
37631 | Subject.create = function (destination, source) {
|
37632 | return new AnonymousSubject(destination, source);
|
37633 | };
|
37634 | return Subject;
|
37635 | }(Observable));
|
37636 | var AnonymousSubject = /*@__PURE__*/ (function (_super) {
|
37637 | __extends(AnonymousSubject, _super);
|
37638 | function AnonymousSubject(destination, source) {
|
37639 | var _this = _super.call(this) || this;
|
37640 | _this.destination = destination;
|
37641 | _this.source = source;
|
37642 | return _this;
|
37643 | }
|
37644 | AnonymousSubject.prototype.next = function (value) {
|
37645 | var destination = this.destination;
|
37646 | if (destination && destination.next) {
|
37647 | destination.next(value);
|
37648 | }
|
37649 | };
|
37650 | AnonymousSubject.prototype.error = function (err) {
|
37651 | var destination = this.destination;
|
37652 | if (destination && destination.error) {
|
37653 | this.destination.error(err);
|
37654 | }
|
37655 | };
|
37656 | AnonymousSubject.prototype.complete = function () {
|
37657 | var destination = this.destination;
|
37658 | if (destination && destination.complete) {
|
37659 | this.destination.complete();
|
37660 | }
|
37661 | };
|
37662 | AnonymousSubject.prototype._subscribe = function (subscriber) {
|
37663 | var source = this.source;
|
37664 | if (source) {
|
37665 | return this.source.subscribe(subscriber);
|
37666 | }
|
37667 | else {
|
37668 | return Subscription.EMPTY;
|
37669 | }
|
37670 | };
|
37671 | return AnonymousSubject;
|
37672 | }(Subject));
|
37673 |
|
37674 | /** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */
|
37675 | function refCount() {
|
37676 | return function refCountOperatorFunction(source) {
|
37677 | return source.lift(new RefCountOperator(source));
|
37678 | };
|
37679 | }
|
37680 | var RefCountOperator = /*@__PURE__*/ (function () {
|
37681 | function RefCountOperator(connectable) {
|
37682 | this.connectable = connectable;
|
37683 | }
|
37684 | RefCountOperator.prototype.call = function (subscriber, source) {
|
37685 | var connectable = this.connectable;
|
37686 | connectable._refCount++;
|
37687 | var refCounter = new RefCountSubscriber(subscriber, connectable);
|
37688 | var subscription = source.subscribe(refCounter);
|
37689 | if (!refCounter.closed) {
|
37690 | refCounter.connection = connectable.connect();
|
37691 | }
|
37692 | return subscription;
|
37693 | };
|
37694 | return RefCountOperator;
|
37695 | }());
|
37696 | var RefCountSubscriber = /*@__PURE__*/ (function (_super) {
|
37697 | __extends(RefCountSubscriber, _super);
|
37698 | function RefCountSubscriber(destination, connectable) {
|
37699 | var _this = _super.call(this, destination) || this;
|
37700 | _this.connectable = connectable;
|
37701 | return _this;
|
37702 | }
|
37703 | RefCountSubscriber.prototype._unsubscribe = function () {
|
37704 | var connectable = this.connectable;
|
37705 | if (!connectable) {
|
37706 | this.connection = null;
|
37707 | return;
|
37708 | }
|
37709 | this.connectable = null;
|
37710 | var refCount = connectable._refCount;
|
37711 | if (refCount <= 0) {
|
37712 | this.connection = null;
|
37713 | return;
|
37714 | }
|
37715 | connectable._refCount = refCount - 1;
|
37716 | if (refCount > 1) {
|
37717 | this.connection = null;
|
37718 | return;
|
37719 | }
|
37720 | var connection = this.connection;
|
37721 | var sharedConnection = connectable._connection;
|
37722 | this.connection = null;
|
37723 | if (sharedConnection && (!connection || sharedConnection === connection)) {
|
37724 | sharedConnection.unsubscribe();
|
37725 | }
|
37726 | };
|
37727 | return RefCountSubscriber;
|
37728 | }(Subscriber));
|
37729 |
|
37730 | /** PURE_IMPORTS_START tslib,_Subject,_Observable,_Subscriber,_Subscription,_operators_refCount PURE_IMPORTS_END */
|
37731 | var ConnectableObservable = /*@__PURE__*/ (function (_super) {
|
37732 | __extends(ConnectableObservable, _super);
|
37733 | function ConnectableObservable(source, subjectFactory) {
|
37734 | var _this = _super.call(this) || this;
|
37735 | _this.source = source;
|
37736 | _this.subjectFactory = subjectFactory;
|
37737 | _this._refCount = 0;
|
37738 | _this._isComplete = false;
|
37739 | return _this;
|
37740 | }
|
37741 | ConnectableObservable.prototype._subscribe = function (subscriber) {
|
37742 | return this.getSubject().subscribe(subscriber);
|
37743 | };
|
37744 | ConnectableObservable.prototype.getSubject = function () {
|
37745 | var subject = this._subject;
|
37746 | if (!subject || subject.isStopped) {
|
37747 | this._subject = this.subjectFactory();
|
37748 | }
|
37749 | return this._subject;
|
37750 | };
|
37751 | ConnectableObservable.prototype.connect = function () {
|
37752 | var connection = this._connection;
|
37753 | if (!connection) {
|
37754 | this._isComplete = false;
|
37755 | connection = this._connection = new Subscription();
|
37756 | connection.add(this.source
|
37757 | .subscribe(new ConnectableSubscriber(this.getSubject(), this)));
|
37758 | if (connection.closed) {
|
37759 | this._connection = null;
|
37760 | connection = Subscription.EMPTY;
|
37761 | }
|
37762 | }
|
37763 | return connection;
|
37764 | };
|
37765 | ConnectableObservable.prototype.refCount = function () {
|
37766 | return refCount()(this);
|
37767 | };
|
37768 | return ConnectableObservable;
|
37769 | }(Observable));
|
37770 | var connectableObservableDescriptor = /*@__PURE__*/ (function () {
|
37771 | var connectableProto = ConnectableObservable.prototype;
|
37772 | return {
|
37773 | operator: { value: null },
|
37774 | _refCount: { value: 0, writable: true },
|
37775 | _subject: { value: null, writable: true },
|
37776 | _connection: { value: null, writable: true },
|
37777 | _subscribe: { value: connectableProto._subscribe },
|
37778 | _isComplete: { value: connectableProto._isComplete, writable: true },
|
37779 | getSubject: { value: connectableProto.getSubject },
|
37780 | connect: { value: connectableProto.connect },
|
37781 | refCount: { value: connectableProto.refCount }
|
37782 | };
|
37783 | })();
|
37784 | var ConnectableSubscriber = /*@__PURE__*/ (function (_super) {
|
37785 | __extends(ConnectableSubscriber, _super);
|
37786 | function ConnectableSubscriber(destination, connectable) {
|
37787 | var _this = _super.call(this, destination) || this;
|
37788 | _this.connectable = connectable;
|
37789 | return _this;
|
37790 | }
|
37791 | ConnectableSubscriber.prototype._error = function (err) {
|
37792 | this._unsubscribe();
|
37793 | _super.prototype._error.call(this, err);
|
37794 | };
|
37795 | ConnectableSubscriber.prototype._complete = function () {
|
37796 | this.connectable._isComplete = true;
|
37797 | this._unsubscribe();
|
37798 | _super.prototype._complete.call(this);
|
37799 | };
|
37800 | ConnectableSubscriber.prototype._unsubscribe = function () {
|
37801 | var connectable = this.connectable;
|
37802 | if (connectable) {
|
37803 | this.connectable = null;
|
37804 | var connection = connectable._connection;
|
37805 | connectable._refCount = 0;
|
37806 | connectable._subject = null;
|
37807 | connectable._connection = null;
|
37808 | if (connection) {
|
37809 | connection.unsubscribe();
|
37810 | }
|
37811 | }
|
37812 | };
|
37813 | return ConnectableSubscriber;
|
37814 | }(SubjectSubscriber));
|
37815 |
|
37816 | /** PURE_IMPORTS_START PURE_IMPORTS_END */
|
37817 | function isScheduler(value) {
|
37818 | return value && typeof value.schedule === 'function';
|
37819 | }
|
37820 |
|
37821 | /** PURE_IMPORTS_START PURE_IMPORTS_END */
|
37822 | var subscribeToArray = function (array) {
|
37823 | return function (subscriber) {
|
37824 | for (var i = 0, len = array.length; i < len && !subscriber.closed; i++) {
|
37825 | subscriber.next(array[i]);
|
37826 | }
|
37827 | subscriber.complete();
|
37828 | };
|
37829 | };
|
37830 |
|
37831 | /** PURE_IMPORTS_START _Observable,_Subscription PURE_IMPORTS_END */
|
37832 | function scheduleArray(input, scheduler) {
|
37833 | return new Observable(function (subscriber) {
|
37834 | var sub = new Subscription();
|
37835 | var i = 0;
|
37836 | sub.add(scheduler.schedule(function () {
|
37837 | if (i === input.length) {
|
37838 | subscriber.complete();
|
37839 | return;
|
37840 | }
|
37841 | subscriber.next(input[i++]);
|
37842 | if (!subscriber.closed) {
|
37843 | sub.add(this.schedule());
|
37844 | }
|
37845 | }));
|
37846 | return sub;
|
37847 | });
|
37848 | }
|
37849 |
|
37850 | /** PURE_IMPORTS_START _Observable,_util_subscribeToArray,_scheduled_scheduleArray PURE_IMPORTS_END */
|
37851 | function fromArray(input, scheduler) {
|
37852 | if (!scheduler) {
|
37853 | return new Observable(subscribeToArray(input));
|
37854 | }
|
37855 | else {
|
37856 | return scheduleArray(input, scheduler);
|
37857 | }
|
37858 | }
|
37859 |
|
37860 | /** PURE_IMPORTS_START PURE_IMPORTS_END */
|
37861 | function identity(x) {
|
37862 | return x;
|
37863 | }
|
37864 |
|
37865 | /** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */
|
37866 | function map(project, thisArg) {
|
37867 | return function mapOperation(source) {
|
37868 | if (typeof project !== 'function') {
|
37869 | throw new TypeError('argument is not a function. Are you looking for `mapTo()`?');
|
37870 | }
|
37871 | return source.lift(new MapOperator(project, thisArg));
|
37872 | };
|
37873 | }
|
37874 | var MapOperator = /*@__PURE__*/ (function () {
|
37875 | function MapOperator(project, thisArg) {
|
37876 | this.project = project;
|
37877 | this.thisArg = thisArg;
|
37878 | }
|
37879 | MapOperator.prototype.call = function (subscriber, source) {
|
37880 | return source.subscribe(new MapSubscriber(subscriber, this.project, this.thisArg));
|
37881 | };
|
37882 | return MapOperator;
|
37883 | }());
|
37884 | var MapSubscriber = /*@__PURE__*/ (function (_super) {
|
37885 | __extends(MapSubscriber, _super);
|
37886 | function MapSubscriber(destination, project, thisArg) {
|
37887 | var _this = _super.call(this, destination) || this;
|
37888 | _this.project = project;
|
37889 | _this.count = 0;
|
37890 | _this.thisArg = thisArg || _this;
|
37891 | return _this;
|
37892 | }
|
37893 | MapSubscriber.prototype._next = function (value) {
|
37894 | var result;
|
37895 | try {
|
37896 | result = this.project.call(this.thisArg, value, this.count++);
|
37897 | }
|
37898 | catch (err) {
|
37899 | this.destination.error(err);
|
37900 | return;
|
37901 | }
|
37902 | this.destination.next(result);
|
37903 | };
|
37904 | return MapSubscriber;
|
37905 | }(Subscriber));
|
37906 |
|
37907 | /** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */
|
37908 | var OuterSubscriber = /*@__PURE__*/ (function (_super) {
|
37909 | __extends(OuterSubscriber, _super);
|
37910 | function OuterSubscriber() {
|
37911 | return _super !== null && _super.apply(this, arguments) || this;
|
37912 | }
|
37913 | OuterSubscriber.prototype.notifyNext = function (outerValue, innerValue, outerIndex, innerIndex, innerSub) {
|
37914 | this.destination.next(innerValue);
|
37915 | };
|
37916 | OuterSubscriber.prototype.notifyError = function (error, innerSub) {
|
37917 | this.destination.error(error);
|
37918 | };
|
37919 | OuterSubscriber.prototype.notifyComplete = function (innerSub) {
|
37920 | this.destination.complete();
|
37921 | };
|
37922 | return OuterSubscriber;
|
37923 | }(Subscriber));
|
37924 |
|
37925 | /** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */
|
37926 | var InnerSubscriber = /*@__PURE__*/ (function (_super) {
|
37927 | __extends(InnerSubscriber, _super);
|
37928 | function InnerSubscriber(parent, outerValue, outerIndex) {
|
37929 | var _this = _super.call(this) || this;
|
37930 | _this.parent = parent;
|
37931 | _this.outerValue = outerValue;
|
37932 | _this.outerIndex = outerIndex;
|
37933 | _this.index = 0;
|
37934 | return _this;
|
37935 | }
|
37936 | InnerSubscriber.prototype._next = function (value) {
|
37937 | this.parent.notifyNext(this.outerValue, value, this.outerIndex, this.index++, this);
|
37938 | };
|
37939 | InnerSubscriber.prototype._error = function (error) {
|
37940 | this.parent.notifyError(error, this);
|
37941 | this.unsubscribe();
|
37942 | };
|
37943 | InnerSubscriber.prototype._complete = function () {
|
37944 | this.parent.notifyComplete(this);
|
37945 | this.unsubscribe();
|
37946 | };
|
37947 | return InnerSubscriber;
|
37948 | }(Subscriber));
|
37949 |
|
37950 | /** PURE_IMPORTS_START _hostReportError PURE_IMPORTS_END */
|
37951 | var subscribeToPromise = function (promise) {
|
37952 | return function (subscriber) {
|
37953 | promise.then(function (value) {
|
37954 | if (!subscriber.closed) {
|
37955 | subscriber.next(value);
|
37956 | subscriber.complete();
|
37957 | }
|
37958 | }, function (err) { return subscriber.error(err); })
|
37959 | .then(null, hostReportError);
|
37960 | return subscriber;
|
37961 | };
|
37962 | };
|
37963 |
|
37964 | /** PURE_IMPORTS_START PURE_IMPORTS_END */
|
37965 | function getSymbolIterator$1() {
|
37966 | if (typeof Symbol !== 'function' || !Symbol.iterator) {
|
37967 | return '@@iterator';
|
37968 | }
|
37969 | return Symbol.iterator;
|
37970 | }
|
37971 | var iterator = /*@__PURE__*/ getSymbolIterator$1();
|
37972 |
|
37973 | /** PURE_IMPORTS_START _symbol_iterator PURE_IMPORTS_END */
|
37974 | var subscribeToIterable = function (iterable) {
|
37975 | return function (subscriber) {
|
37976 | var iterator$1 = iterable[iterator]();
|
37977 | do {
|
37978 | var item = iterator$1.next();
|
37979 | if (item.done) {
|
37980 | subscriber.complete();
|
37981 | break;
|
37982 | }
|
37983 | subscriber.next(item.value);
|
37984 | if (subscriber.closed) {
|
37985 | break;
|
37986 | }
|
37987 | } while (true);
|
37988 | if (typeof iterator$1.return === 'function') {
|
37989 | subscriber.add(function () {
|
37990 | if (iterator$1.return) {
|
37991 | iterator$1.return();
|
37992 | }
|
37993 | });
|
37994 | }
|
37995 | return subscriber;
|
37996 | };
|
37997 | };
|
37998 |
|
37999 | /** PURE_IMPORTS_START _symbol_observable PURE_IMPORTS_END */
|
38000 | var subscribeToObservable = function (obj) {
|
38001 | return function (subscriber) {
|
38002 | var obs = obj[observable]();
|
38003 | if (typeof obs.subscribe !== 'function') {
|
38004 | throw new TypeError('Provided object does not correctly implement Symbol.observable');
|
38005 | }
|
38006 | else {
|
38007 | return obs.subscribe(subscriber);
|
38008 | }
|
38009 | };
|
38010 | };
|
38011 |
|
38012 | /** PURE_IMPORTS_START PURE_IMPORTS_END */
|
38013 | var isArrayLike = (function (x) { return x && typeof x.length === 'number' && typeof x !== 'function'; });
|
38014 |
|
38015 | /** PURE_IMPORTS_START PURE_IMPORTS_END */
|
38016 | function isPromise$2(value) {
|
38017 | return !!value && typeof value.subscribe !== 'function' && typeof value.then === 'function';
|
38018 | }
|
38019 |
|
38020 | /** PURE_IMPORTS_START _subscribeToArray,_subscribeToPromise,_subscribeToIterable,_subscribeToObservable,_isArrayLike,_isPromise,_isObject,_symbol_iterator,_symbol_observable PURE_IMPORTS_END */
|
38021 | var subscribeTo = function (result) {
|
38022 | if (!!result && typeof result[observable] === 'function') {
|
38023 | return subscribeToObservable(result);
|
38024 | }
|
38025 | else if (isArrayLike(result)) {
|
38026 | return subscribeToArray(result);
|
38027 | }
|
38028 | else if (isPromise$2(result)) {
|
38029 | return subscribeToPromise(result);
|
38030 | }
|
38031 | else if (!!result && typeof result[iterator] === 'function') {
|
38032 | return subscribeToIterable(result);
|
38033 | }
|
38034 | else {
|
38035 | var value = isObject(result) ? 'an invalid object' : "'" + result + "'";
|
38036 | var msg = "You provided " + value + " where a stream was expected."
|
38037 | + ' You can provide an Observable, Promise, Array, or Iterable.';
|
38038 | throw new TypeError(msg);
|
38039 | }
|
38040 | };
|
38041 |
|
38042 | /** PURE_IMPORTS_START _InnerSubscriber,_subscribeTo,_Observable PURE_IMPORTS_END */
|
38043 | function subscribeToResult(outerSubscriber, result, outerValue, outerIndex, innerSubscriber) {
|
38044 | if (innerSubscriber === void 0) {
|
38045 | innerSubscriber = new InnerSubscriber(outerSubscriber, outerValue, outerIndex);
|
38046 | }
|
38047 | if (innerSubscriber.closed) {
|
38048 | return undefined;
|
38049 | }
|
38050 | if (result instanceof Observable) {
|
38051 | return result.subscribe(innerSubscriber);
|
38052 | }
|
38053 | return subscribeTo(result)(innerSubscriber);
|
38054 | }
|
38055 |
|
38056 | /** PURE_IMPORTS_START _Observable,_Subscription,_symbol_observable PURE_IMPORTS_END */
|
38057 | function scheduleObservable(input, scheduler) {
|
38058 | return new Observable(function (subscriber) {
|
38059 | var sub = new Subscription();
|
38060 | sub.add(scheduler.schedule(function () {
|
38061 | var observable$1 = input[observable]();
|
38062 | sub.add(observable$1.subscribe({
|
38063 | next: function (value) { sub.add(scheduler.schedule(function () { return subscriber.next(value); })); },
|
38064 | error: function (err) { sub.add(scheduler.schedule(function () { return subscriber.error(err); })); },
|
38065 | complete: function () { sub.add(scheduler.schedule(function () { return subscriber.complete(); })); },
|
38066 | }));
|
38067 | }));
|
38068 | return sub;
|
38069 | });
|
38070 | }
|
38071 |
|
38072 | /** PURE_IMPORTS_START _Observable,_Subscription PURE_IMPORTS_END */
|
38073 | function schedulePromise(input, scheduler) {
|
38074 | return new Observable(function (subscriber) {
|
38075 | var sub = new Subscription();
|
38076 | sub.add(scheduler.schedule(function () {
|
38077 | return input.then(function (value) {
|
38078 | sub.add(scheduler.schedule(function () {
|
38079 | subscriber.next(value);
|
38080 | sub.add(scheduler.schedule(function () { return subscriber.complete(); }));
|
38081 | }));
|
38082 | }, function (err) {
|
38083 | sub.add(scheduler.schedule(function () { return subscriber.error(err); }));
|
38084 | });
|
38085 | }));
|
38086 | return sub;
|
38087 | });
|
38088 | }
|
38089 |
|
38090 | /** PURE_IMPORTS_START _Observable,_Subscription,_symbol_iterator PURE_IMPORTS_END */
|
38091 | function scheduleIterable(input, scheduler) {
|
38092 | if (!input) {
|
38093 | throw new Error('Iterable cannot be null');
|
38094 | }
|
38095 | return new Observable(function (subscriber) {
|
38096 | var sub = new Subscription();
|
38097 | var iterator$1;
|
38098 | sub.add(function () {
|
38099 | if (iterator$1 && typeof iterator$1.return === 'function') {
|
38100 | iterator$1.return();
|
38101 | }
|
38102 | });
|
38103 | sub.add(scheduler.schedule(function () {
|
38104 | iterator$1 = input[iterator]();
|
38105 | sub.add(scheduler.schedule(function () {
|
38106 | if (subscriber.closed) {
|
38107 | return;
|
38108 | }
|
38109 | var value;
|
38110 | var done;
|
38111 | try {
|
38112 | var result = iterator$1.next();
|
38113 | value = result.value;
|
38114 | done = result.done;
|
38115 | }
|
38116 | catch (err) {
|
38117 | subscriber.error(err);
|
38118 | return;
|
38119 | }
|
38120 | if (done) {
|
38121 | subscriber.complete();
|
38122 | }
|
38123 | else {
|
38124 | subscriber.next(value);
|
38125 | this.schedule();
|
38126 | }
|
38127 | }));
|
38128 | }));
|
38129 | return sub;
|
38130 | });
|
38131 | }
|
38132 |
|
38133 | /** PURE_IMPORTS_START _symbol_observable PURE_IMPORTS_END */
|
38134 | function isInteropObservable(input) {
|
38135 | return input && typeof input[observable] === 'function';
|
38136 | }
|
38137 |
|
38138 | /** PURE_IMPORTS_START _symbol_iterator PURE_IMPORTS_END */
|
38139 | function isIterable(input) {
|
38140 | return input && typeof input[iterator] === 'function';
|
38141 | }
|
38142 |
|
38143 | /** PURE_IMPORTS_START _scheduleObservable,_schedulePromise,_scheduleArray,_scheduleIterable,_util_isInteropObservable,_util_isPromise,_util_isArrayLike,_util_isIterable PURE_IMPORTS_END */
|
38144 | function scheduled(input, scheduler) {
|
38145 | if (input != null) {
|
38146 | if (isInteropObservable(input)) {
|
38147 | return scheduleObservable(input, scheduler);
|
38148 | }
|
38149 | else if (isPromise$2(input)) {
|
38150 | return schedulePromise(input, scheduler);
|
38151 | }
|
38152 | else if (isArrayLike(input)) {
|
38153 | return scheduleArray(input, scheduler);
|
38154 | }
|
38155 | else if (isIterable(input) || typeof input === 'string') {
|
38156 | return scheduleIterable(input, scheduler);
|
38157 | }
|
38158 | }
|
38159 | throw new TypeError((input !== null && typeof input || input) + ' is not observable');
|
38160 | }
|
38161 |
|
38162 | /** PURE_IMPORTS_START _Observable,_util_subscribeTo,_scheduled_scheduled PURE_IMPORTS_END */
|
38163 | function from(input, scheduler) {
|
38164 | if (!scheduler) {
|
38165 | if (input instanceof Observable) {
|
38166 | return input;
|
38167 | }
|
38168 | return new Observable(subscribeTo(input));
|
38169 | }
|
38170 | else {
|
38171 | return scheduled(input, scheduler);
|
38172 | }
|
38173 | }
|
38174 |
|
38175 | /** PURE_IMPORTS_START tslib,_util_subscribeToResult,_OuterSubscriber,_InnerSubscriber,_map,_observable_from PURE_IMPORTS_END */
|
38176 | function mergeMap(project, resultSelector, concurrent) {
|
38177 | if (concurrent === void 0) {
|
38178 | concurrent = Number.POSITIVE_INFINITY;
|
38179 | }
|
38180 | if (typeof resultSelector === 'function') {
|
38181 | 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)); };
|
38182 | }
|
38183 | else if (typeof resultSelector === 'number') {
|
38184 | concurrent = resultSelector;
|
38185 | }
|
38186 | return function (source) { return source.lift(new MergeMapOperator(project, concurrent)); };
|
38187 | }
|
38188 | var MergeMapOperator = /*@__PURE__*/ (function () {
|
38189 | function MergeMapOperator(project, concurrent) {
|
38190 | if (concurrent === void 0) {
|
38191 | concurrent = Number.POSITIVE_INFINITY;
|
38192 | }
|
38193 | this.project = project;
|
38194 | this.concurrent = concurrent;
|
38195 | }
|
38196 | MergeMapOperator.prototype.call = function (observer, source) {
|
38197 | return source.subscribe(new MergeMapSubscriber(observer, this.project, this.concurrent));
|
38198 | };
|
38199 | return MergeMapOperator;
|
38200 | }());
|
38201 | var MergeMapSubscriber = /*@__PURE__*/ (function (_super) {
|
38202 | __extends(MergeMapSubscriber, _super);
|
38203 | function MergeMapSubscriber(destination, project, concurrent) {
|
38204 | if (concurrent === void 0) {
|
38205 | concurrent = Number.POSITIVE_INFINITY;
|
38206 | }
|
38207 | var _this = _super.call(this, destination) || this;
|
38208 | _this.project = project;
|
38209 | _this.concurrent = concurrent;
|
38210 | _this.hasCompleted = false;
|
38211 | _this.buffer = [];
|
38212 | _this.active = 0;
|
38213 | _this.index = 0;
|
38214 | return _this;
|
38215 | }
|
38216 | MergeMapSubscriber.prototype._next = function (value) {
|
38217 | if (this.active < this.concurrent) {
|
38218 | this._tryNext(value);
|
38219 | }
|
38220 | else {
|
38221 | this.buffer.push(value);
|
38222 | }
|
38223 | };
|
38224 | MergeMapSubscriber.prototype._tryNext = function (value) {
|
38225 | var result;
|
38226 | var index = this.index++;
|
38227 | try {
|
38228 | result = this.project(value, index);
|
38229 | }
|
38230 | catch (err) {
|
38231 | this.destination.error(err);
|
38232 | return;
|
38233 | }
|
38234 | this.active++;
|
38235 | this._innerSub(result, value, index);
|
38236 | };
|
38237 | MergeMapSubscriber.prototype._innerSub = function (ish, value, index) {
|
38238 | var innerSubscriber = new InnerSubscriber(this, value, index);
|
38239 | var destination = this.destination;
|
38240 | destination.add(innerSubscriber);
|
38241 | var innerSubscription = subscribeToResult(this, ish, undefined, undefined, innerSubscriber);
|
38242 | if (innerSubscription !== innerSubscriber) {
|
38243 | destination.add(innerSubscription);
|
38244 | }
|
38245 | };
|
38246 | MergeMapSubscriber.prototype._complete = function () {
|
38247 | this.hasCompleted = true;
|
38248 | if (this.active === 0 && this.buffer.length === 0) {
|
38249 | this.destination.complete();
|
38250 | }
|
38251 | this.unsubscribe();
|
38252 | };
|
38253 | MergeMapSubscriber.prototype.notifyNext = function (outerValue, innerValue, outerIndex, innerIndex, innerSub) {
|
38254 | this.destination.next(innerValue);
|
38255 | };
|
38256 | MergeMapSubscriber.prototype.notifyComplete = function (innerSub) {
|
38257 | var buffer = this.buffer;
|
38258 | this.remove(innerSub);
|
38259 | this.active--;
|
38260 | if (buffer.length > 0) {
|
38261 | this._next(buffer.shift());
|
38262 | }
|
38263 | else if (this.active === 0 && this.hasCompleted) {
|
38264 | this.destination.complete();
|
38265 | }
|
38266 | };
|
38267 | return MergeMapSubscriber;
|
38268 | }(OuterSubscriber));
|
38269 |
|
38270 | /** PURE_IMPORTS_START _mergeMap,_util_identity PURE_IMPORTS_END */
|
38271 | function mergeAll(concurrent) {
|
38272 | if (concurrent === void 0) {
|
38273 | concurrent = Number.POSITIVE_INFINITY;
|
38274 | }
|
38275 | return mergeMap(identity, concurrent);
|
38276 | }
|
38277 |
|
38278 | /** PURE_IMPORTS_START _Observable,_util_isScheduler,_operators_mergeAll,_fromArray PURE_IMPORTS_END */
|
38279 | function merge$1() {
|
38280 | var observables = [];
|
38281 | for (var _i = 0; _i < arguments.length; _i++) {
|
38282 | observables[_i] = arguments[_i];
|
38283 | }
|
38284 | var concurrent = Number.POSITIVE_INFINITY;
|
38285 | var scheduler = null;
|
38286 | var last = observables[observables.length - 1];
|
38287 | if (isScheduler(last)) {
|
38288 | scheduler = observables.pop();
|
38289 | if (observables.length > 1 && typeof observables[observables.length - 1] === 'number') {
|
38290 | concurrent = observables.pop();
|
38291 | }
|
38292 | }
|
38293 | else if (typeof last === 'number') {
|
38294 | concurrent = observables.pop();
|
38295 | }
|
38296 | if (scheduler === null && observables.length === 1 && observables[0] instanceof Observable) {
|
38297 | return observables[0];
|
38298 | }
|
38299 | return mergeAll(concurrent)(fromArray(observables, scheduler));
|
38300 | }
|
38301 |
|
38302 | /**
|
38303 | * @license
|
38304 | * Copyright Google LLC All Rights Reserved.
|
38305 | *
|
38306 | * Use of this source code is governed by an MIT-style license that can be
|
38307 | * found in the LICENSE file at https://angular.io/license
|
38308 | */
|
38309 | class EventEmitter_ extends Subject {
|
38310 | constructor(isAsync = false) {
|
38311 | super();
|
38312 | this.__isAsync = isAsync;
|
38313 | }
|
38314 | emit(value) {
|
38315 | super.next(value);
|
38316 | }
|
38317 | subscribe(observerOrNext, error, complete) {
|
38318 | let schedulerFn;
|
38319 | let errorFn = (err) => null;
|
38320 | let completeFn = () => null;
|
38321 | if (observerOrNext && typeof observerOrNext === 'object') {
|
38322 | schedulerFn = this.__isAsync ? (value) => {
|
38323 | setTimeout(() => observerOrNext.next(value));
|
38324 | } : (value) => {
|
38325 | observerOrNext.next(value);
|
38326 | };
|
38327 | if (observerOrNext.error) {
|
38328 | errorFn = this.__isAsync ? (err) => {
|
38329 | setTimeout(() => observerOrNext.error(err));
|
38330 | } : (err) => {
|
38331 | observerOrNext.error(err);
|
38332 | };
|
38333 | }
|
38334 | if (observerOrNext.complete) {
|
38335 | completeFn = this.__isAsync ? () => {
|
38336 | setTimeout(() => observerOrNext.complete());
|
38337 | } : () => {
|
38338 | observerOrNext.complete();
|
38339 | };
|
38340 | }
|
38341 | }
|
38342 | else {
|
38343 | schedulerFn = this.__isAsync ? (value) => {
|
38344 | setTimeout(() => observerOrNext(value));
|
38345 | } : (value) => {
|
38346 | observerOrNext(value);
|
38347 | };
|
38348 | if (error) {
|
38349 | errorFn = this.__isAsync ? (err) => {
|
38350 | setTimeout(() => error(err));
|
38351 | } : (err) => {
|
38352 | error(err);
|
38353 | };
|
38354 | }
|
38355 | if (complete) {
|
38356 | completeFn = this.__isAsync ? () => {
|
38357 | setTimeout(() => complete());
|
38358 | } : () => {
|
38359 | complete();
|
38360 | };
|
38361 | }
|
38362 | }
|
38363 | const sink = super.subscribe(schedulerFn, errorFn, completeFn);
|
38364 | if (observerOrNext instanceof Subscription) {
|
38365 | observerOrNext.add(sink);
|
38366 | }
|
38367 | return sink;
|
38368 | }
|
38369 | }
|
38370 | /**
|
38371 | * @publicApi
|
38372 | */
|
38373 | const EventEmitter = EventEmitter_;
|
38374 |
|
38375 | /**
|
38376 | * @license
|
38377 | * Copyright Google LLC All Rights Reserved.
|
38378 | *
|
38379 | * Use of this source code is governed by an MIT-style license that can be
|
38380 | * found in the LICENSE file at https://angular.io/license
|
38381 | */
|
38382 | const ɵ0$9 = (dir = {}) => dir, ɵ1$1 = (type, meta) => SWITCH_COMPILE_DIRECTIVE(type, meta);
|
38383 | /**
|
38384 | * Type of the Directive metadata.
|
38385 | *
|
38386 | * @publicApi
|
38387 | */
|
38388 | const Directive = makeDecorator('Directive', ɵ0$9, undefined, undefined, ɵ1$1);
|
38389 | const ɵ2$1 = (c = {}) => (Object.assign({ changeDetection: ChangeDetectionStrategy$1.Default }, c)), ɵ3$1 = (type, meta) => SWITCH_COMPILE_COMPONENT(type, meta);
|
38390 | /**
|
38391 | * Component decorator and metadata.
|
38392 | *
|
38393 | * @Annotation
|
38394 | * @publicApi
|
38395 | */
|
38396 | const Component = makeDecorator('Component', ɵ2$1, Directive, undefined, ɵ3$1);
|
38397 | const ɵ4 = (p) => (Object.assign({ pure: true }, p)), ɵ5 = (type, meta) => SWITCH_COMPILE_PIPE(type, meta);
|
38398 | /**
|
38399 | * @Annotation
|
38400 | * @publicApi
|
38401 | */
|
38402 | const Pipe = makeDecorator('Pipe', ɵ4, undefined, undefined, ɵ5);
|
38403 | const ɵ6 = (bindingPropertyName) => ({ bindingPropertyName });
|
38404 | /**
|
38405 | * @Annotation
|
38406 | * @publicApi
|
38407 | */
|
38408 | const Input = makePropDecorator('Input', ɵ6);
|
38409 | const ɵ7 = (bindingPropertyName) => ({ bindingPropertyName });
|
38410 | /**
|
38411 | * @Annotation
|
38412 | * @publicApi
|
38413 | */
|
38414 | const Output = makePropDecorator('Output', ɵ7);
|
38415 | const ɵ8 = (hostPropertyName) => ({ hostPropertyName });
|
38416 | /**
|
38417 | * @Annotation
|
38418 | * @publicApi
|
38419 | */
|
38420 | const HostBinding = makePropDecorator('HostBinding', ɵ8);
|
38421 | const ɵ9 = (eventName, args) => ({ eventName, args });
|
38422 | /**
|
38423 | * Decorator that binds a DOM event to a host listener and supplies configuration metadata.
|
38424 | * Angular invokes the supplied handler method when the host element emits the specified event,
|
38425 | * and updates the bound element with the result.
|
38426 | *
|
38427 | * If the handler method returns false, applies `preventDefault` on the bound element.
|
38428 | *
|
38429 | * @usageNotes
|
38430 | *
|
38431 | * The following example declares a directive
|
38432 | * that attaches a click listener to a button and counts clicks.
|
38433 | *
|
38434 | * ```ts
|
38435 | * @Directive({selector: 'button[counting]'})
|
38436 | * class CountClicks {
|
38437 | * numberOfClicks = 0;
|
38438 | *
|
38439 | * @HostListener('click', ['$event.target'])
|
38440 | * onClick(btn) {
|
38441 | * console.log('button', btn, 'number of clicks:', this.numberOfClicks++);
|
38442 | * }
|
38443 | * }
|
38444 | *
|
38445 | * @Component({
|
38446 | * selector: 'app',
|
38447 | * template: '<button counting>Increment</button>',
|
38448 | * })
|
38449 | * class App {}
|
38450 | *
|
38451 | * ```
|
38452 | *
|
38453 | * The following example registers another DOM event handler that listens for key-press events.
|
38454 | * ``` ts
|
38455 | * import { HostListener, Component } from "@angular/core";
|
38456 | *
|
38457 | * @Component({
|
38458 | * selector: 'app',
|
38459 | * template: `<h1>Hello, you have pressed keys {{counter}} number of times!</h1> Press any key to
|
38460 | * increment the counter.
|
38461 | * <button (click)="resetCounter()">Reset Counter</button>`
|
38462 | * })
|
38463 | * class AppComponent {
|
38464 | * counter = 0;
|
38465 | * @HostListener('window:keydown', ['$event'])
|
38466 | * handleKeyDown(event: KeyboardEvent) {
|
38467 | * this.counter++;
|
38468 | * }
|
38469 | * resetCounter() {
|
38470 | * this.counter = 0;
|
38471 | * }
|
38472 | * }
|
38473 | * ```
|
38474 | *
|
38475 | * @Annotation
|
38476 | * @publicApi
|
38477 | */
|
38478 | const HostListener = makePropDecorator('HostListener', ɵ9);
|
38479 | const SWITCH_COMPILE_COMPONENT__PRE_R3__ = noop;
|
38480 | const SWITCH_COMPILE_DIRECTIVE__PRE_R3__ = noop;
|
38481 | const SWITCH_COMPILE_PIPE__PRE_R3__ = noop;
|
38482 | const SWITCH_COMPILE_COMPONENT = SWITCH_COMPILE_COMPONENT__PRE_R3__;
|
38483 | const SWITCH_COMPILE_DIRECTIVE = SWITCH_COMPILE_DIRECTIVE__PRE_R3__;
|
38484 | const SWITCH_COMPILE_PIPE = SWITCH_COMPILE_PIPE__PRE_R3__;
|
38485 |
|
38486 | /**
|
38487 | * @license
|
38488 | * Copyright Google LLC All Rights Reserved.
|
38489 | *
|
38490 | * Use of this source code is governed by an MIT-style license that can be
|
38491 | * found in the LICENSE file at https://angular.io/license
|
38492 | */
|
38493 | const ɵ0$a = (ngModule) => ngModule, ɵ1$2 =
|
38494 | /**
|
38495 | * Decorator that marks the following class as an NgModule, and supplies
|
38496 | * configuration metadata for it.
|
38497 | *
|
38498 | * * The `declarations` and `entryComponents` options configure the compiler
|
38499 | * with information about what belongs to the NgModule.
|
38500 | * * The `providers` options configures the NgModule's injector to provide
|
38501 | * dependencies the NgModule members.
|
38502 | * * The `imports` and `exports` options bring in members from other modules, and make
|
38503 | * this module's members available to others.
|
38504 | */
|
38505 | (type, meta) => SWITCH_COMPILE_NGMODULE(type, meta);
|
38506 | /**
|
38507 | * @Annotation
|
38508 | * @publicApi
|
38509 | */
|
38510 | const NgModule = makeDecorator('NgModule', ɵ0$a, undefined, undefined, ɵ1$2);
|
38511 | function preR3NgModuleCompile(moduleType, metadata) {
|
38512 | let imports = (metadata && metadata.imports) || [];
|
38513 | if (metadata && metadata.exports) {
|
38514 | imports = [...imports, metadata.exports];
|
38515 | }
|
38516 | moduleType.ɵinj = ɵɵdefineInjector({
|
38517 | factory: convertInjectableProviderToFactory(moduleType, { useClass: moduleType }),
|
38518 | providers: metadata && metadata.providers,
|
38519 | imports: imports,
|
38520 | });
|
38521 | }
|
38522 | const SWITCH_COMPILE_NGMODULE__PRE_R3__ = preR3NgModuleCompile;
|
38523 | const SWITCH_COMPILE_NGMODULE = SWITCH_COMPILE_NGMODULE__PRE_R3__;
|
38524 |
|
38525 | /** PURE_IMPORTS_START _observable_ConnectableObservable PURE_IMPORTS_END */
|
38526 | function multicast(subjectOrSubjectFactory, selector) {
|
38527 | return function multicastOperatorFunction(source) {
|
38528 | var subjectFactory;
|
38529 | if (typeof subjectOrSubjectFactory === 'function') {
|
38530 | subjectFactory = subjectOrSubjectFactory;
|
38531 | }
|
38532 | else {
|
38533 | subjectFactory = function subjectFactory() {
|
38534 | return subjectOrSubjectFactory;
|
38535 | };
|
38536 | }
|
38537 | if (typeof selector === 'function') {
|
38538 | return source.lift(new MulticastOperator(subjectFactory, selector));
|
38539 | }
|
38540 | var connectable = Object.create(source, connectableObservableDescriptor);
|
38541 | connectable.source = source;
|
38542 | connectable.subjectFactory = subjectFactory;
|
38543 | return connectable;
|
38544 | };
|
38545 | }
|
38546 | var MulticastOperator = /*@__PURE__*/ (function () {
|
38547 | function MulticastOperator(subjectFactory, selector) {
|
38548 | this.subjectFactory = subjectFactory;
|
38549 | this.selector = selector;
|
38550 | }
|
38551 | MulticastOperator.prototype.call = function (subscriber, source) {
|
38552 | var selector = this.selector;
|
38553 | var subject = this.subjectFactory();
|
38554 | var subscription = selector(subject).subscribe(subscriber);
|
38555 | subscription.add(source.subscribe(subject));
|
38556 | return subscription;
|
38557 | };
|
38558 | return MulticastOperator;
|
38559 | }());
|
38560 |
|
38561 | /** PURE_IMPORTS_START _multicast,_refCount,_Subject PURE_IMPORTS_END */
|
38562 | function shareSubjectFactory() {
|
38563 | return new Subject();
|
38564 | }
|
38565 | function share() {
|
38566 | return function (source) { return refCount()(multicast(shareSubjectFactory)(source)); };
|
38567 | }
|
38568 |
|
38569 | /**
|
38570 | * @license
|
38571 | * Copyright Google LLC All Rights Reserved.
|
38572 | *
|
38573 | * Use of this source code is governed by an MIT-style license that can be
|
38574 | * found in the LICENSE file at https://angular.io/license
|
38575 | */
|
38576 | /**
|
38577 | * A [DI token](guide/glossary#di-token "DI token definition") that you can use to provide
|
38578 | * one or more initialization functions.
|
38579 | *
|
38580 | * The provided functions are injected at application startup and executed during
|
38581 | * app initialization. If any of these functions returns a Promise, initialization
|
38582 | * does not complete until the Promise is resolved.
|
38583 | *
|
38584 | * You can, for example, create a factory function that loads language data
|
38585 | * or an external configuration, and provide that function to the `APP_INITIALIZER` token.
|
38586 | * The function is executed during the application bootstrap process,
|
38587 | * and the needed data is available on startup.
|
38588 | *
|
38589 | * @see `ApplicationInitStatus`
|
38590 | *
|
38591 | * @publicApi
|
38592 | */
|
38593 | const APP_INITIALIZER = new InjectionToken('Application Initializer');
|
38594 | /**
|
38595 | * A class that reflects the state of running {@link APP_INITIALIZER} functions.
|
38596 | *
|
38597 | * @publicApi
|
38598 | */
|
38599 | class ApplicationInitStatus {
|
38600 | constructor(appInits) {
|
38601 | this.appInits = appInits;
|
38602 | this.resolve = noop;
|
38603 | this.reject = noop;
|
38604 | this.initialized = false;
|
38605 | this.done = false;
|
38606 | this.donePromise = new Promise((res, rej) => {
|
38607 | this.resolve = res;
|
38608 | this.reject = rej;
|
38609 | });
|
38610 | }
|
38611 | /** @internal */
|
38612 | runInitializers() {
|
38613 | if (this.initialized) {
|
38614 | return;
|
38615 | }
|
38616 | const asyncInitPromises = [];
|
38617 | const complete = () => {
|
38618 | this.done = true;
|
38619 | this.resolve();
|
38620 | };
|
38621 | if (this.appInits) {
|
38622 | for (let i = 0; i < this.appInits.length; i++) {
|
38623 | const initResult = this.appInits[i]();
|
38624 | if (isPromise$1(initResult)) {
|
38625 | asyncInitPromises.push(initResult);
|
38626 | }
|
38627 | }
|
38628 | }
|
38629 | Promise.all(asyncInitPromises)
|
38630 | .then(() => {
|
38631 | complete();
|
38632 | })
|
38633 | .catch(e => {
|
38634 | this.reject(e);
|
38635 | });
|
38636 | if (asyncInitPromises.length === 0) {
|
38637 | complete();
|
38638 | }
|
38639 | this.initialized = true;
|
38640 | }
|
38641 | }
|
38642 | ApplicationInitStatus.decorators = [
|
38643 | { type: Injectable }
|
38644 | ];
|
38645 | ApplicationInitStatus.ctorParameters = () => [
|
38646 | { type: Array, decorators: [{ type: Inject, args: [APP_INITIALIZER,] }, { type: Optional }] }
|
38647 | ];
|
38648 |
|
38649 | /**
|
38650 | * @license
|
38651 | * Copyright Google LLC All Rights Reserved.
|
38652 | *
|
38653 | * Use of this source code is governed by an MIT-style license that can be
|
38654 | * found in the LICENSE file at https://angular.io/license
|
38655 | */
|
38656 | /**
|
38657 | * A [DI token](guide/glossary#di-token "DI token definition") representing a unique string ID, used
|
38658 | * primarily for prefixing application attributes and CSS styles when
|
38659 | * {@link ViewEncapsulation#Emulated ViewEncapsulation.Emulated} is being used.
|
38660 | *
|
38661 | * BY default, the value is randomly generated and assigned to the application by Angular.
|
38662 | * To provide a custom ID value, use a DI provider <!-- TODO: provider --> to configure
|
38663 | * the root {@link Injector} that uses this token.
|
38664 | *
|
38665 | * @publicApi
|
38666 | */
|
38667 | const APP_ID = new InjectionToken('AppId');
|
38668 | function _appIdRandomProviderFactory() {
|
38669 | return `${_randomChar()}${_randomChar()}${_randomChar()}`;
|
38670 | }
|
38671 | /**
|
38672 | * Providers that generate a random `APP_ID_TOKEN`.
|
38673 | * @publicApi
|
38674 | */
|
38675 | const APP_ID_RANDOM_PROVIDER = {
|
38676 | provide: APP_ID,
|
38677 | useFactory: _appIdRandomProviderFactory,
|
38678 | deps: [],
|
38679 | };
|
38680 | function _randomChar() {
|
38681 | return String.fromCharCode(97 + Math.floor(Math.random() * 25));
|
38682 | }
|
38683 | /**
|
38684 | * A function that is executed when a platform is initialized.
|
38685 | * @publicApi
|
38686 | */
|
38687 | const PLATFORM_INITIALIZER = new InjectionToken('Platform Initializer');
|
38688 | /**
|
38689 | * A token that indicates an opaque platform ID.
|
38690 | * @publicApi
|
38691 | */
|
38692 | const PLATFORM_ID = new InjectionToken('Platform ID');
|
38693 | /**
|
38694 | * A [DI token](guide/glossary#di-token "DI token definition") that provides a set of callbacks to
|
38695 | * be called for every component that is bootstrapped.
|
38696 | *
|
38697 | * Each callback must take a `ComponentRef` instance and return nothing.
|
38698 | *
|
38699 | * `(componentRef: ComponentRef) => void`
|
38700 | *
|
38701 | * @publicApi
|
38702 | */
|
38703 | const APP_BOOTSTRAP_LISTENER = new InjectionToken('appBootstrapListener');
|
38704 | /**
|
38705 | * A [DI token](guide/glossary#di-token "DI token definition") that indicates the root directory of
|
38706 | * the application
|
38707 | * @publicApi
|
38708 | */
|
38709 | const PACKAGE_ROOT_URL = new InjectionToken('Application Packages Root URL');
|
38710 |
|
38711 | /**
|
38712 | * @license
|
38713 | * Copyright Google LLC All Rights Reserved.
|
38714 | *
|
38715 | * Use of this source code is governed by an MIT-style license that can be
|
38716 | * found in the LICENSE file at https://angular.io/license
|
38717 | */
|
38718 | class Console {
|
38719 | log(message) {
|
38720 | // tslint:disable-next-line:no-console
|
38721 | console.log(message);
|
38722 | }
|
38723 | // Note: for reporting errors use `DOM.logError()` as it is platform specific
|
38724 | warn(message) {
|
38725 | // tslint:disable-next-line:no-console
|
38726 | console.warn(message);
|
38727 | }
|
38728 | }
|
38729 | Console.decorators = [
|
38730 | { type: Injectable }
|
38731 | ];
|
38732 |
|
38733 | /**
|
38734 | * @license
|
38735 | * Copyright Google LLC All Rights Reserved.
|
38736 | *
|
38737 | * Use of this source code is governed by an MIT-style license that can be
|
38738 | * found in the LICENSE file at https://angular.io/license
|
38739 | */
|
38740 | /**
|
38741 | * Provide this token to set the locale of your application.
|
38742 | * It is used for i18n extraction, by i18n pipes (DatePipe, I18nPluralPipe, CurrencyPipe,
|
38743 | * DecimalPipe and PercentPipe) and by ICU expressions.
|
38744 | *
|
38745 | * See the [i18n guide](guide/i18n#setting-up-locale) for more information.
|
38746 | *
|
38747 | * @usageNotes
|
38748 | * ### Example
|
38749 | *
|
38750 | * ```typescript
|
38751 | * import { LOCALE_ID } from '@angular/core';
|
38752 | * import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
38753 | * import { AppModule } from './app/app.module';
|
38754 | *
|
38755 | * platformBrowserDynamic().bootstrapModule(AppModule, {
|
38756 | * providers: [{provide: LOCALE_ID, useValue: 'en-US' }]
|
38757 | * });
|
38758 | * ```
|
38759 | *
|
38760 | * @publicApi
|
38761 | */
|
38762 | const LOCALE_ID$1 = new InjectionToken('LocaleId');
|
38763 | /**
|
38764 | * Provide this token to set the default currency code your application uses for
|
38765 | * CurrencyPipe when there is no currency code passed into it. This is only used by
|
38766 | * CurrencyPipe and has no relation to locale currency. Defaults to USD if not configured.
|
38767 | *
|
38768 | * See the [i18n guide](guide/i18n#setting-up-locale) for more information.
|
38769 | *
|
38770 | * <div class="alert is-helpful">
|
38771 | *
|
38772 | * **Deprecation notice:**
|
38773 | *
|
38774 | * The default currency code is currently always `USD` but this is deprecated from v9.
|
38775 | *
|
38776 | * **In v10 the default currency code will be taken from the current locale.**
|
38777 | *
|
38778 | * If you need the previous behavior then set it by creating a `DEFAULT_CURRENCY_CODE` provider in
|
38779 | * your application `NgModule`:
|
38780 | *
|
38781 | * ```ts
|
38782 | * {provide: DEFAULT_CURRENCY_CODE, useValue: 'USD'}
|
38783 | * ```
|
38784 | *
|
38785 | * </div>
|
38786 | *
|
38787 | * @usageNotes
|
38788 | * ### Example
|
38789 | *
|
38790 | * ```typescript
|
38791 | * import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
38792 | * import { AppModule } from './app/app.module';
|
38793 | *
|
38794 | * platformBrowserDynamic().bootstrapModule(AppModule, {
|
38795 | * providers: [{provide: DEFAULT_CURRENCY_CODE, useValue: 'EUR' }]
|
38796 | * });
|
38797 | * ```
|
38798 | *
|
38799 | * @publicApi
|
38800 | */
|
38801 | const DEFAULT_CURRENCY_CODE = new InjectionToken('DefaultCurrencyCode');
|
38802 | /**
|
38803 | * Use this token at bootstrap to provide the content of your translation file (`xtb`,
|
38804 | * `xlf` or `xlf2`) when you want to translate your application in another language.
|
38805 | *
|
38806 | * See the [i18n guide](guide/i18n#merge) for more information.
|
38807 | *
|
38808 | * @usageNotes
|
38809 | * ### Example
|
38810 | *
|
38811 | * ```typescript
|
38812 | * import { TRANSLATIONS } from '@angular/core';
|
38813 | * import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
38814 | * import { AppModule } from './app/app.module';
|
38815 | *
|
38816 | * // content of your translation file
|
38817 | * const translations = '....';
|
38818 | *
|
38819 | * platformBrowserDynamic().bootstrapModule(AppModule, {
|
38820 | * providers: [{provide: TRANSLATIONS, useValue: translations }]
|
38821 | * });
|
38822 | * ```
|
38823 | *
|
38824 | * @publicApi
|
38825 | */
|
38826 | const TRANSLATIONS = new InjectionToken('Translations');
|
38827 | /**
|
38828 | * Provide this token at bootstrap to set the format of your {@link TRANSLATIONS}: `xtb`,
|
38829 | * `xlf` or `xlf2`.
|
38830 | *
|
38831 | * See the [i18n guide](guide/i18n#merge) for more information.
|
38832 | *
|
38833 | * @usageNotes
|
38834 | * ### Example
|
38835 | *
|
38836 | * ```typescript
|
38837 | * import { TRANSLATIONS_FORMAT } from '@angular/core';
|
38838 | * import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
38839 | * import { AppModule } from './app/app.module';
|
38840 | *
|
38841 | * platformBrowserDynamic().bootstrapModule(AppModule, {
|
38842 | * providers: [{provide: TRANSLATIONS_FORMAT, useValue: 'xlf' }]
|
38843 | * });
|
38844 | * ```
|
38845 | *
|
38846 | * @publicApi
|
38847 | */
|
38848 | const TRANSLATIONS_FORMAT = new InjectionToken('TranslationsFormat');
|
38849 | /**
|
38850 | * Use this enum at bootstrap as an option of `bootstrapModule` to define the strategy
|
38851 | * that the compiler should use in case of missing translations:
|
38852 | * - Error: throw if you have missing translations.
|
38853 | * - Warning (default): show a warning in the console and/or shell.
|
38854 | * - Ignore: do nothing.
|
38855 | *
|
38856 | * See the [i18n guide](guide/i18n#missing-translation) for more information.
|
38857 | *
|
38858 | * @usageNotes
|
38859 | * ### Example
|
38860 | * ```typescript
|
38861 | * import { MissingTranslationStrategy } from '@angular/core';
|
38862 | * import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
38863 | * import { AppModule } from './app/app.module';
|
38864 | *
|
38865 | * platformBrowserDynamic().bootstrapModule(AppModule, {
|
38866 | * missingTranslation: MissingTranslationStrategy.Error
|
38867 | * });
|
38868 | * ```
|
38869 | *
|
38870 | * @publicApi
|
38871 | */
|
38872 | var MissingTranslationStrategy$1;
|
38873 | (function (MissingTranslationStrategy) {
|
38874 | MissingTranslationStrategy[MissingTranslationStrategy["Error"] = 0] = "Error";
|
38875 | MissingTranslationStrategy[MissingTranslationStrategy["Warning"] = 1] = "Warning";
|
38876 | MissingTranslationStrategy[MissingTranslationStrategy["Ignore"] = 2] = "Ignore";
|
38877 | })(MissingTranslationStrategy$1 || (MissingTranslationStrategy$1 = {}));
|
38878 |
|
38879 | /**
|
38880 | * @license
|
38881 | * Copyright Google LLC All Rights Reserved.
|
38882 | *
|
38883 | * Use of this source code is governed by an MIT-style license that can be
|
38884 | * found in the LICENSE file at https://angular.io/license
|
38885 | */
|
38886 | const SWITCH_IVY_ENABLED__PRE_R3__ = false;
|
38887 | const ivyEnabled = SWITCH_IVY_ENABLED__PRE_R3__;
|
38888 |
|
38889 | /**
|
38890 | * @license
|
38891 | * Copyright Google LLC All Rights Reserved.
|
38892 | *
|
38893 | * Use of this source code is governed by an MIT-style license that can be
|
38894 | * found in the LICENSE file at https://angular.io/license
|
38895 | */
|
38896 | function _throwError() {
|
38897 | throw new Error(`Runtime compiler is not loaded`);
|
38898 | }
|
38899 | const Compiler_compileModuleSync__PRE_R3__ = _throwError;
|
38900 | const Compiler_compileModuleSync = Compiler_compileModuleSync__PRE_R3__;
|
38901 | const Compiler_compileModuleAsync__PRE_R3__ = _throwError;
|
38902 | const Compiler_compileModuleAsync = Compiler_compileModuleAsync__PRE_R3__;
|
38903 | const Compiler_compileModuleAndAllComponentsSync__PRE_R3__ = _throwError;
|
38904 | const Compiler_compileModuleAndAllComponentsSync = Compiler_compileModuleAndAllComponentsSync__PRE_R3__;
|
38905 | const Compiler_compileModuleAndAllComponentsAsync__PRE_R3__ = _throwError;
|
38906 | const Compiler_compileModuleAndAllComponentsAsync = Compiler_compileModuleAndAllComponentsAsync__PRE_R3__;
|
38907 | /**
|
38908 | * Low-level service for running the angular compiler during runtime
|
38909 | * to create {@link ComponentFactory}s, which
|
38910 | * can later be used to create and render a Component instance.
|
38911 | *
|
38912 | * Each `@NgModule` provides an own `Compiler` to its injector,
|
38913 | * that will use the directives/pipes of the ng module for compilation
|
38914 | * of components.
|
38915 | *
|
38916 | * @publicApi
|
38917 | */
|
38918 | class Compiler {
|
38919 | constructor() {
|
38920 | /**
|
38921 | * Compiles the given NgModule and all of its components. All templates of the components listed
|
38922 | * in `entryComponents` have to be inlined.
|
38923 | */
|
38924 | this.compileModuleSync = Compiler_compileModuleSync;
|
38925 | /**
|
38926 | * Compiles the given NgModule and all of its components
|
38927 | */
|
38928 | this.compileModuleAsync = Compiler_compileModuleAsync;
|
38929 | /**
|
38930 | * Same as {@link #compileModuleSync} but also creates ComponentFactories for all components.
|
38931 | */
|
38932 | this.compileModuleAndAllComponentsSync = Compiler_compileModuleAndAllComponentsSync;
|
38933 | /**
|
38934 | * Same as {@link #compileModuleAsync} but also creates ComponentFactories for all components.
|
38935 | */
|
38936 | this.compileModuleAndAllComponentsAsync = Compiler_compileModuleAndAllComponentsAsync;
|
38937 | }
|
38938 | /**
|
38939 | * Clears all caches.
|
38940 | */
|
38941 | clearCache() { }
|
38942 | /**
|
38943 | * Clears the cache for the given component/ngModule.
|
38944 | */
|
38945 | clearCacheFor(type) { }
|
38946 | /**
|
38947 | * Returns the id for a given NgModule, if one is defined and known to the compiler.
|
38948 | */
|
38949 | getModuleId(moduleType) {
|
38950 | return undefined;
|
38951 | }
|
38952 | }
|
38953 | Compiler.decorators = [
|
38954 | { type: Injectable }
|
38955 | ];
|
38956 | /**
|
38957 | * Token to provide CompilerOptions in the platform injector.
|
38958 | *
|
38959 | * @publicApi
|
38960 | */
|
38961 | const COMPILER_OPTIONS = new InjectionToken('compilerOptions');
|
38962 | /**
|
38963 | * A factory for creating a Compiler
|
38964 | *
|
38965 | * @publicApi
|
38966 | */
|
38967 | class CompilerFactory {
|
38968 | }
|
38969 |
|
38970 | /**
|
38971 | * @license
|
38972 | * Copyright Google LLC All Rights Reserved.
|
38973 | *
|
38974 | * Use of this source code is governed by an MIT-style license that can be
|
38975 | * found in the LICENSE file at https://angular.io/license
|
38976 | */
|
38977 | const promise = (() => Promise.resolve(0))();
|
38978 | function scheduleMicroTask(fn) {
|
38979 | if (typeof Zone === 'undefined') {
|
38980 | // use promise to schedule microTask instead of use Zone
|
38981 | promise.then(() => {
|
38982 | fn && fn.apply(null, null);
|
38983 | });
|
38984 | }
|
38985 | else {
|
38986 | Zone.current.scheduleMicroTask('scheduleMicrotask', fn);
|
38987 | }
|
38988 | }
|
38989 |
|
38990 | /**
|
38991 | * @license
|
38992 | * Copyright Google LLC All Rights Reserved.
|
38993 | *
|
38994 | * Use of this source code is governed by an MIT-style license that can be
|
38995 | * found in the LICENSE file at https://angular.io/license
|
38996 | */
|
38997 | function getNativeRequestAnimationFrame() {
|
38998 | let nativeRequestAnimationFrame = _global$1['requestAnimationFrame'];
|
38999 | let nativeCancelAnimationFrame = _global$1['cancelAnimationFrame'];
|
39000 | if (typeof Zone !== 'undefined' && nativeRequestAnimationFrame && nativeCancelAnimationFrame) {
|
39001 | // use unpatched version of requestAnimationFrame(native delegate) if possible
|
39002 | // to avoid another Change detection
|
39003 | const unpatchedRequestAnimationFrame = nativeRequestAnimationFrame[Zone.__symbol__('OriginalDelegate')];
|
39004 | if (unpatchedRequestAnimationFrame) {
|
39005 | nativeRequestAnimationFrame = unpatchedRequestAnimationFrame;
|
39006 | }
|
39007 | const unpatchedCancelAnimationFrame = nativeCancelAnimationFrame[Zone.__symbol__('OriginalDelegate')];
|
39008 | if (unpatchedCancelAnimationFrame) {
|
39009 | nativeCancelAnimationFrame = unpatchedCancelAnimationFrame;
|
39010 | }
|
39011 | }
|
39012 | return { nativeRequestAnimationFrame, nativeCancelAnimationFrame };
|
39013 | }
|
39014 |
|
39015 | /**
|
39016 | * @license
|
39017 | * Copyright Google LLC All Rights Reserved.
|
39018 | *
|
39019 | * Use of this source code is governed by an MIT-style license that can be
|
39020 | * found in the LICENSE file at https://angular.io/license
|
39021 | */
|
39022 | /**
|
39023 | * An injectable service for executing work inside or outside of the Angular zone.
|
39024 | *
|
39025 | * The most common use of this service is to optimize performance when starting a work consisting of
|
39026 | * one or more asynchronous tasks that don't require UI updates or error handling to be handled by
|
39027 | * Angular. Such tasks can be kicked off via {@link #runOutsideAngular} and if needed, these tasks
|
39028 | * can reenter the Angular zone via {@link #run}.
|
39029 | *
|
39030 | * <!-- TODO: add/fix links to:
|
39031 | * - docs explaining zones and the use of zones in Angular and change-detection
|
39032 | * - link to runOutsideAngular/run (throughout this file!)
|
39033 | * -->
|
39034 | *
|
39035 | * @usageNotes
|
39036 | * ### Example
|
39037 | *
|
39038 | * ```
|
39039 | * import {Component, NgZone} from '@angular/core';
|
39040 | * import {NgIf} from '@angular/common';
|
39041 | *
|
39042 | * @Component({
|
39043 | * selector: 'ng-zone-demo',
|
39044 | * template: `
|
39045 | * <h2>Demo: NgZone</h2>
|
39046 | *
|
39047 | * <p>Progress: {{progress}}%</p>
|
39048 | * <p *ngIf="progress >= 100">Done processing {{label}} of Angular zone!</p>
|
39049 | *
|
39050 | * <button (click)="processWithinAngularZone()">Process within Angular zone</button>
|
39051 | * <button (click)="processOutsideOfAngularZone()">Process outside of Angular zone</button>
|
39052 | * `,
|
39053 | * })
|
39054 | * export class NgZoneDemo {
|
39055 | * progress: number = 0;
|
39056 | * label: string;
|
39057 | *
|
39058 | * constructor(private _ngZone: NgZone) {}
|
39059 | *
|
39060 | * // Loop inside the Angular zone
|
39061 | * // so the UI DOES refresh after each setTimeout cycle
|
39062 | * processWithinAngularZone() {
|
39063 | * this.label = 'inside';
|
39064 | * this.progress = 0;
|
39065 | * this._increaseProgress(() => console.log('Inside Done!'));
|
39066 | * }
|
39067 | *
|
39068 | * // Loop outside of the Angular zone
|
39069 | * // so the UI DOES NOT refresh after each setTimeout cycle
|
39070 | * processOutsideOfAngularZone() {
|
39071 | * this.label = 'outside';
|
39072 | * this.progress = 0;
|
39073 | * this._ngZone.runOutsideAngular(() => {
|
39074 | * this._increaseProgress(() => {
|
39075 | * // reenter the Angular zone and display done
|
39076 | * this._ngZone.run(() => { console.log('Outside Done!'); });
|
39077 | * });
|
39078 | * });
|
39079 | * }
|
39080 | *
|
39081 | * _increaseProgress(doneCallback: () => void) {
|
39082 | * this.progress += 1;
|
39083 | * console.log(`Current progress: ${this.progress}%`);
|
39084 | *
|
39085 | * if (this.progress < 100) {
|
39086 | * window.setTimeout(() => this._increaseProgress(doneCallback), 10);
|
39087 | * } else {
|
39088 | * doneCallback();
|
39089 | * }
|
39090 | * }
|
39091 | * }
|
39092 | * ```
|
39093 | *
|
39094 | * @publicApi
|
39095 | */
|
39096 | class NgZone {
|
39097 | constructor({ enableLongStackTrace = false, shouldCoalesceEventChangeDetection = false, shouldCoalesceRunChangeDetection = false }) {
|
39098 | this.hasPendingMacrotasks = false;
|
39099 | this.hasPendingMicrotasks = false;
|
39100 | /**
|
39101 | * Whether there are no outstanding microtasks or macrotasks.
|
39102 | */
|
39103 | this.isStable = true;
|
39104 | /**
|
39105 | * Notifies when code enters Angular Zone. This gets fired first on VM Turn.
|
39106 | */
|
39107 | this.onUnstable = new EventEmitter(false);
|
39108 | /**
|
39109 | * Notifies when there is no more microtasks enqueued in the current VM Turn.
|
39110 | * This is a hint for Angular to do change detection, which may enqueue more microtasks.
|
39111 | * For this reason this event can fire multiple times per VM Turn.
|
39112 | */
|
39113 | this.onMicrotaskEmpty = new EventEmitter(false);
|
39114 | /**
|
39115 | * Notifies when the last `onMicrotaskEmpty` has run and there are no more microtasks, which
|
39116 | * implies we are about to relinquish VM turn.
|
39117 | * This event gets called just once.
|
39118 | */
|
39119 | this.onStable = new EventEmitter(false);
|
39120 | /**
|
39121 | * Notifies that an error has been delivered.
|
39122 | */
|
39123 | this.onError = new EventEmitter(false);
|
39124 | if (typeof Zone == 'undefined') {
|
39125 | throw new Error(`In this configuration Angular requires Zone.js`);
|
39126 | }
|
39127 | Zone.assertZonePatched();
|
39128 | const self = this;
|
39129 | self._nesting = 0;
|
39130 | self._outer = self._inner = Zone.current;
|
39131 | if (Zone['TaskTrackingZoneSpec']) {
|
39132 | self._inner = self._inner.fork(new Zone['TaskTrackingZoneSpec']);
|
39133 | }
|
39134 | if (enableLongStackTrace && Zone['longStackTraceZoneSpec']) {
|
39135 | self._inner = self._inner.fork(Zone['longStackTraceZoneSpec']);
|
39136 | }
|
39137 | // if shouldCoalesceRunChangeDetection is true, all tasks including event tasks will be
|
39138 | // coalesced, so shouldCoalesceEventChangeDetection option is not necessary and can be skipped.
|
39139 | self.shouldCoalesceEventChangeDetection =
|
39140 | !shouldCoalesceRunChangeDetection && shouldCoalesceEventChangeDetection;
|
39141 | self.shouldCoalesceRunChangeDetection = shouldCoalesceRunChangeDetection;
|
39142 | self.lastRequestAnimationFrameId = -1;
|
39143 | self.nativeRequestAnimationFrame = getNativeRequestAnimationFrame().nativeRequestAnimationFrame;
|
39144 | forkInnerZoneWithAngularBehavior(self);
|
39145 | }
|
39146 | static isInAngularZone() {
|
39147 | return Zone.current.get('isAngularZone') === true;
|
39148 | }
|
39149 | static assertInAngularZone() {
|
39150 | if (!NgZone.isInAngularZone()) {
|
39151 | throw new Error('Expected to be in Angular Zone, but it is not!');
|
39152 | }
|
39153 | }
|
39154 | static assertNotInAngularZone() {
|
39155 | if (NgZone.isInAngularZone()) {
|
39156 | throw new Error('Expected to not be in Angular Zone, but it is!');
|
39157 | }
|
39158 | }
|
39159 | /**
|
39160 | * Executes the `fn` function synchronously within the Angular zone and returns value returned by
|
39161 | * the function.
|
39162 | *
|
39163 | * Running functions via `run` allows you to reenter Angular zone from a task that was executed
|
39164 | * outside of the Angular zone (typically started via {@link #runOutsideAngular}).
|
39165 | *
|
39166 | * Any future tasks or microtasks scheduled from within this function will continue executing from
|
39167 | * within the Angular zone.
|
39168 | *
|
39169 | * If a synchronous error happens it will be rethrown and not reported via `onError`.
|
39170 | */
|
39171 | run(fn, applyThis, applyArgs) {
|
39172 | return this._inner.run(fn, applyThis, applyArgs);
|
39173 | }
|
39174 | /**
|
39175 | * Executes the `fn` function synchronously within the Angular zone as a task and returns value
|
39176 | * returned by the function.
|
39177 | *
|
39178 | * Running functions via `run` allows you to reenter Angular zone from a task that was executed
|
39179 | * outside of the Angular zone (typically started via {@link #runOutsideAngular}).
|
39180 | *
|
39181 | * Any future tasks or microtasks scheduled from within this function will continue executing from
|
39182 | * within the Angular zone.
|
39183 | *
|
39184 | * If a synchronous error happens it will be rethrown and not reported via `onError`.
|
39185 | */
|
39186 | runTask(fn, applyThis, applyArgs, name) {
|
39187 | const zone = this._inner;
|
39188 | const task = zone.scheduleEventTask('NgZoneEvent: ' + name, fn, EMPTY_PAYLOAD, noop, noop);
|
39189 | try {
|
39190 | return zone.runTask(task, applyThis, applyArgs);
|
39191 | }
|
39192 | finally {
|
39193 | zone.cancelTask(task);
|
39194 | }
|
39195 | }
|
39196 | /**
|
39197 | * Same as `run`, except that synchronous errors are caught and forwarded via `onError` and not
|
39198 | * rethrown.
|
39199 | */
|
39200 | runGuarded(fn, applyThis, applyArgs) {
|
39201 | return this._inner.runGuarded(fn, applyThis, applyArgs);
|
39202 | }
|
39203 | /**
|
39204 | * Executes the `fn` function synchronously in Angular's parent zone and returns value returned by
|
39205 | * the function.
|
39206 | *
|
39207 | * Running functions via {@link #runOutsideAngular} allows you to escape Angular's zone and do
|
39208 | * work that
|
39209 | * doesn't trigger Angular change-detection or is subject to Angular's error handling.
|
39210 | *
|
39211 | * Any future tasks or microtasks scheduled from within this function will continue executing from
|
39212 | * outside of the Angular zone.
|
39213 | *
|
39214 | * Use {@link #run} to reenter the Angular zone and do work that updates the application model.
|
39215 | */
|
39216 | runOutsideAngular(fn) {
|
39217 | return this._outer.run(fn);
|
39218 | }
|
39219 | }
|
39220 | const EMPTY_PAYLOAD = {};
|
39221 | function checkStable(zone) {
|
39222 | if (zone._nesting == 0 && !zone.hasPendingMicrotasks && !zone.isStable) {
|
39223 | try {
|
39224 | zone._nesting++;
|
39225 | zone.onMicrotaskEmpty.emit(null);
|
39226 | }
|
39227 | finally {
|
39228 | zone._nesting--;
|
39229 | if (!zone.hasPendingMicrotasks) {
|
39230 | try {
|
39231 | zone.runOutsideAngular(() => zone.onStable.emit(null));
|
39232 | }
|
39233 | finally {
|
39234 | zone.isStable = true;
|
39235 | }
|
39236 | }
|
39237 | }
|
39238 | }
|
39239 | }
|
39240 | function delayChangeDetectionForEvents(zone) {
|
39241 | if (zone.lastRequestAnimationFrameId !== -1) {
|
39242 | return;
|
39243 | }
|
39244 | zone.lastRequestAnimationFrameId = zone.nativeRequestAnimationFrame.call(_global$1, () => {
|
39245 | // This is a work around for https://github.com/angular/angular/issues/36839.
|
39246 | // The core issue is that when event coalescing is enabled it is possible for microtasks
|
39247 | // to get flushed too early (As is the case with `Promise.then`) between the
|
39248 | // coalescing eventTasks.
|
39249 | //
|
39250 | // To workaround this we schedule a "fake" eventTask before we process the
|
39251 | // coalescing eventTasks. The benefit of this is that the "fake" container eventTask
|
39252 | // will prevent the microtasks queue from getting drained in between the coalescing
|
39253 | // eventTask execution.
|
39254 | if (!zone.fakeTopEventTask) {
|
39255 | zone.fakeTopEventTask = Zone.root.scheduleEventTask('fakeTopEventTask', () => {
|
39256 | zone.lastRequestAnimationFrameId = -1;
|
39257 | updateMicroTaskStatus(zone);
|
39258 | checkStable(zone);
|
39259 | }, undefined, () => { }, () => { });
|
39260 | }
|
39261 | zone.fakeTopEventTask.invoke();
|
39262 | });
|
39263 | updateMicroTaskStatus(zone);
|
39264 | }
|
39265 | function forkInnerZoneWithAngularBehavior(zone) {
|
39266 | const delayChangeDetectionForEventsDelegate = () => {
|
39267 | delayChangeDetectionForEvents(zone);
|
39268 | };
|
39269 | zone._inner = zone._inner.fork({
|
39270 | name: 'angular',
|
39271 | properties: { 'isAngularZone': true },
|
39272 | onInvokeTask: (delegate, current, target, task, applyThis, applyArgs) => {
|
39273 | try {
|
39274 | onEnter(zone);
|
39275 | return delegate.invokeTask(target, task, applyThis, applyArgs);
|
39276 | }
|
39277 | finally {
|
39278 | if ((zone.shouldCoalesceEventChangeDetection && task.type === 'eventTask') ||
|
39279 | zone.shouldCoalesceRunChangeDetection) {
|
39280 | delayChangeDetectionForEventsDelegate();
|
39281 | }
|
39282 | onLeave(zone);
|
39283 | }
|
39284 | },
|
39285 | onInvoke: (delegate, current, target, callback, applyThis, applyArgs, source) => {
|
39286 | try {
|
39287 | onEnter(zone);
|
39288 | return delegate.invoke(target, callback, applyThis, applyArgs, source);
|
39289 | }
|
39290 | finally {
|
39291 | if (zone.shouldCoalesceRunChangeDetection) {
|
39292 | delayChangeDetectionForEventsDelegate();
|
39293 | }
|
39294 | onLeave(zone);
|
39295 | }
|
39296 | },
|
39297 | onHasTask: (delegate, current, target, hasTaskState) => {
|
39298 | delegate.hasTask(target, hasTaskState);
|
39299 | if (current === target) {
|
39300 | // We are only interested in hasTask events which originate from our zone
|
39301 | // (A child hasTask event is not interesting to us)
|
39302 | if (hasTaskState.change == 'microTask') {
|
39303 | zone._hasPendingMicrotasks = hasTaskState.microTask;
|
39304 | updateMicroTaskStatus(zone);
|
39305 | checkStable(zone);
|
39306 | }
|
39307 | else if (hasTaskState.change == 'macroTask') {
|
39308 | zone.hasPendingMacrotasks = hasTaskState.macroTask;
|
39309 | }
|
39310 | }
|
39311 | },
|
39312 | onHandleError: (delegate, current, target, error) => {
|
39313 | delegate.handleError(target, error);
|
39314 | zone.runOutsideAngular(() => zone.onError.emit(error));
|
39315 | return false;
|
39316 | }
|
39317 | });
|
39318 | }
|
39319 | function updateMicroTaskStatus(zone) {
|
39320 | if (zone._hasPendingMicrotasks ||
|
39321 | ((zone.shouldCoalesceEventChangeDetection || zone.shouldCoalesceRunChangeDetection) &&
|
39322 | zone.lastRequestAnimationFrameId !== -1)) {
|
39323 | zone.hasPendingMicrotasks = true;
|
39324 | }
|
39325 | else {
|
39326 | zone.hasPendingMicrotasks = false;
|
39327 | }
|
39328 | }
|
39329 | function onEnter(zone) {
|
39330 | zone._nesting++;
|
39331 | if (zone.isStable) {
|
39332 | zone.isStable = false;
|
39333 | zone.onUnstable.emit(null);
|
39334 | }
|
39335 | }
|
39336 | function onLeave(zone) {
|
39337 | zone._nesting--;
|
39338 | checkStable(zone);
|
39339 | }
|
39340 | /**
|
39341 | * Provides a noop implementation of `NgZone` which does nothing. This zone requires explicit calls
|
39342 | * to framework to perform rendering.
|
39343 | */
|
39344 | class NoopNgZone {
|
39345 | constructor() {
|
39346 | this.hasPendingMicrotasks = false;
|
39347 | this.hasPendingMacrotasks = false;
|
39348 | this.isStable = true;
|
39349 | this.onUnstable = new EventEmitter();
|
39350 | this.onMicrotaskEmpty = new EventEmitter();
|
39351 | this.onStable = new EventEmitter();
|
39352 | this.onError = new EventEmitter();
|
39353 | }
|
39354 | run(fn, applyThis, applyArgs) {
|
39355 | return fn.apply(applyThis, applyArgs);
|
39356 | }
|
39357 | runGuarded(fn, applyThis, applyArgs) {
|
39358 | return fn.apply(applyThis, applyArgs);
|
39359 | }
|
39360 | runOutsideAngular(fn) {
|
39361 | return fn();
|
39362 | }
|
39363 | runTask(fn, applyThis, applyArgs, name) {
|
39364 | return fn.apply(applyThis, applyArgs);
|
39365 | }
|
39366 | }
|
39367 |
|
39368 | /**
|
39369 | * @license
|
39370 | * Copyright Google LLC All Rights Reserved.
|
39371 | *
|
39372 | * Use of this source code is governed by an MIT-style license that can be
|
39373 | * found in the LICENSE file at https://angular.io/license
|
39374 | */
|
39375 | /**
|
39376 | * The Testability service provides testing hooks that can be accessed from
|
39377 | * the browser and by services such as Protractor. Each bootstrapped Angular
|
39378 | * application on the page will have an instance of Testability.
|
39379 | * @publicApi
|
39380 | */
|
39381 | class Testability {
|
39382 | constructor(_ngZone) {
|
39383 | this._ngZone = _ngZone;
|
39384 | this._pendingCount = 0;
|
39385 | this._isZoneStable = true;
|
39386 | /**
|
39387 | * Whether any work was done since the last 'whenStable' callback. This is
|
39388 | * useful to detect if this could have potentially destabilized another
|
39389 | * component while it is stabilizing.
|
39390 | * @internal
|
39391 | */
|
39392 | this._didWork = false;
|
39393 | this._callbacks = [];
|
39394 | this.taskTrackingZone = null;
|
39395 | this._watchAngularEvents();
|
39396 | _ngZone.run(() => {
|
39397 | this.taskTrackingZone =
|
39398 | typeof Zone == 'undefined' ? null : Zone.current.get('TaskTrackingZone');
|
39399 | });
|
39400 | }
|
39401 | _watchAngularEvents() {
|
39402 | this._ngZone.onUnstable.subscribe({
|
39403 | next: () => {
|
39404 | this._didWork = true;
|
39405 | this._isZoneStable = false;
|
39406 | }
|
39407 | });
|
39408 | this._ngZone.runOutsideAngular(() => {
|
39409 | this._ngZone.onStable.subscribe({
|
39410 | next: () => {
|
39411 | NgZone.assertNotInAngularZone();
|
39412 | scheduleMicroTask(() => {
|
39413 | this._isZoneStable = true;
|
39414 | this._runCallbacksIfReady();
|
39415 | });
|
39416 | }
|
39417 | });
|
39418 | });
|
39419 | }
|
39420 | /**
|
39421 | * Increases the number of pending request
|
39422 | * @deprecated pending requests are now tracked with zones.
|
39423 | */
|
39424 | increasePendingRequestCount() {
|
39425 | this._pendingCount += 1;
|
39426 | this._didWork = true;
|
39427 | return this._pendingCount;
|
39428 | }
|
39429 | /**
|
39430 | * Decreases the number of pending request
|
39431 | * @deprecated pending requests are now tracked with zones
|
39432 | */
|
39433 | decreasePendingRequestCount() {
|
39434 | this._pendingCount -= 1;
|
39435 | if (this._pendingCount < 0) {
|
39436 | throw new Error('pending async requests below zero');
|
39437 | }
|
39438 | this._runCallbacksIfReady();
|
39439 | return this._pendingCount;
|
39440 | }
|
39441 | /**
|
39442 | * Whether an associated application is stable
|
39443 | */
|
39444 | isStable() {
|
39445 | return this._isZoneStable && this._pendingCount === 0 && !this._ngZone.hasPendingMacrotasks;
|
39446 | }
|
39447 | _runCallbacksIfReady() {
|
39448 | if (this.isStable()) {
|
39449 | // Schedules the call backs in a new frame so that it is always async.
|
39450 | scheduleMicroTask(() => {
|
39451 | while (this._callbacks.length !== 0) {
|
39452 | let cb = this._callbacks.pop();
|
39453 | clearTimeout(cb.timeoutId);
|
39454 | cb.doneCb(this._didWork);
|
39455 | }
|
39456 | this._didWork = false;
|
39457 | });
|
39458 | }
|
39459 | else {
|
39460 | // Still not stable, send updates.
|
39461 | let pending = this.getPendingTasks();
|
39462 | this._callbacks = this._callbacks.filter((cb) => {
|
39463 | if (cb.updateCb && cb.updateCb(pending)) {
|
39464 | clearTimeout(cb.timeoutId);
|
39465 | return false;
|
39466 | }
|
39467 | return true;
|
39468 | });
|
39469 | this._didWork = true;
|
39470 | }
|
39471 | }
|
39472 | getPendingTasks() {
|
39473 | if (!this.taskTrackingZone) {
|
39474 | return [];
|
39475 | }
|
39476 | // Copy the tasks data so that we don't leak tasks.
|
39477 | return this.taskTrackingZone.macroTasks.map((t) => {
|
39478 | return {
|
39479 | source: t.source,
|
39480 | // From TaskTrackingZone:
|
39481 | // https://github.com/angular/zone.js/blob/master/lib/zone-spec/task-tracking.ts#L40
|
39482 | creationLocation: t.creationLocation,
|
39483 | data: t.data
|
39484 | };
|
39485 | });
|
39486 | }
|
39487 | addCallback(cb, timeout, updateCb) {
|
39488 | let timeoutId = -1;
|
39489 | if (timeout && timeout > 0) {
|
39490 | timeoutId = setTimeout(() => {
|
39491 | this._callbacks = this._callbacks.filter((cb) => cb.timeoutId !== timeoutId);
|
39492 | cb(this._didWork, this.getPendingTasks());
|
39493 | }, timeout);
|
39494 | }
|
39495 | this._callbacks.push({ doneCb: cb, timeoutId: timeoutId, updateCb: updateCb });
|
39496 | }
|
39497 | /**
|
39498 | * Wait for the application to be stable with a timeout. If the timeout is reached before that
|
39499 | * happens, the callback receives a list of the macro tasks that were pending, otherwise null.
|
39500 | *
|
39501 | * @param doneCb The callback to invoke when Angular is stable or the timeout expires
|
39502 | * whichever comes first.
|
39503 | * @param timeout Optional. The maximum time to wait for Angular to become stable. If not
|
39504 | * specified, whenStable() will wait forever.
|
39505 | * @param updateCb Optional. If specified, this callback will be invoked whenever the set of
|
39506 | * pending macrotasks changes. If this callback returns true doneCb will not be invoked
|
39507 | * and no further updates will be issued.
|
39508 | */
|
39509 | whenStable(doneCb, timeout, updateCb) {
|
39510 | if (updateCb && !this.taskTrackingZone) {
|
39511 | throw new Error('Task tracking zone is required when passing an update callback to ' +
|
39512 | 'whenStable(). Is "zone.js/dist/task-tracking.js" loaded?');
|
39513 | }
|
39514 | // These arguments are 'Function' above to keep the public API simple.
|
39515 | this.addCallback(doneCb, timeout, updateCb);
|
39516 | this._runCallbacksIfReady();
|
39517 | }
|
39518 | /**
|
39519 | * Get the number of pending requests
|
39520 | * @deprecated pending requests are now tracked with zones
|
39521 | */
|
39522 | getPendingRequestCount() {
|
39523 | return this._pendingCount;
|
39524 | }
|
39525 | /**
|
39526 | * Find providers by name
|
39527 | * @param using The root element to search from
|
39528 | * @param provider The name of binding variable
|
39529 | * @param exactMatch Whether using exactMatch
|
39530 | */
|
39531 | findProviders(using, provider, exactMatch) {
|
39532 | // TODO(juliemr): implement.
|
39533 | return [];
|
39534 | }
|
39535 | }
|
39536 | Testability.decorators = [
|
39537 | { type: Injectable }
|
39538 | ];
|
39539 | Testability.ctorParameters = () => [
|
39540 | { type: NgZone }
|
39541 | ];
|
39542 | /**
|
39543 | * A global registry of {@link Testability} instances for specific elements.
|
39544 | * @publicApi
|
39545 | */
|
39546 | class TestabilityRegistry {
|
39547 | constructor() {
|
39548 | /** @internal */
|
39549 | this._applications = new Map();
|
39550 | _testabilityGetter.addToWindow(this);
|
39551 | }
|
39552 | /**
|
39553 | * Registers an application with a testability hook so that it can be tracked
|
39554 | * @param token token of application, root element
|
39555 | * @param testability Testability hook
|
39556 | */
|
39557 | registerApplication(token, testability) {
|
39558 | this._applications.set(token, testability);
|
39559 | }
|
39560 | /**
|
39561 | * Unregisters an application.
|
39562 | * @param token token of application, root element
|
39563 | */
|
39564 | unregisterApplication(token) {
|
39565 | this._applications.delete(token);
|
39566 | }
|
39567 | /**
|
39568 | * Unregisters all applications
|
39569 | */
|
39570 | unregisterAllApplications() {
|
39571 | this._applications.clear();
|
39572 | }
|
39573 | /**
|
39574 | * Get a testability hook associated with the application
|
39575 | * @param elem root element
|
39576 | */
|
39577 | getTestability(elem) {
|
39578 | return this._applications.get(elem) || null;
|
39579 | }
|
39580 | /**
|
39581 | * Get all registered testabilities
|
39582 | */
|
39583 | getAllTestabilities() {
|
39584 | return Array.from(this._applications.values());
|
39585 | }
|
39586 | /**
|
39587 | * Get all registered applications(root elements)
|
39588 | */
|
39589 | getAllRootElements() {
|
39590 | return Array.from(this._applications.keys());
|
39591 | }
|
39592 | /**
|
39593 | * Find testability of a node in the Tree
|
39594 | * @param elem node
|
39595 | * @param findInAncestors whether finding testability in ancestors if testability was not found in
|
39596 | * current node
|
39597 | */
|
39598 | findTestabilityInTree(elem, findInAncestors = true) {
|
39599 | return _testabilityGetter.findTestabilityInTree(this, elem, findInAncestors);
|
39600 | }
|
39601 | }
|
39602 | TestabilityRegistry.decorators = [
|
39603 | { type: Injectable }
|
39604 | ];
|
39605 | TestabilityRegistry.ctorParameters = () => [];
|
39606 | class _NoopGetTestability {
|
39607 | addToWindow(registry) { }
|
39608 | findTestabilityInTree(registry, elem, findInAncestors) {
|
39609 | return null;
|
39610 | }
|
39611 | }
|
39612 | let _testabilityGetter = new _NoopGetTestability();
|
39613 |
|
39614 | /**
|
39615 | * @license
|
39616 | * Copyright Google LLC All Rights Reserved.
|
39617 | *
|
39618 | * Use of this source code is governed by an MIT-style license that can be
|
39619 | * found in the LICENSE file at https://angular.io/license
|
39620 | */
|
39621 | /**
|
39622 | * This file is used to control if the default rendering pipeline should be `ViewEngine` or `Ivy`.
|
39623 | *
|
39624 | * For more information on how to run and debug tests with either Ivy or View Engine (legacy),
|
39625 | * please see [BAZEL.md](./docs/BAZEL.md).
|
39626 | */
|
39627 | let _devMode = true;
|
39628 | /**
|
39629 | * Returns whether Angular is in development mode. After called once,
|
39630 | * the value is locked and won't change any more.
|
39631 | *
|
39632 | * By default, this is true, unless a user calls `enableProdMode` before calling this.
|
39633 | *
|
39634 | * @publicApi
|
39635 | */
|
39636 | function isDevMode() {
|
39637 | return _devMode;
|
39638 | }
|
39639 |
|
39640 | /**
|
39641 | * @license
|
39642 | * Copyright Google LLC All Rights Reserved.
|
39643 | *
|
39644 | * Use of this source code is governed by an MIT-style license that can be
|
39645 | * found in the LICENSE file at https://angular.io/license
|
39646 | */
|
39647 | let _platform;
|
39648 | let compileNgModuleFactory = compileNgModuleFactory__PRE_R3__;
|
39649 | function compileNgModuleFactory__PRE_R3__(injector, options, moduleType) {
|
39650 | const compilerFactory = injector.get(CompilerFactory);
|
39651 | const compiler = compilerFactory.createCompiler([options]);
|
39652 | return compiler.compileModuleAsync(moduleType);
|
39653 | }
|
39654 | let isBoundToModule = isBoundToModule__PRE_R3__;
|
39655 | function isBoundToModule__PRE_R3__(cf) {
|
39656 | return cf instanceof ComponentFactoryBoundToModule;
|
39657 | }
|
39658 | const ALLOW_MULTIPLE_PLATFORMS = new InjectionToken('AllowMultipleToken');
|
39659 | /**
|
39660 | * Creates a platform.
|
39661 | * Platforms must be created on launch using this function.
|
39662 | *
|
39663 | * @publicApi
|
39664 | */
|
39665 | function createPlatform(injector) {
|
39666 | if (_platform && !_platform.destroyed &&
|
39667 | !_platform.injector.get(ALLOW_MULTIPLE_PLATFORMS, false)) {
|
39668 | throw new Error('There can be only one platform. Destroy the previous one to create a new one.');
|
39669 | }
|
39670 | _platform = injector.get(PlatformRef);
|
39671 | const inits = injector.get(PLATFORM_INITIALIZER, null);
|
39672 | if (inits)
|
39673 | inits.forEach((init) => init());
|
39674 | return _platform;
|
39675 | }
|
39676 | /**
|
39677 | * Creates a factory for a platform. Can be used to provide or override `Providers` specific to
|
39678 | * your application's runtime needs, such as `PLATFORM_INITIALIZER` and `PLATFORM_ID`.
|
39679 | * @param parentPlatformFactory Another platform factory to modify. Allows you to compose factories
|
39680 | * to build up configurations that might be required by different libraries or parts of the
|
39681 | * application.
|
39682 | * @param name Identifies the new platform factory.
|
39683 | * @param providers A set of dependency providers for platforms created with the new factory.
|
39684 | *
|
39685 | * @publicApi
|
39686 | */
|
39687 | function createPlatformFactory(parentPlatformFactory, name, providers = []) {
|
39688 | const desc = `Platform: ${name}`;
|
39689 | const marker = new InjectionToken(desc);
|
39690 | return (extraProviders = []) => {
|
39691 | let platform = getPlatform();
|
39692 | if (!platform || platform.injector.get(ALLOW_MULTIPLE_PLATFORMS, false)) {
|
39693 | if (parentPlatformFactory) {
|
39694 | parentPlatformFactory(providers.concat(extraProviders).concat({ provide: marker, useValue: true }));
|
39695 | }
|
39696 | else {
|
39697 | const injectedProviders = providers.concat(extraProviders).concat({ provide: marker, useValue: true }, {
|
39698 | provide: INJECTOR_SCOPE,
|
39699 | useValue: 'platform'
|
39700 | });
|
39701 | createPlatform(Injector.create({ providers: injectedProviders, name: desc }));
|
39702 | }
|
39703 | }
|
39704 | return assertPlatform(marker);
|
39705 | };
|
39706 | }
|
39707 | /**
|
39708 | * Checks that there is currently a platform that contains the given token as a provider.
|
39709 | *
|
39710 | * @publicApi
|
39711 | */
|
39712 | function assertPlatform(requiredToken) {
|
39713 | const platform = getPlatform();
|
39714 | if (!platform) {
|
39715 | throw new Error('No platform exists!');
|
39716 | }
|
39717 | if (!platform.injector.get(requiredToken, null)) {
|
39718 | throw new Error('A platform with a different configuration has been created. Please destroy it first.');
|
39719 | }
|
39720 | return platform;
|
39721 | }
|
39722 | /**
|
39723 | * Returns the current platform.
|
39724 | *
|
39725 | * @publicApi
|
39726 | */
|
39727 | function getPlatform() {
|
39728 | return _platform && !_platform.destroyed ? _platform : null;
|
39729 | }
|
39730 | /**
|
39731 | * The Angular platform is the entry point for Angular on a web page.
|
39732 | * Each page has exactly one platform. Services (such as reflection) which are common
|
39733 | * to every Angular application running on the page are bound in its scope.
|
39734 | * A page's platform is initialized implicitly when a platform is created using a platform
|
39735 | * factory such as `PlatformBrowser`, or explicitly by calling the `createPlatform()` function.
|
39736 | *
|
39737 | * @publicApi
|
39738 | */
|
39739 | class PlatformRef {
|
39740 | /** @internal */
|
39741 | constructor(_injector) {
|
39742 | this._injector = _injector;
|
39743 | this._modules = [];
|
39744 | this._destroyListeners = [];
|
39745 | this._destroyed = false;
|
39746 | }
|
39747 | /**
|
39748 | * Creates an instance of an `@NgModule` for the given platform for offline compilation.
|
39749 | *
|
39750 | * @usageNotes
|
39751 | *
|
39752 | * The following example creates the NgModule for a browser platform.
|
39753 | *
|
39754 | * ```typescript
|
39755 | * my_module.ts:
|
39756 | *
|
39757 | * @NgModule({
|
39758 | * imports: [BrowserModule]
|
39759 | * })
|
39760 | * class MyModule {}
|
39761 | *
|
39762 | * main.ts:
|
39763 | * import {MyModuleNgFactory} from './my_module.ngfactory';
|
39764 | * import {platformBrowser} from '@angular/platform-browser';
|
39765 | *
|
39766 | * let moduleRef = platformBrowser().bootstrapModuleFactory(MyModuleNgFactory);
|
39767 | * ```
|
39768 | */
|
39769 | bootstrapModuleFactory(moduleFactory, options) {
|
39770 | // Note: We need to create the NgZone _before_ we instantiate the module,
|
39771 | // as instantiating the module creates some providers eagerly.
|
39772 | // So we create a mini parent injector that just contains the new NgZone and
|
39773 | // pass that as parent to the NgModuleFactory.
|
39774 | const ngZoneOption = options ? options.ngZone : undefined;
|
39775 | const ngZoneEventCoalescing = (options && options.ngZoneEventCoalescing) || false;
|
39776 | const ngZoneRunCoalescing = (options && options.ngZoneRunCoalescing) || false;
|
39777 | const ngZone = getNgZone(ngZoneOption, { ngZoneEventCoalescing, ngZoneRunCoalescing });
|
39778 | const providers = [{ provide: NgZone, useValue: ngZone }];
|
39779 | // Note: Create ngZoneInjector within ngZone.run so that all of the instantiated services are
|
39780 | // created within the Angular zone
|
39781 | // Do not try to replace ngZone.run with ApplicationRef#run because ApplicationRef would then be
|
39782 | // created outside of the Angular zone.
|
39783 | return ngZone.run(() => {
|
39784 | const ngZoneInjector = Injector.create({ providers: providers, parent: this.injector, name: moduleFactory.moduleType.name });
|
39785 | const moduleRef = moduleFactory.create(ngZoneInjector);
|
39786 | const exceptionHandler = moduleRef.injector.get(ErrorHandler, null);
|
39787 | if (!exceptionHandler) {
|
39788 | throw new Error('No ErrorHandler. Is platform module (BrowserModule) included?');
|
39789 | }
|
39790 | ngZone.runOutsideAngular(() => {
|
39791 | const subscription = ngZone.onError.subscribe({
|
39792 | next: (error) => {
|
39793 | exceptionHandler.handleError(error);
|
39794 | }
|
39795 | });
|
39796 | moduleRef.onDestroy(() => {
|
39797 | remove(this._modules, moduleRef);
|
39798 | subscription.unsubscribe();
|
39799 | });
|
39800 | });
|
39801 | return _callAndReportToErrorHandler(exceptionHandler, ngZone, () => {
|
39802 | const initStatus = moduleRef.injector.get(ApplicationInitStatus);
|
39803 | initStatus.runInitializers();
|
39804 | return initStatus.donePromise.then(() => {
|
39805 | if (ivyEnabled) {
|
39806 | // If the `LOCALE_ID` provider is defined at bootstrap then we set the value for ivy
|
39807 | const localeId = moduleRef.injector.get(LOCALE_ID$1, DEFAULT_LOCALE_ID);
|
39808 | setLocaleId(localeId || DEFAULT_LOCALE_ID);
|
39809 | }
|
39810 | this._moduleDoBootstrap(moduleRef);
|
39811 | return moduleRef;
|
39812 | });
|
39813 | });
|
39814 | });
|
39815 | }
|
39816 | /**
|
39817 | * Creates an instance of an `@NgModule` for a given platform using the given runtime compiler.
|
39818 | *
|
39819 | * @usageNotes
|
39820 | * ### Simple Example
|
39821 | *
|
39822 | * ```typescript
|
39823 | * @NgModule({
|
39824 | * imports: [BrowserModule]
|
39825 | * })
|
39826 | * class MyModule {}
|
39827 | *
|
39828 | * let moduleRef = platformBrowser().bootstrapModule(MyModule);
|
39829 | * ```
|
39830 | *
|
39831 | */
|
39832 | bootstrapModule(moduleType, compilerOptions = []) {
|
39833 | const options = optionsReducer({}, compilerOptions);
|
39834 | return compileNgModuleFactory(this.injector, options, moduleType)
|
39835 | .then(moduleFactory => this.bootstrapModuleFactory(moduleFactory, options));
|
39836 | }
|
39837 | _moduleDoBootstrap(moduleRef) {
|
39838 | const appRef = moduleRef.injector.get(ApplicationRef);
|
39839 | if (moduleRef._bootstrapComponents.length > 0) {
|
39840 | moduleRef._bootstrapComponents.forEach(f => appRef.bootstrap(f));
|
39841 | }
|
39842 | else if (moduleRef.instance.ngDoBootstrap) {
|
39843 | moduleRef.instance.ngDoBootstrap(appRef);
|
39844 | }
|
39845 | else {
|
39846 | throw new Error(`The module ${stringify$1(moduleRef.instance
|
39847 | .constructor)} was bootstrapped, but it does not declare "@NgModule.bootstrap" components nor a "ngDoBootstrap" method. ` +
|
39848 | `Please define one of these.`);
|
39849 | }
|
39850 | this._modules.push(moduleRef);
|
39851 | }
|
39852 | /**
|
39853 | * Registers a listener to be called when the platform is destroyed.
|
39854 | */
|
39855 | onDestroy(callback) {
|
39856 | this._destroyListeners.push(callback);
|
39857 | }
|
39858 | /**
|
39859 | * Retrieves the platform {@link Injector}, which is the parent injector for
|
39860 | * every Angular application on the page and provides singleton providers.
|
39861 | */
|
39862 | get injector() {
|
39863 | return this._injector;
|
39864 | }
|
39865 | /**
|
39866 | * Destroys the current Angular platform and all Angular applications on the page.
|
39867 | * Destroys all modules and listeners registered with the platform.
|
39868 | */
|
39869 | destroy() {
|
39870 | if (this._destroyed) {
|
39871 | throw new Error('The platform has already been destroyed!');
|
39872 | }
|
39873 | this._modules.slice().forEach(module => module.destroy());
|
39874 | this._destroyListeners.forEach(listener => listener());
|
39875 | this._destroyed = true;
|
39876 | }
|
39877 | get destroyed() {
|
39878 | return this._destroyed;
|
39879 | }
|
39880 | }
|
39881 | PlatformRef.decorators = [
|
39882 | { type: Injectable }
|
39883 | ];
|
39884 | PlatformRef.ctorParameters = () => [
|
39885 | { type: Injector }
|
39886 | ];
|
39887 | function getNgZone(ngZoneOption, extra) {
|
39888 | let ngZone;
|
39889 | if (ngZoneOption === 'noop') {
|
39890 | ngZone = new NoopNgZone();
|
39891 | }
|
39892 | else {
|
39893 | ngZone = (ngZoneOption === 'zone.js' ? undefined : ngZoneOption) || new NgZone({
|
39894 | enableLongStackTrace: isDevMode(),
|
39895 | shouldCoalesceEventChangeDetection: !!(extra === null || extra === void 0 ? void 0 : extra.ngZoneEventCoalescing),
|
39896 | shouldCoalesceRunChangeDetection: !!(extra === null || extra === void 0 ? void 0 : extra.ngZoneRunCoalescing)
|
39897 | });
|
39898 | }
|
39899 | return ngZone;
|
39900 | }
|
39901 | function _callAndReportToErrorHandler(errorHandler, ngZone, callback) {
|
39902 | try {
|
39903 | const result = callback();
|
39904 | if (isPromise$1(result)) {
|
39905 | return result.catch((e) => {
|
39906 | ngZone.runOutsideAngular(() => errorHandler.handleError(e));
|
39907 | // rethrow as the exception handler might not do it
|
39908 | throw e;
|
39909 | });
|
39910 | }
|
39911 | return result;
|
39912 | }
|
39913 | catch (e) {
|
39914 | ngZone.runOutsideAngular(() => errorHandler.handleError(e));
|
39915 | // rethrow as the exception handler might not do it
|
39916 | throw e;
|
39917 | }
|
39918 | }
|
39919 | function optionsReducer(dst, objs) {
|
39920 | if (Array.isArray(objs)) {
|
39921 | dst = objs.reduce(optionsReducer, dst);
|
39922 | }
|
39923 | else {
|
39924 | dst = Object.assign(Object.assign({}, dst), objs);
|
39925 | }
|
39926 | return dst;
|
39927 | }
|
39928 | /**
|
39929 | * A reference to an Angular application running on a page.
|
39930 | *
|
39931 | * @usageNotes
|
39932 | *
|
39933 | * {@a is-stable-examples}
|
39934 | * ### isStable examples and caveats
|
39935 | *
|
39936 | * Note two important points about `isStable`, demonstrated in the examples below:
|
39937 | * - the application will never be stable if you start any kind
|
39938 | * of recurrent asynchronous task when the application starts
|
39939 | * (for example for a polling process, started with a `setInterval`, a `setTimeout`
|
39940 | * or using RxJS operators like `interval`);
|
39941 | * - the `isStable` Observable runs outside of the Angular zone.
|
39942 | *
|
39943 | * Let's imagine that you start a recurrent task
|
39944 | * (here incrementing a counter, using RxJS `interval`),
|
39945 | * and at the same time subscribe to `isStable`.
|
39946 | *
|
39947 | * ```
|
39948 | * constructor(appRef: ApplicationRef) {
|
39949 | * appRef.isStable.pipe(
|
39950 | * filter(stable => stable)
|
39951 | * ).subscribe(() => console.log('App is stable now');
|
39952 | * interval(1000).subscribe(counter => console.log(counter));
|
39953 | * }
|
39954 | * ```
|
39955 | * In this example, `isStable` will never emit `true`,
|
39956 | * and the trace "App is stable now" will never get logged.
|
39957 | *
|
39958 | * If you want to execute something when the app is stable,
|
39959 | * you have to wait for the application to be stable
|
39960 | * before starting your polling process.
|
39961 | *
|
39962 | * ```
|
39963 | * constructor(appRef: ApplicationRef) {
|
39964 | * appRef.isStable.pipe(
|
39965 | * first(stable => stable),
|
39966 | * tap(stable => console.log('App is stable now')),
|
39967 | * switchMap(() => interval(1000))
|
39968 | * ).subscribe(counter => console.log(counter));
|
39969 | * }
|
39970 | * ```
|
39971 | * In this example, the trace "App is stable now" will be logged
|
39972 | * and then the counter starts incrementing every second.
|
39973 | *
|
39974 | * Note also that this Observable runs outside of the Angular zone,
|
39975 | * which means that the code in the subscription
|
39976 | * to this Observable will not trigger the change detection.
|
39977 | *
|
39978 | * Let's imagine that instead of logging the counter value,
|
39979 | * you update a field of your component
|
39980 | * and display it in its template.
|
39981 | *
|
39982 | * ```
|
39983 | * constructor(appRef: ApplicationRef) {
|
39984 | * appRef.isStable.pipe(
|
39985 | * first(stable => stable),
|
39986 | * switchMap(() => interval(1000))
|
39987 | * ).subscribe(counter => this.value = counter);
|
39988 | * }
|
39989 | * ```
|
39990 | * As the `isStable` Observable runs outside the zone,
|
39991 | * the `value` field will be updated properly,
|
39992 | * but the template will not be refreshed!
|
39993 | *
|
39994 | * You'll have to manually trigger the change detection to update the template.
|
39995 | *
|
39996 | * ```
|
39997 | * constructor(appRef: ApplicationRef, cd: ChangeDetectorRef) {
|
39998 | * appRef.isStable.pipe(
|
39999 | * first(stable => stable),
|
40000 | * switchMap(() => interval(1000))
|
40001 | * ).subscribe(counter => {
|
40002 | * this.value = counter;
|
40003 | * cd.detectChanges();
|
40004 | * });
|
40005 | * }
|
40006 | * ```
|
40007 | *
|
40008 | * Or make the subscription callback run inside the zone.
|
40009 | *
|
40010 | * ```
|
40011 | * constructor(appRef: ApplicationRef, zone: NgZone) {
|
40012 | * appRef.isStable.pipe(
|
40013 | * first(stable => stable),
|
40014 | * switchMap(() => interval(1000))
|
40015 | * ).subscribe(counter => zone.run(() => this.value = counter));
|
40016 | * }
|
40017 | * ```
|
40018 | *
|
40019 | * @publicApi
|
40020 | */
|
40021 | class ApplicationRef {
|
40022 | /** @internal */
|
40023 | constructor(_zone, _console, _injector, _exceptionHandler, _componentFactoryResolver, _initStatus) {
|
40024 | this._zone = _zone;
|
40025 | this._console = _console;
|
40026 | this._injector = _injector;
|
40027 | this._exceptionHandler = _exceptionHandler;
|
40028 | this._componentFactoryResolver = _componentFactoryResolver;
|
40029 | this._initStatus = _initStatus;
|
40030 | /** @internal */
|
40031 | this._bootstrapListeners = [];
|
40032 | this._views = [];
|
40033 | this._runningTick = false;
|
40034 | this._stable = true;
|
40035 | /**
|
40036 | * Get a list of component types registered to this application.
|
40037 | * This list is populated even before the component is created.
|
40038 | */
|
40039 | this.componentTypes = [];
|
40040 | /**
|
40041 | * Get a list of components registered to this application.
|
40042 | */
|
40043 | this.components = [];
|
40044 | this._onMicrotaskEmptySubscription = this._zone.onMicrotaskEmpty.subscribe({
|
40045 | next: () => {
|
40046 | this._zone.run(() => {
|
40047 | this.tick();
|
40048 | });
|
40049 | }
|
40050 | });
|
40051 | const isCurrentlyStable = new Observable((observer) => {
|
40052 | this._stable = this._zone.isStable && !this._zone.hasPendingMacrotasks &&
|
40053 | !this._zone.hasPendingMicrotasks;
|
40054 | this._zone.runOutsideAngular(() => {
|
40055 | observer.next(this._stable);
|
40056 | observer.complete();
|
40057 | });
|
40058 | });
|
40059 | const isStable = new Observable((observer) => {
|
40060 | // Create the subscription to onStable outside the Angular Zone so that
|
40061 | // the callback is run outside the Angular Zone.
|
40062 | let stableSub;
|
40063 | this._zone.runOutsideAngular(() => {
|
40064 | stableSub = this._zone.onStable.subscribe(() => {
|
40065 | NgZone.assertNotInAngularZone();
|
40066 | // Check whether there are no pending macro/micro tasks in the next tick
|
40067 | // to allow for NgZone to update the state.
|
40068 | scheduleMicroTask(() => {
|
40069 | if (!this._stable && !this._zone.hasPendingMacrotasks &&
|
40070 | !this._zone.hasPendingMicrotasks) {
|
40071 | this._stable = true;
|
40072 | observer.next(true);
|
40073 | }
|
40074 | });
|
40075 | });
|
40076 | });
|
40077 | const unstableSub = this._zone.onUnstable.subscribe(() => {
|
40078 | NgZone.assertInAngularZone();
|
40079 | if (this._stable) {
|
40080 | this._stable = false;
|
40081 | this._zone.runOutsideAngular(() => {
|
40082 | observer.next(false);
|
40083 | });
|
40084 | }
|
40085 | });
|
40086 | return () => {
|
40087 | stableSub.unsubscribe();
|
40088 | unstableSub.unsubscribe();
|
40089 | };
|
40090 | });
|
40091 | this.isStable =
|
40092 | merge$1(isCurrentlyStable, isStable.pipe(share()));
|
40093 | }
|
40094 | /**
|
40095 | * Bootstrap a new component at the root level of the application.
|
40096 | *
|
40097 | * @usageNotes
|
40098 | * ### Bootstrap process
|
40099 | *
|
40100 | * When bootstrapping a new root component into an application, Angular mounts the
|
40101 | * specified application component onto DOM elements identified by the componentType's
|
40102 | * selector and kicks off automatic change detection to finish initializing the component.
|
40103 | *
|
40104 | * Optionally, a component can be mounted onto a DOM element that does not match the
|
40105 | * componentType's selector.
|
40106 | *
|
40107 | * ### Example
|
40108 | * {@example core/ts/platform/platform.ts region='longform'}
|
40109 | */
|
40110 | bootstrap(componentOrFactory, rootSelectorOrNode) {
|
40111 | if (!this._initStatus.done) {
|
40112 | throw new Error('Cannot bootstrap as there are still asynchronous initializers running. Bootstrap components in the `ngDoBootstrap` method of the root module.');
|
40113 | }
|
40114 | let componentFactory;
|
40115 | if (componentOrFactory instanceof ComponentFactory) {
|
40116 | componentFactory = componentOrFactory;
|
40117 | }
|
40118 | else {
|
40119 | componentFactory =
|
40120 | this._componentFactoryResolver.resolveComponentFactory(componentOrFactory);
|
40121 | }
|
40122 | this.componentTypes.push(componentFactory.componentType);
|
40123 | // Create a factory associated with the current module if it's not bound to some other
|
40124 | const ngModule = isBoundToModule(componentFactory) ? undefined : this._injector.get(NgModuleRef);
|
40125 | const selectorOrNode = rootSelectorOrNode || componentFactory.selector;
|
40126 | const compRef = componentFactory.create(Injector.NULL, [], selectorOrNode, ngModule);
|
40127 | const nativeElement = compRef.location.nativeElement;
|
40128 | const testability = compRef.injector.get(Testability, null);
|
40129 | const testabilityRegistry = testability && compRef.injector.get(TestabilityRegistry);
|
40130 | if (testability && testabilityRegistry) {
|
40131 | testabilityRegistry.registerApplication(nativeElement, testability);
|
40132 | }
|
40133 | compRef.onDestroy(() => {
|
40134 | this.detachView(compRef.hostView);
|
40135 | remove(this.components, compRef);
|
40136 | if (testabilityRegistry) {
|
40137 | testabilityRegistry.unregisterApplication(nativeElement);
|
40138 | }
|
40139 | });
|
40140 | this._loadComponent(compRef);
|
40141 | {
|
40142 | this._console.log(`Angular is running in development mode. Call enableProdMode() to enable production mode.`);
|
40143 | }
|
40144 | return compRef;
|
40145 | }
|
40146 | /**
|
40147 | * Invoke this method to explicitly process change detection and its side-effects.
|
40148 | *
|
40149 | * In development mode, `tick()` also performs a second change detection cycle to ensure that no
|
40150 | * further changes are detected. If additional changes are picked up during this second cycle,
|
40151 | * bindings in the app have side-effects that cannot be resolved in a single change detection
|
40152 | * pass.
|
40153 | * In this case, Angular throws an error, since an Angular application can only have one change
|
40154 | * detection pass during which all change detection must complete.
|
40155 | */
|
40156 | tick() {
|
40157 | if (this._runningTick) {
|
40158 | throw new Error('ApplicationRef.tick is called recursively');
|
40159 | }
|
40160 | try {
|
40161 | this._runningTick = true;
|
40162 | for (let view of this._views) {
|
40163 | view.detectChanges();
|
40164 | }
|
40165 | // Note that we have still left the `isDevMode()` condition in order to avoid
|
40166 | // creating a breaking change for projects that still use the View Engine.
|
40167 | if ((typeof ngDevMode === 'undefined' || ngDevMode) && isDevMode()) {
|
40168 | for (let view of this._views) {
|
40169 | view.checkNoChanges();
|
40170 | }
|
40171 | }
|
40172 | }
|
40173 | catch (e) {
|
40174 | // Attention: Don't rethrow as it could cancel subscriptions to Observables!
|
40175 | this._zone.runOutsideAngular(() => this._exceptionHandler.handleError(e));
|
40176 | }
|
40177 | finally {
|
40178 | this._runningTick = false;
|
40179 | }
|
40180 | }
|
40181 | /**
|
40182 | * Attaches a view so that it will be dirty checked.
|
40183 | * The view will be automatically detached when it is destroyed.
|
40184 | * This will throw if the view is already attached to a ViewContainer.
|
40185 | */
|
40186 | attachView(viewRef) {
|
40187 | const view = viewRef;
|
40188 | this._views.push(view);
|
40189 | view.attachToAppRef(this);
|
40190 | }
|
40191 | /**
|
40192 | * Detaches a view from dirty checking again.
|
40193 | */
|
40194 | detachView(viewRef) {
|
40195 | const view = viewRef;
|
40196 | remove(this._views, view);
|
40197 | view.detachFromAppRef();
|
40198 | }
|
40199 | _loadComponent(componentRef) {
|
40200 | this.attachView(componentRef.hostView);
|
40201 | this.tick();
|
40202 | this.components.push(componentRef);
|
40203 | // Get the listeners lazily to prevent DI cycles.
|
40204 | const listeners = this._injector.get(APP_BOOTSTRAP_LISTENER, []).concat(this._bootstrapListeners);
|
40205 | listeners.forEach((listener) => listener(componentRef));
|
40206 | }
|
40207 | /** @internal */
|
40208 | ngOnDestroy() {
|
40209 | this._views.slice().forEach((view) => view.destroy());
|
40210 | this._onMicrotaskEmptySubscription.unsubscribe();
|
40211 | }
|
40212 | /**
|
40213 | * Returns the number of attached views.
|
40214 | */
|
40215 | get viewCount() {
|
40216 | return this._views.length;
|
40217 | }
|
40218 | }
|
40219 | ApplicationRef.decorators = [
|
40220 | { type: Injectable }
|
40221 | ];
|
40222 | ApplicationRef.ctorParameters = () => [
|
40223 | { type: NgZone },
|
40224 | { type: Console },
|
40225 | { type: Injector },
|
40226 | { type: ErrorHandler },
|
40227 | { type: ComponentFactoryResolver },
|
40228 | { type: ApplicationInitStatus }
|
40229 | ];
|
40230 | function remove(list, el) {
|
40231 | const index = list.indexOf(el);
|
40232 | if (index > -1) {
|
40233 | list.splice(index, 1);
|
40234 | }
|
40235 | }
|
40236 |
|
40237 | /**
|
40238 | * @license
|
40239 | * Copyright Google LLC All Rights Reserved.
|
40240 | *
|
40241 | * Use of this source code is governed by an MIT-style license that can be
|
40242 | * found in the LICENSE file at https://angular.io/license
|
40243 | */
|
40244 | const _CORE_PLATFORM_PROVIDERS = [
|
40245 | // Set a default platform name for platforms that don't set it explicitly.
|
40246 | { provide: PLATFORM_ID, useValue: 'unknown' },
|
40247 | { provide: PlatformRef, deps: [Injector] },
|
40248 | { provide: TestabilityRegistry, deps: [] },
|
40249 | { provide: Console, deps: [] },
|
40250 | ];
|
40251 | /**
|
40252 | * This platform has to be included in any other platform
|
40253 | *
|
40254 | * @publicApi
|
40255 | */
|
40256 | const platformCore = createPlatformFactory(null, 'core', _CORE_PLATFORM_PROVIDERS);
|
40257 |
|
40258 | /**
|
40259 | * @license
|
40260 | * Copyright Google LLC All Rights Reserved.
|
40261 | *
|
40262 | * Use of this source code is governed by an MIT-style license that can be
|
40263 | * found in the LICENSE file at https://angular.io/license
|
40264 | */
|
40265 | function _iterableDiffersFactory() {
|
40266 | return defaultIterableDiffers;
|
40267 | }
|
40268 | function _keyValueDiffersFactory() {
|
40269 | return defaultKeyValueDiffers;
|
40270 | }
|
40271 | function _localeFactory(locale) {
|
40272 | locale = locale || getGlobalLocale();
|
40273 | return locale;
|
40274 | }
|
40275 | /**
|
40276 | * Work out the locale from the potential global properties.
|
40277 | *
|
40278 | * * Closure Compiler: use `goog.LOCALE`.
|
40279 | * * Ivy enabled: use `$localize.locale`
|
40280 | */
|
40281 | function getGlobalLocale() {
|
40282 | if (typeof ngI18nClosureMode !== 'undefined' && ngI18nClosureMode &&
|
40283 | typeof goog !== 'undefined' && goog.LOCALE !== 'en') {
|
40284 | // * The default `goog.LOCALE` value is `en`, while Angular used `en-US`.
|
40285 | // * In order to preserve backwards compatibility, we use Angular default value over
|
40286 | // Closure Compiler's one.
|
40287 | return goog.LOCALE;
|
40288 | }
|
40289 | else {
|
40290 | // KEEP `typeof $localize !== 'undefined' && $localize.locale` IN SYNC WITH THE LOCALIZE
|
40291 | // COMPILE-TIME INLINER.
|
40292 | //
|
40293 | // * During compile time inlining of translations the expression will be replaced
|
40294 | // with a string literal that is the current locale. Other forms of this expression are not
|
40295 | // guaranteed to be replaced.
|
40296 | //
|
40297 | // * During runtime translation evaluation, the developer is required to set `$localize.locale`
|
40298 | // if required, or just to provide their own `LOCALE_ID` provider.
|
40299 | return DEFAULT_LOCALE_ID;
|
40300 | }
|
40301 | }
|
40302 | const ɵ0$b = USD_CURRENCY_CODE;
|
40303 | /**
|
40304 | * A built-in [dependency injection token](guide/glossary#di-token)
|
40305 | * that is used to configure the root injector for bootstrapping.
|
40306 | */
|
40307 | const APPLICATION_MODULE_PROVIDERS = [
|
40308 | {
|
40309 | provide: ApplicationRef,
|
40310 | useClass: ApplicationRef,
|
40311 | deps: [NgZone, Console, Injector, ErrorHandler, ComponentFactoryResolver, ApplicationInitStatus]
|
40312 | },
|
40313 | { provide: SCHEDULER, deps: [NgZone], useFactory: zoneSchedulerFactory },
|
40314 | {
|
40315 | provide: ApplicationInitStatus,
|
40316 | useClass: ApplicationInitStatus,
|
40317 | deps: [[new Optional(), APP_INITIALIZER]]
|
40318 | },
|
40319 | { provide: Compiler, useClass: Compiler, deps: [] },
|
40320 | APP_ID_RANDOM_PROVIDER,
|
40321 | { provide: IterableDiffers, useFactory: _iterableDiffersFactory, deps: [] },
|
40322 | { provide: KeyValueDiffers, useFactory: _keyValueDiffersFactory, deps: [] },
|
40323 | {
|
40324 | provide: LOCALE_ID$1,
|
40325 | useFactory: _localeFactory,
|
40326 | deps: [[new Inject(LOCALE_ID$1), new Optional(), new SkipSelf()]]
|
40327 | },
|
40328 | { provide: DEFAULT_CURRENCY_CODE, useValue: ɵ0$b },
|
40329 | ];
|
40330 | /**
|
40331 | * Schedule work at next available slot.
|
40332 | *
|
40333 | * In Ivy this is just `requestAnimationFrame`. For compatibility reasons when bootstrapped
|
40334 | * using `platformRef.bootstrap` we need to use `NgZone.onStable` as the scheduling mechanism.
|
40335 | * This overrides the scheduling mechanism in Ivy to `NgZone.onStable`.
|
40336 | *
|
40337 | * @param ngZone NgZone to use for scheduling.
|
40338 | */
|
40339 | function zoneSchedulerFactory(ngZone) {
|
40340 | let queue = [];
|
40341 | ngZone.onStable.subscribe(() => {
|
40342 | while (queue.length) {
|
40343 | queue.pop()();
|
40344 | }
|
40345 | });
|
40346 | return function (fn) {
|
40347 | queue.push(fn);
|
40348 | };
|
40349 | }
|
40350 |
|
40351 | /**
|
40352 | * @license
|
40353 | * Copyright Google LLC All Rights Reserved.
|
40354 | *
|
40355 | * Use of this source code is governed by an MIT-style license that can be
|
40356 | * found in the LICENSE file at https://angular.io/license
|
40357 | */
|
40358 | var ViewAction;
|
40359 | (function (ViewAction) {
|
40360 | ViewAction[ViewAction["CreateViewNodes"] = 0] = "CreateViewNodes";
|
40361 | ViewAction[ViewAction["CheckNoChanges"] = 1] = "CheckNoChanges";
|
40362 | ViewAction[ViewAction["CheckNoChangesProjectedViews"] = 2] = "CheckNoChangesProjectedViews";
|
40363 | ViewAction[ViewAction["CheckAndUpdate"] = 3] = "CheckAndUpdate";
|
40364 | ViewAction[ViewAction["CheckAndUpdateProjectedViews"] = 4] = "CheckAndUpdateProjectedViews";
|
40365 | ViewAction[ViewAction["Destroy"] = 5] = "Destroy";
|
40366 | })(ViewAction || (ViewAction = {}));
|
40367 |
|
40368 | /**
|
40369 | * @license
|
40370 | * Copyright Google LLC All Rights Reserved.
|
40371 | *
|
40372 | * Use of this source code is governed by an MIT-style license that can be
|
40373 | * found in the LICENSE file at https://angular.io/license
|
40374 | */
|
40375 | var DebugAction;
|
40376 | (function (DebugAction) {
|
40377 | DebugAction[DebugAction["create"] = 0] = "create";
|
40378 | DebugAction[DebugAction["detectChanges"] = 1] = "detectChanges";
|
40379 | DebugAction[DebugAction["checkNoChanges"] = 2] = "checkNoChanges";
|
40380 | DebugAction[DebugAction["destroy"] = 3] = "destroy";
|
40381 | DebugAction[DebugAction["handleEvent"] = 4] = "handleEvent";
|
40382 | })(DebugAction || (DebugAction = {}));
|
40383 |
|
40384 | /**
|
40385 | * @license
|
40386 | * Copyright Google LLC All Rights Reserved.
|
40387 | *
|
40388 | * Use of this source code is governed by an MIT-style license that can be
|
40389 | * found in the LICENSE file at https://angular.io/license
|
40390 | */
|
40391 | if (typeof ngDevMode !== 'undefined' && ngDevMode) {
|
40392 | // This helper is to give a reasonable error message to people upgrading to v9 that have not yet
|
40393 | // installed `@angular/localize` in their app.
|
40394 | // tslint:disable-next-line: no-toplevel-property-access
|
40395 | _global$1.$localize = _global$1.$localize || function () {
|
40396 | throw new Error('It looks like your application or one of its dependencies is using i18n.\n' +
|
40397 | 'Angular 9 introduced a global `$localize()` function that needs to be loaded.\n' +
|
40398 | 'Please run `ng add @angular/localize` from the Angular CLI.\n' +
|
40399 | '(For non-CLI projects, add `import \'@angular/localize/init\';` to your `polyfills.ts` file.\n' +
|
40400 | 'For server-side rendering applications add the import to your `main.server.ts` file.)');
|
40401 | };
|
40402 | }
|
40403 |
|
40404 | /**
|
40405 | * @license
|
40406 | * Copyright Google LLC All Rights Reserved.
|
40407 | *
|
40408 | * Use of this source code is governed by an MIT-style license that can be
|
40409 | * found in the LICENSE file at https://angular.io/license
|
40410 | */
|
40411 | // Metadata Schema
|
40412 | // If you make a backwards incompatible change to the schema, increment the METADTA_VERSION number.
|
40413 | // If you make a backwards compatible change to the metadata (such as adding an option field) then
|
40414 | // leave METADATA_VERSION the same. If possible, supply as many versions of the metadata that can
|
40415 | // represent the semantics of the file in an array. For example, when generating a version 2 file,
|
40416 | // if version 1 can accurately represent the metadata, generate both version 1 and version 2 in
|
40417 | // an array.
|
40418 | const METADATA_VERSION = 4;
|
40419 | function isClassMetadata(value) {
|
40420 | return value && value.__symbolic === 'class';
|
40421 | }
|
40422 | function isMethodMetadata(value) {
|
40423 | return value && (value.__symbolic === 'constructor' || value.__symbolic === 'method');
|
40424 | }
|
40425 | function isConstructorMetadata(value) {
|
40426 | return value && value.__symbolic === 'constructor';
|
40427 | }
|
40428 | function isFunctionMetadata(value) {
|
40429 | return value && value.__symbolic === 'function';
|
40430 | }
|
40431 | function isMetadataSymbolicExpression(value) {
|
40432 | if (value) {
|
40433 | switch (value.__symbolic) {
|
40434 | case 'binary':
|
40435 | case 'call':
|
40436 | case 'index':
|
40437 | case 'new':
|
40438 | case 'pre':
|
40439 | case 'reference':
|
40440 | case 'select':
|
40441 | case 'spread':
|
40442 | case 'if':
|
40443 | return true;
|
40444 | }
|
40445 | }
|
40446 | return false;
|
40447 | }
|
40448 | function isMetadataGlobalReferenceExpression(value) {
|
40449 | return value && value.name && !value.module && isMetadataSymbolicReferenceExpression(value);
|
40450 | }
|
40451 | function isMetadataModuleReferenceExpression(value) {
|
40452 | return value && value.module && !value.name && !value.default &&
|
40453 | isMetadataSymbolicReferenceExpression(value);
|
40454 | }
|
40455 | function isMetadataImportedSymbolReferenceExpression(value) {
|
40456 | return value && value.module && !!value.name && isMetadataSymbolicReferenceExpression(value);
|
40457 | }
|
40458 | function isMetadataImportDefaultReference(value) {
|
40459 | return value && value.module && value.default && isMetadataSymbolicReferenceExpression(value);
|
40460 | }
|
40461 | function isMetadataSymbolicReferenceExpression(value) {
|
40462 | return value && value.__symbolic === 'reference';
|
40463 | }
|
40464 | function isMetadataSymbolicSelectExpression(value) {
|
40465 | return value && value.__symbolic === 'select';
|
40466 | }
|
40467 | function isMetadataSymbolicSpreadExpression(value) {
|
40468 | return value && value.__symbolic === 'spread';
|
40469 | }
|
40470 | function isMetadataError$1(value) {
|
40471 | return value && value.__symbolic === 'error';
|
40472 | }
|
40473 |
|
40474 | /**
|
40475 | * @license
|
40476 | * Copyright Google LLC All Rights Reserved.
|
40477 | *
|
40478 | * Use of this source code is governed by an MIT-style license that can be
|
40479 | * found in the LICENSE file at https://angular.io/license
|
40480 | */
|
40481 | // In TypeScript 2.1 the spread element kind was renamed.
|
40482 | const spreadElementSyntaxKind = ts.SyntaxKind.SpreadElement || ts.SyntaxKind.SpreadElementExpression;
|
40483 | function isMethodCallOf(callExpression, memberName) {
|
40484 | const expression = callExpression.expression;
|
40485 | if (expression.kind === ts.SyntaxKind.PropertyAccessExpression) {
|
40486 | const propertyAccessExpression = expression;
|
40487 | const name = propertyAccessExpression.name;
|
40488 | if (name.kind == ts.SyntaxKind.Identifier) {
|
40489 | return name.text === memberName;
|
40490 | }
|
40491 | }
|
40492 | return false;
|
40493 | }
|
40494 | function isCallOf(callExpression, ident) {
|
40495 | const expression = callExpression.expression;
|
40496 | if (expression.kind === ts.SyntaxKind.Identifier) {
|
40497 | const identifier = expression;
|
40498 | return identifier.text === ident;
|
40499 | }
|
40500 | return false;
|
40501 | }
|
40502 | /* @internal */
|
40503 | function recordMapEntry(entry, node, nodeMap, sourceFile) {
|
40504 | if (!nodeMap.has(entry)) {
|
40505 | nodeMap.set(entry, node);
|
40506 | if (node &&
|
40507 | (isMetadataImportedSymbolReferenceExpression(entry) ||
|
40508 | isMetadataImportDefaultReference(entry)) &&
|
40509 | entry.line == null) {
|
40510 | const info = sourceInfo(node, sourceFile);
|
40511 | if (info.line != null)
|
40512 | entry.line = info.line;
|
40513 | if (info.character != null)
|
40514 | entry.character = info.character;
|
40515 | }
|
40516 | }
|
40517 | return entry;
|
40518 | }
|
40519 | /**
|
40520 | * ts.forEachChild stops iterating children when the callback return a truthy value.
|
40521 | * This method inverts this to implement an `every` style iterator. It will return
|
40522 | * true if every call to `cb` returns `true`.
|
40523 | */
|
40524 | function everyNodeChild(node, cb) {
|
40525 | return !ts.forEachChild(node, node => !cb(node));
|
40526 | }
|
40527 | function isPrimitive$1(value) {
|
40528 | return Object(value) !== value;
|
40529 | }
|
40530 | function isDefined$1(obj) {
|
40531 | return obj !== undefined;
|
40532 | }
|
40533 | function getSourceFileOfNode(node) {
|
40534 | while (node && node.kind != ts.SyntaxKind.SourceFile) {
|
40535 | node = node.parent;
|
40536 | }
|
40537 | return node;
|
40538 | }
|
40539 | /* @internal */
|
40540 | function sourceInfo(node, sourceFile) {
|
40541 | if (node) {
|
40542 | sourceFile = sourceFile || getSourceFileOfNode(node);
|
40543 | if (sourceFile) {
|
40544 | return ts.getLineAndCharacterOfPosition(sourceFile, node.getStart(sourceFile));
|
40545 | }
|
40546 | }
|
40547 | return {};
|
40548 | }
|
40549 | /* @internal */
|
40550 | function errorSymbol(message, node, context, sourceFile) {
|
40551 | const result = Object.assign({ __symbolic: 'error', message }, sourceInfo(node, sourceFile));
|
40552 | if (context) {
|
40553 | result.context = context;
|
40554 | }
|
40555 | return result;
|
40556 | }
|
40557 | /**
|
40558 | * Produce a symbolic representation of an expression folding values into their final value when
|
40559 | * possible.
|
40560 | */
|
40561 | class Evaluator {
|
40562 | constructor(symbols, nodeMap, options = {}, recordExport) {
|
40563 | this.symbols = symbols;
|
40564 | this.nodeMap = nodeMap;
|
40565 | this.options = options;
|
40566 | this.recordExport = recordExport;
|
40567 | }
|
40568 | nameOf(node) {
|
40569 | if (node && node.kind == ts.SyntaxKind.Identifier) {
|
40570 | return node.text;
|
40571 | }
|
40572 | const result = node && this.evaluateNode(node);
|
40573 | if (isMetadataError$1(result) || typeof result === 'string') {
|
40574 | return result;
|
40575 | }
|
40576 | else {
|
40577 | return errorSymbol('Name expected', node, { received: (node && node.getText()) || '<missing>' });
|
40578 | }
|
40579 | }
|
40580 | /**
|
40581 | * Returns true if the expression represented by `node` can be folded into a literal expression.
|
40582 | *
|
40583 | * For example, a literal is always foldable. This means that literal expressions such as `1.2`
|
40584 | * `"Some value"` `true` `false` are foldable.
|
40585 | *
|
40586 | * - An object literal is foldable if all the properties in the literal are foldable.
|
40587 | * - An array literal is foldable if all the elements are foldable.
|
40588 | * - A call is foldable if it is a call to a Array.prototype.concat or a call to CONST_EXPR.
|
40589 | * - A property access is foldable if the object is foldable.
|
40590 | * - A array index is foldable if index expression is foldable and the array is foldable.
|
40591 | * - Binary operator expressions are foldable if the left and right expressions are foldable and
|
40592 | * it is one of '+', '-', '*', '/', '%', '||', and '&&'.
|
40593 | * - An identifier is foldable if a value can be found for its symbol in the evaluator symbol
|
40594 | * table.
|
40595 | */
|
40596 | isFoldable(node) {
|
40597 | return this.isFoldableWorker(node, new Map());
|
40598 | }
|
40599 | isFoldableWorker(node, folding) {
|
40600 | if (node) {
|
40601 | switch (node.kind) {
|
40602 | case ts.SyntaxKind.ObjectLiteralExpression:
|
40603 | return everyNodeChild(node, child => {
|
40604 | if (child.kind === ts.SyntaxKind.PropertyAssignment) {
|
40605 | const propertyAssignment = child;
|
40606 | return this.isFoldableWorker(propertyAssignment.initializer, folding);
|
40607 | }
|
40608 | return false;
|
40609 | });
|
40610 | case ts.SyntaxKind.ArrayLiteralExpression:
|
40611 | return everyNodeChild(node, child => this.isFoldableWorker(child, folding));
|
40612 | case ts.SyntaxKind.CallExpression:
|
40613 | const callExpression = node;
|
40614 | // We can fold a <array>.concat(<v>).
|
40615 | if (isMethodCallOf(callExpression, 'concat') &&
|
40616 | arrayOrEmpty(callExpression.arguments).length === 1) {
|
40617 | const arrayNode = callExpression.expression.expression;
|
40618 | if (this.isFoldableWorker(arrayNode, folding) &&
|
40619 | this.isFoldableWorker(callExpression.arguments[0], folding)) {
|
40620 | // It needs to be an array.
|
40621 | const arrayValue = this.evaluateNode(arrayNode);
|
40622 | if (arrayValue && Array.isArray(arrayValue)) {
|
40623 | return true;
|
40624 | }
|
40625 | }
|
40626 | }
|
40627 | // We can fold a call to CONST_EXPR
|
40628 | if (isCallOf(callExpression, 'CONST_EXPR') &&
|
40629 | arrayOrEmpty(callExpression.arguments).length === 1)
|
40630 | return this.isFoldableWorker(callExpression.arguments[0], folding);
|
40631 | return false;
|
40632 | case ts.SyntaxKind.NoSubstitutionTemplateLiteral:
|
40633 | case ts.SyntaxKind.StringLiteral:
|
40634 | case ts.SyntaxKind.NumericLiteral:
|
40635 | case ts.SyntaxKind.NullKeyword:
|
40636 | case ts.SyntaxKind.TrueKeyword:
|
40637 | case ts.SyntaxKind.FalseKeyword:
|
40638 | case ts.SyntaxKind.TemplateHead:
|
40639 | case ts.SyntaxKind.TemplateMiddle:
|
40640 | case ts.SyntaxKind.TemplateTail:
|
40641 | return true;
|
40642 | case ts.SyntaxKind.ParenthesizedExpression:
|
40643 | const parenthesizedExpression = node;
|
40644 | return this.isFoldableWorker(parenthesizedExpression.expression, folding);
|
40645 | case ts.SyntaxKind.BinaryExpression:
|
40646 | const binaryExpression = node;
|
40647 | switch (binaryExpression.operatorToken.kind) {
|
40648 | case ts.SyntaxKind.PlusToken:
|
40649 | case ts.SyntaxKind.MinusToken:
|
40650 | case ts.SyntaxKind.AsteriskToken:
|
40651 | case ts.SyntaxKind.SlashToken:
|
40652 | case ts.SyntaxKind.PercentToken:
|
40653 | case ts.SyntaxKind.AmpersandAmpersandToken:
|
40654 | case ts.SyntaxKind.BarBarToken:
|
40655 | return this.isFoldableWorker(binaryExpression.left, folding) &&
|
40656 | this.isFoldableWorker(binaryExpression.right, folding);
|
40657 | default:
|
40658 | return false;
|
40659 | }
|
40660 | case ts.SyntaxKind.PropertyAccessExpression:
|
40661 | const propertyAccessExpression = node;
|
40662 | return this.isFoldableWorker(propertyAccessExpression.expression, folding);
|
40663 | case ts.SyntaxKind.ElementAccessExpression:
|
40664 | const elementAccessExpression = node;
|
40665 | return this.isFoldableWorker(elementAccessExpression.expression, folding) &&
|
40666 | this.isFoldableWorker(elementAccessExpression.argumentExpression, folding);
|
40667 | case ts.SyntaxKind.Identifier:
|
40668 | let identifier = node;
|
40669 | let reference = this.symbols.resolve(identifier.text);
|
40670 | if (reference !== undefined && isPrimitive$1(reference)) {
|
40671 | return true;
|
40672 | }
|
40673 | break;
|
40674 | case ts.SyntaxKind.TemplateExpression:
|
40675 | const templateExpression = node;
|
40676 | return templateExpression.templateSpans.every(span => this.isFoldableWorker(span.expression, folding));
|
40677 | }
|
40678 | }
|
40679 | return false;
|
40680 | }
|
40681 | /**
|
40682 | * Produce a JSON serialiable object representing `node`. The foldable values in the expression
|
40683 | * tree are folded. For example, a node representing `1 + 2` is folded into `3`.
|
40684 | */
|
40685 | evaluateNode(node, preferReference) {
|
40686 | const t = this;
|
40687 | let error;
|
40688 | function recordEntry(entry, node) {
|
40689 | if (t.options.substituteExpression) {
|
40690 | const newEntry = t.options.substituteExpression(entry, node);
|
40691 | if (t.recordExport && newEntry != entry && isMetadataGlobalReferenceExpression(newEntry)) {
|
40692 | t.recordExport(newEntry.name, entry);
|
40693 | }
|
40694 | entry = newEntry;
|
40695 | }
|
40696 | return recordMapEntry(entry, node, t.nodeMap);
|
40697 | }
|
40698 | function isFoldableError(value) {
|
40699 | return !t.options.verboseInvalidExpression && isMetadataError$1(value);
|
40700 | }
|
40701 | const resolveName = (name, preferReference) => {
|
40702 | const reference = this.symbols.resolve(name, preferReference);
|
40703 | if (reference === undefined) {
|
40704 | // Encode as a global reference. StaticReflector will check the reference.
|
40705 | return recordEntry({ __symbolic: 'reference', name }, node);
|
40706 | }
|
40707 | if (reference && isMetadataSymbolicReferenceExpression(reference)) {
|
40708 | return recordEntry(Object.assign({}, reference), node);
|
40709 | }
|
40710 | return reference;
|
40711 | };
|
40712 | switch (node.kind) {
|
40713 | case ts.SyntaxKind.ObjectLiteralExpression:
|
40714 | let obj = {};
|
40715 | let quoted = [];
|
40716 | ts.forEachChild(node, child => {
|
40717 | switch (child.kind) {
|
40718 | case ts.SyntaxKind.ShorthandPropertyAssignment:
|
40719 | case ts.SyntaxKind.PropertyAssignment:
|
40720 | const assignment = child;
|
40721 | if (assignment.name.kind == ts.SyntaxKind.StringLiteral) {
|
40722 | const name = assignment.name.text;
|
40723 | quoted.push(name);
|
40724 | }
|
40725 | const propertyName = this.nameOf(assignment.name);
|
40726 | if (isFoldableError(propertyName)) {
|
40727 | error = propertyName;
|
40728 | return true;
|
40729 | }
|
40730 | const propertyValue = isPropertyAssignment(assignment) ?
|
40731 | this.evaluateNode(assignment.initializer, /* preferReference */ true) :
|
40732 | resolveName(propertyName, /* preferReference */ true);
|
40733 | if (isFoldableError(propertyValue)) {
|
40734 | error = propertyValue;
|
40735 | return true; // Stop the forEachChild.
|
40736 | }
|
40737 | else {
|
40738 | obj[propertyName] = isPropertyAssignment(assignment) ?
|
40739 | recordEntry(propertyValue, assignment.initializer) :
|
40740 | propertyValue;
|
40741 | }
|
40742 | }
|
40743 | });
|
40744 | if (error)
|
40745 | return error;
|
40746 | if (this.options.quotedNames && quoted.length) {
|
40747 | obj['$quoted$'] = quoted;
|
40748 | }
|
40749 | return recordEntry(obj, node);
|
40750 | case ts.SyntaxKind.ArrayLiteralExpression:
|
40751 | let arr = [];
|
40752 | ts.forEachChild(node, child => {
|
40753 | const value = this.evaluateNode(child, /* preferReference */ true);
|
40754 | // Check for error
|
40755 | if (isFoldableError(value)) {
|
40756 | error = value;
|
40757 | return true; // Stop the forEachChild.
|
40758 | }
|
40759 | // Handle spread expressions
|
40760 | if (isMetadataSymbolicSpreadExpression(value)) {
|
40761 | if (Array.isArray(value.expression)) {
|
40762 | for (const spreadValue of value.expression) {
|
40763 | arr.push(spreadValue);
|
40764 | }
|
40765 | return;
|
40766 | }
|
40767 | }
|
40768 | arr.push(value);
|
40769 | });
|
40770 | if (error)
|
40771 | return error;
|
40772 | return recordEntry(arr, node);
|
40773 | case spreadElementSyntaxKind:
|
40774 | let spreadExpression = this.evaluateNode(node.expression);
|
40775 | return recordEntry({ __symbolic: 'spread', expression: spreadExpression }, node);
|
40776 | case ts.SyntaxKind.CallExpression:
|
40777 | const callExpression = node;
|
40778 | if (isCallOf(callExpression, 'forwardRef') &&
|
40779 | arrayOrEmpty(callExpression.arguments).length === 1) {
|
40780 | const firstArgument = callExpression.arguments[0];
|
40781 | if (firstArgument.kind == ts.SyntaxKind.ArrowFunction) {
|
40782 | const arrowFunction = firstArgument;
|
40783 | return recordEntry(this.evaluateNode(arrowFunction.body), node);
|
40784 | }
|
40785 | }
|
40786 | const args = arrayOrEmpty(callExpression.arguments).map(arg => this.evaluateNode(arg));
|
40787 | if (this.isFoldable(callExpression)) {
|
40788 | if (isMethodCallOf(callExpression, 'concat')) {
|
40789 | const arrayValue = this.evaluateNode(callExpression.expression.expression);
|
40790 | if (isFoldableError(arrayValue))
|
40791 | return arrayValue;
|
40792 | return arrayValue.concat(args[0]);
|
40793 | }
|
40794 | }
|
40795 | // Always fold a CONST_EXPR even if the argument is not foldable.
|
40796 | if (isCallOf(callExpression, 'CONST_EXPR') &&
|
40797 | arrayOrEmpty(callExpression.arguments).length === 1) {
|
40798 | return recordEntry(args[0], node);
|
40799 | }
|
40800 | const expression = this.evaluateNode(callExpression.expression);
|
40801 | if (isFoldableError(expression)) {
|
40802 | return recordEntry(expression, node);
|
40803 | }
|
40804 | let result = { __symbolic: 'call', expression: expression };
|
40805 | if (args && args.length) {
|
40806 | result.arguments = args;
|
40807 | }
|
40808 | return recordEntry(result, node);
|
40809 | case ts.SyntaxKind.NewExpression:
|
40810 | const newExpression = node;
|
40811 | const newArgs = arrayOrEmpty(newExpression.arguments).map(arg => this.evaluateNode(arg));
|
40812 | const newTarget = this.evaluateNode(newExpression.expression);
|
40813 | if (isMetadataError$1(newTarget)) {
|
40814 | return recordEntry(newTarget, node);
|
40815 | }
|
40816 | const call = { __symbolic: 'new', expression: newTarget };
|
40817 | if (newArgs.length) {
|
40818 | call.arguments = newArgs;
|
40819 | }
|
40820 | return recordEntry(call, node);
|
40821 | case ts.SyntaxKind.PropertyAccessExpression: {
|
40822 | const propertyAccessExpression = node;
|
40823 | const expression = this.evaluateNode(propertyAccessExpression.expression);
|
40824 | if (isFoldableError(expression)) {
|
40825 | return recordEntry(expression, node);
|
40826 | }
|
40827 | const member = this.nameOf(propertyAccessExpression.name);
|
40828 | if (isFoldableError(member)) {
|
40829 | return recordEntry(member, node);
|
40830 | }
|
40831 | if (expression && this.isFoldable(propertyAccessExpression.expression))
|
40832 | return expression[member];
|
40833 | if (isMetadataModuleReferenceExpression(expression)) {
|
40834 | // A select into a module reference and be converted into a reference to the symbol
|
40835 | // in the module
|
40836 | return recordEntry({ __symbolic: 'reference', module: expression.module, name: member }, node);
|
40837 | }
|
40838 | return recordEntry({ __symbolic: 'select', expression, member }, node);
|
40839 | }
|
40840 | case ts.SyntaxKind.ElementAccessExpression: {
|
40841 | const elementAccessExpression = node;
|
40842 | const expression = this.evaluateNode(elementAccessExpression.expression);
|
40843 | if (isFoldableError(expression)) {
|
40844 | return recordEntry(expression, node);
|
40845 | }
|
40846 | if (!elementAccessExpression.argumentExpression) {
|
40847 | return recordEntry(errorSymbol('Expression form not supported', node), node);
|
40848 | }
|
40849 | const index = this.evaluateNode(elementAccessExpression.argumentExpression);
|
40850 | if (isFoldableError(expression)) {
|
40851 | return recordEntry(expression, node);
|
40852 | }
|
40853 | if (this.isFoldable(elementAccessExpression.expression) &&
|
40854 | this.isFoldable(elementAccessExpression.argumentExpression))
|
40855 | return expression[index];
|
40856 | return recordEntry({ __symbolic: 'index', expression, index }, node);
|
40857 | }
|
40858 | case ts.SyntaxKind.Identifier:
|
40859 | const identifier = node;
|
40860 | const name = identifier.text;
|
40861 | return resolveName(name, preferReference);
|
40862 | case ts.SyntaxKind.TypeReference:
|
40863 | const typeReferenceNode = node;
|
40864 | const typeNameNode = typeReferenceNode.typeName;
|
40865 | const getReference = node => {
|
40866 | if (typeNameNode.kind === ts.SyntaxKind.QualifiedName) {
|
40867 | const qualifiedName = node;
|
40868 | const left = this.evaluateNode(qualifiedName.left);
|
40869 | if (isMetadataModuleReferenceExpression(left)) {
|
40870 | return recordEntry({
|
40871 | __symbolic: 'reference',
|
40872 | module: left.module,
|
40873 | name: qualifiedName.right.text
|
40874 | }, node);
|
40875 | }
|
40876 | // Record a type reference to a declared type as a select.
|
40877 | return { __symbolic: 'select', expression: left, member: qualifiedName.right.text };
|
40878 | }
|
40879 | else {
|
40880 | const identifier = typeNameNode;
|
40881 | const symbol = this.symbols.resolve(identifier.text);
|
40882 | if (isFoldableError(symbol) || isMetadataSymbolicReferenceExpression(symbol)) {
|
40883 | return recordEntry(symbol, node);
|
40884 | }
|
40885 | return recordEntry(errorSymbol('Could not resolve type', node, { typeName: identifier.text }), node);
|
40886 | }
|
40887 | };
|
40888 | const typeReference = getReference(typeNameNode);
|
40889 | if (isFoldableError(typeReference)) {
|
40890 | return recordEntry(typeReference, node);
|
40891 | }
|
40892 | if (!isMetadataModuleReferenceExpression(typeReference) &&
|
40893 | typeReferenceNode.typeArguments && typeReferenceNode.typeArguments.length) {
|
40894 | const args = typeReferenceNode.typeArguments.map(element => this.evaluateNode(element));
|
40895 | // TODO: Remove typecast when upgraded to 2.0 as it will be correctly inferred.
|
40896 | // Some versions of 1.9 do not infer this correctly.
|
40897 | typeReference.arguments = args;
|
40898 | }
|
40899 | return recordEntry(typeReference, node);
|
40900 | case ts.SyntaxKind.UnionType:
|
40901 | const unionType = node;
|
40902 | // Remove null and undefined from the list of unions.
|
40903 | const references = unionType.types
|
40904 | .filter(n => n.kind !== ts.SyntaxKind.UndefinedKeyword &&
|
40905 | !(ts.isLiteralTypeNode(n) && n.literal.kind === ts.SyntaxKind.NullKeyword))
|
40906 | .map(n => this.evaluateNode(n));
|
40907 | // The remmaining reference must be the same. If two have type arguments consider them
|
40908 | // different even if the type arguments are the same.
|
40909 | let candidate = null;
|
40910 | for (let i = 0; i < references.length; i++) {
|
40911 | const reference = references[i];
|
40912 | if (isMetadataSymbolicReferenceExpression(reference)) {
|
40913 | if (candidate) {
|
40914 | if (reference.name == candidate.name &&
|
40915 | reference.module == candidate.module && !reference.arguments) {
|
40916 | candidate = reference;
|
40917 | }
|
40918 | }
|
40919 | else {
|
40920 | candidate = reference;
|
40921 | }
|
40922 | }
|
40923 | else {
|
40924 | return reference;
|
40925 | }
|
40926 | }
|
40927 | if (candidate)
|
40928 | return candidate;
|
40929 | break;
|
40930 | case ts.SyntaxKind.NoSubstitutionTemplateLiteral:
|
40931 | case ts.SyntaxKind.StringLiteral:
|
40932 | case ts.SyntaxKind.TemplateHead:
|
40933 | case ts.SyntaxKind.TemplateTail:
|
40934 | case ts.SyntaxKind.TemplateMiddle:
|
40935 | return node.text;
|
40936 | case ts.SyntaxKind.NumericLiteral:
|
40937 | return parseFloat(node.text);
|
40938 | case ts.SyntaxKind.AnyKeyword:
|
40939 | return recordEntry({ __symbolic: 'reference', name: 'any' }, node);
|
40940 | case ts.SyntaxKind.StringKeyword:
|
40941 | return recordEntry({ __symbolic: 'reference', name: 'string' }, node);
|
40942 | case ts.SyntaxKind.NumberKeyword:
|
40943 | return recordEntry({ __symbolic: 'reference', name: 'number' }, node);
|
40944 | case ts.SyntaxKind.BooleanKeyword:
|
40945 | return recordEntry({ __symbolic: 'reference', name: 'boolean' }, node);
|
40946 | case ts.SyntaxKind.ArrayType:
|
40947 | const arrayTypeNode = node;
|
40948 | return recordEntry({
|
40949 | __symbolic: 'reference',
|
40950 | name: 'Array',
|
40951 | arguments: [this.evaluateNode(arrayTypeNode.elementType)]
|
40952 | }, node);
|
40953 | case ts.SyntaxKind.NullKeyword:
|
40954 | return null;
|
40955 | case ts.SyntaxKind.TrueKeyword:
|
40956 | return true;
|
40957 | case ts.SyntaxKind.FalseKeyword:
|
40958 | return false;
|
40959 | case ts.SyntaxKind.ParenthesizedExpression:
|
40960 | const parenthesizedExpression = node;
|
40961 | return this.evaluateNode(parenthesizedExpression.expression);
|
40962 | case ts.SyntaxKind.TypeAssertionExpression:
|
40963 | const typeAssertion = node;
|
40964 | return this.evaluateNode(typeAssertion.expression);
|
40965 | case ts.SyntaxKind.PrefixUnaryExpression:
|
40966 | const prefixUnaryExpression = node;
|
40967 | const operand = this.evaluateNode(prefixUnaryExpression.operand);
|
40968 | if (isDefined$1(operand) && isPrimitive$1(operand)) {
|
40969 | switch (prefixUnaryExpression.operator) {
|
40970 | case ts.SyntaxKind.PlusToken:
|
40971 | return +operand;
|
40972 | case ts.SyntaxKind.MinusToken:
|
40973 | return -operand;
|
40974 | case ts.SyntaxKind.TildeToken:
|
40975 | return ~operand;
|
40976 | case ts.SyntaxKind.ExclamationToken:
|
40977 | return !operand;
|
40978 | }
|
40979 | }
|
40980 | let operatorText;
|
40981 | switch (prefixUnaryExpression.operator) {
|
40982 | case ts.SyntaxKind.PlusToken:
|
40983 | operatorText = '+';
|
40984 | break;
|
40985 | case ts.SyntaxKind.MinusToken:
|
40986 | operatorText = '-';
|
40987 | break;
|
40988 | case ts.SyntaxKind.TildeToken:
|
40989 | operatorText = '~';
|
40990 | break;
|
40991 | case ts.SyntaxKind.ExclamationToken:
|
40992 | operatorText = '!';
|
40993 | break;
|
40994 | default:
|
40995 | return undefined;
|
40996 | }
|
40997 | return recordEntry({ __symbolic: 'pre', operator: operatorText, operand: operand }, node);
|
40998 | case ts.SyntaxKind.BinaryExpression:
|
40999 | const binaryExpression = node;
|
41000 | const left = this.evaluateNode(binaryExpression.left);
|
41001 | const right = this.evaluateNode(binaryExpression.right);
|
41002 | if (isDefined$1(left) && isDefined$1(right)) {
|
41003 | if (isPrimitive$1(left) && isPrimitive$1(right))
|
41004 | switch (binaryExpression.operatorToken.kind) {
|
41005 | case ts.SyntaxKind.BarBarToken:
|
41006 | return left || right;
|
41007 | case ts.SyntaxKind.AmpersandAmpersandToken:
|
41008 | return left && right;
|
41009 | case ts.SyntaxKind.AmpersandToken:
|
41010 | return left & right;
|
41011 | case ts.SyntaxKind.BarToken:
|
41012 | return left | right;
|
41013 | case ts.SyntaxKind.CaretToken:
|
41014 | return left ^ right;
|
41015 | case ts.SyntaxKind.EqualsEqualsToken:
|
41016 | return left == right;
|
41017 | case ts.SyntaxKind.ExclamationEqualsToken:
|
41018 | return left != right;
|
41019 | case ts.SyntaxKind.EqualsEqualsEqualsToken:
|
41020 | return left === right;
|
41021 | case ts.SyntaxKind.ExclamationEqualsEqualsToken:
|
41022 | return left !== right;
|
41023 | case ts.SyntaxKind.LessThanToken:
|
41024 | return left < right;
|
41025 | case ts.SyntaxKind.GreaterThanToken:
|
41026 | return left > right;
|
41027 | case ts.SyntaxKind.LessThanEqualsToken:
|
41028 | return left <= right;
|
41029 | case ts.SyntaxKind.GreaterThanEqualsToken:
|
41030 | return left >= right;
|
41031 | case ts.SyntaxKind.LessThanLessThanToken:
|
41032 | return left << right;
|
41033 | case ts.SyntaxKind.GreaterThanGreaterThanToken:
|
41034 | return left >> right;
|
41035 | case ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken:
|
41036 | return left >>> right;
|
41037 | case ts.SyntaxKind.PlusToken:
|
41038 | return left + right;
|
41039 | case ts.SyntaxKind.MinusToken:
|
41040 | return left - right;
|
41041 | case ts.SyntaxKind.AsteriskToken:
|
41042 | return left * right;
|
41043 | case ts.SyntaxKind.SlashToken:
|
41044 | return left / right;
|
41045 | case ts.SyntaxKind.PercentToken:
|
41046 | return left % right;
|
41047 | }
|
41048 | return recordEntry({
|
41049 | __symbolic: 'binop',
|
41050 | operator: binaryExpression.operatorToken.getText(),
|
41051 | left: left,
|
41052 | right: right
|
41053 | }, node);
|
41054 | }
|
41055 | break;
|
41056 | case ts.SyntaxKind.ConditionalExpression:
|
41057 | const conditionalExpression = node;
|
41058 | const condition = this.evaluateNode(conditionalExpression.condition);
|
41059 | const thenExpression = this.evaluateNode(conditionalExpression.whenTrue);
|
41060 | const elseExpression = this.evaluateNode(conditionalExpression.whenFalse);
|
41061 | if (isPrimitive$1(condition)) {
|
41062 | return condition ? thenExpression : elseExpression;
|
41063 | }
|
41064 | return recordEntry({ __symbolic: 'if', condition, thenExpression, elseExpression }, node);
|
41065 | case ts.SyntaxKind.FunctionExpression:
|
41066 | case ts.SyntaxKind.ArrowFunction:
|
41067 | return recordEntry(errorSymbol('Lambda not supported', node), node);
|
41068 | case ts.SyntaxKind.TaggedTemplateExpression:
|
41069 | return recordEntry(errorSymbol('Tagged template expressions are not supported in metadata', node), node);
|
41070 | case ts.SyntaxKind.TemplateExpression:
|
41071 | const templateExpression = node;
|
41072 | if (this.isFoldable(node)) {
|
41073 | return templateExpression.templateSpans.reduce((previous, current) => previous + this.evaluateNode(current.expression) +
|
41074 | this.evaluateNode(current.literal), this.evaluateNode(templateExpression.head));
|
41075 | }
|
41076 | else {
|
41077 | return templateExpression.templateSpans.reduce((previous, current) => {
|
41078 | const expr = this.evaluateNode(current.expression);
|
41079 | const literal = this.evaluateNode(current.literal);
|
41080 | if (isFoldableError(expr))
|
41081 | return expr;
|
41082 | if (isFoldableError(literal))
|
41083 | return literal;
|
41084 | if (typeof previous === 'string' && typeof expr === 'string' &&
|
41085 | typeof literal === 'string') {
|
41086 | return previous + expr + literal;
|
41087 | }
|
41088 | let result = expr;
|
41089 | if (previous !== '') {
|
41090 | result = { __symbolic: 'binop', operator: '+', left: previous, right: expr };
|
41091 | }
|
41092 | if (literal != '') {
|
41093 | result = { __symbolic: 'binop', operator: '+', left: result, right: literal };
|
41094 | }
|
41095 | return result;
|
41096 | }, this.evaluateNode(templateExpression.head));
|
41097 | }
|
41098 | case ts.SyntaxKind.AsExpression:
|
41099 | const asExpression = node;
|
41100 | return this.evaluateNode(asExpression.expression);
|
41101 | case ts.SyntaxKind.ClassExpression:
|
41102 | return { __symbolic: 'class' };
|
41103 | }
|
41104 | return recordEntry(errorSymbol('Expression form not supported', node), node);
|
41105 | }
|
41106 | }
|
41107 | function isPropertyAssignment(node) {
|
41108 | return node.kind == ts.SyntaxKind.PropertyAssignment;
|
41109 | }
|
41110 | const empty$1 = ts.createNodeArray();
|
41111 | function arrayOrEmpty(v) {
|
41112 | return v || empty$1;
|
41113 | }
|
41114 |
|
41115 | /**
|
41116 | * @license
|
41117 | * Copyright Google LLC All Rights Reserved.
|
41118 | *
|
41119 | * Use of this source code is governed by an MIT-style license that can be
|
41120 | * found in the LICENSE file at https://angular.io/license
|
41121 | */
|
41122 | class Symbols {
|
41123 | constructor(sourceFile) {
|
41124 | this.sourceFile = sourceFile;
|
41125 | this.references = new Map();
|
41126 | }
|
41127 | resolve(name, preferReference) {
|
41128 | return (preferReference && this.references.get(name)) || this.symbols.get(name);
|
41129 | }
|
41130 | define(name, value) {
|
41131 | this.symbols.set(name, value);
|
41132 | }
|
41133 | defineReference(name, value) {
|
41134 | this.references.set(name, value);
|
41135 | }
|
41136 | has(name) {
|
41137 | return this.symbols.has(name);
|
41138 | }
|
41139 | get symbols() {
|
41140 | let result = this._symbols;
|
41141 | if (!result) {
|
41142 | result = this._symbols = new Map();
|
41143 | populateBuiltins(result);
|
41144 | this.buildImports();
|
41145 | }
|
41146 | return result;
|
41147 | }
|
41148 | buildImports() {
|
41149 | const symbols = this._symbols;
|
41150 | // Collect the imported symbols into this.symbols
|
41151 | const stripQuotes = (s) => s.replace(/^['"]|['"]$/g, '');
|
41152 | const visit = (node) => {
|
41153 | switch (node.kind) {
|
41154 | case ts.SyntaxKind.ImportEqualsDeclaration:
|
41155 | const importEqualsDeclaration = node;
|
41156 | if (importEqualsDeclaration.moduleReference.kind ===
|
41157 | ts.SyntaxKind.ExternalModuleReference) {
|
41158 | const externalReference = importEqualsDeclaration.moduleReference;
|
41159 | if (externalReference.expression) {
|
41160 | // An `import <identifier> = require(<module-specifier>);
|
41161 | if (!externalReference.expression.parent) {
|
41162 | // The `parent` field of a node is set by the TypeScript binder (run as
|
41163 | // part of the type checker). Setting it here allows us to call `getText()`
|
41164 | // even if the `SourceFile` was not type checked (which looks for `SourceFile`
|
41165 | // in the parent chain). This doesn't damage the node as the binder unconditionally
|
41166 | // sets the parent.
|
41167 | externalReference.expression.parent = externalReference;
|
41168 | externalReference.parent = this.sourceFile;
|
41169 | }
|
41170 | const from = stripQuotes(externalReference.expression.getText());
|
41171 | symbols.set(importEqualsDeclaration.name.text, { __symbolic: 'reference', module: from });
|
41172 | break;
|
41173 | }
|
41174 | }
|
41175 | symbols.set(importEqualsDeclaration.name.text, { __symbolic: 'error', message: `Unsupported import syntax` });
|
41176 | break;
|
41177 | case ts.SyntaxKind.ImportDeclaration:
|
41178 | const importDecl = node;
|
41179 | if (!importDecl.importClause) {
|
41180 | // An `import <module-specifier>` clause which does not bring symbols into scope.
|
41181 | break;
|
41182 | }
|
41183 | if (!importDecl.moduleSpecifier.parent) {
|
41184 | // See note above in the `ImportEqualDeclaration` case.
|
41185 | importDecl.moduleSpecifier.parent = importDecl;
|
41186 | importDecl.parent = this.sourceFile;
|
41187 | }
|
41188 | const from = stripQuotes(importDecl.moduleSpecifier.getText());
|
41189 | if (importDecl.importClause.name) {
|
41190 | // An `import <identifier> form <module-specifier>` clause. Record the default symbol.
|
41191 | symbols.set(importDecl.importClause.name.text, { __symbolic: 'reference', module: from, default: true });
|
41192 | }
|
41193 | const bindings = importDecl.importClause.namedBindings;
|
41194 | if (bindings) {
|
41195 | switch (bindings.kind) {
|
41196 | case ts.SyntaxKind.NamedImports:
|
41197 | // An `import { [<identifier> [, <identifier>] } from <module-specifier>` clause
|
41198 | for (const binding of bindings.elements) {
|
41199 | symbols.set(binding.name.text, {
|
41200 | __symbolic: 'reference',
|
41201 | module: from,
|
41202 | name: binding.propertyName ? binding.propertyName.text : binding.name.text
|
41203 | });
|
41204 | }
|
41205 | break;
|
41206 | case ts.SyntaxKind.NamespaceImport:
|
41207 | // An `input * as <identifier> from <module-specifier>` clause.
|
41208 | symbols.set(bindings.name.text, { __symbolic: 'reference', module: from });
|
41209 | break;
|
41210 | }
|
41211 | }
|
41212 | break;
|
41213 | }
|
41214 | ts.forEachChild(node, visit);
|
41215 | };
|
41216 | if (this.sourceFile) {
|
41217 | ts.forEachChild(this.sourceFile, visit);
|
41218 | }
|
41219 | }
|
41220 | }
|
41221 | function populateBuiltins(symbols) {
|
41222 | // From lib.core.d.ts (all "define const")
|
41223 | ['Object', 'Function', 'String', 'Number', 'Array', 'Boolean', 'Map', 'NaN', 'Infinity', 'Math',
|
41224 | 'Date', 'RegExp', 'Error', 'Error', 'EvalError', 'RangeError', 'ReferenceError', 'SyntaxError',
|
41225 | 'TypeError', 'URIError', 'JSON', 'ArrayBuffer', 'DataView', 'Int8Array', 'Uint8Array',
|
41226 | 'Uint8ClampedArray', 'Uint16Array', 'Int16Array', 'Int32Array', 'Uint32Array', 'Float32Array',
|
41227 | 'Float64Array']
|
41228 | .forEach(name => symbols.set(name, { __symbolic: 'reference', name }));
|
41229 | }
|
41230 |
|
41231 | /**
|
41232 | * @license
|
41233 | * Copyright Google LLC All Rights Reserved.
|
41234 | *
|
41235 | * Use of this source code is governed by an MIT-style license that can be
|
41236 | * found in the LICENSE file at https://angular.io/license
|
41237 | */
|
41238 | const isStatic = (node) => ts.getCombinedModifierFlags(node) & ts.ModifierFlags.Static;
|
41239 | /**
|
41240 | * Collect decorator metadata from a TypeScript module.
|
41241 | */
|
41242 | class MetadataCollector {
|
41243 | constructor(options = {}) {
|
41244 | this.options = options;
|
41245 | }
|
41246 | /**
|
41247 | * Returns a JSON.stringify friendly form describing the decorators of the exported classes from
|
41248 | * the source file that is expected to correspond to a module.
|
41249 | */
|
41250 | getMetadata(sourceFile, strict = false, substituteExpression) {
|
41251 | const locals = new Symbols(sourceFile);
|
41252 | const nodeMap = new Map();
|
41253 | const composedSubstituter = substituteExpression && this.options.substituteExpression ?
|
41254 | (value, node) => this.options.substituteExpression(substituteExpression(value, node), node) :
|
41255 | substituteExpression;
|
41256 | const evaluatorOptions = substituteExpression ? Object.assign(Object.assign({}, this.options), { substituteExpression: composedSubstituter }) :
|
41257 | this.options;
|
41258 | let metadata;
|
41259 | const evaluator = new Evaluator(locals, nodeMap, evaluatorOptions, (name, value) => {
|
41260 | if (!metadata)
|
41261 | metadata = {};
|
41262 | metadata[name] = value;
|
41263 | });
|
41264 | let exports = undefined;
|
41265 | function objFromDecorator(decoratorNode) {
|
41266 | return evaluator.evaluateNode(decoratorNode.expression);
|
41267 | }
|
41268 | function recordEntry(entry, node) {
|
41269 | if (composedSubstituter) {
|
41270 | entry = composedSubstituter(entry, node);
|
41271 | }
|
41272 | return recordMapEntry(entry, node, nodeMap, sourceFile);
|
41273 | }
|
41274 | function errorSym(message, node, context) {
|
41275 | return errorSymbol(message, node, context, sourceFile);
|
41276 | }
|
41277 | function maybeGetSimpleFunction(functionDeclaration) {
|
41278 | if (functionDeclaration.name && functionDeclaration.name.kind == ts.SyntaxKind.Identifier) {
|
41279 | const nameNode = functionDeclaration.name;
|
41280 | const functionName = nameNode.text;
|
41281 | const functionBody = functionDeclaration.body;
|
41282 | if (functionBody && functionBody.statements.length == 1) {
|
41283 | const statement = functionBody.statements[0];
|
41284 | if (statement.kind === ts.SyntaxKind.ReturnStatement) {
|
41285 | const returnStatement = statement;
|
41286 | if (returnStatement.expression) {
|
41287 | const func = {
|
41288 | __symbolic: 'function',
|
41289 | parameters: namesOf(functionDeclaration.parameters),
|
41290 | value: evaluator.evaluateNode(returnStatement.expression)
|
41291 | };
|
41292 | if (functionDeclaration.parameters.some(p => p.initializer != null)) {
|
41293 | func.defaults = functionDeclaration.parameters.map(p => p.initializer && evaluator.evaluateNode(p.initializer));
|
41294 | }
|
41295 | return recordEntry({ func, name: functionName }, functionDeclaration);
|
41296 | }
|
41297 | }
|
41298 | }
|
41299 | }
|
41300 | }
|
41301 | function classMetadataOf(classDeclaration) {
|
41302 | const result = { __symbolic: 'class' };
|
41303 | function getDecorators(decorators) {
|
41304 | if (decorators && decorators.length)
|
41305 | return decorators.map(decorator => objFromDecorator(decorator));
|
41306 | return undefined;
|
41307 | }
|
41308 | function referenceFrom(node) {
|
41309 | const result = evaluator.evaluateNode(node);
|
41310 | if (isMetadataError$1(result) || isMetadataSymbolicReferenceExpression(result) ||
|
41311 | isMetadataSymbolicSelectExpression(result)) {
|
41312 | return result;
|
41313 | }
|
41314 | else {
|
41315 | return errorSym('Symbol reference expected', node);
|
41316 | }
|
41317 | }
|
41318 | // Add class parents
|
41319 | if (classDeclaration.heritageClauses) {
|
41320 | classDeclaration.heritageClauses.forEach((hc) => {
|
41321 | if (hc.token === ts.SyntaxKind.ExtendsKeyword && hc.types) {
|
41322 | hc.types.forEach(type => result.extends = referenceFrom(type.expression));
|
41323 | }
|
41324 | });
|
41325 | }
|
41326 | // Add arity if the type is generic
|
41327 | const typeParameters = classDeclaration.typeParameters;
|
41328 | if (typeParameters && typeParameters.length) {
|
41329 | result.arity = typeParameters.length;
|
41330 | }
|
41331 | // Add class decorators
|
41332 | if (classDeclaration.decorators) {
|
41333 | result.decorators = getDecorators(classDeclaration.decorators);
|
41334 | }
|
41335 | // member decorators
|
41336 | let members = null;
|
41337 | function recordMember(name, metadata) {
|
41338 | if (!members)
|
41339 | members = {};
|
41340 | const data = members.hasOwnProperty(name) ? members[name] : [];
|
41341 | data.push(metadata);
|
41342 | members[name] = data;
|
41343 | }
|
41344 | // static member
|
41345 | let statics = null;
|
41346 | function recordStaticMember(name, value) {
|
41347 | if (!statics)
|
41348 | statics = {};
|
41349 | statics[name] = value;
|
41350 | }
|
41351 | for (const member of classDeclaration.members) {
|
41352 | let isConstructor = false;
|
41353 | switch (member.kind) {
|
41354 | case ts.SyntaxKind.Constructor:
|
41355 | case ts.SyntaxKind.MethodDeclaration:
|
41356 | isConstructor = member.kind === ts.SyntaxKind.Constructor;
|
41357 | const method = member;
|
41358 | if (isStatic(method)) {
|
41359 | const maybeFunc = maybeGetSimpleFunction(method);
|
41360 | if (maybeFunc) {
|
41361 | recordStaticMember(maybeFunc.name, maybeFunc.func);
|
41362 | }
|
41363 | continue;
|
41364 | }
|
41365 | const methodDecorators = getDecorators(method.decorators);
|
41366 | const parameters = method.parameters;
|
41367 | const parameterDecoratorData = [];
|
41368 | const parametersData = [];
|
41369 | let hasDecoratorData = false;
|
41370 | let hasParameterData = false;
|
41371 | for (const parameter of parameters) {
|
41372 | const parameterData = getDecorators(parameter.decorators);
|
41373 | parameterDecoratorData.push(parameterData);
|
41374 | hasDecoratorData = hasDecoratorData || !!parameterData;
|
41375 | if (isConstructor) {
|
41376 | if (parameter.type) {
|
41377 | parametersData.push(referenceFrom(parameter.type));
|
41378 | }
|
41379 | else {
|
41380 | parametersData.push(null);
|
41381 | }
|
41382 | hasParameterData = true;
|
41383 | }
|
41384 | }
|
41385 | const data = { __symbolic: isConstructor ? 'constructor' : 'method' };
|
41386 | const name = isConstructor ? '__ctor__' : evaluator.nameOf(member.name);
|
41387 | if (methodDecorators) {
|
41388 | data.decorators = methodDecorators;
|
41389 | }
|
41390 | if (hasDecoratorData) {
|
41391 | data.parameterDecorators = parameterDecoratorData;
|
41392 | }
|
41393 | if (hasParameterData) {
|
41394 | data.parameters = parametersData;
|
41395 | }
|
41396 | if (!isMetadataError$1(name)) {
|
41397 | recordMember(name, data);
|
41398 | }
|
41399 | break;
|
41400 | case ts.SyntaxKind.PropertyDeclaration:
|
41401 | case ts.SyntaxKind.GetAccessor:
|
41402 | case ts.SyntaxKind.SetAccessor:
|
41403 | const property = member;
|
41404 | if (isStatic(property)) {
|
41405 | const name = evaluator.nameOf(property.name);
|
41406 | if (!isMetadataError$1(name) && !shouldIgnoreStaticMember(name)) {
|
41407 | if (property.initializer) {
|
41408 | const value = evaluator.evaluateNode(property.initializer);
|
41409 | recordStaticMember(name, value);
|
41410 | }
|
41411 | else {
|
41412 | recordStaticMember(name, errorSym('Variable not initialized', property.name));
|
41413 | }
|
41414 | }
|
41415 | }
|
41416 | const propertyDecorators = getDecorators(property.decorators);
|
41417 | if (propertyDecorators) {
|
41418 | const name = evaluator.nameOf(property.name);
|
41419 | if (!isMetadataError$1(name)) {
|
41420 | recordMember(name, { __symbolic: 'property', decorators: propertyDecorators });
|
41421 | }
|
41422 | }
|
41423 | break;
|
41424 | }
|
41425 | }
|
41426 | if (members) {
|
41427 | result.members = members;
|
41428 | }
|
41429 | if (statics) {
|
41430 | result.statics = statics;
|
41431 | }
|
41432 | return recordEntry(result, classDeclaration);
|
41433 | }
|
41434 | // Collect all exported symbols from an exports clause.
|
41435 | const exportMap = new Map();
|
41436 | ts.forEachChild(sourceFile, node => {
|
41437 | switch (node.kind) {
|
41438 | case ts.SyntaxKind.ExportDeclaration:
|
41439 | const exportDeclaration = node;
|
41440 | const { moduleSpecifier, exportClause } = exportDeclaration;
|
41441 | if (!moduleSpecifier && exportClause && ts.isNamedExports(exportClause)) {
|
41442 | // If there is a module specifier there is also an exportClause
|
41443 | exportClause.elements.forEach(spec => {
|
41444 | const exportedAs = spec.name.text;
|
41445 | const name = (spec.propertyName || spec.name).text;
|
41446 | exportMap.set(name, exportedAs);
|
41447 | });
|
41448 | }
|
41449 | }
|
41450 | });
|
41451 | const isExport = (node) => sourceFile.isDeclarationFile ||
|
41452 | ts.getCombinedModifierFlags(node) & ts.ModifierFlags.Export;
|
41453 | const isExportedIdentifier = (identifier) => identifier && exportMap.has(identifier.text);
|
41454 | const isExported = (node) => isExport(node) || isExportedIdentifier(node.name);
|
41455 | const exportedIdentifierName = (identifier) => identifier && (exportMap.get(identifier.text) || identifier.text);
|
41456 | const exportedName = (node) => exportedIdentifierName(node.name);
|
41457 | // Pre-declare classes and functions
|
41458 | ts.forEachChild(sourceFile, node => {
|
41459 | switch (node.kind) {
|
41460 | case ts.SyntaxKind.ClassDeclaration:
|
41461 | const classDeclaration = node;
|
41462 | if (classDeclaration.name) {
|
41463 | const className = classDeclaration.name.text;
|
41464 | if (isExported(classDeclaration)) {
|
41465 | locals.define(className, { __symbolic: 'reference', name: exportedName(classDeclaration) });
|
41466 | }
|
41467 | else {
|
41468 | locals.define(className, errorSym('Reference to non-exported class', node, { className }));
|
41469 | }
|
41470 | }
|
41471 | break;
|
41472 | case ts.SyntaxKind.InterfaceDeclaration:
|
41473 | const interfaceDeclaration = node;
|
41474 | if (interfaceDeclaration.name) {
|
41475 | const interfaceName = interfaceDeclaration.name.text;
|
41476 | // All references to interfaces should be converted to references to `any`.
|
41477 | locals.define(interfaceName, { __symbolic: 'reference', name: 'any' });
|
41478 | }
|
41479 | break;
|
41480 | case ts.SyntaxKind.FunctionDeclaration:
|
41481 | const functionDeclaration = node;
|
41482 | if (!isExported(functionDeclaration)) {
|
41483 | // Report references to this function as an error.
|
41484 | const nameNode = functionDeclaration.name;
|
41485 | if (nameNode && nameNode.text) {
|
41486 | locals.define(nameNode.text, errorSym('Reference to a non-exported function', nameNode, { name: nameNode.text }));
|
41487 | }
|
41488 | }
|
41489 | break;
|
41490 | }
|
41491 | });
|
41492 | ts.forEachChild(sourceFile, node => {
|
41493 | switch (node.kind) {
|
41494 | case ts.SyntaxKind.ExportDeclaration:
|
41495 | // Record export declarations
|
41496 | const exportDeclaration = node;
|
41497 | const { moduleSpecifier, exportClause } = exportDeclaration;
|
41498 | if (!moduleSpecifier) {
|
41499 | // no module specifier -> export {propName as name};
|
41500 | if (exportClause && ts.isNamedExports(exportClause)) {
|
41501 | exportClause.elements.forEach(spec => {
|
41502 | const name = spec.name.text;
|
41503 | // If the symbol was not already exported, export a reference since it is a
|
41504 | // reference to an import
|
41505 | if (!metadata || !metadata[name]) {
|
41506 | const propNode = spec.propertyName || spec.name;
|
41507 | const value = evaluator.evaluateNode(propNode);
|
41508 | if (!metadata)
|
41509 | metadata = {};
|
41510 | metadata[name] = recordEntry(value, node);
|
41511 | }
|
41512 | });
|
41513 | }
|
41514 | }
|
41515 | if (moduleSpecifier && moduleSpecifier.kind == ts.SyntaxKind.StringLiteral) {
|
41516 | // Ignore exports that don't have string literals as exports.
|
41517 | // This is allowed by the syntax but will be flagged as an error by the type checker.
|
41518 | const from = moduleSpecifier.text;
|
41519 | const moduleExport = { from };
|
41520 | if (exportClause && ts.isNamedExports(exportClause)) {
|
41521 | moduleExport.export = exportClause.elements.map(spec => spec.propertyName ? { name: spec.propertyName.text, as: spec.name.text } :
|
41522 | spec.name.text);
|
41523 | }
|
41524 | if (!exports)
|
41525 | exports = [];
|
41526 | exports.push(moduleExport);
|
41527 | }
|
41528 | break;
|
41529 | case ts.SyntaxKind.ClassDeclaration:
|
41530 | const classDeclaration = node;
|
41531 | if (classDeclaration.name) {
|
41532 | if (isExported(classDeclaration)) {
|
41533 | const name = exportedName(classDeclaration);
|
41534 | if (name) {
|
41535 | if (!metadata)
|
41536 | metadata = {};
|
41537 | metadata[name] = classMetadataOf(classDeclaration);
|
41538 | }
|
41539 | }
|
41540 | }
|
41541 | // Otherwise don't record metadata for the class.
|
41542 | break;
|
41543 | case ts.SyntaxKind.TypeAliasDeclaration:
|
41544 | const typeDeclaration = node;
|
41545 | if (typeDeclaration.name && isExported(typeDeclaration)) {
|
41546 | const name = exportedName(typeDeclaration);
|
41547 | if (name) {
|
41548 | if (!metadata)
|
41549 | metadata = {};
|
41550 | metadata[name] = { __symbolic: 'interface' };
|
41551 | }
|
41552 | }
|
41553 | break;
|
41554 | case ts.SyntaxKind.InterfaceDeclaration:
|
41555 | const interfaceDeclaration = node;
|
41556 | if (interfaceDeclaration.name && isExported(interfaceDeclaration)) {
|
41557 | const name = exportedName(interfaceDeclaration);
|
41558 | if (name) {
|
41559 | if (!metadata)
|
41560 | metadata = {};
|
41561 | metadata[name] = { __symbolic: 'interface' };
|
41562 | }
|
41563 | }
|
41564 | break;
|
41565 | case ts.SyntaxKind.FunctionDeclaration:
|
41566 | // Record functions that return a single value. Record the parameter
|
41567 | // names substitution will be performed by the StaticReflector.
|
41568 | const functionDeclaration = node;
|
41569 | if (isExported(functionDeclaration) && functionDeclaration.name) {
|
41570 | const name = exportedName(functionDeclaration);
|
41571 | const maybeFunc = maybeGetSimpleFunction(functionDeclaration);
|
41572 | if (name) {
|
41573 | if (!metadata)
|
41574 | metadata = {};
|
41575 | // TODO(alxhub): The literal here is not valid FunctionMetadata.
|
41576 | metadata[name] =
|
41577 | maybeFunc ? recordEntry(maybeFunc.func, node) : { __symbolic: 'function' };
|
41578 | }
|
41579 | }
|
41580 | break;
|
41581 | case ts.SyntaxKind.EnumDeclaration:
|
41582 | const enumDeclaration = node;
|
41583 | if (isExported(enumDeclaration)) {
|
41584 | const enumValueHolder = {};
|
41585 | const enumName = exportedName(enumDeclaration);
|
41586 | let nextDefaultValue = 0;
|
41587 | let writtenMembers = 0;
|
41588 | for (const member of enumDeclaration.members) {
|
41589 | let enumValue;
|
41590 | if (!member.initializer) {
|
41591 | enumValue = nextDefaultValue;
|
41592 | }
|
41593 | else {
|
41594 | enumValue = evaluator.evaluateNode(member.initializer);
|
41595 | }
|
41596 | let name = undefined;
|
41597 | if (member.name.kind == ts.SyntaxKind.Identifier) {
|
41598 | const identifier = member.name;
|
41599 | name = identifier.text;
|
41600 | enumValueHolder[name] = enumValue;
|
41601 | writtenMembers++;
|
41602 | }
|
41603 | if (typeof enumValue === 'number') {
|
41604 | nextDefaultValue = enumValue + 1;
|
41605 | }
|
41606 | else if (name) {
|
41607 | // TODO(alxhub): 'left' here has a name propery which is not valid for
|
41608 | // MetadataSymbolicSelectExpression.
|
41609 | nextDefaultValue = {
|
41610 | __symbolic: 'binary',
|
41611 | operator: '+',
|
41612 | left: {
|
41613 | __symbolic: 'select',
|
41614 | expression: recordEntry({ __symbolic: 'reference', name: enumName }, node),
|
41615 | name
|
41616 | },
|
41617 | };
|
41618 | }
|
41619 | else {
|
41620 | nextDefaultValue =
|
41621 | recordEntry(errorSym('Unsupported enum member name', member.name), node);
|
41622 | }
|
41623 | }
|
41624 | if (writtenMembers) {
|
41625 | if (enumName) {
|
41626 | if (!metadata)
|
41627 | metadata = {};
|
41628 | metadata[enumName] = recordEntry(enumValueHolder, node);
|
41629 | }
|
41630 | }
|
41631 | }
|
41632 | break;
|
41633 | case ts.SyntaxKind.VariableStatement:
|
41634 | const variableStatement = node;
|
41635 | for (const variableDeclaration of variableStatement.declarationList.declarations) {
|
41636 | if (variableDeclaration.name.kind == ts.SyntaxKind.Identifier) {
|
41637 | const nameNode = variableDeclaration.name;
|
41638 | let varValue;
|
41639 | if (variableDeclaration.initializer) {
|
41640 | varValue = evaluator.evaluateNode(variableDeclaration.initializer);
|
41641 | }
|
41642 | else {
|
41643 | varValue = recordEntry(errorSym('Variable not initialized', nameNode), nameNode);
|
41644 | }
|
41645 | let exported = false;
|
41646 | if (isExport(variableStatement) || isExport(variableDeclaration) ||
|
41647 | isExportedIdentifier(nameNode)) {
|
41648 | const name = exportedIdentifierName(nameNode);
|
41649 | if (name) {
|
41650 | if (!metadata)
|
41651 | metadata = {};
|
41652 | metadata[name] = recordEntry(varValue, node);
|
41653 | }
|
41654 | exported = true;
|
41655 | }
|
41656 | if (typeof varValue == 'string' || typeof varValue == 'number' ||
|
41657 | typeof varValue == 'boolean') {
|
41658 | locals.define(nameNode.text, varValue);
|
41659 | if (exported) {
|
41660 | locals.defineReference(nameNode.text, { __symbolic: 'reference', name: nameNode.text });
|
41661 | }
|
41662 | }
|
41663 | else if (!exported) {
|
41664 | if (varValue && !isMetadataError$1(varValue)) {
|
41665 | locals.define(nameNode.text, recordEntry(varValue, node));
|
41666 | }
|
41667 | else {
|
41668 | locals.define(nameNode.text, recordEntry(errorSym('Reference to a local symbol', nameNode, { name: nameNode.text }), node));
|
41669 | }
|
41670 | }
|
41671 | }
|
41672 | else {
|
41673 | // Destructuring (or binding) declarations are not supported,
|
41674 | // var {<identifier>[, <identifier>]+} = <expression>;
|
41675 | // or
|
41676 | // var [<identifier>[, <identifier}+] = <expression>;
|
41677 | // are not supported.
|
41678 | const report = (nameNode) => {
|
41679 | switch (nameNode.kind) {
|
41680 | case ts.SyntaxKind.Identifier:
|
41681 | const name = nameNode;
|
41682 | const varValue = errorSym('Destructuring not supported', name);
|
41683 | locals.define(name.text, varValue);
|
41684 | if (isExport(node)) {
|
41685 | if (!metadata)
|
41686 | metadata = {};
|
41687 | metadata[name.text] = varValue;
|
41688 | }
|
41689 | break;
|
41690 | case ts.SyntaxKind.BindingElement:
|
41691 | const bindingElement = nameNode;
|
41692 | report(bindingElement.name);
|
41693 | break;
|
41694 | case ts.SyntaxKind.ObjectBindingPattern:
|
41695 | case ts.SyntaxKind.ArrayBindingPattern:
|
41696 | const bindings = nameNode;
|
41697 | bindings.elements.forEach(report);
|
41698 | break;
|
41699 | }
|
41700 | };
|
41701 | report(variableDeclaration.name);
|
41702 | }
|
41703 | }
|
41704 | break;
|
41705 | }
|
41706 | });
|
41707 | if (metadata || exports) {
|
41708 | if (!metadata)
|
41709 | metadata = {};
|
41710 | else if (strict) {
|
41711 | validateMetadata(sourceFile, nodeMap, metadata);
|
41712 | }
|
41713 | const result = {
|
41714 | __symbolic: 'module',
|
41715 | version: this.options.version || METADATA_VERSION,
|
41716 | metadata
|
41717 | };
|
41718 | if (sourceFile.moduleName)
|
41719 | result.importAs = sourceFile.moduleName;
|
41720 | if (exports)
|
41721 | result.exports = exports;
|
41722 | return result;
|
41723 | }
|
41724 | }
|
41725 | }
|
41726 | // This will throw if the metadata entry given contains an error node.
|
41727 | function validateMetadata(sourceFile, nodeMap, metadata) {
|
41728 | let locals = new Set(['Array', 'Object', 'Set', 'Map', 'string', 'number', 'any']);
|
41729 | function validateExpression(expression) {
|
41730 | if (!expression) {
|
41731 | return;
|
41732 | }
|
41733 | else if (Array.isArray(expression)) {
|
41734 | expression.forEach(validateExpression);
|
41735 | }
|
41736 | else if (typeof expression === 'object' && !expression.hasOwnProperty('__symbolic')) {
|
41737 | Object.getOwnPropertyNames(expression).forEach(v => validateExpression(expression[v]));
|
41738 | }
|
41739 | else if (isMetadataError$1(expression)) {
|
41740 | reportError(expression);
|
41741 | }
|
41742 | else if (isMetadataGlobalReferenceExpression(expression)) {
|
41743 | if (!locals.has(expression.name)) {
|
41744 | const reference = metadata[expression.name];
|
41745 | if (reference) {
|
41746 | validateExpression(reference);
|
41747 | }
|
41748 | }
|
41749 | }
|
41750 | else if (isFunctionMetadata(expression)) {
|
41751 | validateFunction(expression);
|
41752 | }
|
41753 | else if (isMetadataSymbolicExpression(expression)) {
|
41754 | switch (expression.__symbolic) {
|
41755 | case 'binary':
|
41756 | const binaryExpression = expression;
|
41757 | validateExpression(binaryExpression.left);
|
41758 | validateExpression(binaryExpression.right);
|
41759 | break;
|
41760 | case 'call':
|
41761 | case 'new':
|
41762 | const callExpression = expression;
|
41763 | validateExpression(callExpression.expression);
|
41764 | if (callExpression.arguments)
|
41765 | callExpression.arguments.forEach(validateExpression);
|
41766 | break;
|
41767 | case 'index':
|
41768 | const indexExpression = expression;
|
41769 | validateExpression(indexExpression.expression);
|
41770 | validateExpression(indexExpression.index);
|
41771 | break;
|
41772 | case 'pre':
|
41773 | const prefixExpression = expression;
|
41774 | validateExpression(prefixExpression.operand);
|
41775 | break;
|
41776 | case 'select':
|
41777 | const selectExpression = expression;
|
41778 | validateExpression(selectExpression.expression);
|
41779 | break;
|
41780 | case 'spread':
|
41781 | const spreadExpression = expression;
|
41782 | validateExpression(spreadExpression.expression);
|
41783 | break;
|
41784 | case 'if':
|
41785 | const ifExpression = expression;
|
41786 | validateExpression(ifExpression.condition);
|
41787 | validateExpression(ifExpression.elseExpression);
|
41788 | validateExpression(ifExpression.thenExpression);
|
41789 | break;
|
41790 | }
|
41791 | }
|
41792 | }
|
41793 | function validateMember(classData, member) {
|
41794 | if (member.decorators) {
|
41795 | member.decorators.forEach(validateExpression);
|
41796 | }
|
41797 | if (isMethodMetadata(member) && member.parameterDecorators) {
|
41798 | member.parameterDecorators.forEach(validateExpression);
|
41799 | }
|
41800 | // Only validate parameters of classes for which we know that are used with our DI
|
41801 | if (classData.decorators && isConstructorMetadata(member) && member.parameters) {
|
41802 | member.parameters.forEach(validateExpression);
|
41803 | }
|
41804 | }
|
41805 | function validateClass(classData) {
|
41806 | if (classData.decorators) {
|
41807 | classData.decorators.forEach(validateExpression);
|
41808 | }
|
41809 | if (classData.members) {
|
41810 | Object.getOwnPropertyNames(classData.members)
|
41811 | .forEach(name => classData.members[name].forEach((m) => validateMember(classData, m)));
|
41812 | }
|
41813 | if (classData.statics) {
|
41814 | Object.getOwnPropertyNames(classData.statics).forEach(name => {
|
41815 | const staticMember = classData.statics[name];
|
41816 | if (isFunctionMetadata(staticMember)) {
|
41817 | validateExpression(staticMember.value);
|
41818 | }
|
41819 | else {
|
41820 | validateExpression(staticMember);
|
41821 | }
|
41822 | });
|
41823 | }
|
41824 | }
|
41825 | function validateFunction(functionDeclaration) {
|
41826 | if (functionDeclaration.value) {
|
41827 | const oldLocals = locals;
|
41828 | if (functionDeclaration.parameters) {
|
41829 | locals = new Set(oldLocals.values());
|
41830 | if (functionDeclaration.parameters)
|
41831 | functionDeclaration.parameters.forEach(n => locals.add(n));
|
41832 | }
|
41833 | validateExpression(functionDeclaration.value);
|
41834 | locals = oldLocals;
|
41835 | }
|
41836 | }
|
41837 | function shouldReportNode(node) {
|
41838 | if (node) {
|
41839 | const nodeStart = node.getStart();
|
41840 | return !(node.pos != nodeStart &&
|
41841 | sourceFile.text.substring(node.pos, nodeStart).indexOf('@dynamic') >= 0);
|
41842 | }
|
41843 | return true;
|
41844 | }
|
41845 | function reportError(error) {
|
41846 | const node = nodeMap.get(error);
|
41847 | if (shouldReportNode(node)) {
|
41848 | const lineInfo = error.line != undefined ? error.character != undefined ?
|
41849 | `:${error.line + 1}:${error.character + 1}` :
|
41850 | `:${error.line + 1}` :
|
41851 | '';
|
41852 | throw new Error(`${sourceFile.fileName}${lineInfo}: Metadata collected contains an error that will be reported at runtime: ${expandedMessage$1(error)}.\n ${JSON.stringify(error)}`);
|
41853 | }
|
41854 | }
|
41855 | Object.getOwnPropertyNames(metadata).forEach(name => {
|
41856 | const entry = metadata[name];
|
41857 | try {
|
41858 | if (isClassMetadata(entry)) {
|
41859 | validateClass(entry);
|
41860 | }
|
41861 | }
|
41862 | catch (e) {
|
41863 | const node = nodeMap.get(entry);
|
41864 | if (shouldReportNode(node)) {
|
41865 | if (node) {
|
41866 | const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
|
41867 | throw new Error(`${sourceFile.fileName}:${line + 1}:${character + 1}: Error encountered in metadata generated for exported symbol '${name}': \n ${e.message}`);
|
41868 | }
|
41869 | throw new Error(`Error encountered in metadata generated for exported symbol ${name}: \n ${e.message}`);
|
41870 | }
|
41871 | }
|
41872 | });
|
41873 | }
|
41874 | // Collect parameter names from a function.
|
41875 | function namesOf(parameters) {
|
41876 | const result = [];
|
41877 | function addNamesOf(name) {
|
41878 | if (name.kind == ts.SyntaxKind.Identifier) {
|
41879 | const identifier = name;
|
41880 | result.push(identifier.text);
|
41881 | }
|
41882 | else {
|
41883 | const bindingPattern = name;
|
41884 | for (const element of bindingPattern.elements) {
|
41885 | const name = element.name;
|
41886 | if (name) {
|
41887 | addNamesOf(name);
|
41888 | }
|
41889 | }
|
41890 | }
|
41891 | }
|
41892 | for (const parameter of parameters) {
|
41893 | addNamesOf(parameter.name);
|
41894 | }
|
41895 | return result;
|
41896 | }
|
41897 | function shouldIgnoreStaticMember(memberName) {
|
41898 | return memberName.startsWith('ngAcceptInputType_') || memberName.startsWith('ngTemplateGuard_');
|
41899 | }
|
41900 | function expandedMessage$1(error) {
|
41901 | switch (error.message) {
|
41902 | case 'Reference to non-exported class':
|
41903 | if (error.context && error.context.className) {
|
41904 | return `Reference to a non-exported class ${error.context.className}. Consider exporting the class`;
|
41905 | }
|
41906 | break;
|
41907 | case 'Variable not initialized':
|
41908 | return 'Only initialized variables and constants can be referenced because the value of this variable is needed by the template compiler';
|
41909 | case 'Destructuring not supported':
|
41910 | return 'Referencing an exported destructured variable or constant is not supported by the template compiler. Consider simplifying this to avoid destructuring';
|
41911 | case 'Could not resolve type':
|
41912 | if (error.context && error.context.typeName) {
|
41913 | return `Could not resolve type ${error.context.typeName}`;
|
41914 | }
|
41915 | break;
|
41916 | case 'Function call not supported':
|
41917 | let prefix = error.context && error.context.name ? `Calling function '${error.context.name}', f` : 'F';
|
41918 | return prefix +
|
41919 | 'unction calls are not supported. Consider replacing the function or lambda with a reference to an exported function';
|
41920 | case 'Reference to a local symbol':
|
41921 | if (error.context && error.context.name) {
|
41922 | return `Reference to a local (non-exported) symbol '${error.context.name}'. Consider exporting the symbol`;
|
41923 | }
|
41924 | }
|
41925 | return error.message;
|
41926 | }
|
41927 |
|
41928 | /**
|
41929 | * @license
|
41930 | * Copyright Google LLC All Rights Reserved.
|
41931 | *
|
41932 | * Use of this source code is governed by an MIT-style license that can be
|
41933 | * found in the LICENSE file at https://angular.io/license
|
41934 | */
|
41935 | var EmitFlags;
|
41936 | (function (EmitFlags) {
|
41937 | EmitFlags[EmitFlags["DTS"] = 1] = "DTS";
|
41938 | EmitFlags[EmitFlags["JS"] = 2] = "JS";
|
41939 | EmitFlags[EmitFlags["Metadata"] = 4] = "Metadata";
|
41940 | EmitFlags[EmitFlags["I18nBundle"] = 8] = "I18nBundle";
|
41941 | EmitFlags[EmitFlags["Codegen"] = 16] = "Codegen";
|
41942 | EmitFlags[EmitFlags["Default"] = 19] = "Default";
|
41943 | EmitFlags[EmitFlags["All"] = 31] = "All";
|
41944 | })(EmitFlags || (EmitFlags = {}));
|
41945 |
|
41946 | /**
|
41947 | * @license
|
41948 | * Copyright Google LLC All Rights Reserved.
|
41949 | *
|
41950 | * Use of this source code is governed by an MIT-style license that can be
|
41951 | * found in the LICENSE file at https://angular.io/license
|
41952 | */
|
41953 | const DTS = /\.d\.ts$/;
|
41954 |
|
41955 | /**
|
41956 | * @license
|
41957 | * Copyright Google LLC All Rights Reserved.
|
41958 | *
|
41959 | * Use of this source code is governed by an MIT-style license that can be
|
41960 | * found in the LICENSE file at https://angular.io/license
|
41961 | */
|
41962 | function createMetadataReaderCache() {
|
41963 | const data = new Map();
|
41964 | return { data };
|
41965 | }
|
41966 | function readMetadata(filePath, host, cache) {
|
41967 | let metadatas = cache && cache.data.get(filePath);
|
41968 | if (metadatas) {
|
41969 | return metadatas;
|
41970 | }
|
41971 | if (host.fileExists(filePath)) {
|
41972 | // If the file doesn't exists then we cannot return metadata for the file.
|
41973 | // This will occur if the user referenced a declared module for which no file
|
41974 | // exists for the module (i.e. jQuery or angularjs).
|
41975 | if (DTS.test(filePath)) {
|
41976 | metadatas = readMetadataFile(host, filePath);
|
41977 | if (!metadatas) {
|
41978 | // If there is a .d.ts file but no metadata file we need to produce a
|
41979 | // metadata from the .d.ts file as metadata files capture reexports
|
41980 | // (starting with v3).
|
41981 | metadatas = [upgradeMetadataWithDtsData(host, { '__symbolic': 'module', 'version': 1, 'metadata': {} }, filePath)];
|
41982 | }
|
41983 | }
|
41984 | else {
|
41985 | const metadata = host.getSourceFileMetadata(filePath);
|
41986 | metadatas = metadata ? [metadata] : [];
|
41987 | }
|
41988 | }
|
41989 | if (cache && (!host.cacheMetadata || host.cacheMetadata(filePath))) {
|
41990 | cache.data.set(filePath, metadatas);
|
41991 | }
|
41992 | return metadatas;
|
41993 | }
|
41994 | function readMetadataFile(host, dtsFilePath) {
|
41995 | const metadataPath = dtsFilePath.replace(DTS, '.metadata.json');
|
41996 | if (!host.fileExists(metadataPath)) {
|
41997 | return undefined;
|
41998 | }
|
41999 | try {
|
42000 | const metadataOrMetadatas = JSON.parse(host.readFile(metadataPath));
|
42001 | const metadatas = metadataOrMetadatas ?
|
42002 | (Array.isArray(metadataOrMetadatas) ? metadataOrMetadatas : [metadataOrMetadatas]) :
|
42003 | [];
|
42004 | if (metadatas.length) {
|
42005 | let maxMetadata = metadatas.reduce((p, c) => p.version > c.version ? p : c);
|
42006 | if (maxMetadata.version < METADATA_VERSION) {
|
42007 | metadatas.push(upgradeMetadataWithDtsData(host, maxMetadata, dtsFilePath));
|
42008 | }
|
42009 | }
|
42010 | return metadatas;
|
42011 | }
|
42012 | catch (e) {
|
42013 | console.error(`Failed to read JSON file ${metadataPath}`);
|
42014 | throw e;
|
42015 | }
|
42016 | }
|
42017 | function upgradeMetadataWithDtsData(host, oldMetadata, dtsFilePath) {
|
42018 | // patch v1 to v3 by adding exports and the `extends` clause.
|
42019 | // patch v3 to v4 by adding `interface` symbols for TypeAlias
|
42020 | let newMetadata = {
|
42021 | '__symbolic': 'module',
|
42022 | 'version': METADATA_VERSION,
|
42023 | 'metadata': Object.assign({}, oldMetadata.metadata),
|
42024 | };
|
42025 | if (oldMetadata.exports) {
|
42026 | newMetadata.exports = oldMetadata.exports;
|
42027 | }
|
42028 | if (oldMetadata.importAs) {
|
42029 | newMetadata.importAs = oldMetadata.importAs;
|
42030 | }
|
42031 | if (oldMetadata.origins) {
|
42032 | newMetadata.origins = oldMetadata.origins;
|
42033 | }
|
42034 | const dtsMetadata = host.getSourceFileMetadata(dtsFilePath);
|
42035 | if (dtsMetadata) {
|
42036 | for (let prop in dtsMetadata.metadata) {
|
42037 | if (!newMetadata.metadata[prop]) {
|
42038 | newMetadata.metadata[prop] = dtsMetadata.metadata[prop];
|
42039 | }
|
42040 | }
|
42041 | if (dtsMetadata['importAs'])
|
42042 | newMetadata['importAs'] = dtsMetadata['importAs'];
|
42043 | // Only copy exports from exports from metadata prior to version 3.
|
42044 | // Starting with version 3 the collector began collecting exports and
|
42045 | // this should be redundant. Also, with bundler will rewrite the exports
|
42046 | // which will hoist the exports from modules referenced indirectly causing
|
42047 | // the imports to be different than the .d.ts files and using the .d.ts file
|
42048 | // exports would cause the StaticSymbolResolver to redirect symbols to the
|
42049 | // incorrect location.
|
42050 | if ((!oldMetadata.version || oldMetadata.version < 3) && dtsMetadata.exports) {
|
42051 | newMetadata.exports = dtsMetadata.exports;
|
42052 | }
|
42053 | }
|
42054 | return newMetadata;
|
42055 | }
|
42056 |
|
42057 | /**
|
42058 | * @license
|
42059 | * Copyright Google LLC All Rights Reserved.
|
42060 | *
|
42061 | * Use of this source code is governed by an MIT-style license that can be
|
42062 | * found in the LICENSE file at https://angular.io/license
|
42063 | */
|
42064 | class ReflectorModuleModuleResolutionHost {
|
42065 | constructor(tsLSHost, getProgram) {
|
42066 | this.tsLSHost = tsLSHost;
|
42067 | this.getProgram = getProgram;
|
42068 | this.metadataCollector = new MetadataCollector({
|
42069 | // Note: verboseInvalidExpressions is important so that
|
42070 | // the collector will collect errors instead of throwing
|
42071 | verboseInvalidExpression: true,
|
42072 | });
|
42073 | if (tsLSHost.directoryExists) {
|
42074 | this.directoryExists = directoryName => tsLSHost.directoryExists(directoryName);
|
42075 | }
|
42076 | if (tsLSHost.realpath) {
|
42077 | this.realpath = path => tsLSHost.realpath(path);
|
42078 | }
|
42079 | }
|
42080 | fileExists(fileName) {
|
42081 | // TypeScript resolution logic walks through the following sequence in order:
|
42082 | // package.json (read "types" field) -> .ts -> .tsx -> .d.ts
|
42083 | // For more info, see
|
42084 | // https://www.typescriptlang.org/docs/handbook/module-resolution.html
|
42085 | // For Angular specifically, we can skip .tsx lookup
|
42086 | if (fileName.endsWith('.tsx')) {
|
42087 | return false;
|
42088 | }
|
42089 | if (this.tsLSHost.fileExists) {
|
42090 | return this.tsLSHost.fileExists(fileName);
|
42091 | }
|
42092 | return !!this.tsLSHost.getScriptSnapshot(fileName);
|
42093 | }
|
42094 | readFile(fileName) {
|
42095 | // readFile() is used by TypeScript to read package.json during module
|
42096 | // resolution, and it's used by Angular to read metadata.json during
|
42097 | // metadata resolution.
|
42098 | if (this.tsLSHost.readFile) {
|
42099 | return this.tsLSHost.readFile(fileName);
|
42100 | }
|
42101 | // As a fallback, read the JSON files from the editor snapshot.
|
42102 | const snapshot = this.tsLSHost.getScriptSnapshot(fileName);
|
42103 | if (!snapshot) {
|
42104 | // MetadataReaderHost readFile() declaration should be
|
42105 | // `readFile(fileName: string): string | undefined`
|
42106 | return undefined;
|
42107 | }
|
42108 | return snapshot.getText(0, snapshot.getLength());
|
42109 | }
|
42110 | getSourceFileMetadata(fileName) {
|
42111 | const sf = this.getProgram().getSourceFile(fileName);
|
42112 | return sf ? this.metadataCollector.getMetadata(sf) : undefined;
|
42113 | }
|
42114 | cacheMetadata(fileName) {
|
42115 | // Don't cache the metadata for .ts files as they might change in the editor!
|
42116 | return fileName.endsWith('.d.ts');
|
42117 | }
|
42118 | }
|
42119 | class ReflectorHost {
|
42120 | constructor(getProgram, tsLSHost) {
|
42121 | this.tsLSHost = tsLSHost;
|
42122 | this.metadataReaderCache = createMetadataReaderCache();
|
42123 | // tsLSHost.getCurrentDirectory() returns the directory where tsconfig.json
|
42124 | // is located. This is not the same as process.cwd() because the language
|
42125 | // service host sets the "project root path" as its current directory.
|
42126 | const currentDir = tsLSHost.getCurrentDirectory();
|
42127 | this.fakeContainingPath = currentDir ? path.join(currentDir, 'fakeContainingFile.ts') : '';
|
42128 | this.hostAdapter = new ReflectorModuleModuleResolutionHost(tsLSHost, getProgram);
|
42129 | this.moduleResolutionCache = ts.createModuleResolutionCache(currentDir, s => s, // getCanonicalFileName
|
42130 | tsLSHost.getCompilationSettings());
|
42131 | }
|
42132 | getMetadataFor(modulePath) {
|
42133 | return readMetadata(modulePath, this.hostAdapter, this.metadataReaderCache);
|
42134 | }
|
42135 | moduleNameToFileName(moduleName, containingFile) {
|
42136 | if (!containingFile) {
|
42137 | if (moduleName.startsWith('.')) {
|
42138 | throw new Error('Resolution of relative paths requires a containing file.');
|
42139 | }
|
42140 | if (!this.fakeContainingPath) {
|
42141 | // If current directory is empty then the file must belong to an inferred
|
42142 | // project (no tsconfig.json), in which case it's not possible to resolve
|
42143 | // the module without the caller explicitly providing a containing file.
|
42144 | throw new Error(`Could not resolve '${moduleName}' without a containing file.`);
|
42145 | }
|
42146 | containingFile = this.fakeContainingPath;
|
42147 | }
|
42148 | const compilerOptions = this.tsLSHost.getCompilationSettings();
|
42149 | const resolved = ts.resolveModuleName(moduleName, containingFile, compilerOptions, this.hostAdapter, this.moduleResolutionCache)
|
42150 | .resolvedModule;
|
42151 | return resolved ? resolved.resolvedFileName : null;
|
42152 | }
|
42153 | getOutputName(filePath) {
|
42154 | return filePath;
|
42155 | }
|
42156 | }
|
42157 |
|
42158 | /**
|
42159 | * @license
|
42160 | * Copyright Google LLC All Rights Reserved.
|
42161 | *
|
42162 | * Use of this source code is governed by an MIT-style license that can be
|
42163 | * found in the LICENSE file at https://angular.io/license
|
42164 | */
|
42165 | /**
|
42166 | * The language service never needs the normalized versions of the metadata. To avoid parsing
|
42167 | * the content and resolving references, return an empty file. This also allows normalizing
|
42168 | * template that are syntatically incorrect which is required to provide completions in
|
42169 | * syntactically incorrect templates.
|
42170 | */
|
42171 | class DummyHtmlParser extends HtmlParser {
|
42172 | parse() {
|
42173 | return new ParseTreeResult([], []);
|
42174 | }
|
42175 | }
|
42176 | /**
|
42177 | * Avoid loading resources in the language servcie by using a dummy loader.
|
42178 | */
|
42179 | class DummyResourceLoader extends ResourceLoader {
|
42180 | get(_url) {
|
42181 | return Promise.resolve('');
|
42182 | }
|
42183 | }
|
42184 | /**
|
42185 | * An implementation of a `LanguageServiceHost` for a TypeScript project.
|
42186 | *
|
42187 | * The `TypeScriptServiceHost` implements the Angular `LanguageServiceHost` using
|
42188 | * the TypeScript language services.
|
42189 | *
|
42190 | * @publicApi
|
42191 | */
|
42192 | class TypeScriptServiceHost {
|
42193 | constructor(tsLsHost, tsLS) {
|
42194 | this.tsLsHost = tsLsHost;
|
42195 | this.tsLS = tsLS;
|
42196 | this.staticSymbolCache = new StaticSymbolCache();
|
42197 | /**
|
42198 | * Key of the `fileToComponent` map must be TS internal normalized path (path
|
42199 | * separator must be `/`), value of the map is the StaticSymbol for the
|
42200 | * Component class declaration.
|
42201 | */
|
42202 | this.fileToComponent = new Map();
|
42203 | this.collectedErrors = new Map();
|
42204 | this.fileVersions = new Map();
|
42205 | this.lastProgram = undefined;
|
42206 | this.analyzedModules = {
|
42207 | files: [],
|
42208 | ngModuleByPipeOrDirective: new Map(),
|
42209 | ngModules: [],
|
42210 | };
|
42211 | this.summaryResolver = new AotSummaryResolver({
|
42212 | loadSummary(_filePath) {
|
42213 | return null;
|
42214 | },
|
42215 | isSourceFile(_sourceFilePath) {
|
42216 | return true;
|
42217 | },
|
42218 | toSummaryFileName(sourceFilePath) {
|
42219 | return sourceFilePath;
|
42220 | },
|
42221 | fromSummaryFileName(filePath) {
|
42222 | return filePath;
|
42223 | },
|
42224 | }, this.staticSymbolCache);
|
42225 | this.reflectorHost = new ReflectorHost(() => this.program, tsLsHost);
|
42226 | this.staticSymbolResolver = new StaticSymbolResolver(this.reflectorHost, this.staticSymbolCache, this.summaryResolver, (e, filePath) => this.collectError(e, filePath));
|
42227 | this.urlResolver = {
|
42228 | resolve: (baseUrl, url) => {
|
42229 | // In practice, `directoryExists` is always defined.
|
42230 | // https://github.com/microsoft/TypeScript/blob/0b6c9254a850dd07056259d4eefca7721745af75/src/server/project.ts#L1608-L1614
|
42231 | if (tsLsHost.directoryExists(baseUrl)) {
|
42232 | return path.resolve(baseUrl, url);
|
42233 | }
|
42234 | return path.resolve(path.dirname(baseUrl), url);
|
42235 | }
|
42236 | };
|
42237 | }
|
42238 | /**
|
42239 | * Return the singleton instance of the MetadataResolver.
|
42240 | */
|
42241 | get resolver() {
|
42242 | if (this._resolver) {
|
42243 | return this._resolver;
|
42244 | }
|
42245 | // StaticReflector keeps its own private caches that are not clearable.
|
42246 | // We have no choice but to create a new instance to invalidate the caches.
|
42247 | // TODO: Revisit this when language service gets rewritten for Ivy.
|
42248 | const staticReflector = new StaticReflector(this.summaryResolver, this.staticSymbolResolver, [], // knownMetadataClasses
|
42249 | [], // knownMetadataFunctions
|
42250 | (e, filePath) => this.collectError(e, filePath));
|
42251 | // Because static reflector above is changed, we need to create a new
|
42252 | // resolver.
|
42253 | const moduleResolver = new NgModuleResolver(staticReflector);
|
42254 | const directiveResolver = new DirectiveResolver(staticReflector);
|
42255 | const pipeResolver = new PipeResolver(staticReflector);
|
42256 | const elementSchemaRegistry = new DomElementSchemaRegistry();
|
42257 | const resourceLoader = new DummyResourceLoader();
|
42258 | const htmlParser = new DummyHtmlParser();
|
42259 | // This tracks the CompileConfig in codegen.ts. Currently these options
|
42260 | // are hard-coded.
|
42261 | const config = new CompilerConfig({
|
42262 | defaultEncapsulation: ViewEncapsulation$1.Emulated,
|
42263 | useJit: false,
|
42264 | });
|
42265 | const directiveNormalizer = new DirectiveNormalizer(resourceLoader, this.urlResolver, htmlParser, config);
|
42266 | 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));
|
42267 | return this._resolver;
|
42268 | }
|
42269 | /**
|
42270 | * Return the singleton instance of the StaticReflector hosted in the
|
42271 | * MetadataResolver.
|
42272 | */
|
42273 | get reflector() {
|
42274 | return this.resolver.getReflector();
|
42275 | }
|
42276 | /**
|
42277 | * Return all known external templates.
|
42278 | */
|
42279 | getExternalTemplates() {
|
42280 | return [...this.fileToComponent.keys()];
|
42281 | }
|
42282 | /**
|
42283 | * Checks whether the program has changed and returns all analyzed modules.
|
42284 | * If program has changed, invalidate all caches and update fileToComponent
|
42285 | * and templateReferences.
|
42286 | * In addition to returning information about NgModules, this method plays the
|
42287 | * same role as 'synchronizeHostData' in tsserver.
|
42288 | */
|
42289 | getAnalyzedModules() {
|
42290 | if (this.upToDate()) {
|
42291 | return this.analyzedModules;
|
42292 | }
|
42293 | // Invalidate caches
|
42294 | this.fileToComponent.clear();
|
42295 | this.collectedErrors.clear();
|
42296 | this.resolver.clearCache();
|
42297 | const analyzeHost = {
|
42298 | isSourceFile(_filePath) {
|
42299 | return true;
|
42300 | }
|
42301 | };
|
42302 | const programFiles = this.program.getSourceFiles().map(sf => sf.fileName);
|
42303 | try {
|
42304 | this.analyzedModules =
|
42305 | analyzeNgModules(programFiles, analyzeHost, this.staticSymbolResolver, this.resolver);
|
42306 | }
|
42307 | catch (e) {
|
42308 | // Analyzing modules may throw; in that case, reuse the old modules.
|
42309 | this.error(`Analyzing NgModules failed. ${e}`);
|
42310 | return this.analyzedModules;
|
42311 | }
|
42312 | // update template references and fileToComponent
|
42313 | for (const ngModule of this.analyzedModules.ngModules) {
|
42314 | for (const directive of ngModule.declaredDirectives) {
|
42315 | const { metadata } = this.resolver.getNonNormalizedDirectiveMetadata(directive.reference);
|
42316 | if (metadata.isComponent && metadata.template && metadata.template.templateUrl) {
|
42317 | const templateName = this.urlResolver.resolve(this.reflector.componentModuleUrl(directive.reference), metadata.template.templateUrl);
|
42318 | this.fileToComponent.set(tss.server.toNormalizedPath(templateName), directive.reference);
|
42319 | }
|
42320 | }
|
42321 | }
|
42322 | return this.analyzedModules;
|
42323 | }
|
42324 | /**
|
42325 | * Checks whether the program has changed, and invalidate static symbols in
|
42326 | * the source files that have changed.
|
42327 | * Returns true if modules are up-to-date, false otherwise.
|
42328 | * This should only be called by getAnalyzedModules().
|
42329 | */
|
42330 | upToDate() {
|
42331 | const { lastProgram, program } = this;
|
42332 | if (lastProgram === program) {
|
42333 | return true;
|
42334 | }
|
42335 | this.lastProgram = program;
|
42336 | // Even though the program has changed, it could be the case that none of
|
42337 | // the source files have changed. If all source files remain the same, then
|
42338 | // program is still up-to-date, and we should not invalidate caches.
|
42339 | let filesAdded = 0;
|
42340 | const filesChangedOrRemoved = [];
|
42341 | // Check if any source files have been added / changed since last computation.
|
42342 | const seen = new Set();
|
42343 | const ANGULAR_CORE = '@angular/core';
|
42344 | const corePath = this.reflectorHost.moduleNameToFileName(ANGULAR_CORE);
|
42345 | for (const { fileName } of program.getSourceFiles()) {
|
42346 | // If `@angular/core` is edited, the language service would have to be
|
42347 | // restarted, so ignore changes to `@angular/core`.
|
42348 | // When the StaticReflector is initialized at startup, it loads core
|
42349 | // symbols from @angular/core by calling initializeConversionMap(). This
|
42350 | // is only done once. If the file is invalidated, some of the core symbols
|
42351 | // will be lost permanently.
|
42352 | if (fileName === corePath) {
|
42353 | continue;
|
42354 | }
|
42355 | seen.add(fileName);
|
42356 | const version = this.tsLsHost.getScriptVersion(fileName);
|
42357 | const lastVersion = this.fileVersions.get(fileName);
|
42358 | if (lastVersion === undefined) {
|
42359 | filesAdded++;
|
42360 | this.fileVersions.set(fileName, version);
|
42361 | }
|
42362 | else if (version !== lastVersion) {
|
42363 | filesChangedOrRemoved.push(fileName); // changed
|
42364 | this.fileVersions.set(fileName, version);
|
42365 | }
|
42366 | }
|
42367 | // Check if any source files have been removed since last computation.
|
42368 | for (const [fileName] of this.fileVersions) {
|
42369 | if (!seen.has(fileName)) {
|
42370 | filesChangedOrRemoved.push(fileName); // removed
|
42371 | // Because Maps are iterated in insertion order, it is safe to delete
|
42372 | // entries from the same map while iterating.
|
42373 | // See https://stackoverflow.com/questions/35940216 and
|
42374 | // https://www.ecma-international.org/ecma-262/10.0/index.html#sec-map.prototype.foreach
|
42375 | this.fileVersions.delete(fileName);
|
42376 | }
|
42377 | }
|
42378 | for (const fileName of filesChangedOrRemoved) {
|
42379 | const symbols = this.staticSymbolResolver.invalidateFile(fileName);
|
42380 | this.reflector.invalidateSymbols(symbols);
|
42381 | }
|
42382 | // Program is up-to-date iff no files are added, changed, or removed.
|
42383 | return filesAdded === 0 && filesChangedOrRemoved.length === 0;
|
42384 | }
|
42385 | /**
|
42386 | * Find all templates in the specified `file`.
|
42387 | * @param fileName TS or HTML file
|
42388 | */
|
42389 | getTemplates(fileName) {
|
42390 | const results = [];
|
42391 | if (fileName.endsWith('.ts')) {
|
42392 | // Find every template string in the file
|
42393 | const visit = (child) => {
|
42394 | const template = this.getInternalTemplate(child);
|
42395 | if (template) {
|
42396 | results.push(template);
|
42397 | }
|
42398 | else {
|
42399 | tss.forEachChild(child, visit);
|
42400 | }
|
42401 | };
|
42402 | const sourceFile = this.getSourceFile(fileName);
|
42403 | if (sourceFile) {
|
42404 | tss.forEachChild(sourceFile, visit);
|
42405 | }
|
42406 | }
|
42407 | else {
|
42408 | const template = this.getExternalTemplate(fileName);
|
42409 | if (template) {
|
42410 | results.push(template);
|
42411 | }
|
42412 | }
|
42413 | return results;
|
42414 | }
|
42415 | /**
|
42416 | * Return metadata about all class declarations in the file that are Angular
|
42417 | * directives. Potential matches are `@NgModule`, `@Component`, `@Directive`,
|
42418 | * `@Pipes`, etc. class declarations.
|
42419 | *
|
42420 | * @param fileName TS file
|
42421 | */
|
42422 | getDeclarations(fileName) {
|
42423 | if (!fileName.endsWith('.ts')) {
|
42424 | return [];
|
42425 | }
|
42426 | const sourceFile = this.getSourceFile(fileName);
|
42427 | if (!sourceFile) {
|
42428 | return [];
|
42429 | }
|
42430 | const results = [];
|
42431 | const visit = (child) => {
|
42432 | const candidate = getDirectiveClassLike(child);
|
42433 | if (candidate) {
|
42434 | const { classId } = candidate;
|
42435 | const declarationSpan = spanOf$2(classId);
|
42436 | const className = classId.getText();
|
42437 | const classSymbol = this.reflector.getStaticSymbol(sourceFile.fileName, className);
|
42438 | // Ask the resolver to check if candidate is actually Angular directive
|
42439 | if (!this.resolver.isDirective(classSymbol)) {
|
42440 | return;
|
42441 | }
|
42442 | const data = this.resolver.getNonNormalizedDirectiveMetadata(classSymbol);
|
42443 | if (!data) {
|
42444 | return;
|
42445 | }
|
42446 | results.push({
|
42447 | type: classSymbol,
|
42448 | declarationSpan,
|
42449 | metadata: data.metadata,
|
42450 | errors: this.getCollectedErrors(declarationSpan, sourceFile),
|
42451 | });
|
42452 | }
|
42453 | else {
|
42454 | child.forEachChild(visit);
|
42455 | }
|
42456 | };
|
42457 | tss.forEachChild(sourceFile, visit);
|
42458 | return results;
|
42459 | }
|
42460 | getSourceFile(fileName) {
|
42461 | if (!fileName.endsWith('.ts')) {
|
42462 | throw new Error(`Non-TS source file requested: ${fileName}`);
|
42463 | }
|
42464 | return this.program.getSourceFile(fileName);
|
42465 | }
|
42466 | get program() {
|
42467 | const program = this.tsLS.getProgram();
|
42468 | if (!program) {
|
42469 | // Program is very very unlikely to be undefined.
|
42470 | throw new Error('No program in language service!');
|
42471 | }
|
42472 | return program;
|
42473 | }
|
42474 | /**
|
42475 | * Return the TemplateSource if `node` is a template node.
|
42476 | *
|
42477 | * For example,
|
42478 | *
|
42479 | * @Component({
|
42480 | * template: '<div></div>' <-- template node
|
42481 | * })
|
42482 | * class AppComponent {}
|
42483 | * ^---- class declaration node
|
42484 | *
|
42485 | * @param node Potential template node
|
42486 | */
|
42487 | getInternalTemplate(node) {
|
42488 | if (!tss.isStringLiteralLike(node)) {
|
42489 | return;
|
42490 | }
|
42491 | const tmplAsgn = getPropertyAssignmentFromValue(node, 'template');
|
42492 | if (!tmplAsgn) {
|
42493 | return;
|
42494 | }
|
42495 | const classDecl = getClassDeclFromDecoratorProp(tmplAsgn);
|
42496 | if (!classDecl || !classDecl.name) { // Does not handle anonymous class
|
42497 | return;
|
42498 | }
|
42499 | const fileName = node.getSourceFile().fileName;
|
42500 | const classSymbol = this.reflector.getStaticSymbol(fileName, classDecl.name.text);
|
42501 | return new InlineTemplate(node, classDecl, classSymbol, this);
|
42502 | }
|
42503 | /**
|
42504 | * Return the external template for `fileName`.
|
42505 | * @param fileName HTML file
|
42506 | */
|
42507 | getExternalTemplate(fileName) {
|
42508 | // First get the text for the template
|
42509 | const snapshot = this.tsLsHost.getScriptSnapshot(fileName);
|
42510 | if (!snapshot) {
|
42511 | return;
|
42512 | }
|
42513 | const source = snapshot.getText(0, snapshot.getLength());
|
42514 | // Next find the component class symbol
|
42515 | const classSymbol = this.fileToComponent.get(tss.server.toNormalizedPath(fileName));
|
42516 | if (!classSymbol) {
|
42517 | return;
|
42518 | }
|
42519 | // Then use the class symbol to find the actual ts.ClassDeclaration node
|
42520 | const sourceFile = this.getSourceFile(classSymbol.filePath);
|
42521 | if (!sourceFile) {
|
42522 | return;
|
42523 | }
|
42524 | // TODO: This only considers top-level class declarations in a source file.
|
42525 | // This would not find a class declaration in a namespace, for example.
|
42526 | const classDecl = sourceFile.forEachChild((child) => {
|
42527 | if (tss.isClassDeclaration(child) && child.name && child.name.text === classSymbol.name) {
|
42528 | return child;
|
42529 | }
|
42530 | });
|
42531 | if (!classDecl) {
|
42532 | return;
|
42533 | }
|
42534 | return new ExternalTemplate(source, fileName, classDecl, classSymbol, this);
|
42535 | }
|
42536 | collectError(error, filePath) {
|
42537 | if (filePath) {
|
42538 | let errors = this.collectedErrors.get(filePath);
|
42539 | if (!errors) {
|
42540 | errors = [];
|
42541 | this.collectedErrors.set(filePath, errors);
|
42542 | }
|
42543 | errors.push(error);
|
42544 | }
|
42545 | }
|
42546 | getCollectedErrors(defaultSpan, sourceFile) {
|
42547 | const errors = this.collectedErrors.get(sourceFile.fileName);
|
42548 | if (!errors) {
|
42549 | return [];
|
42550 | }
|
42551 | // TODO: Add better typings for the errors
|
42552 | return errors.map((e) => {
|
42553 | const line = e.line || (e.position && e.position.line);
|
42554 | const column = e.column || (e.position && e.position.column);
|
42555 | const span = spanAt$1(sourceFile, line, column) || defaultSpan;
|
42556 | if (isFormattedError(e)) {
|
42557 | return errorToDiagnosticWithChain(e, span);
|
42558 | }
|
42559 | return { message: e.message, span };
|
42560 | });
|
42561 | }
|
42562 | /**
|
42563 | * Return the parsed template for the template at the specified `position`.
|
42564 | * @param fileName TS or HTML file
|
42565 | * @param position Position of the template in the TS file, otherwise ignored.
|
42566 | */
|
42567 | getTemplateAstAtPosition(fileName, position) {
|
42568 | let template;
|
42569 | if (fileName.endsWith('.ts')) {
|
42570 | const sourceFile = this.getSourceFile(fileName);
|
42571 | if (!sourceFile) {
|
42572 | return;
|
42573 | }
|
42574 | // Find the node that most closely matches the position
|
42575 | const node = findTightestNode(sourceFile, position);
|
42576 | if (!node) {
|
42577 | return;
|
42578 | }
|
42579 | template = this.getInternalTemplate(node);
|
42580 | }
|
42581 | else {
|
42582 | template = this.getExternalTemplate(fileName);
|
42583 | }
|
42584 | if (!template) {
|
42585 | return;
|
42586 | }
|
42587 | return this.getTemplateAst(template);
|
42588 | }
|
42589 | /**
|
42590 | * Find the NgModule which the directive associated with the `classSymbol`
|
42591 | * belongs to, then return its schema and transitive directives and pipes.
|
42592 | * @param classSymbol Angular Symbol that defines a directive
|
42593 | */
|
42594 | getModuleMetadataForDirective(classSymbol) {
|
42595 | const result = {
|
42596 | directives: [],
|
42597 | pipes: [],
|
42598 | schemas: [],
|
42599 | };
|
42600 | // First find which NgModule the directive belongs to.
|
42601 | const ngModule = this.analyzedModules.ngModuleByPipeOrDirective.get(classSymbol) ||
|
42602 | findSuitableDefaultModule(this.analyzedModules);
|
42603 | if (!ngModule) {
|
42604 | return result;
|
42605 | }
|
42606 | // Then gather all transitive directives and pipes.
|
42607 | const { directives, pipes } = ngModule.transitiveModule;
|
42608 | for (const directive of directives) {
|
42609 | const data = this.resolver.getNonNormalizedDirectiveMetadata(directive.reference);
|
42610 | if (data) {
|
42611 | result.directives.push(data.metadata.toSummary());
|
42612 | }
|
42613 | }
|
42614 | for (const pipe of pipes) {
|
42615 | const metadata = this.resolver.getOrLoadPipeMetadata(pipe.reference);
|
42616 | result.pipes.push(metadata.toSummary());
|
42617 | }
|
42618 | result.schemas.push(...ngModule.schemas);
|
42619 | return result;
|
42620 | }
|
42621 | /**
|
42622 | * Parse the `template` and return its AST, if any.
|
42623 | * @param template template to be parsed
|
42624 | */
|
42625 | getTemplateAst(template) {
|
42626 | const { type: classSymbol, fileName } = template;
|
42627 | const data = this.resolver.getNonNormalizedDirectiveMetadata(classSymbol);
|
42628 | if (!data) {
|
42629 | return;
|
42630 | }
|
42631 | const htmlParser = new HtmlParser();
|
42632 | const expressionParser = new Parser$1(new Lexer());
|
42633 | const parser = new TemplateParser(new CompilerConfig(), this.reflector, expressionParser, new DomElementSchemaRegistry(), htmlParser, null, // console
|
42634 | [] // tranforms
|
42635 | );
|
42636 | const htmlResult = htmlParser.parse(template.source, fileName, {
|
42637 | tokenizeExpansionForms: true,
|
42638 | preserveLineEndings: true,
|
42639 | });
|
42640 | const { directives, pipes, schemas } = this.getModuleMetadataForDirective(classSymbol);
|
42641 | const parseResult = parser.tryParseHtml(htmlResult, data.metadata, directives, pipes, schemas);
|
42642 | if (!parseResult.templateAst) {
|
42643 | return;
|
42644 | }
|
42645 | return {
|
42646 | htmlAst: htmlResult.rootNodes,
|
42647 | templateAst: parseResult.templateAst,
|
42648 | directive: data.metadata,
|
42649 | directives,
|
42650 | pipes,
|
42651 | parseErrors: parseResult.errors,
|
42652 | expressionParser,
|
42653 | template,
|
42654 | };
|
42655 | }
|
42656 | /**
|
42657 | * Log the specified `msg` to file at INFO level. If logging is not enabled
|
42658 | * this method is a no-op.
|
42659 | * @param msg Log message
|
42660 | */
|
42661 | log(msg) {
|
42662 | if (this.tsLsHost.log) {
|
42663 | this.tsLsHost.log(msg);
|
42664 | }
|
42665 | }
|
42666 | /**
|
42667 | * Log the specified `msg` to file at ERROR level. If logging is not enabled
|
42668 | * this method is a no-op.
|
42669 | * @param msg error message
|
42670 | */
|
42671 | error(msg) {
|
42672 | if (this.tsLsHost.error) {
|
42673 | this.tsLsHost.error(msg);
|
42674 | }
|
42675 | }
|
42676 | /**
|
42677 | * Log debugging info to file at INFO level, only if verbose setting is turned
|
42678 | * on. Otherwise, this method is a no-op.
|
42679 | * @param msg debugging message
|
42680 | */
|
42681 | debug(msg) {
|
42682 | const project = this.tsLsHost;
|
42683 | if (!project.projectService) {
|
42684 | // tsLsHost is not a Project
|
42685 | return;
|
42686 | }
|
42687 | const { logger } = project.projectService;
|
42688 | if (logger.hasLevel(tss.server.LogLevel.verbose)) {
|
42689 | logger.info(msg);
|
42690 | }
|
42691 | }
|
42692 | }
|
42693 | function findSuitableDefaultModule(modules) {
|
42694 | let result = undefined;
|
42695 | let resultSize = 0;
|
42696 | for (const module of modules.ngModules) {
|
42697 | const moduleSize = module.transitiveModule.directives.length;
|
42698 | if (moduleSize > resultSize) {
|
42699 | result = module;
|
42700 | resultSize = moduleSize;
|
42701 | }
|
42702 | }
|
42703 | return result;
|
42704 | }
|
42705 | function spanOf$2(node) {
|
42706 | return { start: node.getStart(), end: node.getEnd() };
|
42707 | }
|
42708 | function spanAt$1(sourceFile, line, column) {
|
42709 | if (line != null && column != null) {
|
42710 | const position = tss.getPositionOfLineAndCharacter(sourceFile, line, column);
|
42711 | const findChild = function findChild(node) {
|
42712 | if (node.kind > tss.SyntaxKind.LastToken && node.pos <= position && node.end > position) {
|
42713 | const betterNode = tss.forEachChild(node, findChild);
|
42714 | return betterNode || node;
|
42715 | }
|
42716 | };
|
42717 | const node = tss.forEachChild(sourceFile, findChild);
|
42718 | if (node) {
|
42719 | return { start: node.getStart(), end: node.getEnd() };
|
42720 | }
|
42721 | }
|
42722 | }
|
42723 | function convertChain(chain) {
|
42724 | return { message: chain.message, next: chain.next ? chain.next.map(convertChain) : undefined };
|
42725 | }
|
42726 | function errorToDiagnosticWithChain(error, span) {
|
42727 | return { message: error.chain ? convertChain(error.chain) : error.message, span };
|
42728 | }
|
42729 |
|
42730 | /**
|
42731 | * @license
|
42732 | * Copyright Google LLC All Rights Reserved.
|
42733 | *
|
42734 | * Use of this source code is governed by an MIT-style license that can be
|
42735 | * found in the LICENSE file at https://angular.io/license
|
42736 | */
|
42737 | // Use a WeakMap to keep track of Project to Host mapping so that when Project
|
42738 | // is deleted Host could be garbage collected.
|
42739 | const PROJECT_MAP = new WeakMap();
|
42740 | /**
|
42741 | * This function is called by tsserver to retrieve the external (non-TS) files
|
42742 | * that should belong to the specified `project`. For Angular, these files are
|
42743 | * external templates. This is called once when the project is loaded, then
|
42744 | * every time when the program is updated.
|
42745 | * @param project Project for which external files should be retrieved.
|
42746 | */
|
42747 | function getExternalFiles(project) {
|
42748 | if (!project.hasRoots()) {
|
42749 | // During project initialization where there is no root files yet we should
|
42750 | // not do any work.
|
42751 | return [];
|
42752 | }
|
42753 | const ngLsHost = PROJECT_MAP.get(project);
|
42754 | if (ngLsHost === undefined) {
|
42755 | return [];
|
42756 | }
|
42757 | ngLsHost.getAnalyzedModules();
|
42758 | return ngLsHost.getExternalTemplates().filter(fileName => {
|
42759 | // TODO(kyliau): Remove this when the following PR lands on the version of
|
42760 | // TypeScript used in this repo.
|
42761 | // https://github.com/microsoft/TypeScript/pull/41737
|
42762 | return project.fileExists(fileName);
|
42763 | });
|
42764 | }
|
42765 | function create(info) {
|
42766 | const { languageService: tsLS, languageServiceHost: tsLSHost, config, project } = info;
|
42767 | // This plugin could operate under two different modes:
|
42768 | // 1. TS + Angular
|
42769 | // Plugin augments TS language service to provide additional Angular
|
42770 | // information. This only works with inline templates and is meant to be
|
42771 | // used as a local plugin (configured via tsconfig.json)
|
42772 | // 2. Angular only
|
42773 | // Plugin only provides information on Angular templates, no TS info at all.
|
42774 | // This effectively disables native TS features and is meant for internal
|
42775 | // use only.
|
42776 | const angularOnly = config ? config.angularOnly === true : false;
|
42777 | const ngLSHost = new TypeScriptServiceHost(tsLSHost, tsLS);
|
42778 | const ngLS = createLanguageService(ngLSHost);
|
42779 | PROJECT_MAP.set(project, ngLSHost);
|
42780 | function getCompletionsAtPosition(fileName, position, options) {
|
42781 | if (!angularOnly) {
|
42782 | const results = tsLS.getCompletionsAtPosition(fileName, position, options);
|
42783 | if (results && results.entries.length) {
|
42784 | // If TS could answer the query, then return results immediately.
|
42785 | return results;
|
42786 | }
|
42787 | }
|
42788 | return ngLS.getCompletionsAtPosition(fileName, position, options);
|
42789 | }
|
42790 | function getQuickInfoAtPosition(fileName, position) {
|
42791 | if (!angularOnly) {
|
42792 | const result = tsLS.getQuickInfoAtPosition(fileName, position);
|
42793 | if (result) {
|
42794 | // If TS could answer the query, then return results immediately.
|
42795 | return result;
|
42796 | }
|
42797 | }
|
42798 | return ngLS.getQuickInfoAtPosition(fileName, position);
|
42799 | }
|
42800 | function getSemanticDiagnostics(fileName) {
|
42801 | const results = [];
|
42802 | if (!angularOnly) {
|
42803 | results.push(...tsLS.getSemanticDiagnostics(fileName));
|
42804 | }
|
42805 | // For semantic diagnostics we need to combine both TS + Angular results
|
42806 | results.push(...ngLS.getSemanticDiagnostics(fileName));
|
42807 | return results;
|
42808 | }
|
42809 | function getDefinitionAtPosition(fileName, position) {
|
42810 | if (!angularOnly) {
|
42811 | const results = tsLS.getDefinitionAtPosition(fileName, position);
|
42812 | if (results) {
|
42813 | // If TS could answer the query, then return results immediately.
|
42814 | return results;
|
42815 | }
|
42816 | }
|
42817 | const result = ngLS.getDefinitionAndBoundSpan(fileName, position);
|
42818 | if (!result || !result.definitions || !result.definitions.length) {
|
42819 | return;
|
42820 | }
|
42821 | return result.definitions;
|
42822 | }
|
42823 | function getDefinitionAndBoundSpan(fileName, position) {
|
42824 | if (!angularOnly) {
|
42825 | const result = tsLS.getDefinitionAndBoundSpan(fileName, position);
|
42826 | if (result) {
|
42827 | // If TS could answer the query, then return results immediately.
|
42828 | return result;
|
42829 | }
|
42830 | }
|
42831 | return ngLS.getDefinitionAndBoundSpan(fileName, position);
|
42832 | }
|
42833 | function getTypeDefinitionAtPosition(fileName, position) {
|
42834 | // Not implemented in VE Language Service
|
42835 | return undefined;
|
42836 | }
|
42837 | function getReferencesAtPosition(fileName, position) {
|
42838 | // Not implemented in VE Language Service
|
42839 | return undefined;
|
42840 | }
|
42841 | function findRenameLocations(fileName, position, findInStrings, findInComments, providePrefixAndSuffixTextForRename) {
|
42842 | // not implemented in VE Language Service
|
42843 | return undefined;
|
42844 | }
|
42845 | function getTcb(fileName, position) {
|
42846 | // Not implemented in VE Language Service
|
42847 | return undefined;
|
42848 | }
|
42849 | return Object.assign(Object.assign({}, tsLS), {
|
42850 | // Then override the methods supported by Angular language service
|
42851 | getCompletionsAtPosition,
|
42852 | getQuickInfoAtPosition,
|
42853 | getSemanticDiagnostics,
|
42854 | getDefinitionAtPosition,
|
42855 | getDefinitionAndBoundSpan,
|
42856 | getTypeDefinitionAtPosition,
|
42857 | getReferencesAtPosition,
|
42858 | findRenameLocations,
|
42859 | getTcb });
|
42860 | }
|
42861 |
|
42862 | exports.create = create;
|
42863 | exports.getExternalFiles = getExternalFiles;
|
42864 |
|
42865 | Object.defineProperty(exports, '__esModule', { value: true });
|
42866 |
|
42867 | });
|
42868 | //# sourceMappingURL=language-service.js.map
|