@angular/language-service 11.1.0-next.5 → 11.1.2

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-next.5
2
+ * @license Angular v11.1.2
3
3
  * Copyright Google LLC All Rights Reserved.
4
4
  * License: MIT
5
5
  */
@@ -827,6 +827,10 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
827
827
  * Use of this source code is governed by an MIT-style license that can be
828
828
  * found in the LICENSE file at https://angular.io/license
829
829
  */
830
+ // Stores the default value of `emitDistinctChangesOnly` when the `emitDistinctChangesOnly` is not
831
+ // explicitly set. This value will be changed to `true` in v12.
832
+ // TODO(misko): switch the default in v12 to `true`. See: packages/core/src/metadata/di.ts
833
+ const emitDistinctChangesOnlyDefaultValue = false;
830
834
  var ViewEncapsulation;
831
835
  (function (ViewEncapsulation) {
832
836
  ViewEncapsulation[ViewEncapsulation["Emulated"] = 0] = "Emulated";
@@ -2655,8 +2659,6 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
2655
2659
  Identifiers$1.definePipe = { name: 'ɵɵdefinePipe', moduleName: CORE$1 };
2656
2660
  Identifiers$1.queryRefresh = { name: 'ɵɵqueryRefresh', moduleName: CORE$1 };
2657
2661
  Identifiers$1.viewQuery = { name: 'ɵɵviewQuery', moduleName: CORE$1 };
2658
- Identifiers$1.staticViewQuery = { name: 'ɵɵstaticViewQuery', moduleName: CORE$1 };
2659
- Identifiers$1.staticContentQuery = { name: 'ɵɵstaticContentQuery', moduleName: CORE$1 };
2660
2662
  Identifiers$1.loadQuery = { name: 'ɵɵloadQuery', moduleName: CORE$1 };
2661
2663
  Identifiers$1.contentQuery = { name: 'ɵɵcontentQuery', moduleName: CORE$1 };
2662
2664
  Identifiers$1.NgOnChangesFeature = { name: 'ɵɵNgOnChangesFeature', moduleName: CORE$1 };
@@ -9355,7 +9357,7 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
9355
9357
  this._advance();
9356
9358
  selfClosing = false;
9357
9359
  }
9358
- const end = this._peek.sourceSpan.start;
9360
+ const end = this._peek.sourceSpan.fullStart;
9359
9361
  const span = new ParseSourceSpan(startTagToken.sourceSpan.start, end, startTagToken.sourceSpan.fullStart);
9360
9362
  // Create a separate `startSpan` because `span` will be modified when there is an `end` span.
9361
9363
  const startSpan = new ParseSourceSpan(startTagToken.sourceSpan.start, end, startTagToken.sourceSpan.fullStart);
@@ -9687,8 +9689,9 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
9687
9689
  }
9688
9690
  parseInterpolation(value, sourceSpan) {
9689
9691
  const sourceInfo = sourceSpan.start.toString();
9692
+ const absoluteOffset = sourceSpan.fullStart.offset;
9690
9693
  try {
9691
- const ast = this._exprParser.parseInterpolation(value, sourceInfo, sourceSpan.start.offset, this._interpolationConfig);
9694
+ const ast = this._exprParser.parseInterpolation(value, sourceInfo, absoluteOffset, this._interpolationConfig);
9692
9695
  if (ast)
9693
9696
  this._reportExpressionParserErrors(ast.errors, sourceSpan);
9694
9697
  this._checkPipes(ast, sourceSpan);
@@ -9696,7 +9699,7 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
9696
9699
  }
9697
9700
  catch (e) {
9698
9701
  this._reportError(`${e}`, sourceSpan);
9699
- return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo, sourceSpan.start.offset);
9702
+ return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo, absoluteOffset);
9700
9703
  }
9701
9704
  }
9702
9705
  /**
@@ -9706,8 +9709,9 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
9706
9709
  */
9707
9710
  parseInterpolationExpression(expression, sourceSpan) {
9708
9711
  const sourceInfo = sourceSpan.start.toString();
9712
+ const absoluteOffset = sourceSpan.start.offset;
9709
9713
  try {
9710
- const ast = this._exprParser.parseInterpolationExpression(expression, sourceInfo, sourceSpan.start.offset);
9714
+ const ast = this._exprParser.parseInterpolationExpression(expression, sourceInfo, absoluteOffset);
9711
9715
  if (ast)
9712
9716
  this._reportExpressionParserErrors(ast.errors, sourceSpan);
9713
9717
  this._checkPipes(ast, sourceSpan);
@@ -9715,7 +9719,7 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
9715
9719
  }
9716
9720
  catch (e) {
9717
9721
  this._reportError(`${e}`, sourceSpan);
9718
- return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo, sourceSpan.start.offset);
9722
+ return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo, absoluteOffset);
9719
9723
  }
9720
9724
  }
9721
9725
  /**
@@ -9795,6 +9799,9 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
9795
9799
  targetProps, keySpan) {
9796
9800
  if (isAnimationLabel(name)) {
9797
9801
  name = name.substring(1);
9802
+ if (keySpan !== undefined) {
9803
+ keySpan = moveParseSourceSpan(keySpan, new AbsoluteSourceSpan(keySpan.start.offset + 1, keySpan.end.offset));
9804
+ }
9798
9805
  if (value) {
9799
9806
  this._reportError(`Assigning animation triggers via @prop="exp" attributes with an expression is invalid.` +
9800
9807
  ` Use property bindings (e.g. [@prop]="exp") or use an attribute without a value (e.g. @prop) instead.`, sourceSpan, ParseErrorLevel.ERROR);
@@ -9816,10 +9823,16 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
9816
9823
  if (name.startsWith(ANIMATE_PROP_PREFIX)) {
9817
9824
  isAnimationProp = true;
9818
9825
  name = name.substring(ANIMATE_PROP_PREFIX.length);
9826
+ if (keySpan !== undefined) {
9827
+ keySpan = moveParseSourceSpan(keySpan, new AbsoluteSourceSpan(keySpan.start.offset + ANIMATE_PROP_PREFIX.length, keySpan.end.offset));
9828
+ }
9819
9829
  }
9820
9830
  else if (isAnimationLabel(name)) {
9821
9831
  isAnimationProp = true;
9822
9832
  name = name.substring(1);
9833
+ if (keySpan !== undefined) {
9834
+ keySpan = moveParseSourceSpan(keySpan, new AbsoluteSourceSpan(keySpan.start.offset + 1, keySpan.end.offset));
9835
+ }
9823
9836
  }
9824
9837
  if (isAnimationProp) {
9825
9838
  this._parseAnimation(name, expression, sourceSpan, absoluteOffset, keySpan, valueSpan, targetMatchableAttrs, targetProps);
@@ -9926,6 +9939,9 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
9926
9939
  }
9927
9940
  if (isAnimationLabel(name)) {
9928
9941
  name = name.substr(1);
9942
+ if (keySpan !== undefined) {
9943
+ keySpan = moveParseSourceSpan(keySpan, new AbsoluteSourceSpan(keySpan.start.offset + 1, keySpan.end.offset));
9944
+ }
9929
9945
  this._parseAnimationEvent(name, expression, sourceSpan, handlerSpan, targetEvents, keySpan);
9930
9946
  }
9931
9947
  else {
@@ -11340,13 +11356,10 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
11340
11356
  }
11341
11357
  const fullEnd = exprEnd + interpEnd.length;
11342
11358
  const text = input.substring(exprStart, exprEnd);
11343
- if (text.trim().length > 0) {
11344
- expressions.push({ text, start: fullStart, end: fullEnd });
11345
- }
11346
- else {
11359
+ if (text.trim().length === 0) {
11347
11360
  this._reportError('Blank expressions are not allowed in interpolated strings', input, `at column ${i} in`, location);
11348
- expressions.push({ text: '$implicit', start: fullStart, end: fullEnd });
11349
11361
  }
11362
+ expressions.push({ text, start: fullStart, end: fullEnd });
11350
11363
  offsets.push(exprStart);
11351
11364
  i = fullEnd;
11352
11365
  atInterpolation = false;
@@ -11529,13 +11542,25 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
11529
11542
  get currentAbsoluteOffset() {
11530
11543
  return this.absoluteOffset + this.inputIndex;
11531
11544
  }
11532
- span(start) {
11533
- return new ParseSpan(start, this.currentEndIndex);
11545
+ /**
11546
+ * Retrieve a `ParseSpan` from `start` to the current position (or to `artificialEndIndex` if
11547
+ * provided).
11548
+ *
11549
+ * @param start Position from which the `ParseSpan` will start.
11550
+ * @param artificialEndIndex Optional ending index to be used if provided (and if greater than the
11551
+ * natural ending index)
11552
+ */
11553
+ span(start, artificialEndIndex) {
11554
+ let endIndex = this.currentEndIndex;
11555
+ if (artificialEndIndex !== undefined && artificialEndIndex > this.currentEndIndex) {
11556
+ endIndex = artificialEndIndex;
11557
+ }
11558
+ return new ParseSpan(start, endIndex);
11534
11559
  }
11535
- sourceSpan(start) {
11536
- const serial = `${start}@${this.inputIndex}`;
11560
+ sourceSpan(start, artificialEndIndex) {
11561
+ const serial = `${start}@${this.inputIndex}:${artificialEndIndex}`;
11537
11562
  if (!this.sourceSpanCache.has(serial)) {
11538
- this.sourceSpanCache.set(serial, this.span(start).toAbsolute(this.absoluteOffset));
11563
+ this.sourceSpanCache.set(serial, this.span(start, artificialEndIndex).toAbsolute(this.absoluteOffset));
11539
11564
  }
11540
11565
  return this.sourceSpanCache.get(serial);
11541
11566
  }
@@ -11598,7 +11623,7 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
11598
11623
  const n = this.next;
11599
11624
  if (!n.isIdentifier() && !n.isKeyword()) {
11600
11625
  this.error(`Unexpected ${this.prettyPrintToken(n)}, expected identifier or keyword`);
11601
- return '';
11626
+ return null;
11602
11627
  }
11603
11628
  this.advance();
11604
11629
  return n.toString();
@@ -11629,8 +11654,12 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
11629
11654
  this.error(`Unexpected token '${this.next}'`);
11630
11655
  }
11631
11656
  }
