@angular/language-service 11.1.0 → 11.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bundles/ivy.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Angular v11.1.0
2
+ * @license Angular v11.1.1
3
3
  * Copyright Google LLC All Rights Reserved.
4
4
  * License: MIT
5
5
  */
@@ -11543,13 +11543,25 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
11543
11543
  get currentAbsoluteOffset() {
11544
11544
  return this.absoluteOffset + this.inputIndex;
11545
11545
  }
11546
- span(start) {
11547
- return new ParseSpan(start, this.currentEndIndex);
11546
+ /**
11547
+ * Retrieve a `ParseSpan` from `start` to the current position (or to `artificialEndIndex` if
11548
+ * provided).
11549
+ *
11550
+ * @param start Position from which the `ParseSpan` will start.
11551
+ * @param artificialEndIndex Optional ending index to be used if provided (and if greater than the
11552
+ * natural ending index)
11553
+ */
11554
+ span(start, artificialEndIndex) {
11555
+ let endIndex = this.currentEndIndex;
11556
+ if (artificialEndIndex !== undefined && artificialEndIndex > this.currentEndIndex) {
11557
+ endIndex = artificialEndIndex;
11558
+ }
11559
+ return new ParseSpan(start, endIndex);
11548
11560
  }
11549
- sourceSpan(start) {
11550
- const serial = `${start}@${this.inputIndex}`;
11561
+ sourceSpan(start, artificialEndIndex) {
11562
+ const serial = `${start}@${this.inputIndex}:${artificialEndIndex}`;
11551
11563
  if (!this.sourceSpanCache.has(serial)) {
11552
- this.sourceSpanCache.set(serial, this.span(start).toAbsolute(this.absoluteOffset));
11564
+ this.sourceSpanCache.set(serial, this.span(start, artificialEndIndex).toAbsolute(this.absoluteOffset));
11553
11565
  }
11554
11566
  return this.sourceSpanCache.get(serial);
11555
11567
  }
@@ -11612,7 +11624,7 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
11612
11624
  const n = this.next;
11613
11625
  if (!n.isIdentifier() && !n.isKeyword()) {
11614
11626
  this.error(`Unexpected ${this.prettyPrintToken(n)}, expected identifier or keyword`);
11615
- return '';
11627
+ return null;
11616
11628
  }
11617
11629
  this.advance();
11618
11630
  return n.toString();
@@ -11657,15 +11669,36 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
11657
11669
  }
11658
11670
  do {
11659
11671
  const nameStart = this.inputIndex;
11660
- const name = this.expectIdentifierOrKeyword();
11661
- const nameSpan = this.sourceSpan(nameStart);
11672
+ let nameId = this.expectIdentifierOrKeyword();
11673
+ let nameSpan;
11674
+ let fullSpanEnd = undefined;
11675
+ if (nameId !== null) {
11676
+ nameSpan = this.sourceSpan(nameStart);
11677
+ }
11678
+ else {
11679
+ // No valid identifier was found, so we'll assume an empty pipe name ('').
11680
+ nameId = '';
11681
+ // However, there may have been whitespace present between the pipe character and the next
11682
+ // token in the sequence (or the end of input). We want to track this whitespace so that
11683
+ // the `BindingPipe` we produce covers not just the pipe character, but any trailing
11684
+ // whitespace beyond it. Another way of thinking about this is that the zero-length name
11685
+ // is assumed to be at the end of any whitespace beyond the pipe character.
11686
+ //
11687
+ // Therefore, we push the end of the `ParseSpan` for this pipe all the way up to the
11688
+ // beginning of the next token, or until the end of input if the next token is EOF.
11689
+ fullSpanEnd = this.next.index !== -1 ? this.next.index : this.inputLength + this.offset;
11690
+ // The `nameSpan` for an empty pipe name is zero-length at the end of any whitespace
11691
+ // beyond the pipe character.
11692
+ nameSpan = new ParseSpan(fullSpanEnd, fullSpanEnd).toAbsolute(this.absoluteOffset);
11693
+ }
11662
11694
  const args = [];
11663
11695
  while (this.consumeOptionalCharacter($COLON)) {
11664
11696
  args.push(this.parseExpression());
11697
+ // If there are additional expressions beyond the name, then the artificial end for the
11698
+ // name is no longer relevant.
11665
11699
  }
11666
11700
  const { start } = result.span;
11667
- result =
11668
- new BindingPipe(this.span(start), this.sourceSpan(start), result, name, args, nameSpan);
11701
+ result = new BindingPipe(this.span(start), this.sourceSpan(start, fullSpanEnd), result, nameId, args, nameSpan);
11669
11702
  } while (this.consumeOptionalOperator('|'));
11670
11703
  }
11671
11704
  return result;
@@ -11954,7 +11987,8 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
11954
11987
  const start = receiver.span.start;
11955
11988
  const nameStart = this.inputIndex;