11632
- if (exprs.length == 0)
11633
- return new EmptyExpr(this.span(start), this.sourceSpan(start));
11657
+ if (exprs.length == 0) {
11658
+ // We have no expressions so create an empty expression that spans the entire input length
11659
+ const artificialStart = this.offset;
11660
+ const artificialEnd = this.offset + this.inputLength;
11661
+ return new EmptyExpr(this.span(artificialStart, artificialEnd), this.sourceSpan(artificialStart, artificialEnd));
11662
+ }
11634
11663
  if (exprs.length == 1)
11635
11664
  return exprs[0];
11636
11665
  return new Chain(this.span(start), this.sourceSpan(start), exprs);
@@ -11643,15 +11672,36 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
11643
11672
  }
11644
11673
  do {
11645
11674
  const nameStart = this.inputIndex;
11646
- const name = this.expectIdentifierOrKeyword();
11647
- const nameSpan = this.sourceSpan(nameStart);
11675
+ let nameId = this.expectIdentifierOrKeyword();
11676
+ let nameSpan;
11677
+ let fullSpanEnd = undefined;
11678
+ if (nameId !== null) {
11679
+ nameSpan = this.sourceSpan(nameStart);
11680
+ }
11681
+ else {
11682
+ // No valid identifier was found, so we'll assume an empty pipe name ('').
11683
+ nameId = '';
11684
+ // However, there may have been whitespace present between the pipe character and the next
11685
+ // token in the sequence (or the end of input). We want to track this whitespace so that
11686
+ // the `BindingPipe` we produce covers not just the pipe character, but any trailing
11687
+ // whitespace beyond it. Another way of thinking about this is that the zero-length name
11688
+ // is assumed to be at the end of any whitespace beyond the pipe character.
11689
+ //
11690
+ // Therefore, we push the end of the `ParseSpan` for this pipe all the way up to the
11691
+ // beginning of the next token, or until the end of input if the next token is EOF.
11692
+ fullSpanEnd = this.next.index !== -1 ? this.next.index : this.inputLength + this.offset;
11693
+ // The `nameSpan` for an empty pipe name is zero-length at the end of any whitespace
11694
+ // beyond the pipe character.
11695
+ nameSpan = new ParseSpan(fullSpanEnd, fullSpanEnd).toAbsolute(this.absoluteOffset);
11696
+ }
11648
11697
  const args = [];
11649
11698
  while (this.consumeOptionalCharacter($COLON)) {
11650
11699
  args.push(this.parseExpression());
11700
+ // If there are additional expressions beyond the name, then the artificial end for the
11701
+ // name is no longer relevant.
11651
11702
  }
11652
11703
  const { start } = result.span;
11653
- result =
11654
- new BindingPipe(this.span(start), this.sourceSpan(start), result, name, args, nameSpan);
11704
+ result = new BindingPipe(this.span(start), this.sourceSpan(start, fullSpanEnd), result, nameId, args, nameSpan);
11655
11705
  } while (this.consumeOptionalOperator('|'));
11656
11706
  }
11657
11707
  return result;
@@ -11940,7 +11990,8 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
11940
11990
  const start = receiver.span.start;
11941
11991
  const nameStart = this.inputIndex;
11942
11992
  const id = this.withContext(ParseContextFlags.Writable, () => {
11943
- const id = this.expectIdentifierOrKeyword();
11993
+ var _a;
11994
+ const id = (_a = this.expectIdentifierOrKeyword()) !== null && _a !== void 0 ? _a : '';
11944
11995
  if (id.length === 0) {
11945
11996
  this.error(`Expected identifier for property access`, receiver.span.end);
11946
11997
  }
@@ -16169,12 +16220,21 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
16169
16220
  }
16170
16221
  }
16171
16222
  function prepareQueryParams(query, constantPool) {
16172
- const parameters = [getQueryPredicate(query, constantPool), literal(query.descendants)];
16223
+ const parameters = [getQueryPredicate(query, constantPool), literal(toQueryFlags(query))];
16173
16224
  if (query.read) {
16174
16225
  parameters.push(query.read);
16175
16226
  }
16176
16227
  return parameters;
16177
16228
  }
16229
+ /**
16230
+ * Translates query flags into `TQueryFlags` type in packages/core/src/render3/interfaces/query.ts
16231
+ * @param query
16232
+ */
16233
+ function toQueryFlags(query) {
16234
+ return (query.descendants ? 1 /* descendants */ : 0 /* none */) |
16235
+ (query.static ? 2 /* isStatic */ : 0 /* none */) |
16236
+ (query.emitDistinctChangesOnly ? 4 /* emitDistinctChangesOnly */ : 0 /* none */);
16237
+ }
16178
16238
  function convertAttributesToExpressions(attributes) {
16179
16239
  const values = [];
16180
16240
  for (let key of Object.getOwnPropertyNames(attributes)) {
@@ -16189,9 +16249,8 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
16189
16249
  const updateStatements = [];
16190
16250
  const tempAllocator = temporaryAllocator(updateStatements, TEMPORARY_NAME);
16191
16251
  for (const query of queries) {
16192
- const queryInstruction = query.static ? Identifiers$1.staticContentQuery : Identifiers$1.contentQuery;
16193
16252
  // creation, e.g. r3.contentQuery(dirIndex, somePredicate, true, null);
16194
- createStatements.push(importExpr(queryInstruction)
16253
+ createStatements.push(importExpr(Identifiers$1.contentQuery)
16195
16254
  .callFn([variable('dirIndex'), ...prepareQueryParams(query, constantPool)])
16196
16255
  .toStmt());
16197
16256
  // update, e.g. (r3.queryRefresh(tmp = r3.loadQuery()) && (ctx.someDir = tmp));
@@ -16257,9 +16316,8 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
16257
16316
  const updateStatements = [];
16258
16317
  const tempAllocator = temporaryAllocator(updateStatements, TEMPORARY_NAME);
16259
16318
  viewQueries.forEach((query) => {
16260
- const queryInstruction = query.static ? Identifiers$1.staticViewQuery : Identifiers$1.viewQuery;
16261
16319
  // creation, e.g. r3.viewQuery(somePredicate, true);
16262
- const queryDefinition = importExpr(queryInstruction).callFn(prepareQueryParams(query, constantPool));
16320
+ const queryDefinition = importExpr(Identifiers$1.viewQuery).callFn(prepareQueryParams(query, constantPool));
16263
16321
  createStatements.push(queryDefinition.toStmt());
16264
16322
  // update, e.g. (r3.queryRefresh(tmp = r3.loadQuery()) && (ctx.someDir = tmp));
16265
16323
  const temporary = tempAllocator();
@@ -16738,10 +16796,10 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
16738
16796
  };
16739
16797
  function convertToR3QueryMetadata(facade) {
16740
16798
  return Object.assign(Object.assign({}, facade), { predicate: Array.isArray(facade.predicate) ? facade.predicate :
16741
- new WrappedNodeExpr(facade.predicate), read: facade.read ? new WrappedNodeExpr(facade.read) : null, static: facade.static });
16799
+ new WrappedNodeExpr(facade.predicate), read: facade.read ? new WrappedNodeExpr(facade.read) : null, static: facade.static, emitDistinctChangesOnly: facade.emitDistinctChangesOnly });
16742
16800
  }
16743
16801
  function convertQueryDeclarationToMetadata(declaration) {
16744
- var _a, _b, _c;
16802
+ var _a, _b, _c, _d;
16745
16803
  return {
16746
16804
  propertyName: declaration.propertyName,
16747
16805
  first: (_a = declaration.first) !== null && _a !== void 0 ? _a : false,
@@ -16750,6 +16808,7 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
16750
16808
  descendants: (_b = declaration.descendants) !== null && _b !== void 0 ? _b : false,
16751
16809
  read: declaration.read ? new WrappedNodeExpr(declaration.read) : null,
16752
16810
  static: (_c = declaration.static) !== null && _c !== void 0 ? _c : false,
16811
+ emitDistinctChangesOnly: (_d = declaration.emitDistinctChangesOnly) !== null && _d !== void 0 ? _d : true,
16753
16812
  };
16754
16813
  }
16755
16814
  function convertDirectiveFacadeToMetadata(facade) {
@@ -16952,7 +17011,7 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
16952
17011
  * Use of this source code is governed by an MIT-style license that can be
16953
17012
  * found in the LICENSE file at https://angular.io/license
16954
17013
  */
16955
- const VERSION$1 = new Version('11.1.0-next.5');
17014
+ const VERSION$1 = new Version('11.1.2');
16956
17015
 
16957
17016
  /**
16958
17017
  * @license
@@ -17609,7 +17668,7 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
17609
17668
  */
17610
17669
  function createDirectiveDefinitionMap(meta) {
17611
17670
  const definitionMap = new DefinitionMap();
17612
- definitionMap.set('version', literal('11.1.0-next.5'));
17671
+ definitionMap.set('version', literal('11.1.2'));
17613
17672
  // e.g. `type: MyDirective`
17614
17673
  definitionMap.set('type', meta.internalType);
17615
17674
  // e.g. `selector: 'some-dir'`
@@ -17649,6 +17708,11 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
17649
17708
  meta.set('first', literal(true));
17650
17709
  }
17651
17710
  meta.set('predicate', Array.isArray(query.predicate) ? asLiteral(query.predicate) : query.predicate);
17711
+ if (!query.emitDistinctChangesOnly) {
17712
+ // `emitDistinctChangesOnly` is special because in future we expect it to be `true`. For this
17713
+ // reason the absence should be interpreted as `true`.
17714
+ meta.set('emitDistinctChangesOnly', literal(false));
17715
+ }
17652
17716
  if (query.descendants) {
17653
17717
  meta.set('descendants', literal(true));
17654
17718
  }
@@ -21052,7 +21116,7 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
21052
21116
  * Use of this source code is governed by an MIT-style license that can be
21053
21117
  * found in the LICENSE file at https://angular.io/license
21054
21118
  */
21055
- const VERSION$2 = new Version('11.1.0-next.5');
21119
+ const VERSION$2 = new Version('11.1.2');
21056
21120
 
21057
21121
  /**
21058
21122
  * @license
@@ -21244,7 +21308,34 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
21244
21308
  * An injectable already has a `ɵprov` property.
21245
21309
  */
21246
21310
  ErrorCode[ErrorCode["INJECTABLE_DUPLICATE_PROV"] = 9001] = "INJECTABLE_DUPLICATE_PROV";
21311
+ // 10XXX error codes are reserved for diagnostics with category
21312
+ // `ts.DiagnosticCategory.Suggestion`. These diagnostics are generated by
21313
+ // language service.
21314
+ /**
21315
+ * Suggest users to enable `strictTemplates` to make use of full capabilities
21316
+ * provided by Angular language service.
21317
+ */
21318
+ ErrorCode[ErrorCode["SUGGEST_STRICT_TEMPLATES"] = 10001] = "SUGGEST_STRICT_TEMPLATES";
21247
21319
  })(ErrorCode || (ErrorCode = {}));
21320
+ /**
21321
+ * @internal
21322
+ * Base URL for the error details page.
21323
+ * Keep this value in sync with a similar const in
21324
+ * `packages/core/src/render3/error_code.ts`.
21325
+ */
21326
+ const ERROR_DETAILS_PAGE_BASE_URL = 'https://angular.io/errors';
21327
+ /**
21328
+ * @internal
21329
+ * Contains a set of error messages that have detailed guides at angular.io.
21330
+ * Full list of available error guides can be found at https://angular.io/errors
21331
+ */
21332
+ const COMPILER_ERRORS_WITH_GUIDES = new Set([
21333
+ ErrorCode.DECORATOR_ARG_NOT_LITERAL,
21334
+ ErrorCode.PARAM_MISSING_TOKEN,
21335
+ ErrorCode.SCHEMA_INVALID_ELEMENT,
21336
+ ErrorCode.SCHEMA_INVALID_ATTRIBUTE,
21337
+ ErrorCode.MISSING_REFERENCE_TARGET,
21338
+ ]);
21248
21339
  /**
21249
21340
  * @internal
21250
21341
  */
@@ -25399,6 +25490,18 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
25399
25490
  }
25400
25491
  }
25401
25492
  }
25493
+ updateResources(clazz) {
25494
+ if (!this.reflector.isClass(clazz) || !this.classes.has(clazz)) {
25495
+ return;
25496
+ }
25497
+ const record = this.classes.get(clazz);
25498
+ for (const trait of record.traits) {
25499
+ if (trait.state !== TraitState.Resolved || trait.handler.updateResources === undefined) {
25500
+ continue;
25501
+ }
25502
+ trait.handler.updateResources(clazz, trait.analysis, trait.resolution);
25503
+ }
25504
+ }
25402
25505
  compile(clazz, constantPool) {
25403
25506
  const original = ts$1.getOriginalNode(clazz);
25404
25507
  if (!this.reflector.isClass(clazz) || !this.reflector.isClass(original) ||
@@ -28101,6 +28204,7 @@ Either add the @Injectable() decorator to '${provider.node.name
28101
28204
  let read = null;
28102
28205
  // The default value for descendants is true for every decorator except @ContentChildren.
28103
28206
  let descendants = name !== 'ContentChildren';
28207
+ let emitDistinctChangesOnly = emitDistinctChangesOnlyDefaultValue;
28104
28208
  if (args.length === 2) {
28105
28209
  const optionsExpr = unwrapExpression(args[1]);
28106
28210
  if (!ts$1.isObjectLiteralExpression(optionsExpr)) {
@@ -28118,6 +28222,14 @@ Either add the @Injectable() decorator to '${provider.node.name
28118
28222
  }
28119
28223
  descendants = descendantsValue;
28120
28224
  }
28225
+ if (options.has('emitDistinctChangesOnly')) {
28226
+ const emitDistinctChangesOnlyExpr = options.get('emitDistinctChangesOnly');
28227
+ const emitDistinctChangesOnlyValue = evaluator.evaluate(emitDistinctChangesOnlyExpr);
28228
+ if (typeof emitDistinctChangesOnlyValue !== 'boolean') {
28229
+ throw createValueHasWrongTypeError(emitDistinctChangesOnlyExpr, emitDistinctChangesOnlyValue, `@${name} options.emitDistinctChangesOnlys must be a boolean`);
28230
+ }
28231
+ emitDistinctChangesOnly = emitDistinctChangesOnlyValue;
28232
+ }
28121
28233
  if (options.has('static')) {
28122
28234
  const staticValue = evaluator.evaluate(options.get('static'));
28123
28235
  if (typeof staticValue !== 'boolean') {
@@ -28137,6 +28249,7 @@ Either add the @Injectable() decorator to '${provider.node.name
28137
28249
  descendants,
28138
28250
  read,
28139
28251
  static: isStatic,
28252
+ emitDistinctChangesOnly,
28140
28253
  };
28141
28254
  }
28142
28255
  function extractQueriesFromDecorator(queryData, reflector, evaluator, isCore) {
@@ -28528,24 +28641,13 @@ Either add the @Injectable() decorator to '${provider.node.name
28528
28641
  template = preanalyzed;
28529
28642
  }
28530
28643
  else {
28531
- // The template was not already parsed. Either there's a templateUrl, or an inline template.
28532
- if (component.has('templateUrl')) {
28533
- const templateUrlExpr = component.get('templateUrl');
28534
- const templateUrl = this.evaluator.evaluate(templateUrlExpr);
28535
- if (typeof templateUrl !== 'string') {
28536
- throw createValueHasWrongTypeError(templateUrlExpr, templateUrl, 'templateUrl must be a string');
28537
- }
28538
- const resourceUrl = this.resourceLoader.resolve(templateUrl, containingFile);
28539
- template = this._extractExternalTemplate(node, component, templateUrlExpr, resourceUrl);
28540
- }
28541
- else {
28542
- // Expect an inline template to be present.
28543
- template = this._extractInlineTemplate(node, decorator, component, containingFile);
28544
- }
28644
+ const templateDecl = this.parseTemplateDeclaration(decorator, component, containingFile);
28645
+ template = this.extractTemplate(node, templateDecl);
28545
28646
  }
28546
- const templateResource = template.isInline ?
28547
- { path: null, expression: component.get('template') } :
28548
- { path: absoluteFrom(template.templateUrl), expression: template.sourceMapping.node };
28647
+ const templateResource = template.isInline ? { path: null, expression: component.get('template') } : {
28648
+ path: absoluteFrom(template.declaration.resolvedTemplateUrl),
28649
+ expression: template.sourceMapping.node
28650
+ };
28549
28651
  // Figure out the set of styles. The ordering here is important: external resources (styleUrls)
28550
28652
  // precede inline styles, and styles defined in the template override styles defined in the
28551
28653
  // component.
@@ -28565,9 +28667,11 @@ Either add the @Injectable() decorator to '${provider.node.name
28565
28667
  }
28566
28668
  }
28567
28669
  }