11956
11989
  const id = this.withContext(ParseContextFlags.Writable, () => {
11957
- const id = this.expectIdentifierOrKeyword();
11990
+ var _a;
11991
+ const id = (_a = this.expectIdentifierOrKeyword()) !== null && _a !== void 0 ? _a : '';
11958
11992
  if (id.length === 0) {
11959
11993
  this.error(`Expected identifier for property access`, receiver.span.end);
11960
11994
  }
@@ -16974,7 +17008,7 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
16974
17008
  * Use of this source code is governed by an MIT-style license that can be
16975
17009
  * found in the LICENSE file at https://angular.io/license
16976
17010
  */
16977
- const VERSION$1 = new Version('11.1.0');
17011
+ const VERSION$1 = new Version('11.1.1');
16978
17012
 
16979
17013
  /**
16980
17014
  * @license
@@ -17631,7 +17665,7 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
17631
17665
  */
17632
17666
  function createDirectiveDefinitionMap(meta) {
17633
17667
  const definitionMap = new DefinitionMap();
17634
- definitionMap.set('version', literal('11.1.0'));
17668
+ definitionMap.set('version', literal('11.1.1'));
17635
17669
  // e.g. `type: MyDirective`
17636
17670
  definitionMap.set('type', meta.internalType);
17637
17671
  // e.g. `selector: 'some-dir'`
@@ -21079,7 +21113,7 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
21079
21113
  * Use of this source code is governed by an MIT-style license that can be
21080
21114
  * found in the LICENSE file at https://angular.io/license
21081
21115
  */
21082
- const VERSION$2 = new Version('11.1.0');
21116
+ const VERSION$2 = new Version('11.1.1');
21083
21117
 
21084
21118
  /**
21085
21119
  * @license
@@ -21271,6 +21305,14 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
21271
21305
  * An injectable already has a `ɵprov` property.
21272
21306
  */
21273
21307
  ErrorCode[ErrorCode["INJECTABLE_DUPLICATE_PROV"] = 9001] = "INJECTABLE_DUPLICATE_PROV";
21308
+ // 10XXX error codes are reserved for diagnostics with category
21309
+ // `ts.DiagnosticCategory.Suggestion`. These diagnostics are generated by
21310
+ // language service.
21311
+ /**
21312
+ * Suggest users to enable `strictTemplates` to make use of full capabilities
21313
+ * provided by Angular language service.
21314
+ */
21315
+ ErrorCode[ErrorCode["SUGGEST_STRICT_TEMPLATES"] = 10001] = "SUGGEST_STRICT_TEMPLATES";
21274
21316
  })(ErrorCode || (ErrorCode = {}));
21275
21317
  /**
21276
21318
  * @internal
@@ -25445,6 +25487,18 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
25445
25487
  }
25446
25488
  }
25447
25489
  }
25490
+ updateResources(clazz) {
25491
+ if (!this.reflector.isClass(clazz) || !this.classes.has(clazz)) {
25492
+ return;
25493
+ }
25494
+ const record = this.classes.get(clazz);
25495
+ for (const trait of record.traits) {
25496
+ if (trait.state !== TraitState.Resolved || trait.handler.updateResources === undefined) {
25497
+ continue;
25498
+ }
25499
+ trait.handler.updateResources(clazz, trait.analysis, trait.resolution);
25500
+ }
25501
+ }
25448
25502
  compile(clazz, constantPool) {
25449
25503
  const original = ts$1.getOriginalNode(clazz);
25450
25504
  if (!this.reflector.isClass(clazz) || !this.reflector.isClass(original) ||
@@ -28584,24 +28638,13 @@ Either add the @Injectable() decorator to '${provider.node.name
28584
28638
  template = preanalyzed;
28585
28639
  }
28586
28640
  else {
28587
- // The template was not already parsed. Either there's a templateUrl, or an inline template.
28588
- if (component.has('templateUrl')) {
28589
- const templateUrlExpr = component.get('templateUrl');
28590
- const templateUrl = this.evaluator.evaluate(templateUrlExpr);
28591
- if (typeof templateUrl !== 'string') {
28592
- throw createValueHasWrongTypeError(templateUrlExpr, templateUrl, 'templateUrl must be a string');
28593
- }
28594
- const resourceUrl = this.resourceLoader.resolve(templateUrl, containingFile);
28595
- template = this._extractExternalTemplate(node, component, templateUrlExpr, resourceUrl);
28596
- }
28597
- else {
28598
- // Expect an inline template to be present.
28599
- template = this._extractInlineTemplate(node, decorator, component, containingFile);
28600
- }
28641
+ const templateDecl = this.parseTemplateDeclaration(decorator, component, containingFile);
28642
+ template = this.extractTemplate(node, templateDecl);
28601
28643
  }
28602
- const templateResource = template.isInline ?
28603
- { path: null, expression: component.get('template') } :
28604
- { path: absoluteFrom(template.templateUrl), expression: template.sourceMapping.node };
28644
+ const templateResource = template.isInline ? { path: null, expression: component.get('template') } : {
28645
+ path: absoluteFrom(template.declaration.resolvedTemplateUrl),
28646
+ expression: template.sourceMapping.node
28647
+ };
28605
28648
  // Figure out the set of styles. The ordering here is important: external resources (styleUrls)
28606
28649
  // precede inline styles, and styles defined in the template override styles defined in the
28607
28650
  // component.
@@ -28621,9 +28664,11 @@ Either add the @Injectable() decorator to '${provider.node.name
28621
28664
  }
28622
28665
  }
28623
28666
  }
28667
+ let inlineStyles = null;
28624
28668
  if (component.has('styles')) {
28625
28669
  const litStyles = parseFieldArrayValue(component, 'styles', this.evaluator);
28626
28670
  if (litStyles !== null) {
28671
+ inlineStyles = [...litStyles];
28627
28672
  if (styles === null) {
28628
28673
  styles = litStyles;
28629
28674
  }
@@ -28663,6 +28708,8 @@ Either add the @Injectable() decorator to '${provider.node.name
28663
28708
  template,
28664
28709
  providersRequiringFactory,
28665
28710
  viewProvidersRequiringFactory,
28711
+ inlineStyles,
28712
+ styleUrls,
28666
28713
  resources: {
28667
28714
  styles: styleResources,
28668
28715
  template: templateResource,
@@ -28831,6 +28878,33 @@ Either add the @Injectable() decorator to '${provider.node.name
28831
28878
  }
28832
28879
  return { data };
28833
28880
  }
28881
+ updateResources(node, analysis) {
28882
+ const containingFile = node.getSourceFile().fileName;
28883
+ // If the template is external, re-parse it.
28884
+ const templateDecl = analysis.template.declaration;
28885
+ if (!templateDecl.isInline) {
28886
+ analysis.template = this.extractTemplate(node, templateDecl);
28887
+ }
28888
+ // Update any external stylesheets and rebuild the combined 'styles' list.
28889
+ // TODO(alxhub): write tests for styles when the primary compiler uses the updateResources path
28890
+ let styles = [];
28891
+ if (analysis.styleUrls !== null) {
28892
+ for (const styleUrl of analysis.styleUrls) {
28893
+ const resolvedStyleUrl = this.resourceLoader.resolve(styleUrl, containingFile);
28894
+ const styleText = this.resourceLoader.load(resolvedStyleUrl);
28895
+ styles.push(styleText);
28896
+ }
28897
+ }
28898
+ if (analysis.inlineStyles !== null) {
28899
+ for (const styleText of analysis.inlineStyles) {
28900
+ styles.push(styleText);
28901
+ }
28902
+ }
28903
+ for (const styleText of analysis.template.styles) {
28904
+ styles.push(styleText);
28905
+ }
28906
+ analysis.meta.styles = styles;
28907
+ }
28834
28908
  compileFull(node, analysis, resolution, pool) {
28835
28909
  if (analysis.template.errors !== null && analysis.template.errors.length > 0) {
28836
28910
  return [];
@@ -28937,7 +29011,8 @@ Either add the @Injectable() decorator to '${provider.node.name
28937
29011
  // URLs to resolve.
28938
29012
  if (templatePromise !== undefined) {
28939
29013
  return templatePromise.then(() => {
28940
- const template = this._extractExternalTemplate(node, component, templateUrlExpr, resourceUrl);
29014
+ const templateDecl = this.parseTemplateDeclaration(decorator, component, containingFile);
29015
+ const template = this.extractTemplate(node, templateDecl);
28941
29016
  this.preanalyzeTemplateCache.set(node, template);
28942
29017
  return template;
28943
29018
  });
@@ -28947,100 +29022,78 @@ Either add the @Injectable() decorator to '${provider.node.name
28947
29022
  }
28948
29023
  }
28949
29024
  else {
28950
- const template = this._extractInlineTemplate(node, decorator, component, containingFile);
29025
+ const templateDecl = this.parseTemplateDeclaration(decorator, component, containingFile);
29026
+ const template = this.extractTemplate(node, templateDecl);
28951
29027
  this.preanalyzeTemplateCache.set(node, template);
28952
29028
  return Promise.resolve(template);
28953
29029
  }
28954
29030
  }
28955
- _extractExternalTemplate(node, component, templateUrlExpr, resourceUrl) {
28956
- const templateStr = this.resourceLoader.load(resourceUrl);
28957
- if (this.depTracker !== null) {
28958
- this.depTracker.addResourceDependency(node.getSourceFile(), absoluteFrom(resourceUrl));
28959
- }
28960
- const template = this._parseTemplate(component, templateStr, /* templateLiteral */ null, sourceMapUrl(resourceUrl),
28961
- /* templateRange */ undefined,
28962
- /* escapedString */ false);
28963
- return Object.assign(Object.assign({}, template), { sourceMapping: {
28964
- type: 'external',
28965
- componentClass: node,
28966
- node: templateUrlExpr,
28967
- template: templateStr,
28968
- templateUrl: resourceUrl,
28969
- } });
28970
- }
28971
- _extractInlineTemplate(node, decorator, component, containingFile) {
28972
- if (!component.has('template')) {
28973
- throw new FatalDiagnosticError(ErrorCode.COMPONENT_MISSING_TEMPLATE, Decorator.nodeForError(decorator), 'component is missing a template');
28974
- }
28975
- const templateExpr = component.get('template');
28976
- let templateStr;
28977
- let templateLiteral = null;
28978
- let templateUrl = '';
28979
- let templateRange = undefined;
28980
- let sourceMapping;
28981
- let escapedString = false;
28982
- // We only support SourceMaps for inline templates that are simple string literals.
28983
- if (ts$1.isStringLiteral(templateExpr) || ts$1.isNoSubstitutionTemplateLiteral(templateExpr)) {
28984
- // the start and end of the `templateExpr` node includes the quotation marks, which we
28985
- // must
28986
- // strip
28987
- templateRange = getTemplateRange(templateExpr);
28988
- templateStr = templateExpr.getSourceFile().text;
28989
- templateLiteral = templateExpr;
28990
- templateUrl = containingFile;
28991
- escapedString = true;
28992
- sourceMapping = {
28993
- type: 'direct',
28994
- node: templateExpr,
28995
- };
28996
- }
28997
- else {
28998
- const resolvedTemplate = this.evaluator.evaluate(templateExpr);
28999
- if (typeof resolvedTemplate !== 'string') {
29000
- throw createValueHasWrongTypeError(templateExpr, resolvedTemplate, 'template must be a string');
29001
- }
29002
- templateStr = resolvedTemplate;
29003
- sourceMapping = {
29004
- type: 'indirect',
29005
- node: templateExpr,
29006
- componentClass: node,
29007
- template: templateStr,
29008
- };
29009
- }
29010
- const template = this._parseTemplate(component, templateStr, templateLiteral, templateUrl, templateRange, escapedString);
29011
- return Object.assign(Object.assign({}, template), { sourceMapping });
29012
- }
29013
- _parseTemplate(component, templateStr, templateLiteral, templateUrl, templateRange, escapedString) {
29014
- let preserveWhitespaces = this.defaultPreserveWhitespaces;
29015
- if (component.has('preserveWhitespaces')) {
29016
- const expr = component.get('preserveWhitespaces');
29017
- const value = this.evaluator.evaluate(expr);
29018
- if (typeof value !== 'boolean') {
29019
- throw createValueHasWrongTypeError(expr, value, 'preserveWhitespaces must be a boolean');
29031
+ extractTemplate(node, template) {
29032
+ if (template.isInline) {
29033
+ let templateStr;
29034
+ let templateLiteral = null;
29035
+ let templateUrl = '';
29036
+ let templateRange = null;
29037
+ let sourceMapping;
29038
+ let escapedString = false;
29039
+ // We only support SourceMaps for inline templates that are simple string literals.
29040
+ if (ts$1.isStringLiteral(template.expression) ||
29041
+ ts$1.isNoSubstitutionTemplateLiteral(template.expression)) {
29042
+ // the start and end of the `templateExpr` node includes the quotation marks, which we must
29043
+ // strip
29044
+ templateRange = getTemplateRange(template.expression);
29045
+ templateStr = template.expression.getSourceFile().text;
29046
+ templateLiteral = template.expression;
29047
+ templateUrl = template.templateUrl;
29048
+ escapedString = true;
29049
+ sourceMapping = {
29050
+ type: 'direct',
29051
+ node: template.expression,
29052
+ };
29020
29053
  }
29021
- preserveWhitespaces = value;
29022
- }
29023
- let interpolationConfig = DEFAULT_INTERPOLATION_CONFIG;
29024
- if (component.has('interpolation')) {
29025
- const expr = component.get('interpolation');
29026
- const value = this.evaluator.evaluate(expr);
29027
- if (!Array.isArray(value) || value.length !== 2 ||
29028
- !value.every(element => typeof element === 'string')) {
29029
- throw createValueHasWrongTypeError(expr, value, 'interpolation must be an array with 2 elements of string type');
29054
+ else {
29055
+ const resolvedTemplate = this.evaluator.evaluate(template.expression);
29056
+ if (typeof resolvedTemplate !== 'string') {
29057
+ throw createValueHasWrongTypeError(template.expression, resolvedTemplate, 'template must be a string');
29058
+ }
29059
+ templateStr = resolvedTemplate;
29060
+ sourceMapping = {
29061
+ type: 'indirect',
29062
+ node: template.expression,
29063
+ componentClass: node,
29064
+ template: templateStr,
29065
+ };
29030
29066
  }
29031
- interpolationConfig = InterpolationConfig.fromArray(value);
29067
+ return Object.assign(Object.assign({}, this._parseTemplate(template, templateStr, templateRange, escapedString)), { sourceMapping, declaration: template });
29032
29068
  }
29069
+ else {
29070
+ const templateStr = this.resourceLoader.load(template.resolvedTemplateUrl);
29071
+ if (this.depTracker !== null) {
29072
+ this.depTracker.addResourceDependency(node.getSourceFile(), absoluteFrom(template.resolvedTemplateUrl));
29073
+ }
29074
+ return Object.assign(Object.assign({}, this._parseTemplate(template, templateStr, /* templateRange */ null,
29075
+ /* escapedString */ false)), { sourceMapping: {
29076
+ type: 'external',
29077
+ componentClass: node,
29078
+ // TODO(alxhub): TS in g3 is unable to make this inference on its own, so cast it here
29079
+ // until g3 is able to figure this out.
29080
+ node: template.templateUrlExpression,
29081
+ template: templateStr,
29082
+ templateUrl: template.resolvedTemplateUrl,
29083
+ }, declaration: template });
29084
+ }
29085
+ }
29086
+ _parseTemplate(template, templateStr, templateRange, escapedString) {
29033
29087
  // We always normalize line endings if the template has been escaped (i.e. is inline).
29034
29088
  const i18nNormalizeLineEndingsInICUs = escapedString || this.i18nNormalizeLineEndingsInICUs;
29035
- const isInline = component.has('template');
29036
- const parsedTemplate = parseTemplate(templateStr, templateUrl, {
29037
- preserveWhitespaces,
29038
- interpolationConfig,
29039
- range: templateRange,
29089
+ const parsedTemplate = parseTemplate(templateStr, template.sourceMapUrl, {
29090
+ preserveWhitespaces: template.preserveWhitespaces,
29091
+ interpolationConfig: template.interpolationConfig,
29092
+ range: templateRange !== null && templateRange !== void 0 ? templateRange : undefined,
29040
29093
  escapedString,
29041
29094
  enableI18nLegacyMessageIdFormat: this.enableI18nLegacyMessageIdFormat,
29042
29095
  i18nNormalizeLineEndingsInICUs,
29043
- isInline,
29096
+ isInline: template.isInline,
29044
29097
  });
29045
29098
  // Unfortunately, the primary parse of the template above may not contain accurate source map
29046
29099
  // information. If used directly, it would result in incorrect code locations in template
@@ -29054,18 +29107,69 @@ Either add the @Injectable() decorator to '${provider.node.name
29054
29107
  //
29055
29108
  // In order to guarantee the correctness of diagnostics, templates are parsed a second time
29056
29109
  // with the above options set to preserve source mappings.
29057
- const { nodes: diagNodes } = parseTemplate(templateStr, templateUrl, {
29110
+ const { nodes: diagNodes } = parseTemplate(templateStr, template.sourceMapUrl, {
29058
29111
  preserveWhitespaces: true,
29059
- interpolationConfig,
29060
- range: templateRange,
29112
+ interpolationConfig: template.interpolationConfig,
29113
+ range: templateRange !== null && templateRange !== void 0 ? templateRange : undefined,
29061
29114
  escapedString,
29062
29115
  enableI18nLegacyMessageIdFormat: this.enableI18nLegacyMessageIdFormat,
29063
29116
  i18nNormalizeLineEndingsInICUs,
29064
29117
  leadingTriviaChars: [],
29065
- isInline,
29118
+ isInline: template.isInline,
29066
29119
  });
29067
- return Object.assign(Object.assign({}, parsedTemplate), { diagNodes, template: templateLiteral !== null ? new WrappedNodeExpr(templateLiteral) : templateStr, templateUrl,
29068
- isInline, file: new ParseSourceFile(templateStr, templateUrl) });
29120
+ return Object.assign(Object.assign({}, parsedTemplate), { diagNodes, template: template.isInline ? new WrappedNodeExpr(template.expression) : templateStr, templateUrl: template.resolvedTemplateUrl, isInline: template.isInline, file: new ParseSourceFile(templateStr, template.resolvedTemplateUrl) });
29121
+ }
29122
+ parseTemplateDeclaration(decorator, component, containingFile) {
29123
+ let preserveWhitespaces = this.defaultPreserveWhitespaces;
29124
+ if (component.has('preserveWhitespaces')) {
29125
+ const expr = component.get('preserveWhitespaces');
29126
+ const value = this.evaluator.evaluate(expr);
29127
+ if (typeof value !== 'boolean') {
29128
+ throw createValueHasWrongTypeError(expr, value, 'preserveWhitespaces must be a boolean');
29129
+ }
29130
+ preserveWhitespaces = value;
29131
+ }
29132
+ let interpolationConfig = DEFAULT_INTERPOLATION_CONFIG;
29133
+ if (component.has('interpolation')) {
29134
+ const expr = component.get('interpolation');
29135
+ const value = this.evaluator.evaluate(expr);
29136
+ if (!Array.isArray(value) || value.length !== 2 ||
29137
+ !value.every(element => typeof element === 'string')) {
29138
+ throw createValueHasWrongTypeError(expr, value, 'interpolation must be an array with 2 elements of string type');
29139
+ }
29140
+ interpolationConfig = InterpolationConfig.fromArray(value);
29141
+ }
29142
+ if (component.has('templateUrl')) {
29143
+ const templateUrlExpr = component.get('templateUrl');
29144
+ const templateUrl = this.evaluator.evaluate(templateUrlExpr);
29145
+ if (typeof templateUrl !== 'string') {
29146
+ throw createValueHasWrongTypeError(templateUrlExpr, templateUrl, 'templateUrl must be a string');
29147
+ }
29148
+ const resourceUrl = this.resourceLoader.resolve(templateUrl, containingFile);
29149
+ return {
29150
+ isInline: false,
29151
+ interpolationConfig,
29152
+ preserveWhitespaces,
29153
+ templateUrl,
29154
+ templateUrlExpression: templateUrlExpr,
29155
+ resolvedTemplateUrl: resourceUrl,
29156
+ sourceMapUrl: sourceMapUrl(resourceUrl),
29157
+ };
29158
+ }
29159
+ else if (component.has('template')) {
29160
+ return {
29161
+ isInline: true,
29162
+ interpolationConfig,
29163
+ preserveWhitespaces,
29164
+ expression: component.get('template'),
29165
+ templateUrl: containingFile,
29166
+ resolvedTemplateUrl: containingFile,
29167
+ sourceMapUrl: containingFile,
29168
+ };
29169
+ }
29170
+ else {
29171
+ throw new FatalDiagnosticError(ErrorCode.COMPONENT_MISSING_TEMPLATE, Decorator.nodeForError(decorator), 'component is missing a template');
29172
+ }
29069
29173
  }
29070
29174
  _expressionToImportedFile(expr, origin) {
29071
29175
  if (!(expr instanceof ExternalExpr)) {
@@ -31308,6 +31412,12 @@ Either add the @Injectable() decorator to '${provider.node.name
31308
31412
  this.cache.set(resolvedUrl, result);
31309
31413
  return result;
31310
31414
  }
31415
+ /**
31416
+ * Invalidate the entire resource cache.
31417
+ */
31418
+ invalidate() {
31419
+ this.cache.clear();
31420
+ }
31311
31421
  /**
31312
31422
  * Attempt to resolve `url` in the context of `fromFile`, while respecting the rootDirs
31313
31423
  * option from the tsconfig. First, normalize the file name.
@@ -37704,6 +37814,22 @@ Either add the @Injectable() decorator to '${provider.node.name
37704
37814
  }
37705
37815
  return engine.getExpressionCompletionLocation(ast);
37706
37816
  }
37817
+ invalidateClass(clazz) {
37818
+ var _a;
37819
+ this.completionCache.delete(clazz);
37820
+ this.symbolBuilderCache.delete(clazz);
37821
+ this.scopeCache.delete(clazz);
37822
+ this.elementTagCache.delete(clazz);
37823
+ const sf = clazz.getSourceFile();
37824
+ const sfPath = absoluteFromSourceFile(sf);
37825
+ const shimPath = this.typeCheckingStrategy.shimPathForComponent(clazz);
37826
+ const fileData = this.getFileData(sfPath);
37827
+ const templateId = fileData.sourceManager.getTemplateId(clazz);
37828
+ fileData.shimData.delete(shimPath);
37829
+ fileData.isComplete = false;
37830
+ (_a = fileData.templateOverrides) === null || _a === void 0 ? void 0 : _a.delete(templateId);
37831
+ this.isComplete = false;
37832
+ }
37707
37833
  getOrCreateCompletionEngine(component) {
37708
37834
  if (this.completionCache.has(component)) {
37709
37835
  return this.completionCache.get(component);
@@ -38089,6 +38215,54 @@ Either add the @Injectable() decorator to '${provider.node.name
38089
38215
  * Use of this source code is governed by an MIT-style license that can be
38090
38216
  * found in the LICENSE file at https://angular.io/license
38091
38217
  */
38218
+ /**
38219
+ * Discriminant type for a `CompilationTicket`.
38220
+ */
38221
+ var CompilationTicketKind;
38222
+ (function (CompilationTicketKind) {
38223
+ CompilationTicketKind[CompilationTicketKind["Fresh"] = 0] = "Fresh";
38224
+ CompilationTicketKind[CompilationTicketKind["IncrementalTypeScript"] = 1] = "IncrementalTypeScript";
38225
+ CompilationTicketKind[CompilationTicketKind["IncrementalResource"] = 2] = "IncrementalResource";
38226
+ })(CompilationTicketKind || (CompilationTicketKind = {}));
38227
+ /**
38228
+ * Create a `CompilationTicket` for a brand new compilation, using no prior state.
38229
+ */
38230
+ function freshCompilationTicket(tsProgram, options, incrementalBuildStrategy, typeCheckingProgramStrategy, enableTemplateTypeChecker, usePoisonedData) {
38231
+ return {
38232
+ kind: CompilationTicketKind.Fresh,
38233
+ tsProgram,
38234
+ options,
38235
+ incrementalBuildStrategy,
38236
+ typeCheckingProgramStrategy,
38237
+ enableTemplateTypeChecker,
38238
+ usePoisonedData,
38239
+ };
38240
+ }
38241
+ /**
38242
+ * Create a `CompilationTicket` as efficiently as possible, based on a previous `NgCompiler`
38243
+ * instance and a new `ts.Program`.
38244
+ */
38245
+ function incrementalFromCompilerTicket(oldCompiler, newProgram, incrementalBuildStrategy, typeCheckingProgramStrategy, modifiedResourceFiles) {
38246
+ const oldProgram = oldCompiler.getNextProgram();
38247
+ const oldDriver = oldCompiler.incrementalStrategy.getIncrementalDriver(oldProgram);
38248
+ if (oldDriver === null) {
38249
+ // No incremental step is possible here, since no IncrementalDriver was found for the old
38250
+ // program.
38251
+ return freshCompilationTicket(newProgram, oldCompiler.options, incrementalBuildStrategy, typeCheckingProgramStrategy, oldCompiler.enableTemplateTypeChecker, oldCompiler.usePoisonedData);
38252
+ }
38253
+ const newDriver = IncrementalDriver.reconcile(oldProgram, oldDriver, newProgram, modifiedResourceFiles);
38254
+ return {
38255
+ kind: CompilationTicketKind.IncrementalTypeScript,
38256
+ enableTemplateTypeChecker: oldCompiler.enableTemplateTypeChecker,
38257
+ usePoisonedData: oldCompiler.usePoisonedData,
38258
+ options: oldCompiler.options,
38259
+ incrementalBuildStrategy,
38260
+ typeCheckingProgramStrategy,
38261
+ newDriver,
38262
+ oldProgram,
38263
+ newProgram,
38264
+ };
38265
+ }
38092
38266
  /**
38093
38267
  * The heart of the Angular Ivy compiler.
38094
38268
  *
@@ -38102,12 +38276,13 @@ Either add the @Injectable() decorator to '${provider.node.name
38102
38276
  * See the README.md for more information.
38103
38277
  */
38104
38278
  class NgCompiler {
38105
- constructor(adapter, options, tsProgram, typeCheckingProgramStrategy, incrementalStrategy, enableTemplateTypeChecker, usePoisonedData, oldProgram = null, perfRecorder = NOOP_PERF_RECORDER) {
38279
+ constructor(adapter, options, tsProgram, typeCheckingProgramStrategy, incrementalStrategy, incrementalDriver, enableTemplateTypeChecker, usePoisonedData, perfRecorder = NOOP_PERF_RECORDER) {
38106
38280
  this.adapter = adapter;
38107
38281
  this.options = options;
38108
38282
  this.tsProgram = tsProgram;
38109
38283
  this.typeCheckingProgramStrategy = typeCheckingProgramStrategy;
38110
38284
  this.incrementalStrategy = incrementalStrategy;
38285
+ this.incrementalDriver = incrementalDriver;
38111
38286
  this.enableTemplateTypeChecker = enableTemplateTypeChecker;
38112
38287
  this.usePoisonedData = usePoisonedData;
38113
38288
  this.perfRecorder = perfRecorder;
@@ -38151,31 +38326,55 @@ Either add the @Injectable() decorator to '${provider.node.name
38151
38326
  new ModuleResolver(tsProgram, this.options, this.adapter, moduleResolutionCache);
38152
38327
  this.resourceManager = new AdapterResourceLoader(adapter, this.options);
38153
38328
  this.cycleAnalyzer = new CycleAnalyzer(new ImportGraph(this.moduleResolver));
38154
- let modifiedResourceFiles = null;
38155
- if (this.adapter.getModifiedResourceFiles !== undefined) {
38156
- modifiedResourceFiles = this.adapter.getModifiedResourceFiles() || null;
38157
- }
38158
- if (oldProgram === null) {
38159
- this.incrementalDriver = IncrementalDriver.fresh(tsProgram);
38160
- }
38161
- else {
38162
- const oldDriver = this.incrementalStrategy.getIncrementalDriver(oldProgram);
38163
- if (oldDriver !== null) {
38164
- this.incrementalDriver =
38165
- IncrementalDriver.reconcile(oldProgram, oldDriver, tsProgram, modifiedResourceFiles);
38166
- }
38167
- else {
38168
- // A previous ts.Program was used to create the current one, but it wasn't from an
38169
- // `NgCompiler`. That doesn't hurt anything, but the Angular analysis will have to start
38170
- // from a fresh state.
38171
- this.incrementalDriver = IncrementalDriver.fresh(tsProgram);
38172
- }
38173
- }
38174
38329
  this.incrementalStrategy.setIncrementalDriver(this.incrementalDriver, tsProgram);
38175
38330
  this.ignoreForDiagnostics =
38176
38331
  new Set(tsProgram.getSourceFiles().filter(sf => this.adapter.isShim(sf)));
38177
38332
  this.ignoreForEmit = this.adapter.ignoreForEmit;
38178
38333
  }
38334
+ /**
38335
+ * Convert a `CompilationTicket` into an `NgCompiler` instance for the requested compilation.
38336
+ *
38337
+ * Depending on the nature of the compilation request, the `NgCompiler` instance may be reused
38338
+ * from a previous compilation and updated with any changes, it may be a new instance which
38339
+ * incrementally reuses state from a previous compilation, or it may represent a fresh compilation
38340
+ * entirely.
38341
+ */
38342
+ static fromTicket(ticket, adapter, perfRecorder) {
38343
+ switch (ticket.kind) {
38344
+ case CompilationTicketKind.Fresh:
38345
+ return new NgCompiler(adapter, ticket.options, ticket.tsProgram, ticket.typeCheckingProgramStrategy, ticket.incrementalBuildStrategy, IncrementalDriver.fresh(ticket.tsProgram), ticket.enableTemplateTypeChecker, ticket.usePoisonedData, perfRecorder);
38346
+ case CompilationTicketKind.IncrementalTypeScript:
38347
+ return new NgCompiler(adapter, ticket.options, ticket.newProgram, ticket.typeCheckingProgramStrategy, ticket.incrementalBuildStrategy, ticket.newDriver, ticket.enableTemplateTypeChecker, ticket.usePoisonedData, perfRecorder);
38348
+ case CompilationTicketKind.IncrementalResource:
38349
+ const compiler = ticket.compiler;
38350
+ compiler.updateWithChangedResources(ticket.modifiedResourceFiles);
38351
+ return compiler;
38352
+ }
38353
+ }
38354
+ updateWithChangedResources(changedResources) {
38355
+ if (this.compilation === null) {
38356
+ // Analysis hasn't happened yet, so no update is necessary - any changes to resources will be
38357
+ // captured by the inital analysis pass itself.
38358
+ return;
38359
+ }
38360
+ this.resourceManager.invalidate();
38361
+ const classesToUpdate = new Set();
38362
+ for (const resourceFile of changedResources) {
38363
+ for (const templateClass of this.getComponentsWithTemplateFile(resourceFile)) {
38364
+ classesToUpdate.add(templateClass);
38365
+ }
38366
+ for (const styleClass of this.getComponentsWithStyleFile(resourceFile)) {
38367
+ classesToUpdate.add(styleClass);
38368
+ }
38369
+ }
38370
+ for (const clazz of classesToUpdate) {
38371
+ this.compilation.traitCompiler.updateResources(clazz);
38372
+ if (!ts$1.isClassDeclaration(clazz)) {
38373
+ continue;
38374
+ }
38375
+ this.compilation.templateTypeChecker.invalidateClass(clazz);
38376
+ }
38377
+ }
38179
38378
  /**
38180
38379
  * Get the resource dependencies of a file.
38181
38380
  *
@@ -38802,7 +39001,7 @@ Either add the @Injectable() decorator to '${provider.node.name
38802
39001
  }
38803
39002
  /**
38804
39003
  * Since "strictTemplates" is a true superset of type checking capabilities compared to
38805
- * "strictTemplateTypeCheck", it is required that the latter is not explicitly disabled if the
39004
+ * "fullTemplateTypeCheck", it is required that the latter is not explicitly disabled if the
38806
39005
  * former is enabled.
38807
39006
  */
38808
39007
  function verifyCompatibleTypeCheckOptions(options) {
@@ -39472,6 +39671,14 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
39472
39671
  getCanonicalFileName(fileName) {
39473
39672
  return this.project.projectService.toCanonicalFileName(fileName);
39474
39673
  }
39674
+ /**
39675
+ * Return the real path of a symlink. This method is required in order to
39676
+ * resolve symlinks in node_modules.
39677
+ */
39678
+ realpath(path) {
39679
+ var _a, _b, _c;
39680
+ return (_c = (_b = (_a = this.project).realpath) === null || _b === void 0 ? void 0 : _b.call(_a, path)) !== null && _c !== void 0 ? _c : path;
39681
+ }
39475
39682
  /**
39476
39683
  * readResource() is an Angular-specific method for reading files that are not
39477
39684
  * managed by the TS compiler host, namely templates and stylesheets.
@@ -39582,11 +39789,14 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
39582
39789
  getOrCreate() {
39583
39790
  const program = this.programStrategy.getProgram();
39584
39791
  if (this.compiler === null || program !== this.lastKnownProgram) {
39585
- this.compiler = new NgCompiler(this.adapter, // like compiler host
39586
- this.options, // angular compiler options
39587
- program, this.programStrategy, this.incrementalStrategy, true, // enableTemplateTypeChecker
39588
- true, // usePoisonedData
39589
- this.lastKnownProgram, undefined);
39792
+ let ticket;
39793
+ if (this.compiler === null || this.lastKnownProgram === null) {
39794
+ ticket = freshCompilationTicket(program, this.options, this.incrementalStrategy, this.programStrategy, true, true);
39795
+ }
39796
+ else {
39797
+ ticket = incrementalFromCompilerTicket(this.compiler, program, this.incrementalStrategy, this.programStrategy, new Set());
39798
+ }
39799
+ this.compiler = NgCompiler.fromTicket(ticket, this.adapter);
39590
39800
  this.lastKnownProgram = program;
39591
39801
  }
39592
39802
  return this.compiler;
@@ -41522,6 +41732,7 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
41522
41732
  */
41523
41733
  class LanguageService {
41524
41734
  constructor(project, tsLS) {
41735
+ this.project = project;
41525
41736
  this.tsLS = tsLS;
41526
41737
  this.parseConfigHost = new LSParseConfigHost(project.projectService.host);
41527
41738
  this.options = parseNgCompilerOptions(project, this.parseConfigHost);
@@ -41638,6 +41849,28 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
41638
41849
  this.compilerFactory.registerLastKnownProgram();
41639
41850
  return result;
41640
41851
  }
41852
+ getCompilerOptionsDiagnostics() {
41853
+ const project = this.project;
41854
+ if (!(project instanceof ts.server.ConfiguredProject)) {
41855
+ return [];
41856
+ }
41857
+ const diagnostics = [];
41858
+ const configSourceFile = ts.readJsonConfigFile(project.getConfigFilePath(), (path) => project.readFile(path));
41859
+ if (!this.options.strictTemplates && !this.options.fullTemplateTypeCheck) {
41860
+ diagnostics.push({
41861
+ messageText: 'Some language features are not available. ' +
41862
+ 'To access all features, enable `strictTemplates` in `angularCompilerOptions`.',
41863
+ category: ts.DiagnosticCategory.Suggestion,
41864
+ code: ngErrorCode(ErrorCode.SUGGEST_STRICT_TEMPLATES),
41865
+ file: configSourceFile,
41866
+ start: undefined,
41867
+ length: undefined,
41868
+ });
41869
+ }
41870
+ const compiler = this.compilerFactory.getOrCreate();
41871
+ diagnostics.push(...compiler.getOptionDiagnostics());
41872
+ return diagnostics;
41873
+ }
41641
41874
  watchConfigFile(project) {
41642
41875
  // TODO: Check the case when the project is disposed. An InferredProject
41643
41876
  // could be disposed when a tsconfig.json is added to the workspace,
@@ -41838,6 +42071,17 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
41838
42071
  return (_a = tsLS.getCompletionEntrySymbol(fileName, position, name, source)) !== null && _a !== void 0 ? _a : ngLS.getCompletionEntrySymbol(fileName, position, name);
41839
42072
  }
41840
42073
  }
42074
+ /**
42075
+ * Gets global diagnostics related to the program configuration and compiler options.
42076
+ */
42077
+ function getCompilerOptionsDiagnostics() {
42078
+ const diagnostics = [];
42079
+ if (!angularOnly) {
42080
+ diagnostics.push(...tsLS.getCompilerOptionsDiagnostics());
42081
+ }
42082
+ diagnostics.push(...ngLS.getCompilerOptionsDiagnostics());
42083
+ return diagnostics;
42084
+ }
41841
42085
  return Object.assign(Object.assign({}, tsLS), { getSemanticDiagnostics,
41842
42086
  getTypeDefinitionAtPosition,
41843
42087
  getQuickInfoAtPosition,
@@ -41846,7 +42090,8 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
41846
42090
  findRenameLocations,
41847
42091
  getCompletionsAtPosition,
41848
42092
  getCompletionEntryDetails,
41849
- getCompletionEntrySymbol });
42093
+ getCompletionEntrySymbol,
42094
+ getCompilerOptionsDiagnostics });
41850
42095
  }
41851
42096
  function getExternalFiles(project) {
41852
42097
  if (!project.hasRoots()) {