28670
+ let inlineStyles = null;
28568
28671
  if (component.has('styles')) {
28569
28672
  const litStyles = parseFieldArrayValue(component, 'styles', this.evaluator);
28570
28673
  if (litStyles !== null) {
28674
+ inlineStyles = [...litStyles];
28571
28675
  if (styles === null) {
28572
28676
  styles = litStyles;
28573
28677
  }
@@ -28607,6 +28711,8 @@ Either add the @Injectable() decorator to '${provider.node.name
28607
28711
  template,
28608
28712
  providersRequiringFactory,
28609
28713
  viewProvidersRequiringFactory,
28714
+ inlineStyles,
28715
+ styleUrls,
28610
28716
  resources: {
28611
28717
  styles: styleResources,
28612
28718
  template: templateResource,
@@ -28775,6 +28881,33 @@ Either add the @Injectable() decorator to '${provider.node.name
28775
28881
  }
28776
28882
  return { data };
28777
28883
  }
28884
+ updateResources(node, analysis) {
28885
+ const containingFile = node.getSourceFile().fileName;
28886
+ // If the template is external, re-parse it.
28887
+ const templateDecl = analysis.template.declaration;
28888
+ if (!templateDecl.isInline) {
28889
+ analysis.template = this.extractTemplate(node, templateDecl);
28890
+ }
28891
+ // Update any external stylesheets and rebuild the combined 'styles' list.
28892
+ // TODO(alxhub): write tests for styles when the primary compiler uses the updateResources path
28893
+ let styles = [];
28894
+ if (analysis.styleUrls !== null) {
28895
+ for (const styleUrl of analysis.styleUrls) {
28896
+ const resolvedStyleUrl = this.resourceLoader.resolve(styleUrl, containingFile);
28897
+ const styleText = this.resourceLoader.load(resolvedStyleUrl);
28898
+ styles.push(styleText);
28899
+ }
28900
+ }
28901
+ if (analysis.inlineStyles !== null) {
28902
+ for (const styleText of analysis.inlineStyles) {
28903
+ styles.push(styleText);
28904
+ }
28905
+ }
28906
+ for (const styleText of analysis.template.styles) {
28907
+ styles.push(styleText);
28908
+ }
28909
+ analysis.meta.styles = styles;
28910
+ }
28778
28911
  compileFull(node, analysis, resolution, pool) {
28779
28912
  if (analysis.template.errors !== null && analysis.template.errors.length > 0) {
28780
28913
  return [];
@@ -28881,7 +29014,8 @@ Either add the @Injectable() decorator to '${provider.node.name
28881
29014
  // URLs to resolve.
28882
29015
  if (templatePromise !== undefined) {
28883
29016
  return templatePromise.then(() => {
28884
- const template = this._extractExternalTemplate(node, component, templateUrlExpr, resourceUrl);
29017
+ const templateDecl = this.parseTemplateDeclaration(decorator, component, containingFile);
29018
+ const template = this.extractTemplate(node, templateDecl);
28885
29019
  this.preanalyzeTemplateCache.set(node, template);
28886
29020
  return template;
28887
29021
  });
@@ -28891,125 +29025,157 @@ Either add the @Injectable() decorator to '${provider.node.name
28891
29025
  }
28892
29026
  }
28893
29027
  else {
28894
- const template = this._extractInlineTemplate(node, decorator, component, containingFile);
29028
+ const templateDecl = this.parseTemplateDeclaration(decorator, component, containingFile);
29029
+ const template = this.extractTemplate(node, templateDecl);
28895
29030
  this.preanalyzeTemplateCache.set(node, template);
28896
29031
  return Promise.resolve(template);
28897
29032
  }
28898
29033
  }
28899
- _extractExternalTemplate(node, component, templateUrlExpr, resourceUrl) {
28900
- const templateStr = this.resourceLoader.load(resourceUrl);
28901
- if (this.depTracker !== null) {
28902
- this.depTracker.addResourceDependency(node.getSourceFile(), absoluteFrom(resourceUrl));
28903
- }
28904
- const template = this._parseTemplate(component, templateStr, /* templateLiteral */ null, sourceMapUrl(resourceUrl),
28905
- /* templateRange */ undefined,
28906
- /* escapedString */ false);
28907
- return Object.assign(Object.assign({}, template), { sourceMapping: {
28908
- type: 'external',
28909
- componentClass: node,
28910
- node: templateUrlExpr,
28911
- template: templateStr,
28912
- templateUrl: resourceUrl,
28913
- } });
28914
- }
28915
- _extractInlineTemplate(node, decorator, component, containingFile) {
28916
- if (!component.has('template')) {
28917
- throw new FatalDiagnosticError(ErrorCode.COMPONENT_MISSING_TEMPLATE, Decorator.nodeForError(decorator), 'component is missing a template');
28918
- }
28919
- const templateExpr = component.get('template');
28920
- let templateStr;
28921
- let templateLiteral = null;
28922
- let templateUrl = '';
28923
- let templateRange = undefined;
28924
- let sourceMapping;
28925
- let escapedString = false;
28926
- // We only support SourceMaps for inline templates that are simple string literals.
28927
- if (ts$1.isStringLiteral(templateExpr) || ts$1.isNoSubstitutionTemplateLiteral(templateExpr)) {
28928
- // the start and end of the `templateExpr` node includes the quotation marks, which we
28929
- // must
28930
- // strip
28931
- templateRange = getTemplateRange(templateExpr);
28932
- templateStr = templateExpr.getSourceFile().text;
28933
- templateLiteral = templateExpr;
28934
- templateUrl = containingFile;
28935
- escapedString = true;
28936
- sourceMapping = {
28937
- type: 'direct',
28938
- node: templateExpr,
28939
- };
28940
- }
28941
- else {
28942
- const resolvedTemplate = this.evaluator.evaluate(templateExpr);
28943
- if (typeof resolvedTemplate !== 'string') {
28944
- throw createValueHasWrongTypeError(templateExpr, resolvedTemplate, 'template must be a string');
28945
- }
28946
- templateStr = resolvedTemplate;
28947
- sourceMapping = {
28948
- type: 'indirect',
28949
- node: templateExpr,
28950
- componentClass: node,
28951
- template: templateStr,
28952
- };
28953
- }
28954
- const template = this._parseTemplate(component, templateStr, templateLiteral, templateUrl, templateRange, escapedString);
28955
- return Object.assign(Object.assign({}, template), { sourceMapping });
28956
- }
28957
- _parseTemplate(component, templateStr, templateLiteral, templateUrl, templateRange, escapedString) {
28958
- let preserveWhitespaces = this.defaultPreserveWhitespaces;
28959
- if (component.has('preserveWhitespaces')) {
28960
- const expr = component.get('preserveWhitespaces');
28961
- const value = this.evaluator.evaluate(expr);
28962
- if (typeof value !== 'boolean') {
28963
- throw createValueHasWrongTypeError(expr, value, 'preserveWhitespaces must be a boolean');
29034
+ extractTemplate(node, template) {
29035
+ if (template.isInline) {
29036
+ let templateStr;
29037
+ let templateLiteral = null;
29038
+ let templateUrl = '';
29039
+ let templateRange = null;
29040
+ let sourceMapping;
29041
+ let escapedString = false;
29042
+ // We only support SourceMaps for inline templates that are simple string literals.
29043
+ if (ts$1.isStringLiteral(template.expression) ||
29044
+ ts$1.isNoSubstitutionTemplateLiteral(template.expression)) {
29045
+ // the start and end of the `templateExpr` node includes the quotation marks, which we must
29046
+ // strip
29047
+ templateRange = getTemplateRange(template.expression);
29048
+ templateStr = template.expression.getSourceFile().text;
29049
+ templateLiteral = template.expression;
29050
+ templateUrl = template.templateUrl;
29051
+ escapedString = true;
29052
+ sourceMapping = {
29053
+ type: 'direct',
29054
+ node: template.expression,
29055
+ };
28964
29056
  }
28965
- preserveWhitespaces = value;
28966
- }
28967
- let interpolationConfig = DEFAULT_INTERPOLATION_CONFIG;
28968
- if (component.has('interpolation')) {
28969
- const expr = component.get('interpolation');
28970
- const value = this.evaluator.evaluate(expr);
28971
- if (!Array.isArray(value) || value.length !== 2 ||
28972
- !value.every(element => typeof element === 'string')) {
28973
- throw createValueHasWrongTypeError(expr, value, 'interpolation must be an array with 2 elements of string type');
29057
+ else {
29058
+ const resolvedTemplate = this.evaluator.evaluate(template.expression);
29059
+ if (typeof resolvedTemplate !== 'string') {
29060
+ throw createValueHasWrongTypeError(template.expression, resolvedTemplate, 'template must be a string');
29061
+ }
29062
+ templateStr = resolvedTemplate;
29063
+ sourceMapping = {
29064
+ type: 'indirect',
29065
+ node: template.expression,
29066
+ componentClass: node,
29067
+ template: templateStr,
29068
+ };
28974
29069
  }
28975
- interpolationConfig = InterpolationConfig.fromArray(value);
29070
+ return Object.assign(Object.assign({}, this._parseTemplate(template, templateStr, templateRange, escapedString)), { sourceMapping, declaration: template });
28976
29071
  }
29072
+ else {
29073
+ const templateStr = this.resourceLoader.load(template.resolvedTemplateUrl);
29074
+ if (this.depTracker !== null) {
29075
+ this.depTracker.addResourceDependency(node.getSourceFile(), absoluteFrom(template.resolvedTemplateUrl));
29076
+ }
29077
+ return Object.assign(Object.assign({}, this._parseTemplate(template, templateStr, /* templateRange */ null,
29078
+ /* escapedString */ false)), { sourceMapping: {
29079
+ type: 'external',
29080
+ componentClass: node,
29081
+ // TODO(alxhub): TS in g3 is unable to make this inference on its own, so cast it here
29082
+ // until g3 is able to figure this out.
29083
+ node: template.templateUrlExpression,
29084
+ template: templateStr,
29085
+ templateUrl: template.resolvedTemplateUrl,
29086
+ }, declaration: template });
29087
+ }
29088
+ }
29089
+ _parseTemplate(template, templateStr, templateRange, escapedString) {
28977
29090
  // We always normalize line endings if the template has been escaped (i.e. is inline).
28978
29091
  const i18nNormalizeLineEndingsInICUs = escapedString || this.i18nNormalizeLineEndingsInICUs;
28979
- const isInline = component.has('template');
28980
- const parsedTemplate = parseTemplate(templateStr, templateUrl, {
28981
- preserveWhitespaces,
28982
- interpolationConfig,
28983
- range: templateRange,
29092
+ const parsedTemplate = parseTemplate(templateStr, template.sourceMapUrl, {
29093
+ preserveWhitespaces: template.preserveWhitespaces,
29094
+ interpolationConfig: template.interpolationConfig,
29095
+ range: templateRange !== null && templateRange !== void 0 ? templateRange : undefined,
28984
29096
  escapedString,
28985
29097
  enableI18nLegacyMessageIdFormat: this.enableI18nLegacyMessageIdFormat,
28986
29098
  i18nNormalizeLineEndingsInICUs,
28987
- isInline,
29099
+ isInline: template.isInline,
28988
29100
  });
28989
29101
  // Unfortunately, the primary parse of the template above may not contain accurate source map
28990
29102
  // information. If used directly, it would result in incorrect code locations in template
28991
- // errors, etc. There are two main problems:
29103
+ // errors, etc. There are three main problems:
28992
29104
  //
28993
29105
  // 1. `preserveWhitespaces: false` annihilates the correctness of template source mapping, as
28994
29106
  // the whitespace transformation changes the contents of HTML text nodes before they're
28995
29107
  // parsed into Angular expressions.
28996
- // 2. By default, the template parser strips leading trivia characters (like spaces, tabs, and
29108
+ // 2. `preserveLineEndings: false` causes growing misalignments in templates that use '\r\n'
29109
+ // line endings, by normalizing them to '\n'.
29110
+ // 3. By default, the template parser strips leading trivia characters (like spaces, tabs, and
28997
29111
  // newlines). This also destroys source mapping information.
28998
29112
  //
28999
29113
  // In order to guarantee the correctness of diagnostics, templates are parsed a second time
29000
29114
  // with the above options set to preserve source mappings.
29001
- const { nodes: diagNodes } = parseTemplate(templateStr, templateUrl, {
29115
+ const { nodes: diagNodes } = parseTemplate(templateStr, template.sourceMapUrl, {
29002
29116
  preserveWhitespaces: true,
29003
- interpolationConfig,
29004
- range: templateRange,
29117
+ preserveLineEndings: true,
29118
+ interpolationConfig: template.interpolationConfig,
29119
+ range: templateRange !== null && templateRange !== void 0 ? templateRange : undefined,
29005
29120
  escapedString,
29006
29121
  enableI18nLegacyMessageIdFormat: this.enableI18nLegacyMessageIdFormat,
29007
29122
  i18nNormalizeLineEndingsInICUs,
29008
29123
  leadingTriviaChars: [],
29009
- isInline,
29124
+ isInline: template.isInline,
29010
29125
  });
29011
- return Object.assign(Object.assign({}, parsedTemplate), { diagNodes, template: templateLiteral !== null ? new WrappedNodeExpr(templateLiteral) : templateStr, templateUrl,
29012
- isInline, file: new ParseSourceFile(templateStr, templateUrl) });
29126
+ 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) });
29127
+ }
29128
+ parseTemplateDeclaration(decorator, component, containingFile) {
29129
+ let preserveWhitespaces = this.defaultPreserveWhitespaces;
29130
+ if (component.has('preserveWhitespaces')) {
29131
+ const expr = component.get('preserveWhitespaces');
29132
+ const value = this.evaluator.evaluate(expr);
29133
+ if (typeof value !== 'boolean') {
29134
+ throw createValueHasWrongTypeError(expr, value, 'preserveWhitespaces must be a boolean');
29135
+ }
29136
+ preserveWhitespaces = value;
29137
+ }
29138
+ let interpolationConfig = DEFAULT_INTERPOLATION_CONFIG;
29139
+ if (component.has('interpolation')) {
29140
+ const expr = component.get('interpolation');
29141
+ const value = this.evaluator.evaluate(expr);
29142
+ if (!Array.isArray(value) || value.length !== 2 ||
29143
+ !value.every(element => typeof element === 'string')) {
29144
+ throw createValueHasWrongTypeError(expr, value, 'interpolation must be an array with 2 elements of string type');
29145
+ }
29146
+ interpolationConfig = InterpolationConfig.fromArray(value);
29147
+ }
29148
+ if (component.has('templateUrl')) {
29149
+ const templateUrlExpr = component.get('templateUrl');
29150
+ const templateUrl = this.evaluator.evaluate(templateUrlExpr);
29151
+ if (typeof templateUrl !== 'string') {
29152
+ throw createValueHasWrongTypeError(templateUrlExpr, templateUrl, 'templateUrl must be a string');
29153
+ }
29154
+ const resourceUrl = this.resourceLoader.resolve(templateUrl, containingFile);
29155
+ return {
29156
+ isInline: false,
29157
+ interpolationConfig,
29158
+ preserveWhitespaces,
29159
+ templateUrl,
29160
+ templateUrlExpression: templateUrlExpr,
29161
+ resolvedTemplateUrl: resourceUrl,
29162
+ sourceMapUrl: sourceMapUrl(resourceUrl),
29163
+ };
29164
+ }
29165
+ else if (component.has('template')) {
29166
+ return {
29167
+ isInline: true,
29168
+ interpolationConfig,
29169
+ preserveWhitespaces,
29170
+ expression: component.get('template'),
29171
+ templateUrl: containingFile,
29172
+ resolvedTemplateUrl: containingFile,
29173
+ sourceMapUrl: containingFile,
29174
+ };
29175
+ }
29176
+ else {
29177
+ throw new FatalDiagnosticError(ErrorCode.COMPONENT_MISSING_TEMPLATE, Decorator.nodeForError(decorator), 'component is missing a template');
29178
+ }
29013
29179
  }
29014
29180
  _expressionToImportedFile(expr, origin) {
29015
29181
  if (!(expr instanceof ExternalExpr)) {
@@ -31252,6 +31418,12 @@ Either add the @Injectable() decorator to '${provider.node.name
31252
31418
  this.cache.set(resolvedUrl, result);
31253
31419
  return result;
31254
31420
  }
31421
+ /**
31422
+ * Invalidate the entire resource cache.
31423
+ */
31424
+ invalidate() {
31425
+ this.cache.clear();
31426
+ }
31255
31427
  /**
31256
31428
  * Attempt to resolve `url` in the context of `fromFile`, while respecting the rootDirs
31257
31429
  * option from the tsconfig. First, normalize the file name.
@@ -36547,12 +36719,6 @@ Either add the @Injectable() decorator to '${provider.node.name
36547
36719
  const shimData = this.pendingShimForComponent(ref.node);
36548
36720
  const templateId = fileData.sourceManager.getTemplateId(ref.node);
36549
36721
  const templateDiagnostics = [];
36550
- const sfPath = absoluteFromSourceFile(ref.node.getSourceFile());
36551
- const overrideTemplate = this.host.getTemplateOverride(sfPath, ref.node);
36552
- if (overrideTemplate !== null) {
36553
- template = overrideTemplate.nodes;
36554
- parseErrors = overrideTemplate.errors;
36555
- }
36556
36722
  if (parseErrors !== null) {
36557
36723
  templateDiagnostics.push(...this.getTemplateDiagnostics(parseErrors, templateId, sourceMapping));
36558
36724
  }
@@ -37459,38 +37625,22 @@ Either add the @Injectable() decorator to '${provider.node.name
37459
37625
  /**
37460
37626
  * Stores directives and pipes that are in scope for each component.
37461
37627
  *
37462
- * Unlike other caches, the scope of a component is not affected by its template, so this
37463
- * cache does not need to be invalidate if the template is overridden. It will be destroyed when
37464
- * the `ts.Program` changes and the `TemplateTypeCheckerImpl` as a whole is destroyed and
37465
- * replaced.
37628
+ * Unlike other caches, the scope of a component is not affected by its template. It will be
37629
+ * destroyed when the `ts.Program` changes and the `TemplateTypeCheckerImpl` as a whole is
37630
+ * destroyed and replaced.
37466
37631
  */
37467
37632
  this.scopeCache = new Map();
37468
37633
  /**
37469
37634
  * Stores potential element tags for each component (a union of DOM tags as well as directive
37470
37635
  * tags).
37471
37636
  *
37472
- * Unlike other caches, the scope of a component is not affected by its template, so this
37473
- * cache does not need to be invalidate if the template is overridden. It will be destroyed when
37474
- * the `ts.Program` changes and the `TemplateTypeCheckerImpl` as a whole is destroyed and
37475
- * replaced.
37637
+ * Unlike other caches, the scope of a component is not affected by its template. It will be
37638
+ * destroyed when the `ts.Program` changes and the `TemplateTypeCheckerImpl` as a whole is
37639
+ * destroyed and replaced.
37476
37640
  */
37477
37641
  this.elementTagCache = new Map();
37478
37642
  this.isComplete = false;
37479
37643
  }
37480
- resetOverrides() {
37481
- for (const fileRecord of this.state.values()) {
37482
- if (fileRecord.templateOverrides !== null) {
37483
- fileRecord.templateOverrides = null;
37484
- fileRecord.shimData.clear();
37485
- fileRecord.isComplete = false;
37486
- }
37487
- }
37488
- // Ideally only those components with overridden templates would have their caches invalidated,
37489
- // but the `TemplateTypeCheckerImpl` does not track the class for components with overrides. As
37490
- // a quick workaround, clear the entire cache instead.
37491
- this.completionCache.clear();
37492
- this.symbolBuilderCache.clear();
37493
- }
37494
37644
  getTemplate(component) {
37495
37645
  const { data } = this.getLatestComponentState(component);
37496
37646
  if (data === null) {
@@ -37527,28 +37677,6 @@ Either add the @Injectable() decorator to '${provider.node.name
37527
37677
  }
37528
37678
  return { data, tcb, shimPath };
37529
37679
  }
37530
- overrideComponentTemplate(component, template) {
37531
- const { nodes, errors } = parseTemplate(template, 'override.html', {
37532
- preserveWhitespaces: true,
37533
- leadingTriviaChars: [],
37534
- });
37535
- const filePath = absoluteFromSourceFile(component.getSourceFile());
37536
- const fileRecord = this.getFileData(filePath);
37537
- const id = fileRecord.sourceManager.getTemplateId(component);
37538
- if (fileRecord.templateOverrides === null) {
37539
- fileRecord.templateOverrides = new Map();
37540
- }
37541
- fileRecord.templateOverrides.set(id, { nodes, errors });
37542
- // Clear data for the shim in question, so it'll be regenerated on the next request.
37543
- const shimFile = this.typeCheckingStrategy.shimPathForComponent(component);
37544
- fileRecord.shimData.delete(shimFile);
37545
- fileRecord.isComplete = false;
37546
- this.isComplete = false;
37547
- // Overriding a component's template invalidates its cached results.
37548
- this.completionCache.delete(component);
37549
- this.symbolBuilderCache.delete(component);
37550
- return { nodes, errors };
37551
- }
37552
37680
  isTrackedTypeCheckFile(filePath) {
37553
37681
  return this.getFileAndShimRecordsForPath(filePath) !== null;
37554
37682
  }
@@ -37648,6 +37776,20 @@ Either add the @Injectable() decorator to '${provider.node.name
37648
37776
  }
37649
37777
  return engine.getExpressionCompletionLocation(ast);
37650
37778
  }
37779
+ invalidateClass(clazz) {
37780
+ this.completionCache.delete(clazz);
37781
+ this.symbolBuilderCache.delete(clazz);
37782
+ this.scopeCache.delete(clazz);
37783
+ this.elementTagCache.delete(clazz);
37784
+ const sf = clazz.getSourceFile();
37785
+ const sfPath = absoluteFromSourceFile(sf);
37786
+ const shimPath = this.typeCheckingStrategy.shimPathForComponent(clazz);
37787
+ const fileData = this.getFileData(sfPath);
37788
+ const templateId = fileData.sourceManager.getTemplateId(clazz);
37789
+ fileData.shimData.delete(shimPath);
37790
+ fileData.isComplete = false;
37791
+ this.isComplete = false;
37792
+ }
37651
37793
  getOrCreateCompletionEngine(component) {
37652
37794
  if (this.completionCache.has(component)) {
37653
37795
  return this.completionCache.get(component);
@@ -37664,18 +37806,13 @@ Either add the @Injectable() decorator to '${provider.node.name
37664
37806
  const sfPath = absoluteFromSourceFile(sf);
37665
37807
  if (this.state.has(sfPath)) {
37666
37808
  const existingResults = this.state.get(sfPath);
37667
- if (existingResults.templateOverrides !== null) {
37668
- // Cannot adopt prior results if template overrides have been requested.
37669
- return;
37670
- }
37671
37809
  if (existingResults.isComplete) {
37672
37810
  // All data for this file has already been generated, so no need to adopt anything.
37673
37811
  return;
37674
37812
  }
37675
37813
  }
37676
37814
  const previousResults = this.priorBuild.priorTypeCheckingResultsFor(sf);
37677
- if (previousResults === null || !previousResults.isComplete ||
37678
- previousResults.templateOverrides !== null) {
37815
+ if (previousResults === null || !previousResults.isComplete) {
37679
37816
  return;
37680
37817
  }
37681
37818
  this.state.set(sfPath, previousResults);
@@ -37766,7 +37903,6 @@ Either add the @Injectable() decorator to '${provider.node.name
37766
37903
  if (!this.state.has(path)) {
37767
37904
  this.state.set(path, {
37768
37905
  hasInlines: false,
37769
- templateOverrides: null,
37770
37906
  sourceManager: new TemplateSourceManager(),
37771
37907
  isComplete: false,
37772
37908
  shimData: new Map(),
@@ -37919,17 +38055,6 @@ Either add the @Injectable() decorator to '${provider.node.name
37919
38055
  // The component needs to be checked unless the shim which would contain it already exists.
37920
38056
  return !fileData.shimData.has(shimPath);
37921
38057
  }
37922
- getTemplateOverride(sfPath, node) {
37923
- const fileData = this.impl.getFileData(sfPath);
37924
- if (fileData.templateOverrides === null) {
37925
- return null;
37926
- }
37927
- const templateId = fileData.sourceManager.getTemplateId(node);
37928
- if (fileData.templateOverrides.has(templateId)) {
37929
- return fileData.templateOverrides.get(templateId);
37930
- }
37931
- return null;
37932
- }
37933
38058
  recordShimData(sfPath, data) {
37934
38059
  const fileData = this.impl.getFileData(sfPath);
37935
38060
  fileData.shimData.set(data.path, data);
@@ -37969,17 +38094,6 @@ Either add the @Injectable() decorator to '${provider.node.name
37969
38094
  // Only need to generate a TCB for the class if no shim exists for it currently.
37970
38095
  return !this.fileData.shimData.has(shimPath);
37971
38096
  }
37972
- getTemplateOverride(sfPath, node) {
37973
- this.assertPath(sfPath);
37974
- if (this.fileData.templateOverrides === null) {
37975
- return null;
37976
- }
37977
- const templateId = this.fileData.sourceManager.getTemplateId(node);
37978
- if (this.fileData.templateOverrides.has(templateId)) {
37979
- return this.fileData.templateOverrides.get(templateId);
37980
- }
37981
- return null;
37982
- }
37983
38097
  recordShimData(sfPath, data) {
37984
38098
  this.assertPath(sfPath);
37985
38099
  // Previous type-checking state may have required the use of inlines (assuming they were
@@ -38033,6 +38147,61 @@ Either add the @Injectable() decorator to '${provider.node.name
38033
38147
  * Use of this source code is governed by an MIT-style license that can be
38034
38148
  * found in the LICENSE file at https://angular.io/license
38035
38149
  */
38150
+ /**
38151
+ * Discriminant type for a `CompilationTicket`.
38152
+ */
38153
+ var CompilationTicketKind;
38154
+ (function (CompilationTicketKind) {
38155
+ CompilationTicketKind[CompilationTicketKind["Fresh"] = 0] = "Fresh";
38156
+ CompilationTicketKind[CompilationTicketKind["IncrementalTypeScript"] = 1] = "IncrementalTypeScript";
38157
+ CompilationTicketKind[CompilationTicketKind["IncrementalResource"] = 2] = "IncrementalResource";
38158
+ })(CompilationTicketKind || (CompilationTicketKind = {}));
38159
+ /**
38160
+ * Create a `CompilationTicket` for a brand new compilation, using no prior state.
38161
+ */
38162
+ function freshCompilationTicket(tsProgram, options, incrementalBuildStrategy, typeCheckingProgramStrategy, enableTemplateTypeChecker, usePoisonedData) {
38163
+ return {
38164
+ kind: CompilationTicketKind.Fresh,
38165
+ tsProgram,
38166
+ options,
38167
+ incrementalBuildStrategy,
38168
+ typeCheckingProgramStrategy,
38169
+ enableTemplateTypeChecker,
38170
+ usePoisonedData,
38171
+ };
38172
+ }
38173
+ /**
38174
+ * Create a `CompilationTicket` as efficiently as possible, based on a previous `NgCompiler`
38175
+ * instance and a new `ts.Program`.
38176
+ */
38177
+ function incrementalFromCompilerTicket(oldCompiler, newProgram, incrementalBuildStrategy, typeCheckingProgramStrategy, modifiedResourceFiles) {
38178
+ const oldProgram = oldCompiler.getNextProgram();
38179
+ const oldDriver = oldCompiler.incrementalStrategy.getIncrementalDriver(oldProgram);
38180
+ if (oldDriver === null) {
38181
+ // No incremental step is possible here, since no IncrementalDriver was found for the old
38182
+ // program.
38183
+ return freshCompilationTicket(newProgram, oldCompiler.options, incrementalBuildStrategy, typeCheckingProgramStrategy, oldCompiler.enableTemplateTypeChecker, oldCompiler.usePoisonedData);
38184
+ }
38185
+ const newDriver = IncrementalDriver.reconcile(oldProgram, oldDriver, newProgram, modifiedResourceFiles);
38186
+ return {
38187
+ kind: CompilationTicketKind.IncrementalTypeScript,
38188
+ enableTemplateTypeChecker: oldCompiler.enableTemplateTypeChecker,
38189
+ usePoisonedData: oldCompiler.usePoisonedData,
38190
+ options: oldCompiler.options,
38191
+ incrementalBuildStrategy,
38192
+ typeCheckingProgramStrategy,
38193
+ newDriver,
38194
+ oldProgram,
38195
+ newProgram,
38196
+ };
38197
+ }
38198
+ function resourceChangeTicket(compiler, modifiedResourceFiles) {
38199
+ return {
38200
+ kind: CompilationTicketKind.IncrementalResource,
38201
+ compiler,
38202
+ modifiedResourceFiles,
38203
+ };
38204
+ }
38036
38205
  /**
38037
38206
  * The heart of the Angular Ivy compiler.
38038
38207
  *
@@ -38046,12 +38215,13 @@ Either add the @Injectable() decorator to '${provider.node.name
38046
38215
  * See the README.md for more information.
38047
38216
  */
38048
38217
  class NgCompiler {
38049
- constructor(adapter, options, tsProgram, typeCheckingProgramStrategy, incrementalStrategy, enableTemplateTypeChecker, usePoisonedData, oldProgram = null, perfRecorder = NOOP_PERF_RECORDER) {
38218
+ constructor(adapter, options, tsProgram, typeCheckingProgramStrategy, incrementalStrategy, incrementalDriver, enableTemplateTypeChecker, usePoisonedData, perfRecorder = NOOP_PERF_RECORDER) {
38050
38219
  this.adapter = adapter;
38051
38220
  this.options = options;
38052
38221
  this.tsProgram = tsProgram;
38053
38222
  this.typeCheckingProgramStrategy = typeCheckingProgramStrategy;
38054
38223
  this.incrementalStrategy = incrementalStrategy;
38224
+ this.incrementalDriver = incrementalDriver;
38055
38225
  this.enableTemplateTypeChecker = enableTemplateTypeChecker;
38056
38226
  this.usePoisonedData = usePoisonedData;
38057
38227
  this.perfRecorder = perfRecorder;
@@ -38095,31 +38265,55 @@ Either add the @Injectable() decorator to '${provider.node.name
38095
38265
  new ModuleResolver(tsProgram, this.options, this.adapter, moduleResolutionCache);
38096
38266
  this.resourceManager = new AdapterResourceLoader(adapter, this.options);
38097
38267
  this.cycleAnalyzer = new CycleAnalyzer(new ImportGraph(this.moduleResolver));
38098
- let modifiedResourceFiles = null;
38099
- if (this.adapter.getModifiedResourceFiles !== undefined) {
38100
- modifiedResourceFiles = this.adapter.getModifiedResourceFiles() || null;
38101
- }
38102
- if (oldProgram === null) {
38103
- this.incrementalDriver = IncrementalDriver.fresh(tsProgram);
38104
- }
38105
- else {
38106
- const oldDriver = this.incrementalStrategy.getIncrementalDriver(oldProgram);
38107
- if (oldDriver !== null) {
38108
- this.incrementalDriver =
38109
- IncrementalDriver.reconcile(oldProgram, oldDriver, tsProgram, modifiedResourceFiles);
38110
- }
38111
- else {
38112
- // A previous ts.Program was used to create the current one, but it wasn't from an
38113
- // `NgCompiler`. That doesn't hurt anything, but the Angular analysis will have to start
38114
- // from a fresh state.
38115
- this.incrementalDriver = IncrementalDriver.fresh(tsProgram);
38116
- }
38117
- }
38118
38268
  this.incrementalStrategy.setIncrementalDriver(this.incrementalDriver, tsProgram);
38119
38269
  this.ignoreForDiagnostics =
38120
38270
  new Set(tsProgram.getSourceFiles().filter(sf => this.adapter.isShim(sf)));
38121
38271
  this.ignoreForEmit = this.adapter.ignoreForEmit;
38122
38272
  }
38273
+ /**
38274
+ * Convert a `CompilationTicket` into an `NgCompiler` instance for the requested compilation.
38275
+ *
38276
+ * Depending on the nature of the compilation request, the `NgCompiler` instance may be reused
38277
+ * from a previous compilation and updated with any changes, it may be a new instance which
38278
+ * incrementally reuses state from a previous compilation, or it may represent a fresh compilation
38279
+ * entirely.
38280
+ */
38281
+ static fromTicket(ticket, adapter, perfRecorder) {
38282
+ switch (ticket.kind) {
38283
+ case CompilationTicketKind.Fresh:
38284
+ return new NgCompiler(adapter, ticket.options, ticket.tsProgram, ticket.typeCheckingProgramStrategy, ticket.incrementalBuildStrategy, IncrementalDriver.fresh(ticket.tsProgram), ticket.enableTemplateTypeChecker, ticket.usePoisonedData, perfRecorder);
38285
+ case CompilationTicketKind.IncrementalTypeScript:
38286
+ return new NgCompiler(adapter, ticket.options, ticket.newProgram, ticket.typeCheckingProgramStrategy, ticket.incrementalBuildStrategy, ticket.newDriver, ticket.enableTemplateTypeChecker, ticket.usePoisonedData, perfRecorder);
38287
+ case CompilationTicketKind.IncrementalResource:
38288
+ const compiler = ticket.compiler;
38289
+ compiler.updateWithChangedResources(ticket.modifiedResourceFiles);
38290
+ return compiler;
38291
+ }
38292
+ }
38293
+ updateWithChangedResources(changedResources) {
38294
+ if (this.compilation === null) {
38295
+ // Analysis hasn't happened yet, so no update is necessary - any changes to resources will be
38296
+ // captured by the inital analysis pass itself.
38297
+ return;
38298
+ }
38299
+ this.resourceManager.invalidate();
38300
+ const classesToUpdate = new Set();
38301
+ for (const resourceFile of changedResources) {
38302
+ for (const templateClass of this.getComponentsWithTemplateFile(resourceFile)) {
38303
+ classesToUpdate.add(templateClass);
38304
+ }
38305
+ for (const styleClass of this.getComponentsWithStyleFile(resourceFile)) {
38306
+ classesToUpdate.add(styleClass);
38307
+ }
38308
+ }
38309
+ for (const clazz of classesToUpdate) {
38310
+ this.compilation.traitCompiler.updateResources(clazz);
38311
+ if (!ts$1.isClassDeclaration(clazz)) {
38312
+ continue;
38313
+ }
38314
+ this.compilation.templateTypeChecker.invalidateClass(clazz);
38315
+ }
38316
+ }
38123
38317
  /**
38124
38318
  * Get the resource dependencies of a file.
38125
38319
  *
@@ -38133,7 +38327,7 @@ Either add the @Injectable() decorator to '${provider.node.name
38133
38327
  * Get all Angular-related diagnostics for this compilation.
38134
38328
  */
38135
38329
  getDiagnostics() {
38136
- return [...this.getNonTemplateDiagnostics(), ...this.getTemplateDiagnostics()];
38330
+ return this.addMessageTextDetails([...this.getNonTemplateDiagnostics(), ...this.getTemplateDiagnostics()]);
38137
38331
  }
38138
38332
  /**
38139
38333
  * Get all Angular-related diagnostics for this compilation.
@@ -38141,10 +38335,22 @@ Either add the @Injectable() decorator to '${provider.node.name
38141
38335
  * If a `ts.SourceFile` is passed, only diagnostics related to that file are returned.
38142
38336
  */
38143
38337
  getDiagnosticsForFile(file, optimizeFor) {
38144
- return [
38338
+ return this.addMessageTextDetails([
38145
38339
  ...this.getNonTemplateDiagnostics().filter(diag => diag.file === file),
38146
38340
  ...this.getTemplateDiagnosticsForFile(file, optimizeFor)
38147
- ];
38341
+ ]);
38342
+ }
38343
+ /**
38344
+ * Add Angular.io error guide links to diagnostics for this compilation.
38345
+ */
38346
+ addMessageTextDetails(diagnostics) {
38347
+ return diagnostics.map(diag => {
38348
+ if (diag.code && COMPILER_ERRORS_WITH_GUIDES.has(ngErrorCode(diag.code))) {
38349
+ return Object.assign(Object.assign({}, diag), { messageText: diag.messageText +
38350
+ `. Find more at ${ERROR_DETAILS_PAGE_BASE_URL}/NG${ngErrorCode(diag.code)}` });
38351
+ }
38352
+ return diag;
38353
+ });
38148
38354
  }
38149
38355
  /**
38150
38356
  * Get all setup-related diagnostics for this compilation.
@@ -38734,7 +38940,7 @@ Either add the @Injectable() decorator to '${provider.node.name
38734
38940
  }
38735
38941
  /**
38736
38942
  * Since "strictTemplates" is a true superset of type checking capabilities compared to
38737
- * "strictTemplateTypeCheck", it is required that the latter is not explicitly disabled if the
38943
+ * "fullTemplateTypeCheck", it is required that the latter is not explicitly disabled if the
38738
38944
  * former is enabled.
38739
38945
  */
38740
38946
  function verifyCompatibleTypeCheckOptions(options) {
@@ -39328,9 +39534,6 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
39328
39534
  function isTypeScriptFile(fileName) {
39329
39535
  return fileName.endsWith('.ts');
39330
39536
  }
39331
- function isExternalTemplate(fileName) {
39332
- return !isTypeScriptFile(fileName);
39333
- }
39334
39537
  function isWithin(position, span) {
39335
39538
  let start, end;
39336
39539
  if (span instanceof ParseSourceSpan) {
@@ -39345,6 +39548,31 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
39345
39548
  // like ¦start and end¦ where ¦ is the cursor.
39346
39549
  return start <= position && position <= end;
39347
39550
  }
39551
+ /**
39552
+ * For a given location in a shim file, retrieves the corresponding file url for the template and
39553
+ * the span in the template.
39554
+ */
39555
+ function getTemplateLocationFromShimLocation(templateTypeChecker, shimPath, positionInShimFile) {
39556
+ const mapping = templateTypeChecker.getTemplateMappingAtShimLocation({ shimPath, positionInShimFile });
39557
+ if (mapping === null) {
39558
+ return null;
39559
+ }
39560
+ const { templateSourceMapping, span } = mapping;
39561
+ let templateUrl;
39562
+ if (templateSourceMapping.type === 'direct') {
39563
+ templateUrl = absoluteFromSourceFile(templateSourceMapping.node.getSourceFile());
39564
+ }
39565
+ else if (templateSourceMapping.type === 'external') {
39566
+ templateUrl = absoluteFrom(templateSourceMapping.templateUrl);
39567
+ }
39568
+ else {
39569
+ // This includes indirect mappings, which are difficult to map directly to the code
39570
+ // location. Diagnostics similarly return a synthetic template string for this case rather
39571
+ // than a real location.
39572
+ return null;
39573
+ }
39574
+ return { templateUrl, span };
39575
+ }
39348
39576
 
39349
39577
  /**
39350
39578
  * @license
@@ -39361,7 +39589,12 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
39361
39589
  this.ignoreForEmit = new Set();
39362
39590
  this.factoryTracker = null; // no .ngfactory shims
39363
39591
  this.unifiedModulesHost = null; // only used in Bazel
39364
- this.templateVersion = new Map();
39592
+ /**
39593
+ * Map of resource filenames to the version of the file last read via `readResource`.
39594
+ *
39595
+ * Used to implement `getModifiedResourceFiles`.
39596
+ */
39597
+ this.lastReadResourceVersion = new Map();
39365
39598
  this.rootDirs = getRootDirs(this, project.getCompilationSettings());
39366
39599
  }
39367
39600
  isShim(sf) {
@@ -39379,6 +39612,14 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
39379
39612
  getCanonicalFileName(fileName) {
39380
39613
  return this.project.projectService.toCanonicalFileName(fileName);
39381
39614
  }
39615
+ /**
39616
+ * Return the real path of a symlink. This method is required in order to
39617
+ * resolve symlinks in node_modules.
39618
+ */
39619
+ realpath(path) {
39620
+ var _a, _b, _c;
39621
+ return (_c = (_b = (_a = this.project).realpath) === null || _b === void 0 ? void 0 : _b.call(_a, path)) !== null && _c !== void 0 ? _c : path;
39622
+ }
39382
39623
  /**
39383
39624
  * readResource() is an Angular-specific method for reading files that are not
39384
39625
  * managed by the TS compiler host, namely templates and stylesheets.
@@ -39401,13 +39642,17 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
39401
39642
  throw new Error(`Failed to get script snapshot while trying to read ${fileName}`);
39402
39643
  }
39403
39644
  const version = this.project.getScriptVersion(fileName);
39404
- this.templateVersion.set(fileName, version);
39645
+ this.lastReadResourceVersion.set(fileName, version);
39405
39646
  return snapshot.getText(0, snapshot.getLength());
39406
39647
  }
39407
- isTemplateDirty(fileName) {
39408
- const lastVersion = this.templateVersion.get(fileName);
39409
- const latestVersion = this.project.getScriptVersion(fileName);
39410
- return lastVersion !== latestVersion;
39648
+ getModifiedResourceFiles() {
39649
+ const modifiedFiles = new Set();
39650
+ for (const [fileName, oldVersion] of this.lastReadResourceVersion) {
39651
+ if (this.project.getScriptVersion(fileName) !== oldVersion) {
39652
+ modifiedFiles.add(fileName);
39653
+ }
39654
+ }
39655
+ return modifiedFiles.size > 0 ? modifiedFiles : undefined;
39411
39656
  }
39412
39657
  }
39413
39658
  /**
@@ -39487,46 +39732,27 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
39487
39732
  this.lastKnownProgram = null;
39488
39733
  }
39489
39734
  getOrCreate() {
39735
+ var _a;
39490
39736
  const program = this.programStrategy.getProgram();
39491
- if (this.compiler === null || program !== this.lastKnownProgram) {
39492
- this.compiler = new NgCompiler(this.adapter, // like compiler host
39493
- this.options, // angular compiler options
39494
- program, this.programStrategy, this.incrementalStrategy, true, // enableTemplateTypeChecker
39495
- true, // usePoisonedData
39496
- this.lastKnownProgram, undefined);
39497
- this.lastKnownProgram = program;
39498
- }
39499
- return this.compiler;
39500
- }
39501
- /**
39502
- * Create a new instance of the Ivy compiler if the program has changed since
39503
- * the last time the compiler was instantiated. If the program has not changed,
39504
- * return the existing instance.
39505
- * @param fileName override the template if this is an external template file
39506
- * @param options angular compiler options
39507
- */
39508
- getOrCreateWithChangedFile(fileName) {
39509
- const compiler = this.getOrCreate();
39510
- if (isExternalTemplate(fileName)) {
39511
- this.overrideTemplate(fileName, compiler);
39737
+ const modifiedResourceFiles = (_a = this.adapter.getModifiedResourceFiles()) !== null && _a !== void 0 ? _a : new Set();
39738
+ if (this.compiler !== null && program === this.lastKnownProgram) {
39739
+ if (modifiedResourceFiles.size > 0) {
39740
+ // Only resource files have changed since the last NgCompiler was created.
39741
+ const ticket = resourceChangeTicket(this.compiler, modifiedResourceFiles);
39742
+ this.compiler = NgCompiler.fromTicket(ticket, this.adapter);
39743
+ }
39744
+ return this.compiler;
39512
39745
  }
39513
- return compiler;
39514
- }
39515
- overrideTemplate(fileName, compiler) {
39516
- if (!this.adapter.isTemplateDirty(fileName)) {
39517
- return;
39746
+ let ticket;
39747
+ if (this.compiler === null || this.lastKnownProgram === null) {
39748
+ ticket = freshCompilationTicket(program, this.options, this.incrementalStrategy, this.programStrategy, true, true);
39518
39749
  }
39519
- // 1. Get the latest snapshot
39520
- const latestTemplate = this.adapter.readResource(fileName);
39521
- // 2. Find all components that use the template
39522
- const ttc = compiler.getTemplateTypeChecker();
39523
- const components = compiler.getComponentsWithTemplateFile(fileName);
39524
- // 3. Update component template
39525
- for (const component of components) {
39526
- if (ts.isClassDeclaration(component)) {
39527
- ttc.overrideComponentTemplate(component, latestTemplate);
39528
- }
39750
+ else {
39751
+ ticket = incrementalFromCompilerTicket(this.compiler, program, this.incrementalStrategy, this.programStrategy, modifiedResourceFiles);
39529
39752
  }
39753
+ this.compiler = NgCompiler.fromTicket(ticket, this.adapter);
39754
+ this.lastKnownProgram = program;
39755
+ return this.compiler;
39530
39756
  }
39531
39757
  registerLastKnownProgram() {
39532
39758
  this.lastKnownProgram = this.programStrategy.getProgram();
@@ -40137,12 +40363,7 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
40137
40363
  return undefined;
40138
40364
  }
40139
40365
  const { componentContext, templateContext } = completions;
40140
- let replacementSpan = undefined;
40141
- // Non-empty nodes get replaced with the completion.
40142
- if (!(this.node instanceof EmptyExpr || this.node instanceof LiteralPrimitive ||
40143
- this.node instanceof BoundEvent)) {
40144
- replacementSpan = makeReplacementSpanFromAst(this.node);
40145
- }
40366
+ const replacementSpan = makeReplacementSpanFromAst(this.node);
40146
40367
  // Merge TS completion results with results from the template scope.
40147
40368
  let entries = [];
40148
40369
  const tsLsCompletions = this.tsLS.getCompletionsAtPosition(componentContext.shimPath, componentContext.positionInShimFile, options);
@@ -40473,6 +40694,11 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
40473
40694
  };
40474
40695
  }
40475
40696
  function makeReplacementSpanFromAst(node) {
40697
+ if ((node instanceof EmptyExpr || node instanceof LiteralPrimitive ||
40698
+ node instanceof BoundEvent)) {
40699
+ // empty nodes do not replace any existing text
40700
+ return undefined;
40701
+ }
40476
40702
  return {
40477
40703
  start: node.nameSpan.start,
40478
40704
  length: node.nameSpan.end - node.nameSpan.start,
@@ -40872,15 +41098,20 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
40872
41098
  case SymbolKind.Reference: {
40873
41099
  const definitions = [];
40874
41100
  if (symbol.declaration !== node) {
40875
- definitions.push({
40876
- name: symbol.declaration.name,
40877
- containerName: '',
40878
- containerKind: ts$1.ScriptElementKind.unknown,
40879
- kind: ts$1.ScriptElementKind.variableElement,
40880
- textSpan: getTextSpanOfNode(symbol.declaration),
40881
- contextSpan: toTextSpan(symbol.declaration.sourceSpan),
40882
- fileName: symbol.declaration.sourceSpan.start.file.url,
40883
- });
41101
+ const shimLocation = symbol.kind === SymbolKind.Variable ? symbol.localVarLocation :
41102
+ symbol.referenceVarLocation;
41103
+ const mapping = getTemplateLocationFromShimLocation(this.compiler.getTemplateTypeChecker(), shimLocation.shimPath, shimLocation.positionInShimFile);
41104
+ if (mapping !== null) {
41105
+ definitions.push({
41106
+ name: symbol.declaration.name,
41107
+ containerName: '',
41108
+ containerKind: ts$1.ScriptElementKind.unknown,
41109
+ kind: ts$1.ScriptElementKind.variableElement,
41110
+ textSpan: getTextSpanOfNode(symbol.declaration),
41111
+ contextSpan: toTextSpan(symbol.declaration.sourceSpan),
41112
+ fileName: mapping.templateUrl,
41113
+ });
41114
+ }
40884
41115
  }
40885
41116
  if (symbol.kind === SymbolKind.Variable) {
40886
41117
  definitions.push(...this.getDefinitionsForSymbols({ shimLocation: symbol.initializerLocation }));
@@ -41403,29 +41634,14 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
41403
41634
  // return references to the parameter in the template itself.
41404
41635
  return null;
41405
41636
  }
41406
- // TODO(atscott): Determine how to consistently resolve paths. i.e. with the project serverHost
41407
- // or LSParseConfigHost in the adapter. We should have a better defined way to normalize paths.
41408
- const mapping = templateTypeChecker.getTemplateMappingAtShimLocation({
41409
- shimPath: absoluteFrom(shimReferenceEntry.fileName),
41410
- positionInShimFile: shimReferenceEntry.textSpan.start,
41411
- });
41637
+ // TODO(atscott): Determine how to consistently resolve paths. i.e. with the project
41638
+ // serverHost or LSParseConfigHost in the adapter. We should have a better defined way to
41639
+ // normalize paths.
41640
+ const mapping = getTemplateLocationFromShimLocation(templateTypeChecker, absoluteFrom(shimReferenceEntry.fileName), shimReferenceEntry.textSpan.start);
41412
41641
  if (mapping === null) {
41413
41642
  return null;
41414
41643
  }
41415
- const { templateSourceMapping, span } = mapping;
41416
- let templateUrl;
41417
- if (templateSourceMapping.type === 'direct') {
41418
- templateUrl = absoluteFromSourceFile(templateSourceMapping.node.getSourceFile());
41419
- }
41420
- else if (templateSourceMapping.type === 'external') {
41421
- templateUrl = absoluteFrom(templateSourceMapping.templateUrl);
41422
- }
41423
- else {
41424
- // This includes indirect mappings, which are difficult to map directly to the code location.
41425
- // Diagnostics similarly return a synthetic template string for this case rather than a real
41426
- // location.
41427
- return null;
41428
- }
41644
+ const { span, templateUrl } = mapping;
41429
41645
  return Object.assign(Object.assign({}, shimReferenceEntry), { fileName: templateUrl, textSpan: toTextSpan(span) });
41430
41646
  }
41431
41647
  }
@@ -41439,6 +41655,7 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
41439
41655
  */
41440
41656
  class LanguageService {
41441
41657
  constructor(project, tsLS) {
41658
+ this.project = project;
41442
41659
  this.tsLS = tsLS;
41443
41660
  this.parseConfigHost = new LSParseConfigHost(project.projectService.host);
41444
41661
  this.options = parseNgCompilerOptions(project, this.parseConfigHost);
@@ -41452,7 +41669,7 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
41452
41669
  return this.options;
41453
41670
  }
41454
41671
  getSemanticDiagnostics(fileName) {
41455
- const compiler = this.compilerFactory.getOrCreateWithChangedFile(fileName);
41672
+ const compiler = this.compilerFactory.getOrCreate();
41456
41673
  const ttc = compiler.getTemplateTypeChecker();
41457
41674
  const diagnostics = [];
41458
41675
  if (isTypeScriptFile(fileName)) {
@@ -41474,19 +41691,19 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
41474
41691
  return diagnostics;
41475
41692
  }
41476
41693
  getDefinitionAndBoundSpan(fileName, position) {
41477
- const compiler = this.compilerFactory.getOrCreateWithChangedFile(fileName);
41694
+ const compiler = this.compilerFactory.getOrCreate();
41478
41695
  const results = new DefinitionBuilder(this.tsLS, compiler).getDefinitionAndBoundSpan(fileName, position);
41479
41696
  this.compilerFactory.registerLastKnownProgram();
41480
41697
  return results;
41481
41698
  }
41482
41699
  getTypeDefinitionAtPosition(fileName, position) {
41483
- const compiler = this.compilerFactory.getOrCreateWithChangedFile(fileName);
41700
+ const compiler = this.compilerFactory.getOrCreate();
41484
41701
  const results = new DefinitionBuilder(this.tsLS, compiler).getTypeDefinitionsAtPosition(fileName, position);
41485
41702
  this.compilerFactory.registerLastKnownProgram();
41486
41703
  return results;
41487
41704
  }
41488
41705
  getQuickInfoAtPosition(fileName, position) {
41489
- const compiler = this.compilerFactory.getOrCreateWithChangedFile(fileName);
41706
+ const compiler = this.compilerFactory.getOrCreate();
41490
41707
  const templateInfo = getTemplateInfoAtPosition(fileName, position, compiler);
41491
41708
  if (templateInfo === undefined) {
41492
41709
  return undefined;
@@ -41506,13 +41723,13 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
41506
41723
  return results;
41507
41724
  }
41508
41725
  getReferencesAtPosition(fileName, position) {
41509
- const compiler = this.compilerFactory.getOrCreateWithChangedFile(fileName);
41726
+ const compiler = this.compilerFactory.getOrCreate();
41510
41727
  const results = new ReferenceBuilder(this.strategy, this.tsLS, compiler).get(fileName, position);
41511
41728
  this.compilerFactory.registerLastKnownProgram();
41512
41729
  return results;
41513
41730
  }
41514
41731
  getCompletionBuilder(fileName, position) {
41515
- const compiler = this.compilerFactory.getOrCreateWithChangedFile(fileName);
41732
+ const compiler = this.compilerFactory.getOrCreate();
41516
41733
  const templateInfo = getTemplateInfoAtPosition(fileName, position, compiler);
41517
41734
  if (templateInfo === undefined) {
41518
41735
  return null;
@@ -41555,6 +41772,28 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
41555
41772
  this.compilerFactory.registerLastKnownProgram();
41556
41773
  return result;
41557
41774
  }
41775
+ getCompilerOptionsDiagnostics() {
41776
+ const project = this.project;
41777
+ if (!(project instanceof ts.server.ConfiguredProject)) {
41778
+ return [];
41779
+ }
41780
+ const diagnostics = [];
41781
+ const configSourceFile = ts.readJsonConfigFile(project.getConfigFilePath(), (path) => project.readFile(path));
41782
+ if (!this.options.strictTemplates && !this.options.fullTemplateTypeCheck) {
41783
+ diagnostics.push({
41784
+ messageText: 'Some language features are not available. ' +
41785
+ 'To access all features, enable `strictTemplates` in `angularCompilerOptions`.',
41786
+ category: ts.DiagnosticCategory.Suggestion,
41787
+ code: ngErrorCode(ErrorCode.SUGGEST_STRICT_TEMPLATES),
41788
+ file: configSourceFile,
41789
+ start: undefined,
41790
+ length: undefined,
41791
+ });
41792
+ }
41793
+ const compiler = this.compilerFactory.getOrCreate();
41794
+ diagnostics.push(...compiler.getOptionDiagnostics());
41795
+ return diagnostics;
41796
+ }
41558
41797
  watchConfigFile(project) {
41559
41798
  // TODO: Check the case when the project is disposed. An InferredProject
41560
41799
  // could be disposed when a tsconfig.json is added to the workspace,
@@ -41755,6 +41994,20 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
41755
41994
  return (_a = tsLS.getCompletionEntrySymbol(fileName, position, name, source)) !== null && _a !== void 0 ? _a : ngLS.getCompletionEntrySymbol(fileName, position, name);
41756
41995
  }
41757
41996
  }
41997
+ /**
41998
+ * Gets global diagnostics related to the program configuration and compiler options.
41999
+ */
42000
+ function getCompilerOptionsDiagnostics() {
42001
+ const diagnostics = [];
42002
+ if (!angularOnly) {
42003
+ diagnostics.push(...tsLS.getCompilerOptionsDiagnostics());
42004
+ }
42005
+ diagnostics.push(...ngLS.getCompilerOptionsDiagnostics());
42006
+ return diagnostics;
42007
+ }
42008
+ function getTcb(fileName, position) {
42009
+ return undefined;
42010
+ }
41758
42011
  return Object.assign(Object.assign({}, tsLS), { getSemanticDiagnostics,
41759
42012
  getTypeDefinitionAtPosition,
41760
42013
  getQuickInfoAtPosition,
@@ -41763,7 +42016,9 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
41763
42016
  findRenameLocations,
41764
42017
  getCompletionsAtPosition,
41765
42018
  getCompletionEntryDetails,
41766
- getCompletionEntrySymbol });
42019
+ getCompletionEntrySymbol,
42020
+ getCompilerOptionsDiagnostics,
42021
+ getTcb });
41767
42022
  }
41768
42023
  function getExternalFiles(project) {
41769
42024
  if (!project.hasRoots()) {