@angular/language-service 11.2.5 → 11.2.9

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.2.5
2
+ * @license Angular v11.2.9
3
3
  * Copyright Google LLC All Rights Reserved.
4
4
  * License: MIT
5
5
  */
@@ -6697,6 +6697,21 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
6697
6697
  * Use of this source code is governed by an MIT-style license that can be
6698
6698
  * found in the LICENSE file at https://angular.io/license
6699
6699
  */
6700
+ /**
6701
+ * This is an R3 `Node`-like wrapper for a raw `html.Comment` node. We do not currently
6702
+ * require the implementation of a visitor for Comments as they are only collected at
6703
+ * the top-level of the R3 AST, and only if `Render3ParseOptions['collectCommentNodes']`
6704
+ * is true.
6705
+ */
6706
+ class Comment {
6707
+ constructor(value, sourceSpan) {
6708
+ this.value = value;
6709
+ this.sourceSpan = sourceSpan;
6710
+ }
6711
+ visit(_visitor) {
6712
+ throw new Error('visit() not implemented for Comment');
6713
+ }
6714
+ }
6700
6715
  class Text {
6701
6716
  constructor(value, sourceSpan) {
6702
6717
  this.value = value;
@@ -11388,6 +11403,7 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
11388
11403
  */
11389
11404
  function combineHostContextSelectors(contextSelectors, otherSelectors) {
11390
11405
  const hostMarker = _polyfillHostNoCombinator;
11406
+ _polyfillHostRe.lastIndex = 0; // reset the regex to ensure we get an accurate test
11391
11407
  const otherSelectorsHasHost = _polyfillHostRe.test(otherSelectors);
11392
11408
  // If there are no context selectors then just output a host marker
11393
11409
  if (contextSelectors.length === 0) {
@@ -11517,7 +11533,7 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
11517
11533
  return visitor.visitElement(this, context);
11518
11534
  }
11519
11535
  }
11520
- class Comment {
11536
+ class Comment$1 {
11521
11537
  constructor(value, sourceSpan) {
11522
11538
  this.value = value;
11523
11539
  this.sourceSpan = sourceSpan;
@@ -12547,7 +12563,7 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
12547
12563
  const text = this._advanceIf(TokenType.RAW_TEXT);
12548
12564
  this._advanceIf(TokenType.COMMENT_END);
12549
12565
  const value = text != null ? text.parts[0].trim() : null;
12550
- this._addToParent(new Comment(value, token.sourceSpan));
12566
+ this._addToParent(new Comment$1(value, token.sourceSpan));
12551
12567
  }
12552
12568
  _consumeExpansion(token) {
12553
12569
  const switchValue = this._advance();
@@ -13755,7 +13771,11 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
13755
13771
  if (isEmptyExpression(value)) {
13756
13772
  return null;
13757
13773
  }
13758
- name = normalizePropName(name);
13774
+ // CSS custom properties are case-sensitive so we shouldn't normalize them.
13775
+ // See: https://www.w3.org/TR/css-variables-1/#defining-variables
13776
+ if (!isCssCustomProperty(name)) {
13777
+ name = hyphenate(name);
13778
+ }
13759
13779
  const { property, hasOverrideFlag, suffix: bindingSuffix } = parseProperty(name);
13760
13780
  suffix = typeof suffix === 'string' && suffix.length !== 0 ? suffix : bindingSuffix;
13761
13781
  const entry = { name: property, suffix: suffix, value, sourceSpan, hasOverrideFlag };
@@ -13779,9 +13799,6 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
13779
13799
  const { property, hasOverrideFlag } = parseProperty(name);
13780
13800
  const entry = { name: property, value, sourceSpan, hasOverrideFlag, suffix: null };
13781
13801
  if (isMapBased) {
13782
- if (this._classMapInput) {
13783
- throw new Error('[class] and [className] bindings cannot be used on the same element simultaneously');
13784
- }
13785
13802
  this._classMapInput = entry;
13786
13803
  }
13787
13804
  else {
@@ -14103,8 +14120,12 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
14103
14120
  return Identifiers$1.stylePropInterpolateV;
14104
14121
  }
14105
14122
  }
14106
- function normalizePropName(prop) {
14107
- return hyphenate(prop);
14123
+ /**
14124
+ * Checks whether property name is a custom CSS property.
14125
+ * See: https://www.w3.org/TR/css-variables-1
14126
+ */
14127
+ function isCssCustomProperty(name) {
14128
+ return name.startsWith('--');
14108
14129
  }
14109
14130
 
14110
14131
  /**
@@ -16222,26 +16243,33 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
16222
16243
  EVENT: { start: '(', end: ')' },
16223
16244
  };
16224
16245
  const TEMPLATE_ATTR_PREFIX$1 = '*';
16225
- function htmlAstToRender3Ast(htmlNodes, bindingParser) {
16226
- const transformer = new HtmlAstToIvyAst(bindingParser);
16246
+ function htmlAstToRender3Ast(htmlNodes, bindingParser, options) {
16247
+ const transformer = new HtmlAstToIvyAst(bindingParser, options);
16227
16248
  const ivyNodes = visitAll$1(transformer, htmlNodes);
16228
16249
  // Errors might originate in either the binding parser or the html to ivy transformer
16229
16250
  const allErrors = bindingParser.errors.concat(transformer.errors);
16230
- return {
16251
+ const result = {
16231
16252
  nodes: ivyNodes,
16232
16253
  errors: allErrors,
16233
16254
  styleUrls: transformer.styleUrls,
16234
16255
  styles: transformer.styles,
16235
- ngContentSelectors: transformer.ngContentSelectors,
16256
+ ngContentSelectors: transformer.ngContentSelectors
16236
16257
  };
16258
+ if (options.collectCommentNodes) {
16259
+ result.commentNodes = transformer.commentNodes;
16260
+ }
16261
+ return result;
16237
16262
  }
16238
16263
  class HtmlAstToIvyAst {
16239
- constructor(bindingParser) {
16264
+ constructor(bindingParser, options) {
16240
16265
  this.bindingParser = bindingParser;
16266
+ this.options = options;
16241
16267
  this.errors = [];
16242
16268
  this.styles = [];
16243
16269
  this.styleUrls = [];
16244
16270
  this.ngContentSelectors = [];
16271
+ // This array will be populated if `Render3ParseOptions['collectCommentNodes']` is true
16272
+ this.commentNodes = [];
16245
16273
  this.inI18nBlock = false;
16246
16274
  }
16247
16275
  // HTML visitor
@@ -16410,6 +16438,9 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
16410
16438
  return null;
16411
16439
  }
16412
16440
  visitComment(comment) {
16441
+ if (this.options.collectCommentNodes) {
16442
+ this.commentNodes.push(new Comment(comment.value || '', comment.sourceSpan));
16443
+ }
16413
16444
  return null;
16414
16445
  }
16415
16446
  // convert view engine `ParsedProperty` to a format suitable for IVY
@@ -16601,7 +16632,7 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
16601
16632
  return node instanceof Text$2 && node.value.trim().length == 0;
16602
16633
  }
16603
16634
  function isCommentNode(node) {
16604
- return node instanceof Comment;
16635
+ return node instanceof Comment$1;
16605
16636
  }
16606
16637
  function textContents(node) {
16607
16638
  if (node.children.length !== 1 || !(node.children[0] instanceof Text$2)) {
@@ -19178,7 +19209,7 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
19178
19209
  const parseResult = htmlParser.parse(template, templateUrl, Object.assign(Object.assign({ leadingTriviaChars: LEADING_TRIVIA_CHARS }, options), { tokenizeExpansionForms: true }));
19179
19210
  if (!options.alwaysAttemptHtmlToR3AstConversion && parseResult.errors &&
19180
19211
  parseResult.errors.length > 0) {
19181
- return {
19212
+ const parsedTemplate = {
19182
19213
  interpolationConfig,
19183
19214
  preserveWhitespaces,
19184
19215
  template,
@@ -19190,6 +19221,10 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
19190
19221
  styles: [],
19191
19222
  ngContentSelectors: []
19192
19223
  };
19224
+ if (options.collectCommentNodes) {
19225
+ parsedTemplate.commentNodes = [];
19226
+ }
19227
+ return parsedTemplate;
19193
19228
  }
19194
19229
  let rootNodes = parseResult.rootNodes;
19195
19230
  // process i18n meta information (scan attributes, generate ids)
@@ -19200,7 +19235,7 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
19200
19235
  const i18nMetaResult = i18nMetaVisitor.visitAllWithErrors(rootNodes);
19201
19236
  if (!options.alwaysAttemptHtmlToR3AstConversion && i18nMetaResult.errors &&
19202
19237
  i18nMetaResult.errors.length > 0) {
19203
- return {
19238
+ const parsedTemplate = {
19204
19239
  interpolationConfig,
19205
19240
  preserveWhitespaces,
19206
19241
  template,
@@ -19212,6 +19247,10 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
19212
19247
  styles: [],
19213
19248
  ngContentSelectors: []
19214
19249
  };
19250
+ if (options.collectCommentNodes) {
19251
+ parsedTemplate.commentNodes = [];
19252
+ }
19253
+ return parsedTemplate;
19215
19254
  }
19216
19255
  rootNodes = i18nMetaResult.rootNodes;
19217
19256
  if (!preserveWhitespaces) {
@@ -19224,9 +19263,9 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
19224
19263
  rootNodes = visitAll$1(new I18nMetaVisitor(interpolationConfig, /* keepI18nAttrs */ false), rootNodes);
19225
19264
  }
19226
19265
  }
19227
- const { nodes, errors, styleUrls, styles, ngContentSelectors } = htmlAstToRender3Ast(rootNodes, bindingParser);
19266
+ const { nodes, errors, styleUrls, styles, ngContentSelectors, commentNodes } = htmlAstToRender3Ast(rootNodes, bindingParser, { collectCommentNodes: !!options.collectCommentNodes });
19228
19267
  errors.push(...parseResult.errors, ...i18nMetaResult.errors);
19229
- return {
19268
+ const parsedTemplate = {
19230
19269
  interpolationConfig,
19231
19270
  preserveWhitespaces,
19232
19271
  errors: errors.length > 0 ? errors : null,
@@ -19238,6 +19277,10 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
19238
19277
  styles,
19239
19278
  ngContentSelectors
19240
19279
  };
19280
+ if (options.collectCommentNodes) {
19281
+ parsedTemplate.commentNodes = commentNodes;
19282
+ }
19283
+ return parsedTemplate;
19241
19284
  }
19242
19285
  const elementRegistry = new DomElementSchemaRegistry();
19243
19286
  /**
@@ -20356,7 +20399,7 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
20356
20399
  * Use of this source code is governed by an MIT-style license that can be
20357
20400
  * found in the LICENSE file at https://angular.io/license
20358
20401
  */
20359
- const VERSION$1 = new Version('11.2.5');
20402
+ const VERSION$1 = new Version('11.2.9');
20360
20403
 
20361
20404
  /**
20362
20405
  * @license
@@ -21013,7 +21056,7 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
21013
21056
  */
21014
21057
  function createDirectiveDefinitionMap(meta) {
21015
21058
  const definitionMap = new DefinitionMap();
21016
- definitionMap.set('version', literal('11.2.5'));
21059
+ definitionMap.set('version', literal('11.2.9'));
21017
21060
  // e.g. `type: MyDirective`
21018
21061
  definitionMap.set('type', meta.internalType);
21019
21062
  // e.g. `selector: 'some-dir'`
@@ -21234,7 +21277,7 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
21234
21277
  */
21235
21278
  function createPipeDefinitionMap(meta) {
21236
21279
  const definitionMap = new DefinitionMap();
21237
- definitionMap.set('version', literal('11.2.5'));
21280
+ definitionMap.set('version', literal('11.2.9'));
21238
21281
  definitionMap.set('ngImport', importExpr(Identifiers$1.core));
21239
21282
  // e.g. `type: MyPipe`
21240
21283
  definitionMap.set('type', meta.internalType);
@@ -21266,7 +21309,7 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
21266
21309
  * Use of this source code is governed by an MIT-style license that can be
21267
21310
  * found in the LICENSE file at https://angular.io/license
21268
21311
  */
21269
- const VERSION$2 = new Version('11.2.5');
21312
+ const VERSION$2 = new Version('11.2.9');
21270
21313
 
21271
21314
  /**
21272
21315
  * @license
@@ -21468,14 +21511,20 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
21468
21511
  * An injectable already has a `ɵprov` property.
21469
21512
  */
21470
21513
  ErrorCode[ErrorCode["INJECTABLE_DUPLICATE_PROV"] = 9001] = "INJECTABLE_DUPLICATE_PROV";
21471
- // 10XXX error codes are reserved for diagnostics with category
21472
- // `ts.DiagnosticCategory.Suggestion`. These diagnostics are generated by
21473
- // language service.
21514
+ // 10XXX error codes are reserved for diagnostics with categories other than
21515
+ // `ts.DiagnosticCategory.Error`. These diagnostics are generated by the compiler when configured
21516
+ // to do so by a tool such as the Language Service, or by the Language Service itself.
21474
21517
  /**
21475
21518
  * Suggest users to enable `strictTemplates` to make use of full capabilities
21476
21519
  * provided by Angular language service.
21477
21520
  */
21478
21521
  ErrorCode[ErrorCode["SUGGEST_STRICT_TEMPLATES"] = 10001] = "SUGGEST_STRICT_TEMPLATES";
21522
+ /**
21523
+ * Indicates that a particular structural directive provides advanced type narrowing
21524
+ * functionality, but the current template type-checking configuration does not allow its usage in
21525
+ * type inference.
21526
+ */
21527
+ ErrorCode[ErrorCode["SUGGEST_SUBOPTIMAL_TYPE_INFERENCE"] = 10002] = "SUGGEST_SUBOPTIMAL_TYPE_INFERENCE";
21479
21528
  })(ErrorCode || (ErrorCode = {}));
21480
21529
  /**
21481
21530
  * @internal
@@ -25488,6 +25537,417 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
25488
25537
  }
25489
25538
  }
25490
25539
 
25540
+ /**
25541
+ * @license
25542
+ * Copyright Google LLC All Rights Reserved.
25543
+ *
25544
+ * Use of this source code is governed by an MIT-style license that can be
25545
+ * found in the LICENSE file at https://angular.io/license
25546
+ */
25547
+ /**
25548
+ * A phase of compilation for which time is tracked in a distinct bucket.
25549
+ */
25550
+ var PerfPhase;
25551
+ (function (PerfPhase) {
25552
+ /**
25553
+ * The "default" phase which tracks time not spent in any other phase.
25554
+ */
25555
+ PerfPhase[PerfPhase["Unaccounted"] = 0] = "Unaccounted";
25556
+ /**
25557
+ * Time spent setting up the compiler, before a TypeScript program is created.
25558
+ *
25559
+ * This includes operations like configuring the `ts.CompilerHost` and any wrappers.
25560
+ */
25561
+ PerfPhase[PerfPhase["Setup"] = 1] = "Setup";
25562
+ /**
25563
+ * Time spent in `ts.createProgram`, including reading and parsing `ts.SourceFile`s in the
25564
+ * `ts.CompilerHost`.
25565
+ *
25566
+ * This might be an incremental program creation operation.
25567
+ */
25568
+ PerfPhase[PerfPhase["TypeScriptProgramCreate"] = 2] = "TypeScriptProgramCreate";
25569
+ /**
25570
+ * Time spent reconciling the contents of an old `ts.Program` with the new incremental one.
25571
+ *
25572
+ * Only present in incremental compilations.
25573
+ */
25574
+ PerfPhase[PerfPhase["Reconciliation"] = 3] = "Reconciliation";
25575
+ /**
25576
+ * Time spent updating an `NgCompiler` instance with a resource-only change.
25577
+ *
25578
+ * Only present in incremental compilations where the change was resource-only.
25579
+ */
25580
+ PerfPhase[PerfPhase["ResourceUpdate"] = 4] = "ResourceUpdate";
25581
+ /**
25582
+ * Time spent calculating the plain TypeScript diagnostics (structural and semantic).
25583
+ */
25584
+ PerfPhase[PerfPhase["TypeScriptDiagnostics"] = 5] = "TypeScriptDiagnostics";
25585
+ /**
25586
+ * Time spent in Angular analysis of individual classes in the program.
25587
+ */
25588
+ PerfPhase[PerfPhase["Analysis"] = 6] = "Analysis";
25589
+ /**
25590
+ * Time spent in Angular global analysis (synthesis of analysis information into a complete
25591
+ * understanding of the program).
25592
+ */
25593
+ PerfPhase[PerfPhase["Resolve"] = 7] = "Resolve";
25594
+ /**
25595
+ * Time spent building the import graph of the program in order to perform cycle detection.
25596
+ */
25597
+ PerfPhase[PerfPhase["CycleDetection"] = 8] = "CycleDetection";
25598
+ /**
25599
+ * Time spent generating the text of Type Check Blocks in order to perform template type checking.
25600
+ */
25601
+ PerfPhase[PerfPhase["TcbGeneration"] = 9] = "TcbGeneration";
25602
+ /**
25603
+ * Time spent updating the `ts.Program` with new Type Check Block code.
25604
+ */
25605
+ PerfPhase[PerfPhase["TcbUpdateProgram"] = 10] = "TcbUpdateProgram";
25606
+ /**
25607
+ * Time spent by TypeScript performing its emit operations, including downleveling and writing
25608
+ * output files.
25609
+ */
25610
+ PerfPhase[PerfPhase["TypeScriptEmit"] = 11] = "TypeScriptEmit";
25611
+ /**
25612
+ * Time spent by Angular performing code transformations of ASTs as they're about to be emitted.
25613
+ *
25614
+ * This includes the actual code generation step for templates, and occurs during the emit phase
25615
+ * (but is tracked separately from `TypeScriptEmit` time).
25616
+ */
25617
+ PerfPhase[PerfPhase["Compile"] = 12] = "Compile";
25618
+ /**
25619
+ * Time spent performing a `TemplateTypeChecker` autocompletion operation.
25620
+ */
25621
+ PerfPhase[PerfPhase["TtcAutocompletion"] = 13] = "TtcAutocompletion";
25622
+ /**
25623
+ * Time spent computing template type-checking diagnostics.
25624
+ */
25625
+ PerfPhase[PerfPhase["TtcDiagnostics"] = 14] = "TtcDiagnostics";
25626
+ /**
25627
+ * Time spent getting a `Symbol` from the `TemplateTypeChecker`.
25628
+ */
25629
+ PerfPhase[PerfPhase["TtcSymbol"] = 15] = "TtcSymbol";
25630
+ /**
25631
+ * Time spent by the Angular Language Service calculating a "get references" or a renaming
25632
+ * operation.
25633
+ */
25634
+ PerfPhase[PerfPhase["LsReferencesAndRenames"] = 16] = "LsReferencesAndRenames";
25635
+ /**
25636
+ * Time spent by the Angular Language Service calculating a "quick info" operation.
25637
+ */
25638
+ PerfPhase[PerfPhase["LsQuickInfo"] = 17] = "LsQuickInfo";
25639
+ /**
25640
+ * Time spent by the Angular Language Service calculating a "get type definition" or "get
25641
+ * definition" operation.
25642
+ */
25643
+ PerfPhase[PerfPhase["LsDefinition"] = 18] = "LsDefinition";
25644
+ /**
25645
+ * Time spent by the Angular Language Service calculating a "get completions" (AKA autocomplete)
25646
+ * operation.
25647
+ */
25648
+ PerfPhase[PerfPhase["LsCompletions"] = 19] = "LsCompletions";
25649
+ /**
25650
+ * Time spent by the Angular Language Service calculating a "view template typecheck block"
25651
+ * operation.
25652
+ */
25653
+ PerfPhase[PerfPhase["LsTcb"] = 20] = "LsTcb";
25654
+ /**
25655
+ * Time spent by the Angular Language Service calculating diagnostics.
25656
+ */
25657
+ PerfPhase[PerfPhase["LsDiagnostics"] = 21] = "LsDiagnostics";
25658
+ /**
25659
+ * Time spent by the Angular Language Service calculating a "get component locations for template"
25660
+ * operation.
25661
+ */
25662
+ PerfPhase[PerfPhase["LsComponentLocations"] = 22] = "LsComponentLocations";
25663
+ /**
25664
+ * Tracks the number of `PerfPhase`s, and must appear at the end of the list.
25665
+ */
25666
+ PerfPhase[PerfPhase["LAST"] = 23] = "LAST";
25667
+ })(PerfPhase || (PerfPhase = {}));
25668
+ /**
25669
+ * Represents some occurrence during compilation, and is tracked with a counter.
25670
+ */
25671
+ var PerfEvent;
25672
+ (function (PerfEvent) {
25673
+ /**
25674
+ * Counts the number of `.d.ts` files in the program.
25675
+ */
25676
+ PerfEvent[PerfEvent["InputDtsFile"] = 0] = "InputDtsFile";
25677
+ /**
25678
+ * Counts the number of non-`.d.ts` files in the program.
25679
+ */
25680
+ PerfEvent[PerfEvent["InputTsFile"] = 1] = "InputTsFile";
25681
+ /**
25682
+ * An `@Component` class was analyzed.
25683
+ */
25684
+ PerfEvent[PerfEvent["AnalyzeComponent"] = 2] = "AnalyzeComponent";
25685
+ /**
25686
+ * An `@Directive` class was analyzed.
25687
+ */
25688
+ PerfEvent[PerfEvent["AnalyzeDirective"] = 3] = "AnalyzeDirective";
25689
+ /**
25690
+ * An `@Injectable` class was analyzed.
25691
+ */
25692
+ PerfEvent[PerfEvent["AnalyzeInjectable"] = 4] = "AnalyzeInjectable";
25693
+ /**
25694
+ * An `@NgModule` class was analyzed.
25695
+ */
25696
+ PerfEvent[PerfEvent["AnalyzeNgModule"] = 5] = "AnalyzeNgModule";
25697
+ /**
25698
+ * An `@Pipe` class was analyzed.
25699
+ */
25700
+ PerfEvent[PerfEvent["AnalyzePipe"] = 6] = "AnalyzePipe";
25701
+ /**
25702
+ * A trait was analyzed.
25703
+ *
25704
+ * In theory, this should be the sum of the `Analyze` counters for each decorator type.
25705
+ */
25706
+ PerfEvent[PerfEvent["TraitAnalyze"] = 7] = "TraitAnalyze";
25707
+ /**
25708
+ * A trait had a prior analysis available from an incremental program, and did not need to be
25709
+ * re-analyzed.
25710
+ */
25711
+ PerfEvent[PerfEvent["TraitReuseAnalysis"] = 8] = "TraitReuseAnalysis";
25712
+ /**
25713
+ * A `ts.SourceFile` directly changed between the prior program and a new incremental compilation.
25714
+ */
25715
+ PerfEvent[PerfEvent["SourceFilePhysicalChange"] = 9] = "SourceFilePhysicalChange";
25716
+ /**
25717
+ * A `ts.SourceFile` did not physically changed, but according to the file dependency graph, has
25718
+ * logically changed between the prior program and a new incremental compilation.
25719
+ */
25720
+ PerfEvent[PerfEvent["SourceFileLogicalChange"] = 10] = "SourceFileLogicalChange";
25721
+ /**
25722
+ * A `ts.SourceFile` has not logically changed and all of its analysis results were thus available
25723
+ * for reuse.
25724
+ */
25725
+ PerfEvent[PerfEvent["SourceFileReuseAnalysis"] = 11] = "SourceFileReuseAnalysis";
25726
+ /**
25727
+ * A Type Check Block (TCB) was generated.
25728
+ */
25729
+ PerfEvent[PerfEvent["GenerateTcb"] = 12] = "GenerateTcb";
25730
+ /**
25731
+ * A Type Check Block (TCB) could not be generated because inlining was disabled, and the block
25732
+ * would've required inlining.
25733
+ */
25734
+ PerfEvent[PerfEvent["SkipGenerateTcbNoInline"] = 13] = "SkipGenerateTcbNoInline";
25735
+ /**
25736
+ * A `.ngtypecheck.ts` file could be reused from the previous program and did not need to be
25737
+ * regenerated.
25738
+ */
25739
+ PerfEvent[PerfEvent["ReuseTypeCheckFile"] = 14] = "ReuseTypeCheckFile";
25740
+ /**
25741
+ * The template type-checking program required changes and had to be updated in an incremental
25742
+ * step.
25743
+ */
25744
+ PerfEvent[PerfEvent["UpdateTypeCheckProgram"] = 15] = "UpdateTypeCheckProgram";
25745
+ /**
25746
+ * The compiler was able to prove that a `ts.SourceFile` did not need to be re-emitted.
25747
+ */
25748
+ PerfEvent[PerfEvent["EmitSkipSourceFile"] = 16] = "EmitSkipSourceFile";
25749
+ /**
25750
+ * A `ts.SourceFile` was emitted.
25751
+ */
25752
+ PerfEvent[PerfEvent["EmitSourceFile"] = 17] = "EmitSourceFile";
25753
+ /**
25754
+ * Tracks the number of `PrefEvent`s, and must appear at the end of the list.
25755
+ */
25756
+ PerfEvent[PerfEvent["LAST"] = 18] = "LAST";
25757
+ })(PerfEvent || (PerfEvent = {}));
25758
+ /**
25759
+ * Represents a checkpoint during compilation at which the memory usage of the compiler should be
25760
+ * recorded.
25761
+ */
25762
+ var PerfCheckpoint;
25763
+ (function (PerfCheckpoint) {
25764
+ /**
25765
+ * The point at which the `PerfRecorder` was created, and ideally tracks memory used before any
25766
+ * compilation structures are created.
25767
+ */
25768
+ PerfCheckpoint[PerfCheckpoint["Initial"] = 0] = "Initial";
25769
+ /**
25770
+ * The point just after the `ts.Program` has been created.
25771
+ */
25772
+ PerfCheckpoint[PerfCheckpoint["TypeScriptProgramCreate"] = 1] = "TypeScriptProgramCreate";
25773
+ /**
25774
+ * The point just before Angular analysis starts.
25775
+ *
25776
+ * In the main usage pattern for the compiler, TypeScript diagnostics have been calculated at this
25777
+ * point, so the `ts.TypeChecker` has fully ingested the current program, all `ts.Type` structures
25778
+ * and `ts.Symbol`s have been created.
25779
+ */
25780
+ PerfCheckpoint[PerfCheckpoint["PreAnalysis"] = 2] = "PreAnalysis";
25781
+ /**
25782
+ * The point just after Angular analysis completes.
25783
+ */
25784
+ PerfCheckpoint[PerfCheckpoint["Analysis"] = 3] = "Analysis";
25785
+ /**
25786
+ * The point just after Angular resolution is complete.
25787
+ */
25788
+ PerfCheckpoint[PerfCheckpoint["Resolve"] = 4] = "Resolve";
25789
+ /**
25790
+ * The point just after Type Check Blocks (TCBs) have been generated.
25791
+ */
25792
+ PerfCheckpoint[PerfCheckpoint["TtcGeneration"] = 5] = "TtcGeneration";
25793
+ /**
25794
+ * The point just after the template type-checking program has been updated with any new TCBs.
25795
+ */
25796
+ PerfCheckpoint[PerfCheckpoint["TtcUpdateProgram"] = 6] = "TtcUpdateProgram";
25797
+ /**
25798
+ * The point just before emit begins.
25799
+ *
25800
+ * In the main usage pattern for the compiler, all template type-checking diagnostics have been
25801
+ * requested at this point.
25802
+ */
25803
+ PerfCheckpoint[PerfCheckpoint["PreEmit"] = 7] = "PreEmit";
25804
+ /**
25805
+ * The point just after the program has been fully emitted.
25806
+ */
25807
+ PerfCheckpoint[PerfCheckpoint["Emit"] = 8] = "Emit";
25808
+ /**
25809
+ * Tracks the number of `PerfCheckpoint`s, and must appear at the end of the list.
25810
+ */
25811
+ PerfCheckpoint[PerfCheckpoint["LAST"] = 9] = "LAST";
25812
+ })(PerfCheckpoint || (PerfCheckpoint = {}));
25813
+
25814
+ /**
25815
+ * @license
25816
+ * Copyright Google LLC All Rights Reserved.
25817
+ *
25818
+ * Use of this source code is governed by an MIT-style license that can be
25819
+ * found in the LICENSE file at https://angular.io/license
25820
+ */
25821
+ function mark() {
25822
+ return process.hrtime();
25823
+ }
25824
+ function timeSinceInMicros(mark) {
25825
+ const delta = process.hrtime(mark);
25826
+ return (delta[0] * 1000000) + Math.floor(delta[1] / 1000);
25827
+ }
25828
+
25829
+ /**
25830
+ * @license
25831
+ * Copyright Google LLC All Rights Reserved.
25832
+ *
25833
+ * Use of this source code is governed by an MIT-style license that can be
25834
+ * found in the LICENSE file at https://angular.io/license
25835
+ */
25836
+ /**
25837
+ * A `PerfRecorder` that actively tracks performance statistics.
25838
+ */
25839
+ class ActivePerfRecorder {
25840
+ constructor(zeroTime) {
25841
+ this.zeroTime = zeroTime;
25842
+ this.currentPhase = PerfPhase.Unaccounted;
25843
+ this.currentPhaseEntered = this.zeroTime;
25844
+ this.counters = Array(PerfEvent.LAST).fill(0);
25845
+ this.phaseTime = Array(PerfPhase.LAST).fill(0);
25846
+ this.bytes = Array(PerfCheckpoint.LAST).fill(0);
25847
+ // Take an initial memory snapshot before any other compilation work begins.
25848
+ this.memory(PerfCheckpoint.Initial);
25849
+ }
25850
+ /**
25851
+ * Creates an `ActivePerfRecoder` with its zero point set to the current time.
25852
+ */
25853
+ static zeroedToNow() {
25854
+ return new ActivePerfRecorder(mark());
25855
+ }
25856
+ reset() {
25857
+ this.counters = Array(PerfEvent.LAST).fill(0);
25858
+ this.phaseTime = Array(PerfPhase.LAST).fill(0);
25859
+ this.bytes = Array(PerfCheckpoint.LAST).fill(0);
25860
+ this.zeroTime = mark();
25861
+ this.currentPhase = PerfPhase.Unaccounted;
25862
+ this.currentPhaseEntered = this.zeroTime;
25863
+ }
25864
+ memory(after) {
25865
+ this.bytes[after] = process.memoryUsage().heapUsed;
25866
+ }
25867
+ phase(phase) {
25868
+ const previous = this.currentPhase;
25869
+ this.phaseTime[this.currentPhase] += timeSinceInMicros(this.currentPhaseEntered);
25870
+ this.currentPhase = phase;
25871
+ this.currentPhaseEntered = mark();
25872
+ return previous;
25873
+ }
25874
+ inPhase(phase, fn) {
25875
+ const previousPhase = this.phase(phase);
25876
+ try {
25877
+ return fn();
25878
+ }
25879
+ finally {
25880
+ this.phase(previousPhase);
25881
+ }
25882
+ }
25883
+ eventCount(counter, incrementBy = 1) {
25884
+ this.counters[counter] += incrementBy;
25885
+ }
25886
+ /**
25887
+ * Return the current performance metrics as a serializable object.
25888
+ */
25889
+ finalize() {
25890
+ // Track the last segment of time spent in `this.currentPhase` in the time array.
25891
+ this.phase(PerfPhase.Unaccounted);
25892
+ const results = {
25893
+ events: {},
25894
+ phases: {},
25895
+ memory: {},
25896
+ };
25897
+ for (let i = 0; i < this.phaseTime.length; i++) {
25898
+ if (this.phaseTime[i] > 0) {
25899
+ results.phases[PerfPhase[i]] = this.phaseTime[i];
25900
+ }
25901
+ }
25902
+ for (let i = 0; i < this.phaseTime.length; i++) {
25903
+ if (this.counters[i] > 0) {
25904
+ results.events[PerfEvent[i]] = this.counters[i];
25905
+ }
25906
+ }
25907
+ for (let i = 0; i < this.bytes.length; i++) {
25908
+ if (this.bytes[i] > 0) {
25909
+ results.memory[PerfCheckpoint[i]] = this.bytes[i];
25910
+ }
25911
+ }
25912
+ return results;
25913
+ }
25914
+ }
25915
+ /**
25916
+ * A `PerfRecorder` that delegates to a target `PerfRecorder` which can be updated later.
25917
+ *
25918
+ * `DelegatingPerfRecorder` is useful when a compiler class that needs a `PerfRecorder` can outlive
25919
+ * the current compilation. This is true for most compiler classes as resource-only changes reuse
25920
+ * the same `NgCompiler` for a new compilation.
25921
+ */
25922
+ class DelegatingPerfRecorder {
25923
+ constructor(target) {
25924
+ this.target = target;
25925
+ }
25926
+ eventCount(counter, incrementBy) {
25927
+ this.target.eventCount(counter, incrementBy);
25928
+ }
25929
+ phase(phase) {
25930
+ return this.target.phase(phase);
25931
+ }
25932
+ inPhase(phase, fn) {
25933
+ // Note: this doesn't delegate to `this.target.inPhase` but instead is implemented manually here
25934
+ // to avoid adding an additional frame of noise to the stack when debugging.
25935
+ const previousPhase = this.target.phase(phase);
25936
+ try {
25937
+ return fn();
25938
+ }
25939
+ finally {
25940
+ this.target.phase(previousPhase);
25941
+ }
25942
+ }
25943
+ memory(after) {
25944
+ this.target.memory(after);
25945
+ }
25946
+ reset() {
25947
+ this.target.reset();
25948
+ }
25949
+ }
25950
+
25491
25951
  /**
25492
25952
  * @license
25493
25953
  * Copyright Google LLC All Rights Reserved.
@@ -25738,6 +26198,8 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
25738
26198
  for (const priorRecord of priorWork) {
25739
26199
  this.adopt(priorRecord);
25740
26200
  }
26201
+ this.perf.eventCount(PerfEvent.SourceFileReuseAnalysis);
26202
+ this.perf.eventCount(PerfEvent.TraitReuseAnalysis, priorWork.length);
25741
26203
  // Skip the rest of analysis, as this file's prior traits are being reused.
25742
26204
  return;
25743
26205
  }
@@ -25941,6 +26403,7 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
25941
26403
  if (trait.state !== TraitState.Pending) {
25942
26404
  throw new Error(`Attempt to analyze trait of ${clazz.name.text} in state ${TraitState[trait.state]} (expected DETECTED)`);
25943
26405
  }
26406
+ this.perf.eventCount(PerfEvent.TraitAnalyze);
25944
26407
  // Attempt analysis. This could fail with a `FatalDiagnosticError`; catch it if it does.
25945
26408
  let result;
25946
26409
  try {
@@ -26079,7 +26542,6 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
26079
26542
  // Cannot compile a trait that is not resolved, or had any errors in its declaration.
26080
26543
  continue;
26081
26544
  }
26082
- const compileSpan = this.perf.start('compileClass', original);
26083
26545
  // `trait.resolution` is non-null asserted here because TypeScript does not recognize that
26084
26546
  // `Readonly<unknown>` is nullable (as `unknown` itself is nullable) due to the way that
26085
26547
  // `Readonly` works.
@@ -26093,7 +26555,6 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
26093
26555
  trait.handler.compileFull(clazz, trait.analysis, trait.resolution, constantPool);
26094
26556
  }
26095
26557
  const compileMatchRes = compileRes;
26096
- this.perf.stop(compileSpan);
26097
26558
  if (Array.isArray(compileMatchRes)) {
26098
26559
  for (const result of compileMatchRes) {
26099
26560
  if (!res.some(r => r.name === result.name)) {
@@ -27345,11 +27806,11 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
27345
27806
  */
27346
27807
  const NO_DECORATORS = new Set();
27347
27808
  const CLOSURE_FILE_OVERVIEW_REGEXP = /\s+@fileoverview\s+/i;
27348
- function ivyTransformFactory(compilation, reflector, importRewriter, defaultImportRecorder, isCore, isClosureCompilerEnabled) {
27809
+ function ivyTransformFactory(compilation, reflector, importRewriter, defaultImportRecorder, perf, isCore, isClosureCompilerEnabled) {
27349
27810
  const recordWrappedNodeExpr = createRecorderFn(defaultImportRecorder);
27350
27811
  return (context) => {
27351
27812
  return (file) => {
27352
- return transformIvySourceFile(compilation, context, reflector, importRewriter, file, isCore, isClosureCompilerEnabled, recordWrappedNodeExpr);
27813
+ return perf.inPhase(PerfPhase.Compile, () => transformIvySourceFile(compilation, context, reflector, importRewriter, file, isCore, isClosureCompilerEnabled, recordWrappedNodeExpr));
27353
27814
  };
27354
27815
  };
27355
27816
  }
@@ -28596,7 +29057,7 @@ Either add the @Injectable() decorator to '${provider.node.name
28596
29057
  return isSymbolEqual(current, previous);
28597
29058
  }
28598
29059
  class DirectiveDecoratorHandler {
28599
- constructor(reflector, evaluator, metaRegistry, scopeRegistry, metaReader, defaultImportRecorder, injectableRegistry, isCore, semanticDepGraphUpdater, annotateForClosureCompiler, compileUndecoratedClassesWithAngularFeatures) {
29060
+ constructor(reflector, evaluator, metaRegistry, scopeRegistry, metaReader, defaultImportRecorder, injectableRegistry, isCore, semanticDepGraphUpdater, annotateForClosureCompiler, compileUndecoratedClassesWithAngularFeatures, perf) {
28600
29061
  this.reflector = reflector;
28601
29062
  this.evaluator = evaluator;
28602
29063
  this.metaRegistry = metaRegistry;
@@ -28608,6 +29069,7 @@ Either add the @Injectable() decorator to '${provider.node.name
28608
29069
  this.semanticDepGraphUpdater = semanticDepGraphUpdater;
28609
29070
  this.annotateForClosureCompiler = annotateForClosureCompiler;
28610
29071
  this.compileUndecoratedClassesWithAngularFeatures = compileUndecoratedClassesWithAngularFeatures;
29072
+ this.perf = perf;
28611
29073
  this.precedence = HandlerPrecedence.PRIMARY;
28612
29074
  this.name = DirectiveDecoratorHandler.name;
28613
29075
  }
@@ -28634,6 +29096,7 @@ Either add the @Injectable() decorator to '${provider.node.name
28634
29096
  if (this.compileUndecoratedClassesWithAngularFeatures === false && decorator === null) {
28635
29097
  return { diagnostics: [getUndecoratedClassWithAngularFeaturesDiagnostic(node)] };
28636
29098
  }
29099
+ this.perf.eventCount(PerfEvent.AnalyzeDirective);
28637
29100
  const directiveResult = extractDirectiveMetadata(node, decorator, this.reflector, this.evaluator, this.defaultImportRecorder, this.isCore, flags, this.annotateForClosureCompiler);
28638
29101
  if (directiveResult === undefined) {
28639
29102
  return {};
@@ -29231,7 +29694,7 @@ Either add the @Injectable() decorator to '${provider.node.name
29231
29694
  * Compiles @NgModule annotations to ngModuleDef fields.
29232
29695
  */
29233
29696
  class NgModuleDecoratorHandler {
29234
- constructor(reflector, evaluator, metaReader, metaRegistry, scopeRegistry, referencesRegistry, isCore, routeAnalyzer, refEmitter, factoryTracker, defaultImportRecorder, annotateForClosureCompiler, injectableRegistry, localeId) {
29697
+ constructor(reflector, evaluator, metaReader, metaRegistry, scopeRegistry, referencesRegistry, isCore, routeAnalyzer, refEmitter, factoryTracker, defaultImportRecorder, annotateForClosureCompiler, injectableRegistry, perf, localeId) {
29235
29698
  this.reflector = reflector;
29236
29699
  this.evaluator = evaluator;
29237
29700
  this.metaReader = metaReader;
@@ -29245,6 +29708,7 @@ Either add the @Injectable() decorator to '${provider.node.name
29245
29708
  this.defaultImportRecorder = defaultImportRecorder;
29246
29709
  this.annotateForClosureCompiler = annotateForClosureCompiler;
29247
29710
  this.injectableRegistry = injectableRegistry;
29711
+ this.perf = perf;
29248
29712
  this.localeId = localeId;
29249
29713
  this.precedence = HandlerPrecedence.PRIMARY;
29250
29714
  this.name = NgModuleDecoratorHandler.name;
@@ -29266,6 +29730,7 @@ Either add the @Injectable() decorator to '${provider.node.name
29266
29730
  }
29267
29731
  }
29268
29732
  analyze(node, decorator) {
29733
+ this.perf.eventCount(PerfEvent.AnalyzeNgModule);
29269
29734
  const name = node.name.text;
29270
29735
  if (decorator.args === null || decorator.args.length > 1) {
29271
29736
  throw new FatalDiagnosticError(ErrorCode.DECORATOR_ARITY_WRONG, Decorator.nodeForError(decorator), `Incorrect number of arguments to @NgModule decorator`);
@@ -29758,7 +30223,7 @@ Either add the @Injectable() decorator to '${provider.node.name
29758
30223
  * `DecoratorHandler` which handles the `@Component` annotation.
29759
30224
  */
29760
30225
  class ComponentDecoratorHandler {
29761
- constructor(reflector, evaluator, metaRegistry, metaReader, scopeReader, scopeRegistry, typeCheckScopeRegistry, resourceRegistry, isCore, resourceLoader, rootDirs, defaultPreserveWhitespaces, i18nUseExternalIds, enableI18nLegacyMessageIdFormat, usePoisonedData, i18nNormalizeLineEndingsInICUs, moduleResolver, cycleAnalyzer, cycleHandlingStrategy, refEmitter, defaultImportRecorder, depTracker, injectableRegistry, semanticDepGraphUpdater, annotateForClosureCompiler) {
30226
+ constructor(reflector, evaluator, metaRegistry, metaReader, scopeReader, scopeRegistry, typeCheckScopeRegistry, resourceRegistry, isCore, resourceLoader, rootDirs, defaultPreserveWhitespaces, i18nUseExternalIds, enableI18nLegacyMessageIdFormat, usePoisonedData, i18nNormalizeLineEndingsInICUs, moduleResolver, cycleAnalyzer, cycleHandlingStrategy, refEmitter, defaultImportRecorder, depTracker, injectableRegistry, semanticDepGraphUpdater, annotateForClosureCompiler, perf) {
29762
30227
  this.reflector = reflector;
29763
30228
  this.evaluator = evaluator;
29764
30229
  this.metaRegistry = metaRegistry;
@@ -29784,6 +30249,7 @@ Either add the @Injectable() decorator to '${provider.node.name
29784
30249
  this.injectableRegistry = injectableRegistry;
29785
30250
  this.semanticDepGraphUpdater = semanticDepGraphUpdater;
29786
30251
  this.annotateForClosureCompiler = annotateForClosureCompiler;
30252
+ this.perf = perf;
29787
30253
  this.literalCache = new Map();
29788
30254
  this.elementSchemaRegistry = new DomElementSchemaRegistry();
29789
30255
  /**
@@ -29829,9 +30295,16 @@ Either add the @Injectable() decorator to '${provider.node.name
29829
30295
  const meta = this._resolveLiteral(decorator);
29830
30296
  const component = reflectObjectLiteral(meta);
29831
30297
  const containingFile = node.getSourceFile().fileName;
29832
- const resolveStyleUrl = (styleUrl, nodeForError, resourceType) => {
29833
- const resourceUrl = this._resolveResourceOrThrow(styleUrl, containingFile, nodeForError, resourceType);
29834
- return this.resourceLoader.preload(resourceUrl);
30298
+ const resolveStyleUrl = (styleUrl) => {
30299
+ try {
30300
+ const resourceUrl = this.resourceLoader.resolve(styleUrl, containingFile);
30301
+ return this.resourceLoader.preload(resourceUrl);
30302
+ }
30303
+ catch (_a) {
30304
+ // Don't worry about failures to preload. We can handle this problem during analysis by
30305
+ // producing a diagnostic.
30306
+ return undefined;
30307
+ }
29835
30308
  };
29836
30309
  // A Promise that waits for the template and all <link>ed styles within it to be preloaded.
29837
30310
  const templateAndTemplateStyleResources = this._preloadAndParseTemplate(node, decorator, component, containingFile)
@@ -29839,9 +30312,7 @@ Either add the @Injectable() decorator to '${provider.node.name
29839
30312
  if (template === null) {
29840
30313
  return undefined;
29841
30314
  }
29842
- const nodeForError = getTemplateDeclarationNodeForError(template.declaration);
29843
- return Promise
29844
- .all(template.styleUrls.map(styleUrl => resolveStyleUrl(styleUrl, nodeForError, 1 /* StylesheetFromTemplate */)))
30315
+ return Promise.all(template.styleUrls.map(styleUrl => resolveStyleUrl(styleUrl)))
29845
30316
  .then(() => undefined);
29846
30317
  });
29847
30318
  // Extract all the styleUrls in the decorator.
@@ -29856,15 +30327,18 @@ Either add the @Injectable() decorator to '${provider.node.name
29856
30327
  return Promise
29857
30328
  .all([
29858
30329
  templateAndTemplateStyleResources,
29859
- ...componentStyleUrls.map(styleUrl => resolveStyleUrl(styleUrl.url, styleUrl.nodeForError, 2 /* StylesheetFromDecorator */))
30330
+ ...componentStyleUrls.map(styleUrl => resolveStyleUrl(styleUrl.url))
29860
30331
  ])
29861
30332
  .then(() => undefined);
29862
30333
  }
29863
30334
  }
29864
30335
  analyze(node, decorator, flags = HandlerFlags.NONE) {
29865
30336
  var _a;
30337
+ this.perf.eventCount(PerfEvent.AnalyzeComponent);
29866
30338
  const containingFile = node.getSourceFile().fileName;
29867
30339
  this.literalCache.delete(decorator);
30340
+ let diagnostics;
30341
+ let isPoisoned = false;
29868
30342
  // @Component inherits @Directive, so begin by extracting the @Directive metadata and building
29869
30343
  // on it.
29870
30344
  const directiveResult = extractDirectiveMetadata(node, decorator, this.reflector, this.evaluator, this.defaultImportRecorder, this.isCore, flags, this.annotateForClosureCompiler, this.elementSchemaRegistry.getDefaultComponentElementName());
@@ -29932,14 +30406,23 @@ Either add the @Injectable() decorator to '${provider.node.name
29932
30406
  ...this._extractComponentStyleUrls(component), ...this._extractTemplateStyleUrls(template)
29933
30407
  ];
29934
30408
  for (const styleUrl of styleUrls) {
29935
- const resourceType = styleUrl.source === 2 /* StylesheetFromDecorator */ ?
29936
- 2 /* StylesheetFromDecorator */ :
29937
- 1 /* StylesheetFromTemplate */;
29938
- const resourceUrl = this._resolveResourceOrThrow(styleUrl.url, containingFile, styleUrl.nodeForError, resourceType);
29939
- const resourceStr = this.resourceLoader.load(resourceUrl);
29940
- styles.push(resourceStr);
29941
- if (this.depTracker !== null) {
29942
- this.depTracker.addResourceDependency(node.getSourceFile(), absoluteFrom(resourceUrl));
30409
+ try {
30410
+ const resourceUrl = this.resourceLoader.resolve(styleUrl.url, containingFile);
30411
+ const resourceStr = this.resourceLoader.load(resourceUrl);
30412
+ styles.push(resourceStr);
30413
+ if (this.depTracker !== null) {
30414
+ this.depTracker.addResourceDependency(node.getSourceFile(), absoluteFrom(resourceUrl));
30415
+ }
30416
+ }
30417
+ catch (_b) {
30418
+ if (diagnostics === undefined) {
30419
+ diagnostics = [];
30420
+ }
30421
+ const resourceType = styleUrl.source === 2 /* StylesheetFromDecorator */ ?
30422
+ 2 /* StylesheetFromDecorator */ :
30423
+ 1 /* StylesheetFromTemplate */;
30424
+ diagnostics.push(this.makeResourceNotFoundError(styleUrl.url, styleUrl.nodeForError, resourceType)
30425
+ .toDiagnostic());
29943
30426
  }
29944
30427
  }
29945
30428
  let inlineStyles = null;
@@ -29982,8 +30465,9 @@ Either add the @Injectable() decorator to '${provider.node.name
29982
30465
  styles: styleResources,
29983
30466
  template: templateResource,
29984
30467
  },
29985
- isPoisoned: false,
30468
+ isPoisoned,
29986
30469
  },
30470
+ diagnostics,
29987
30471
  };
29988
30472
  if (changeDetection !== null) {
29989
30473
  output.analysis.meta.changeDetection = changeDetection;
@@ -30212,12 +30696,15 @@ Either add the @Injectable() decorator to '${provider.node.name
30212
30696
  let styles = [];
30213
30697
  if (analysis.styleUrls !== null) {
30214
30698
  for (const styleUrl of analysis.styleUrls) {
30215
- const resourceType = styleUrl.source === 2 /* StylesheetFromDecorator */ ?
30216
- 2 /* StylesheetFromDecorator */ :
30217
- 1 /* StylesheetFromTemplate */;
30218
- const resolvedStyleUrl = this._resolveResourceOrThrow(styleUrl.url, containingFile, styleUrl.nodeForError, resourceType);
30219
- const styleText = this.resourceLoader.load(resolvedStyleUrl);
30220
- styles.push(styleText);
30699
+ try {
30700
+ const resolvedStyleUrl = this.resourceLoader.resolve(styleUrl.url, containingFile);
30701
+ const styleText = this.resourceLoader.load(resolvedStyleUrl);
30702
+ styles.push(styleText);
30703
+ }
30704
+ catch (e) {
30705
+ // Resource resolve failures should already be in the diagnostics list from the analyze
30706
+ // stage. We do not need to do anything with them when updating resources.
30707
+ }
30221
30708
  }
30222
30709
  }
30223
30710
  if (analysis.inlineStyles !== null) {
@@ -30339,8 +30826,15 @@ Either add the @Injectable() decorator to '${provider.node.name
30339
30826
  const styleUrlsExpr = component.get('styleUrls');
30340
30827
  if (styleUrlsExpr !== undefined && ts$1.isArrayLiteralExpression(styleUrlsExpr)) {
30341
30828
  for (const expression of stringLiteralElements(styleUrlsExpr)) {
30342
- const resourceUrl = this._resolveResourceOrThrow(expression.text, containingFile, expression, 2 /* StylesheetFromDecorator */);
30343
- styles.add({ path: absoluteFrom(resourceUrl), expression });
30829
+ try {
30830
+ const resourceUrl = this.resourceLoader.resolve(expression.text, containingFile);
30831
+ styles.add({ path: absoluteFrom(resourceUrl), expression });
30832
+ }
30833
+ catch (_a) {
30834
+ // Errors in style resource extraction do not need to be handled here. We will produce
30835
+ // diagnostics for each one that fails in the analysis, after we evaluate the `styleUrls`
30836
+ // expression to determine _all_ style resources, not just the string literals.
30837
+ }
30344
30838
  }
30345
30839
  }
30346
30840
  const stylesExpr = component.get('styles');
@@ -30359,20 +30853,25 @@ Either add the @Injectable() decorator to '${provider.node.name
30359
30853
  if (typeof templateUrl !== 'string') {
30360
30854
  throw createValueHasWrongTypeError(templateUrlExpr, templateUrl, 'templateUrl must be a string');
30361
30855
  }
30362
- const resourceUrl = this._resolveResourceOrThrow(templateUrl, containingFile, templateUrlExpr, 0 /* Template */);
30363
- const templatePromise = this.resourceLoader.preload(resourceUrl);
30364
- // If the preload worked, then actually load and parse the template, and wait for any style
30365
- // URLs to resolve.
30366
- if (templatePromise !== undefined) {
30367
- return templatePromise.then(() => {
30368
- const templateDecl = this.parseTemplateDeclaration(decorator, component, containingFile);
30369
- const template = this.extractTemplate(node, templateDecl);
30370
- this.preanalyzeTemplateCache.set(node, template);
30371
- return template;
30372
- });
30856
+ try {
30857
+ const resourceUrl = this.resourceLoader.resolve(templateUrl, containingFile);
30858
+ const templatePromise = this.resourceLoader.preload(resourceUrl);
30859
+ // If the preload worked, then actually load and parse the template, and wait for any style
30860
+ // URLs to resolve.
30861
+ if (templatePromise !== undefined) {
30862
+ return templatePromise.then(() => {
30863
+ const templateDecl = this.parseTemplateDeclaration(decorator, component, containingFile);
30864
+ const template = this.extractTemplate(node, templateDecl);
30865
+ this.preanalyzeTemplateCache.set(node, template);
30866
+ return template;
30867
+ });
30868
+ }
30869
+ else {
30870
+ return Promise.resolve(null);
30871
+ }
30373
30872
  }
30374
- else {
30375
- return Promise.resolve(null);
30873
+ catch (e) {
30874
+ throw this.makeResourceNotFoundError(templateUrl, templateUrlExpr, 0 /* Template */);
30376
30875
  }
30377
30876
  }
30378
30877
  else {
@@ -30504,16 +31003,21 @@ Either add the @Injectable() decorator to '${provider.node.name
30504
31003
  if (typeof templateUrl !== 'string') {
30505
31004
  throw createValueHasWrongTypeError(templateUrlExpr, templateUrl, 'templateUrl must be a string');
30506
31005
  }
30507
- const resourceUrl = this._resolveResourceOrThrow(templateUrl, containingFile, templateUrlExpr, 0 /* Template */);
30508
- return {
30509
- isInline: false,
30510
- interpolationConfig,
30511
- preserveWhitespaces,
30512
- templateUrl,
30513
- templateUrlExpression: templateUrlExpr,
30514
- resolvedTemplateUrl: resourceUrl,
30515
- sourceMapUrl: sourceMapUrl(resourceUrl),
30516
- };
31006
+ try {
31007
+ const resourceUrl = this.resourceLoader.resolve(templateUrl, containingFile);
31008
+ return {
31009
+ isInline: false,
31010
+ interpolationConfig,
31011
+ preserveWhitespaces,
31012
+ templateUrl,
31013
+ templateUrlExpression: templateUrlExpr,
31014
+ resolvedTemplateUrl: resourceUrl,
31015
+ sourceMapUrl: sourceMapUrl(resourceUrl),
31016
+ };
31017
+ }
31018
+ catch (e) {
31019
+ throw this.makeResourceNotFoundError(templateUrl, templateUrlExpr, 0 /* Template */);
31020
+ }
30517
31021
  }
30518
31022
  else if (component.has('template')) {
30519
31023
  return {
@@ -30566,30 +31070,20 @@ Either add the @Injectable() decorator to '${provider.node.name
30566
31070
  }
30567
31071
  this.cycleAnalyzer.recordSyntheticImport(origin, imported);
30568
31072
  }
30569
- /**
30570
- * Resolve the url of a resource relative to the file that contains the reference to it.
30571
- *
30572
- * Throws a FatalDiagnosticError when unable to resolve the file.
30573
- */
30574
- _resolveResourceOrThrow(file, basePath, nodeForError, resourceType) {
30575
- try {
30576
- return this.resourceLoader.resolve(file, basePath);
30577
- }
30578
- catch (e) {
30579
- let errorText;
30580
- switch (resourceType) {
30581
- case 0 /* Template */:
30582
- errorText = `Could not find template file '${file}'.`;
30583
- break;
30584
- case 1 /* StylesheetFromTemplate */:
30585
- errorText = `Could not find stylesheet file '${file}' linked from the template.`;
30586
- break;
30587
- case 2 /* StylesheetFromDecorator */:
30588
- errorText = `Could not find stylesheet file '${file}'.`;
30589
- break;
30590
- }
30591
- throw new FatalDiagnosticError(ErrorCode.COMPONENT_RESOURCE_NOT_FOUND, nodeForError, errorText);
31073
+ makeResourceNotFoundError(file, nodeForError, resourceType) {
31074
+ let errorText;
31075
+ switch (resourceType) {
31076
+ case 0 /* Template */:
31077
+ errorText = `Could not find template file '${file}'.`;
31078
+ break;
31079
+ case 1 /* StylesheetFromTemplate */:
31080
+ errorText = `Could not find stylesheet file '${file}' linked from the template.`;
31081
+ break;
31082
+ case 2 /* StylesheetFromDecorator */:
31083
+ errorText = `Could not find stylesheet file '${file}'.`;
31084
+ break;
30592
31085
  }
31086
+ return new FatalDiagnosticError(ErrorCode.COMPONENT_RESOURCE_NOT_FOUND, nodeForError, errorText);
30593
31087
  }
30594
31088
  _extractTemplateStyleUrls(template) {
30595
31089
  if (template.styleUrls === null) {
@@ -30657,7 +31151,7 @@ Either add the @Injectable() decorator to '${provider.node.name
30657
31151
  * Adapts the `compileIvyInjectable` compiler for `@Injectable` decorators to the Ivy compiler.
30658
31152
  */
30659
31153
  class InjectableDecoratorHandler {
30660
- constructor(reflector, defaultImportRecorder, isCore, strictCtorDeps, injectableRegistry,
31154
+ constructor(reflector, defaultImportRecorder, isCore, strictCtorDeps, injectableRegistry, perf,
30661
31155
  /**
30662
31156
  * What to do if the injectable already contains a ɵprov property.
30663
31157
  *
@@ -30670,6 +31164,7 @@ Either add the @Injectable() decorator to '${provider.node.name
30670
31164
  this.isCore = isCore;
30671
31165
  this.strictCtorDeps = strictCtorDeps;
30672
31166
  this.injectableRegistry = injectableRegistry;
31167
+ this.perf = perf;
30673
31168
  this.errorOnDuplicateProv = errorOnDuplicateProv;
30674
31169
  this.precedence = HandlerPrecedence.SHARED;
30675
31170
  this.name = InjectableDecoratorHandler.name;
@@ -30691,6 +31186,7 @@ Either add the @Injectable() decorator to '${provider.node.name
30691
31186
  }
30692
31187
  }
30693
31188
  analyze(node, decorator) {
31189
+ this.perf.eventCount(PerfEvent.AnalyzeInjectable);
30694
31190
  const meta = extractInjectableMetadata(node, decorator, this.reflector);
30695
31191
  const decorators = this.reflector.getDecoratorsOfDeclaration(node);
30696
31192
  return {
@@ -30946,7 +31442,7 @@ Either add the @Injectable() decorator to '${provider.node.name
30946
31442
  }
30947
31443
  }
30948
31444
  class PipeDecoratorHandler {
30949
- constructor(reflector, evaluator, metaRegistry, scopeRegistry, defaultImportRecorder, injectableRegistry, isCore) {
31445
+ constructor(reflector, evaluator, metaRegistry, scopeRegistry, defaultImportRecorder, injectableRegistry, isCore, perf) {
30950
31446
  this.reflector = reflector;
30951
31447
  this.evaluator = evaluator;
30952
31448
  this.metaRegistry = metaRegistry;
@@ -30954,6 +31450,7 @@ Either add the @Injectable() decorator to '${provider.node.name
30954
31450
  this.defaultImportRecorder = defaultImportRecorder;
30955
31451
  this.injectableRegistry = injectableRegistry;
30956
31452
  this.isCore = isCore;
31453
+ this.perf = perf;
30957
31454
  this.precedence = HandlerPrecedence.PRIMARY;
30958
31455
  this.name = PipeDecoratorHandler.name;
30959
31456
  }
@@ -30974,6 +31471,7 @@ Either add the @Injectable() decorator to '${provider.node.name
30974
31471
  }
30975
31472
  }
30976
31473
  analyze(clazz, decorator) {
31474
+ this.perf.eventCount(PerfEvent.AnalyzePipe);
30977
31475
  const name = clazz.name.text;
30978
31476
  const type = wrapTypeReference(this.reflector, clazz);
30979
31477
  const internalType = new WrappedNodeExpr(this.reflector.getInternalNameOfClass(clazz));
@@ -31152,8 +31650,9 @@ Either add the @Injectable() decorator to '${provider.node.name
31152
31650
  * dependencies within the same program are tracked; imports into packages on NPM are not.
31153
31651
  */
31154
31652
  class ImportGraph {
31155
- constructor(checker) {
31653
+ constructor(checker, perf) {
31156
31654
  this.checker = checker;
31655
+ this.perf = perf;
31157
31656
  this.map = new Map();
31158
31657
  }
31159
31658
  /**
@@ -31229,25 +31728,27 @@ Either add the @Injectable() decorator to '${provider.node.name
31229
31728
  }
31230
31729
  }
31231
31730
  scanImports(sf) {
31232
- const imports = new Set();
31233
- // Look through the source file for import and export statements.
31234
- for (const stmt of sf.statements) {
31235
- if ((!ts$1.isImportDeclaration(stmt) && !ts$1.isExportDeclaration(stmt)) ||
31236
- stmt.moduleSpecifier === undefined) {
31237
- continue;
31238
- }
31239
- const symbol = this.checker.getSymbolAtLocation(stmt.moduleSpecifier);
31240
- if (symbol === undefined || symbol.valueDeclaration === undefined) {
31241
- // No symbol could be found to skip over this import/export.
31242
- continue;
31243
- }
31244
- const moduleFile = symbol.valueDeclaration;
31245
- if (ts$1.isSourceFile(moduleFile) && isLocalFile(moduleFile)) {
31246
- // Record this local import.
31247
- imports.add(moduleFile);
31731
+ return this.perf.inPhase(PerfPhase.CycleDetection, () => {
31732
+ const imports = new Set();
31733
+ // Look through the source file for import and export statements.
31734
+ for (const stmt of sf.statements) {
31735
+ if ((!ts$1.isImportDeclaration(stmt) && !ts$1.isExportDeclaration(stmt)) ||
31736
+ stmt.moduleSpecifier === undefined) {
31737
+ continue;
31738
+ }
31739
+ const symbol = this.checker.getSymbolAtLocation(stmt.moduleSpecifier);
31740
+ if (symbol === undefined || symbol.valueDeclaration === undefined) {
31741
+ // No symbol could be found to skip over this import/export.
31742
+ continue;
31743
+ }
31744
+ const moduleFile = symbol.valueDeclaration;
31745
+ if (ts$1.isSourceFile(moduleFile) && isLocalFile(moduleFile)) {
31746
+ // Record this local import.
31747
+ imports.add(moduleFile);
31748
+ }
31248
31749
  }
31249
- }
31250
- return imports;
31750
+ return imports;
31751
+ });
31251
31752
  }
31252
31753
  }
31253
31754
  function isLocalFile(sf) {
@@ -31609,103 +32110,107 @@ Either add the @Injectable() decorator to '${provider.node.name
31609
32110
  * The previous build's `BuildState` is reconciled with the new program's changes, and the results
31610
32111
  * are merged into the new build's `PendingBuildState`.
31611
32112
  */
31612
- static reconcile(oldProgram, oldDriver, newProgram, modifiedResourceFiles) {
31613
- // Initialize the state of the current build based on the previous one.
31614
- let state;
31615
- if (oldDriver.state.kind === BuildStateKind.Pending) {
31616
- // The previous build never made it past the pending state. Reuse it as the starting state for
31617
- // this build.
31618
- state = oldDriver.state;
31619
- }
31620
- else {
31621
- let priorGraph = null;
31622
- if (oldDriver.state.lastGood !== null) {
31623
- priorGraph = oldDriver.state.lastGood.semanticDepGraph;
31624
- }
31625
- // The previous build was successfully analyzed. `pendingEmit` is the only state carried
31626
- // forward into this build.
31627
- state = {
31628
- kind: BuildStateKind.Pending,
31629
- pendingEmit: oldDriver.state.pendingEmit,
31630
- pendingTypeCheckEmit: oldDriver.state.pendingTypeCheckEmit,
31631
- changedResourcePaths: new Set(),
31632
- changedTsPaths: new Set(),
31633
- lastGood: oldDriver.state.lastGood,
31634
- semanticDepGraphUpdater: new SemanticDepGraphUpdater(priorGraph),
31635
- };
31636
- }
31637
- // Merge the freshly modified resource files with any prior ones.
31638
- if (modifiedResourceFiles !== null) {
31639
- for (const resFile of modifiedResourceFiles) {
31640
- state.changedResourcePaths.add(absoluteFrom(resFile));
31641
- }
31642
- }
31643
- // Next, process the files in the new program, with a couple of goals:
31644
- // 1) Determine which TS files have changed, if any, and merge them into `changedTsFiles`.
31645
- // 2) Produce a list of TS files which no longer exist in the program (they've been deleted
31646
- // since the previous compilation). These need to be removed from the state tracking to avoid
31647
- // leaking memory.
31648
- // All files in the old program, for easy detection of changes.
31649
- const oldFiles = new Set(oldProgram.getSourceFiles());
31650
- // Assume all the old files were deleted to begin with. Only TS files are tracked.
31651
- const deletedTsPaths = new Set(tsOnlyFiles(oldProgram).map(sf => sf.fileName));
31652
- for (const newFile of newProgram.getSourceFiles()) {
31653
- if (!newFile.isDeclarationFile) {
31654
- // This file exists in the new program, so remove it from `deletedTsPaths`.
31655
- deletedTsPaths.delete(newFile.fileName);
31656
- }
31657
- if (oldFiles.has(newFile)) {
31658
- // This file hasn't changed; no need to look at it further.
31659
- continue;
31660
- }
31661
- // The file has changed since the last successful build. The appropriate reaction depends on
31662
- // what kind of file it is.
31663
- if (!newFile.isDeclarationFile) {
31664
- // It's a .ts file, so track it as a change.
31665
- state.changedTsPaths.add(newFile.fileName);
32113
+ static reconcile(oldProgram, oldDriver, newProgram, modifiedResourceFiles, perf) {
32114
+ return perf.inPhase(PerfPhase.Reconciliation, () => {
32115
+ // Initialize the state of the current build based on the previous one.
32116
+ let state;
32117
+ if (oldDriver.state.kind === BuildStateKind.Pending) {
32118
+ // The previous build never made it past the pending state. Reuse it as the starting state
32119
+ // for this build.
32120
+ state = oldDriver.state;
31666
32121
  }
31667
32122
  else {
31668
- // It's a .d.ts file. Currently the compiler does not do a great job of tracking
31669
- // dependencies on .d.ts files, so bail out of incremental builds here and do a full build.
31670
- // This usually only happens if something in node_modules changes.
31671
- return IncrementalDriver.fresh(newProgram);
31672
- }
31673
- }
31674
- // The next step is to remove any deleted files from the state.
31675
- for (const filePath of deletedTsPaths) {
31676
- state.pendingEmit.delete(filePath);
31677
- state.pendingTypeCheckEmit.delete(filePath);
31678
- // Even if the file doesn't exist in the current compilation, it still might have been changed
31679
- // in a previous one, so delete it from the set of changed TS files, just in case.
31680
- state.changedTsPaths.delete(filePath);
31681
- }
31682
- // Now, changedTsPaths contains physically changed TS paths. Use the previous program's logical
31683
- // dependency graph to determine logically changed files.
31684
- const depGraph = new FileDependencyGraph();
31685
- // If a previous compilation exists, use its dependency graph to determine the set of logically
31686
- // changed files.
31687
- let logicalChanges = null;
31688
- if (state.lastGood !== null) {
31689
- // Extract the set of logically changed files. At the same time, this operation populates the
31690
- // current (fresh) dependency graph with information about those files which have not
31691
- // logically changed.
31692
- logicalChanges = depGraph.updateWithPhysicalChanges(state.lastGood.depGraph, state.changedTsPaths, deletedTsPaths, state.changedResourcePaths);
31693
- for (const fileName of state.changedTsPaths) {
31694
- logicalChanges.add(fileName);
31695
- }
31696
- // Any logically changed files need to be re-emitted. Most of the time this would happen
31697
- // regardless because the new dependency graph would _also_ identify the file as stale.
31698
- // However there are edge cases such as removing a component from an NgModule without adding
31699
- // it to another one, where the previous graph identifies the file as logically changed, but
31700
- // the new graph (which does not have that edge) fails to identify that the file should be
31701
- // re-emitted.
31702
- for (const change of logicalChanges) {
31703
- state.pendingEmit.add(change);
31704
- state.pendingTypeCheckEmit.add(change);
32123
+ let priorGraph = null;
32124
+ if (oldDriver.state.lastGood !== null) {
32125
+ priorGraph = oldDriver.state.lastGood.semanticDepGraph;
32126
+ }
32127
+ // The previous build was successfully analyzed. `pendingEmit` is the only state carried
32128
+ // forward into this build.
32129
+ state = {
32130
+ kind: BuildStateKind.Pending,
32131
+ pendingEmit: oldDriver.state.pendingEmit,
32132
+ pendingTypeCheckEmit: oldDriver.state.pendingTypeCheckEmit,
32133
+ changedResourcePaths: new Set(),
32134
+ changedTsPaths: new Set(),
32135
+ lastGood: oldDriver.state.lastGood,
32136
+ semanticDepGraphUpdater: new SemanticDepGraphUpdater(priorGraph),
32137
+ };
31705
32138
  }
31706
- }
31707
- // `state` now reflects the initial pending state of the current compilation.
31708
- return new IncrementalDriver(state, depGraph, logicalChanges);
32139
+ // Merge the freshly modified resource files with any prior ones.
32140
+ if (modifiedResourceFiles !== null) {
32141
+ for (const resFile of modifiedResourceFiles) {
32142
+ state.changedResourcePaths.add(absoluteFrom(resFile));
32143
+ }
32144
+ }
32145
+ // Next, process the files in the new program, with a couple of goals:
32146
+ // 1) Determine which TS files have changed, if any, and merge them into `changedTsFiles`.
32147
+ // 2) Produce a list of TS files which no longer exist in the program (they've been deleted
32148
+ // since the previous compilation). These need to be removed from the state tracking to
32149
+ // avoid leaking memory.
32150
+ // All files in the old program, for easy detection of changes.
32151
+ const oldFiles = new Set(oldProgram.getSourceFiles());
32152
+ // Assume all the old files were deleted to begin with. Only TS files are tracked.
32153
+ const deletedTsPaths = new Set(tsOnlyFiles(oldProgram).map(sf => sf.fileName));
32154
+ for (const newFile of newProgram.getSourceFiles()) {
32155
+ if (!newFile.isDeclarationFile) {
32156
+ // This file exists in the new program, so remove it from `deletedTsPaths`.
32157
+ deletedTsPaths.delete(newFile.fileName);
32158
+ }
32159
+ if (oldFiles.has(newFile)) {
32160
+ // This file hasn't changed; no need to look at it further.
32161
+ continue;
32162
+ }
32163
+ // The file has changed since the last successful build. The appropriate reaction depends on
32164
+ // what kind of file it is.
32165
+ if (!newFile.isDeclarationFile) {
32166
+ // It's a .ts file, so track it as a change.
32167
+ state.changedTsPaths.add(newFile.fileName);
32168
+ }
32169
+ else {
32170
+ // It's a .d.ts file. Currently the compiler does not do a great job of tracking
32171
+ // dependencies on .d.ts files, so bail out of incremental builds here and do a full
32172
+ // build. This usually only happens if something in node_modules changes.
32173
+ return IncrementalDriver.fresh(newProgram);
32174
+ }
32175
+ }
32176
+ // The next step is to remove any deleted files from the state.
32177
+ for (const filePath of deletedTsPaths) {
32178
+ state.pendingEmit.delete(filePath);
32179
+ state.pendingTypeCheckEmit.delete(filePath);
32180
+ // Even if the file doesn't exist in the current compilation, it still might have been
32181
+ // changed in a previous one, so delete it from the set of changed TS files, just in case.
32182
+ state.changedTsPaths.delete(filePath);
32183
+ }
32184
+ perf.eventCount(PerfEvent.SourceFilePhysicalChange, state.changedTsPaths.size);
32185
+ // Now, changedTsPaths contains physically changed TS paths. Use the previous program's
32186
+ // logical dependency graph to determine logically changed files.
32187
+ const depGraph = new FileDependencyGraph();
32188
+ // If a previous compilation exists, use its dependency graph to determine the set of
32189
+ // logically changed files.
32190
+ let logicalChanges = null;
32191
+ if (state.lastGood !== null) {
32192
+ // Extract the set of logically changed files. At the same time, this operation populates
32193
+ // the current (fresh) dependency graph with information about those files which have not
32194
+ // logically changed.
32195
+ logicalChanges = depGraph.updateWithPhysicalChanges(state.lastGood.depGraph, state.changedTsPaths, deletedTsPaths, state.changedResourcePaths);
32196
+ perf.eventCount(PerfEvent.SourceFileLogicalChange, logicalChanges.size);
32197
+ for (const fileName of state.changedTsPaths) {
32198
+ logicalChanges.add(fileName);
32199
+ }
32200
+ // Any logically changed files need to be re-emitted. Most of the time this would happen
32201
+ // regardless because the new dependency graph would _also_ identify the file as stale.
32202
+ // However there are edge cases such as removing a component from an NgModule without adding
32203
+ // it to another one, where the previous graph identifies the file as logically changed, but
32204
+ // the new graph (which does not have that edge) fails to identify that the file should be
32205
+ // re-emitted.
32206
+ for (const change of logicalChanges) {
32207
+ state.pendingEmit.add(change);
32208
+ state.pendingTypeCheckEmit.add(change);
32209
+ }
32210
+ }
32211
+ // `state` now reflects the initial pending state of the current compilation.
32212
+ return new IncrementalDriver(state, depGraph, logicalChanges);
32213
+ });
31709
32214
  }
31710
32215
  static fresh(program) {
31711
32216
  // Initialize the set of files which need to be emitted to the set of all TS files in the
@@ -32390,29 +32895,6 @@ Either add the @Injectable() decorator to '${provider.node.name
32390
32895
  node.modifiers.some(mod => mod.kind === ts$1.SyntaxKind.StaticKeyword);
32391
32896
  }
32392
32897
 
32393
- const NOOP_PERF_RECORDER = {
32394
- enabled: false,
32395
- mark: (name, node, category, detail) => { },
32396
- start: (name, node, category, detail) => {
32397
- return 0;
32398
- },
32399
- stop: (span) => { },
32400
- };
32401
-
32402
- /**
32403
- * @license
32404
- * Copyright Google LLC All Rights Reserved.
32405
- *
32406
- * Use of this source code is governed by an MIT-style license that can be
32407
- * found in the LICENSE file at https://angular.io/license
32408
- */
32409
- var PerfLogEventType;
32410
- (function (PerfLogEventType) {
32411
- PerfLogEventType[PerfLogEventType["SPAN_OPEN"] = 0] = "SPAN_OPEN";
32412
- PerfLogEventType[PerfLogEventType["SPAN_CLOSE"] = 1] = "SPAN_CLOSE";
32413
- PerfLogEventType[PerfLogEventType["MARK"] = 2] = "MARK";
32414
- })(PerfLogEventType || (PerfLogEventType = {}));
32415
-
32416
32898
  /**
32417
32899
  * @license
32418
32900
  * Copyright Google LLC All Rights Reserved.
@@ -35333,6 +35815,30 @@ Either add the @Injectable() decorator to '${provider.node.name
35333
35815
  }
35334
35816
  this._diagnostics.push(makeInlineDiagnostic(templateId, ErrorCode.INLINE_TYPE_CTOR_REQUIRED, node.name, message, directives.map(dir => makeRelatedInformation(dir.name, `Requires an inline type constructor.`))));
35335
35817
  }
35818
+ suboptimalTypeInference(templateId, variables) {
35819
+ const mapping = this.resolver.getSourceMapping(templateId);
35820
+ // Select one of the template variables that's most suitable for reporting the diagnostic. Any
35821
+ // variable will do, but prefer one bound to the context's $implicit if present.
35822
+ let diagnosticVar = null;
35823
+ for (const variable of variables) {
35824
+ if (diagnosticVar === null || (variable.value === '' || variable.value === '$implicit')) {
35825
+ diagnosticVar = variable;
35826
+ }
35827
+ }
35828
+ if (diagnosticVar === null) {
35829
+ // There is no variable on which to report the diagnostic.
35830
+ return;
35831
+ }
35832
+ let varIdentification = `'${diagnosticVar.name}'`;
35833
+ if (variables.length === 2) {
35834
+ varIdentification += ` (and 1 other)`;
35835
+ }
35836
+ else if (variables.length > 2) {
35837
+ varIdentification += ` (and ${variables.length - 1} others)`;
35838
+ }
35839
+ const message = `This structural directive supports advanced type inference, but the current compiler configuration prevents its usage. The variable ${varIdentification} will have type 'any' as a result.\n\nConsider enabling the 'strictTemplates' option in your tsconfig.json for better type inference within this template.`;
35840
+ this._diagnostics.push(makeTemplateDiagnostic(templateId, mapping, diagnosticVar.keySpan, ts$1.DiagnosticCategory.Suggestion, ngErrorCode(ErrorCode.SUGGEST_SUBOPTIMAL_TYPE_INFERENCE), message));
35841
+ }
35336
35842
  }
35337
35843
  function makeInlineDiagnostic(templateId, code, node, messageText, relatedInformation) {
35338
35844
  return Object.assign(Object.assign({}, makeDiagnostic(code, node, messageText, relatedInformation)), { componentFile: node.getSourceFile(), templateId });
@@ -36184,11 +36690,20 @@ Either add the @Injectable() decorator to '${provider.node.name
36184
36690
  });
36185
36691
  // The second kind of guard is a template context guard. This guard narrows the template
36186
36692
  // rendering context variable `ctx`.
36187
- if (dir.hasNgTemplateContextGuard && this.tcb.env.config.applyTemplateContextGuards) {
36188
- const ctx = this.scope.resolve(this.template);
36189
- const guardInvoke = tsCallMethod(dirId, 'ngTemplateContextGuard', [dirInstId, ctx]);
36190
- addParseSpanInfo(guardInvoke, this.template.sourceSpan);
36191
- directiveGuards.push(guardInvoke);
36693
+ if (dir.hasNgTemplateContextGuard) {
36694
+ if (this.tcb.env.config.applyTemplateContextGuards) {
36695
+ const ctx = this.scope.resolve(this.template);
36696
+ const guardInvoke = tsCallMethod(dirId, 'ngTemplateContextGuard', [dirInstId, ctx]);
36697
+ addParseSpanInfo(guardInvoke, this.template.sourceSpan);
36698
+ directiveGuards.push(guardInvoke);
36699
+ }
36700
+ else if (this.template.variables.length > 0 &&
36701
+ this.tcb.env.config.suggestionsForSuboptimalTypeInference) {
36702
+ // The compiler could have inferred a better type for the variables in this template,
36703
+ // but was prevented from doing so by the type-checking configuration. Issue a warning
36704
+ // diagnostic.
36705
+ this.tcb.oobRecorder.suboptimalTypeInference(this.tcb.id, this.template.variables);
36706
+ }
36192
36707
  }
36193
36708
  }
36194
36709
  }
@@ -36246,15 +36761,10 @@ Either add the @Injectable() decorator to '${provider.node.name
36246
36761
  }
36247
36762
  }
36248
36763
  /**
36249
- * A `TcbOp` which constructs an instance of a directive _without_ setting any of its inputs. Inputs
36250
- * are later set in the `TcbDirectiveInputsOp`. Type checking was found to be faster when done in
36251
- * this way as opposed to `TcbDirectiveCtorOp` which is only necessary when the directive is
36252
- * generic.
36253
- *
36254
- * Executing this operation returns a reference to the directive instance variable with its inferred
36255
- * type.
36764
+ * A `TcbOp` which constructs an instance of a directive. For generic directives, generic
36765
+ * parameters are set to `any` type.
36256
36766
  */
36257
- class TcbDirectiveTypeOp extends TcbOp {
36767
+ class TcbDirectiveTypeOpBase extends TcbOp {
36258
36768
  constructor(tcb, scope, node, dir) {
36259
36769
  super();
36260
36770
  this.tcb = tcb;
@@ -36269,14 +36779,65 @@ Either add the @Injectable() decorator to '${provider.node.name
36269
36779
  return true;
36270
36780
  }
36271
36781
  execute() {
36782
+ const dirRef = this.dir.ref;
36783
+ const rawType = this.tcb.env.referenceType(this.dir.ref);
36784
+ let type;
36785
+ if (this.dir.isGeneric === false || dirRef.node.typeParameters === undefined) {
36786
+ type = rawType;
36787
+ }
36788
+ else {
36789
+ if (!ts$1.isTypeReferenceNode(rawType)) {
36790
+ throw new Error(`Expected TypeReferenceNode when referencing the type for ${this.dir.ref.debugName}`);
36791
+ }
36792
+ const typeArguments = dirRef.node.typeParameters.map(() => ts$1.factory.createKeywordTypeNode(ts$1.SyntaxKind.AnyKeyword));
36793
+ type = ts$1.factory.createTypeReferenceNode(rawType.typeName, typeArguments);
36794
+ }
36272
36795
  const id = this.tcb.allocateId();
36273
- const type = this.tcb.env.referenceType(this.dir.ref);
36274
36796
  addExpressionIdentifier(type, ExpressionIdentifier.DIRECTIVE);
36275
36797
  addParseSpanInfo(type, this.node.startSourceSpan || this.node.sourceSpan);
36276
36798
  this.scope.addStatement(tsDeclareVariable(id, type));
36277
36799
  return id;
36278
36800
  }
36279
36801
  }
36802
+ /**
36803
+ * A `TcbOp` which constructs an instance of a non-generic directive _without_ setting any of its
36804
+ * inputs. Inputs are later set in the `TcbDirectiveInputsOp`. Type checking was found to be
36805
+ * faster when done in this way as opposed to `TcbDirectiveCtorOp` which is only necessary when the
36806
+ * directive is generic.
36807
+ *
36808
+ * Executing this operation returns a reference to the directive instance variable with its inferred
36809
+ * type.
36810
+ */
36811
+ class TcbNonGenericDirectiveTypeOp extends TcbDirectiveTypeOpBase {
36812
+ /**
36813
+ * Creates a variable declaration for this op's directive of the argument type. Returns the id of
36814
+ * the newly created variable.
36815
+ */
36816
+ execute() {
36817
+ const dirRef = this.dir.ref;
36818
+ if (this.dir.isGeneric) {
36819
+ throw new Error(`Assertion Error: expected ${dirRef.debugName} not to be generic.`);
36820
+ }
36821
+ return super.execute();
36822
+ }
36823
+ }
36824
+ /**
36825
+ * A `TcbOp` which constructs an instance of a generic directive with its generic parameters set
36826
+ * to `any` type. This op is like `TcbDirectiveTypeOp`, except that generic parameters are set to
36827
+ * `any` type. This is used for situations where we want to avoid inlining.
36828
+ *
36829
+ * Executing this operation returns a reference to the directive instance variable with its generic
36830
+ * type parameters set to `any`.
36831
+ */
36832
+ class TcbGenericDirectiveTypeWithAnyParamsOp extends TcbDirectiveTypeOpBase {
36833
+ execute() {
36834
+ const dirRef = this.dir.ref;
36835
+ if (dirRef.node.typeParameters === undefined) {
36836
+ throw new Error(`Assertion Error: expected typeParameters when creating a declaration for ${dirRef.debugName}`);
36837
+ }
36838
+ return super.execute();
36839
+ }
36840
+ }
36280
36841
  /**
36281
36842
  * A `TcbOp` which creates a variable for a local ref in a template.
36282
36843
  * The initializer for the variable is the variable expression for the directive, template, or
@@ -37198,8 +37759,26 @@ Either add the @Injectable() decorator to '${provider.node.name
37198
37759
  }
37199
37760
  const dirMap = new Map();
37200
37761
  for (const dir of directives) {
37201
- const directiveOp = dir.isGeneric ? new TcbDirectiveCtorOp(this.tcb, this, node, dir) :
37202
- new TcbDirectiveTypeOp(this.tcb, this, node, dir);
37762
+ let directiveOp;
37763
+ const host = this.tcb.env.reflector;
37764
+ const dirRef = dir.ref;
37765
+ if (!dir.isGeneric) {
37766
+ // The most common case is that when a directive is not generic, we use the normal
37767
+ // `TcbNonDirectiveTypeOp`.
37768
+ directiveOp = new TcbNonGenericDirectiveTypeOp(this.tcb, this, node, dir);
37769
+ }
37770
+ else if (!requiresInlineTypeCtor(dirRef.node, host) ||
37771
+ this.tcb.env.config.useInlineTypeConstructors) {
37772
+ // For generic directives, we use a type constructor to infer types. If a directive requires
37773
+ // an inline type constructor, then inlining must be available to use the
37774
+ // `TcbDirectiveCtorOp`. If not we, we fallback to using `any` – see below.
37775
+ directiveOp = new TcbDirectiveCtorOp(this.tcb, this, node, dir);
37776
+ }
37777
+ else {
37778
+ // If inlining is not available, then we give up on infering the generic params, and use
37779
+ // `any` type for the directive's generic parameters.
37780
+ directiveOp = new TcbGenericDirectiveTypeWithAnyParamsOp(this.tcb, this, node, dir);
37781
+ }
37203
37782
  const dirIndex = this.opQueue.push(directiveOp) - 1;
37204
37783
  dirMap.set(dir, dirIndex);
37205
37784
  this.opQueue.push(new TcbDirectiveInputsOp(this.tcb, this, node, dir));
@@ -37623,12 +38202,12 @@ Either add the @Injectable() decorator to '${provider.node.name
37623
38202
  const fn = generateTypeCheckBlock(this, ref, fnId, meta, domSchemaChecker, oobRecorder);
37624
38203
  this.tcbStatements.push(fn);
37625
38204
  }
37626
- render() {
38205
+ render(removeComments) {
37627
38206
  let source = this.importManager.getAllImports(this.contextFile.fileName)
37628
38207
  .map(i => `import * as ${i.qualifier.text} from '${i.specifier}';`)
37629
38208
  .join('\n') +
37630
38209
  '\n\n';
37631
- const printer = ts$1.createPrinter();
38210
+ const printer = ts$1.createPrinter({ removeComments });
37632
38211
  source += '\n';
37633
38212
  for (const stmt of this.pipeInstStatements) {
37634
38213
  source += printer.printNode(ts$1.EmitHint.Unspecified, stmt, this.contextFile) + '\n';
@@ -37679,7 +38258,7 @@ Either add the @Injectable() decorator to '${provider.node.name
37679
38258
  * type checked.
37680
38259
  */
37681
38260
  class TypeCheckContextImpl {
37682
- constructor(config, compilerHost, componentMappingStrategy, refEmitter, reflector, host, inlining) {
38261
+ constructor(config, compilerHost, componentMappingStrategy, refEmitter, reflector, host, inlining, perf) {
37683
38262
  this.config = config;
37684
38263
  this.compilerHost = compilerHost;
37685
38264
  this.componentMappingStrategy = componentMappingStrategy;
@@ -37687,6 +38266,7 @@ Either add the @Injectable() decorator to '${provider.node.name
37687
38266
  this.reflector = reflector;
37688
38267
  this.host = host;
37689
38268
  this.inlining = inlining;
38269
+ this.perf = perf;
37690
38270
  this.fileMap = new Map();
37691
38271
  /**
37692
38272
  * A `Map` of `ts.SourceFile`s that the context has seen to the operations (additions of methods
@@ -37698,6 +38278,10 @@ Either add the @Injectable() decorator to '${provider.node.name
37698
38278
  * queued.
37699
38279
  */
37700
38280
  this.typeCtorPending = new Set();
38281
+ if (inlining === InliningMode.Error && config.useInlineTypeConstructors) {
38282
+ // We cannot use inlining for type checking since this environment does not support it.
38283
+ throw new Error(`AssertionError: invalid inlining configuration.`);
38284
+ }
37701
38285
  }
37702
38286
  /**
37703
38287
  * Register a template to potentially be type-checked.
@@ -37715,20 +38299,18 @@ Either add the @Injectable() decorator to '${provider.node.name
37715
38299
  if (parseErrors !== null) {
37716
38300
  templateDiagnostics.push(...this.getTemplateDiagnostics(parseErrors, templateId, sourceMapping));
37717
38301
  }
37718
- // Accumulate a list of any directives which could not have type constructors generated due to
37719
- // unsupported inlining operations.
37720
- let missingInlines = [];
37721
38302
  const boundTarget = binder.bind({ template });
37722
- // Get all of the directives used in the template and record type constructors for all of them.
37723
- for (const dir of boundTarget.getUsedDirectives()) {
37724
- const dirRef = dir.ref;
37725
- const dirNode = dirRef.node;
37726
- if (dir.isGeneric && requiresInlineTypeCtor(dirNode, this.reflector)) {
37727
- if (this.inlining === InliningMode.Error) {
37728
- missingInlines.push(dirNode);
38303
+ if (this.inlining === InliningMode.InlineOps) {
38304
+ // Get all of the directives used in the template and record inline type constructors when
38305
+ // required.
38306
+ for (const dir of boundTarget.getUsedDirectives()) {
38307
+ const dirRef = dir.ref;
38308
+ const dirNode = dirRef.node;
38309
+ if (!dir.isGeneric || !requiresInlineTypeCtor(dirNode, this.reflector)) {
38310
+ // inlining not required
37729
38311
  continue;
37730
38312
  }
37731
- // Add a type constructor operation for the directive.
38313
+ // Add an inline type constructor operation for the directive.
37732
38314
  this.addInlineTypeCtor(fileData, dirNode.getSourceFile(), dirRef, {
37733
38315
  fnName: 'ngTypeCtor',
37734
38316
  // The constructor should have a body if the directive comes from a .ts file, but not if
@@ -37752,17 +38334,13 @@ Either add the @Injectable() decorator to '${provider.node.name
37752
38334
  const tcbRequiresInline = requiresInlineTypeCheckBlock(ref.node, pipes);
37753
38335
  // If inlining is not supported, but is required for either the TCB or one of its directive
37754
38336
  // dependencies, then exit here with an error.
37755
- if (this.inlining === InliningMode.Error && (tcbRequiresInline || missingInlines.length > 0)) {
38337
+ if (this.inlining === InliningMode.Error && tcbRequiresInline) {
37756
38338
  // This template cannot be supported because the underlying strategy does not support inlining
37757
38339
  // and inlining would be required.
37758
38340
  // Record diagnostics to indicate the issues with this template.
37759
- if (tcbRequiresInline) {
37760
- shimData.oobRecorder.requiresInlineTcb(templateId, ref.node);
37761
- }
37762
- if (missingInlines.length > 0) {
37763
- shimData.oobRecorder.requiresInlineTypeConstructors(templateId, ref.node, missingInlines);
37764
- }
38341
+ shimData.oobRecorder.requiresInlineTcb(templateId, ref.node);
37765
38342
  // Checking this template would be unsupported, so don't try.
38343
+ this.perf.eventCount(PerfEvent.SkipGenerateTcbNoInline);
37766
38344
  return;
37767
38345
  }
37768
38346
  const meta = {
@@ -37771,6 +38349,7 @@ Either add the @Injectable() decorator to '${provider.node.name
37771
38349
  pipes,
37772
38350
  schemas,
37773
38351
  };
38352
+ this.perf.eventCount(PerfEvent.GenerateTcb);
37774
38353
  if (tcbRequiresInline) {
37775
38354
  // This class didn't meet the requirements for external type checking, so generate an inline
37776
38355
  // TCB for the class.
@@ -37857,7 +38436,7 @@ Either add the @Injectable() decorator to '${provider.node.name
37857
38436
  path: pendingShimData.file.fileName,
37858
38437
  templates: pendingShimData.templates,
37859
38438
  });
37860
- updates.set(pendingShimData.file.fileName, pendingShimData.file.render());
38439
+ updates.set(pendingShimData.file.fileName, pendingShimData.file.render(false /* removeComments */));
37861
38440
  }
37862
38441
  }
37863
38442
  return updates;
@@ -38587,7 +39166,7 @@ Either add the @Injectable() decorator to '${provider.node.name
38587
39166
  * `ProgramTypeCheckAdapter` for generation of template type-checking code.
38588
39167
  */
38589
39168
  class TemplateTypeCheckerImpl {
38590
- constructor(originalProgram, typeCheckingStrategy, typeCheckAdapter, config, refEmitter, reflector, compilerHost, priorBuild, componentScopeReader, typeCheckScopeRegistry) {
39169
+ constructor(originalProgram, typeCheckingStrategy, typeCheckAdapter, config, refEmitter, reflector, compilerHost, priorBuild, componentScopeReader, typeCheckScopeRegistry, perf) {
38591
39170
  this.originalProgram = originalProgram;
38592
39171
  this.typeCheckingStrategy = typeCheckingStrategy;
38593
39172
  this.typeCheckAdapter = typeCheckAdapter;
@@ -38598,6 +39177,7 @@ Either add the @Injectable() decorator to '${provider.node.name
38598
39177
  this.priorBuild = priorBuild;
38599
39178
  this.componentScopeReader = componentScopeReader;
38600
39179
  this.typeCheckScopeRegistry = typeCheckScopeRegistry;
39180
+ this.perf = perf;
38601
39181
  this.state = new Map();
38602
39182
  /**
38603
39183
  * Stores the `CompletionEngine` which powers autocompletion for each component class.
@@ -38709,48 +39289,52 @@ Either add the @Injectable() decorator to '${provider.node.name
38709
39289
  this.ensureAllShimsForOneFile(sf);
38710
39290
  break;
38711
39291
  }
38712
- const sfPath = absoluteFromSourceFile(sf);
38713
- const fileRecord = this.state.get(sfPath);
38714
- const typeCheckProgram = this.typeCheckingStrategy.getProgram();
38715
- const diagnostics = [];
38716
- if (fileRecord.hasInlines) {
38717
- const inlineSf = getSourceFileOrError(typeCheckProgram, sfPath);
38718
- diagnostics.push(...typeCheckProgram.getSemanticDiagnostics(inlineSf).map(diag => convertDiagnostic(diag, fileRecord.sourceManager)));
38719
- }
38720
- for (const [shimPath, shimRecord] of fileRecord.shimData) {
39292
+ return this.perf.inPhase(PerfPhase.TtcDiagnostics, () => {
39293
+ const sfPath = absoluteFromSourceFile(sf);
39294
+ const fileRecord = this.state.get(sfPath);
39295
+ const typeCheckProgram = this.typeCheckingStrategy.getProgram();
39296
+ const diagnostics = [];
39297
+ if (fileRecord.hasInlines) {
39298
+ const inlineSf = getSourceFileOrError(typeCheckProgram, sfPath);
39299
+ diagnostics.push(...typeCheckProgram.getSemanticDiagnostics(inlineSf).map(diag => convertDiagnostic(diag, fileRecord.sourceManager)));
39300
+ }
39301
+ for (const [shimPath, shimRecord] of fileRecord.shimData) {
39302
+ const shimSf = getSourceFileOrError(typeCheckProgram, shimPath);
39303
+ diagnostics.push(...typeCheckProgram.getSemanticDiagnostics(shimSf).map(diag => convertDiagnostic(diag, fileRecord.sourceManager)));
39304
+ diagnostics.push(...shimRecord.genesisDiagnostics);
39305
+ for (const templateData of shimRecord.templates.values()) {
39306
+ diagnostics.push(...templateData.templateDiagnostics);
39307
+ }
39308
+ }
39309
+ return diagnostics.filter((diag) => diag !== null);
39310
+ });
39311
+ }
39312
+ getDiagnosticsForComponent(component) {
39313
+ this.ensureShimForComponent(component);
39314
+ return this.perf.inPhase(PerfPhase.TtcDiagnostics, () => {
39315
+ const sf = component.getSourceFile();
39316
+ const sfPath = absoluteFromSourceFile(sf);
39317
+ const shimPath = this.typeCheckingStrategy.shimPathForComponent(component);
39318
+ const fileRecord = this.getFileData(sfPath);
39319
+ if (!fileRecord.shimData.has(shimPath)) {
39320
+ return [];
39321
+ }
39322
+ const templateId = fileRecord.sourceManager.getTemplateId(component);
39323
+ const shimRecord = fileRecord.shimData.get(shimPath);
39324
+ const typeCheckProgram = this.typeCheckingStrategy.getProgram();
39325
+ const diagnostics = [];
39326
+ if (shimRecord.hasInlines) {
39327
+ const inlineSf = getSourceFileOrError(typeCheckProgram, sfPath);
39328
+ diagnostics.push(...typeCheckProgram.getSemanticDiagnostics(inlineSf).map(diag => convertDiagnostic(diag, fileRecord.sourceManager)));
39329
+ }
38721
39330
  const shimSf = getSourceFileOrError(typeCheckProgram, shimPath);
38722
39331
  diagnostics.push(...typeCheckProgram.getSemanticDiagnostics(shimSf).map(diag => convertDiagnostic(diag, fileRecord.sourceManager)));
38723
39332
  diagnostics.push(...shimRecord.genesisDiagnostics);
38724
39333
  for (const templateData of shimRecord.templates.values()) {
38725
39334
  diagnostics.push(...templateData.templateDiagnostics);
38726
39335
  }
38727
- }
38728
- return diagnostics.filter((diag) => diag !== null);
38729
- }
38730
- getDiagnosticsForComponent(component) {
38731
- this.ensureShimForComponent(component);
38732
- const sf = component.getSourceFile();
38733
- const sfPath = absoluteFromSourceFile(sf);
38734
- const shimPath = this.typeCheckingStrategy.shimPathForComponent(component);
38735
- const fileRecord = this.getFileData(sfPath);
38736
- if (!fileRecord.shimData.has(shimPath)) {
38737
- return [];
38738
- }
38739
- const templateId = fileRecord.sourceManager.getTemplateId(component);
38740
- const shimRecord = fileRecord.shimData.get(shimPath);
38741
- const typeCheckProgram = this.typeCheckingStrategy.getProgram();
38742
- const diagnostics = [];
38743
- if (shimRecord.hasInlines) {
38744
- const inlineSf = getSourceFileOrError(typeCheckProgram, sfPath);
38745
- diagnostics.push(...typeCheckProgram.getSemanticDiagnostics(inlineSf).map(diag => convertDiagnostic(diag, fileRecord.sourceManager)));
38746
- }
38747
- const shimSf = getSourceFileOrError(typeCheckProgram, shimPath);
38748
- diagnostics.push(...typeCheckProgram.getSemanticDiagnostics(shimSf).map(diag => convertDiagnostic(diag, fileRecord.sourceManager)));
38749
- diagnostics.push(...shimRecord.genesisDiagnostics);
38750
- for (const templateData of shimRecord.templates.values()) {
38751
- diagnostics.push(...templateData.templateDiagnostics);
38752
- }
38753
- return diagnostics.filter((diag) => diag !== null && diag.templateId === templateId);
39336
+ return diagnostics.filter((diag) => diag !== null && diag.templateId === templateId);
39337
+ });
38754
39338
  }
38755
39339
  getTypeCheckBlock(component) {
38756
39340
  return this.getLatestComponentState(component).tcb;
@@ -38760,14 +39344,14 @@ Either add the @Injectable() decorator to '${provider.node.name
38760
39344
  if (engine === null) {
38761
39345
  return null;
38762
39346
  }
38763
- return engine.getGlobalCompletions(context);
39347
+ return this.perf.inPhase(PerfPhase.TtcAutocompletion, () => engine.getGlobalCompletions(context));
38764
39348
  }
38765
39349
  getExpressionCompletionLocation(ast, component) {
38766
39350
  const engine = this.getOrCreateCompletionEngine(component);
38767
39351
  if (engine === null) {
38768
39352
  return null;
38769
39353
  }
38770
- return engine.getExpressionCompletionLocation(ast);
39354
+ return this.perf.inPhase(PerfPhase.TtcAutocompletion, () => engine.getExpressionCompletionLocation(ast));
38771
39355
  }
38772
39356
  invalidateClass(clazz) {
38773
39357
  this.completionCache.delete(clazz);
@@ -38808,43 +39392,48 @@ Either add the @Injectable() decorator to '${provider.node.name
38808
39392
  if (previousResults === null || !previousResults.isComplete) {
38809
39393
  return;
38810
39394
  }
39395
+ this.perf.eventCount(PerfEvent.ReuseTypeCheckFile);
38811
39396
  this.state.set(sfPath, previousResults);
38812
39397
  }
38813
39398
  ensureAllShimsForAllFiles() {
38814
39399
  if (this.isComplete) {
38815
39400
  return;
38816
39401
  }
38817
- const host = new WholeProgramTypeCheckingHost(this);
38818
- const ctx = this.newContext(host);
38819
- for (const sf of this.originalProgram.getSourceFiles()) {
38820
- if (sf.isDeclarationFile || isShim(sf)) {
38821
- continue;
39402
+ this.perf.inPhase(PerfPhase.TcbGeneration, () => {
39403
+ const host = new WholeProgramTypeCheckingHost(this);
39404
+ const ctx = this.newContext(host);
39405
+ for (const sf of this.originalProgram.getSourceFiles()) {
39406
+ if (sf.isDeclarationFile || isShim(sf)) {
39407
+ continue;
39408
+ }
39409
+ this.maybeAdoptPriorResultsForFile(sf);
39410
+ const sfPath = absoluteFromSourceFile(sf);
39411
+ const fileData = this.getFileData(sfPath);
39412
+ if (fileData.isComplete) {
39413
+ continue;
39414
+ }
39415
+ this.typeCheckAdapter.typeCheck(sf, ctx);
39416
+ fileData.isComplete = true;
38822
39417
  }
39418
+ this.updateFromContext(ctx);
39419
+ this.isComplete = true;
39420
+ });
39421
+ }
39422
+ ensureAllShimsForOneFile(sf) {
39423
+ this.perf.inPhase(PerfPhase.TcbGeneration, () => {
38823
39424
  this.maybeAdoptPriorResultsForFile(sf);
38824
39425
  const sfPath = absoluteFromSourceFile(sf);
38825
39426
  const fileData = this.getFileData(sfPath);
38826
39427
  if (fileData.isComplete) {
38827
- continue;
39428
+ // All data for this file is present and accounted for already.
39429
+ return;
38828
39430
  }
39431
+ const host = new SingleFileTypeCheckingHost(sfPath, fileData, this.typeCheckingStrategy, this);
39432
+ const ctx = this.newContext(host);
38829
39433
  this.typeCheckAdapter.typeCheck(sf, ctx);
38830
39434
  fileData.isComplete = true;
38831
- }
38832
- this.updateFromContext(ctx);
38833
- this.isComplete = true;
38834
- }
38835
- ensureAllShimsForOneFile(sf) {
38836
- this.maybeAdoptPriorResultsForFile(sf);
38837
- const sfPath = absoluteFromSourceFile(sf);
38838
- const fileData = this.getFileData(sfPath);
38839
- if (fileData.isComplete) {
38840
- // All data for this file is present and accounted for already.
38841
- return;
38842
- }
38843
- const host = new SingleFileTypeCheckingHost(sfPath, fileData, this.typeCheckingStrategy, this);
38844
- const ctx = this.newContext(host);
38845
- this.typeCheckAdapter.typeCheck(sf, ctx);
38846
- fileData.isComplete = true;
38847
- this.updateFromContext(ctx);
39435
+ this.updateFromContext(ctx);
39436
+ });
38848
39437
  }
38849
39438
  ensureShimForComponent(component) {
38850
39439
  const sf = component.getSourceFile();
@@ -38864,7 +39453,7 @@ Either add the @Injectable() decorator to '${provider.node.name
38864
39453
  newContext(host) {
38865
39454
  const inlining = this.typeCheckingStrategy.supportsInlineOperations ? InliningMode.InlineOps :
38866
39455
  InliningMode.Error;
38867
- return new TypeCheckContextImpl(this.config, this.compilerHost, this.typeCheckingStrategy, this.refEmitter, this.reflector, host, inlining);
39456
+ return new TypeCheckContextImpl(this.config, this.compilerHost, this.typeCheckingStrategy, this.refEmitter, this.reflector, host, inlining, this.perf);
38868
39457
  }
38869
39458
  /**
38870
39459
  * Remove any shim data that depends on inline operations applied to the type-checking program.
@@ -38889,8 +39478,14 @@ Either add the @Injectable() decorator to '${provider.node.name
38889
39478
  }
38890
39479
  updateFromContext(ctx) {
38891
39480
  const updates = ctx.finalize();
38892
- this.typeCheckingStrategy.updateFiles(updates, UpdateMode.Incremental);
38893
- this.priorBuild.recordSuccessfulTypeCheck(this.state);
39481
+ return this.perf.inPhase(PerfPhase.TcbUpdateProgram, () => {
39482
+ if (updates.size > 0) {
39483
+ this.perf.eventCount(PerfEvent.UpdateTypeCheckProgram);
39484
+ }
39485
+ this.typeCheckingStrategy.updateFiles(updates, UpdateMode.Incremental);
39486
+ this.priorBuild.recordSuccessfulTypeCheck(this.state);
39487
+ this.perf.memory(PerfCheckpoint.TtcUpdateProgram);
39488
+ });
38894
39489
  }
38895
39490
  getFileData(path) {
38896
39491
  if (!this.state.has(path)) {
@@ -38908,7 +39503,7 @@ Either add the @Injectable() decorator to '${provider.node.name
38908
39503
  if (builder === null) {
38909
39504
  return null;
38910
39505
  }
38911
- return builder.getSymbol(node);
39506
+ return this.perf.inPhase(PerfPhase.TtcSymbol, () => builder.getSymbol(node));
38912
39507
  }
38913
39508
  getOrCreateSymbolBuilder(component) {
38914
39509
  if (this.symbolBuilderCache.has(component)) {
@@ -39167,7 +39762,7 @@ Either add the @Injectable() decorator to '${provider.node.name
39167
39762
  /**
39168
39763
  * Create a `CompilationTicket` for a brand new compilation, using no prior state.
39169
39764
  */
39170
- function freshCompilationTicket(tsProgram, options, incrementalBuildStrategy, typeCheckingProgramStrategy, enableTemplateTypeChecker, usePoisonedData) {
39765
+ function freshCompilationTicket(tsProgram, options, incrementalBuildStrategy, typeCheckingProgramStrategy, perfRecorder, enableTemplateTypeChecker, usePoisonedData) {
39171
39766
  return {
39172
39767
  kind: CompilationTicketKind.Fresh,
39173
39768
  tsProgram,
@@ -39176,21 +39771,25 @@ Either add the @Injectable() decorator to '${provider.node.name
39176
39771
  typeCheckingProgramStrategy,
39177
39772
  enableTemplateTypeChecker,
39178
39773
  usePoisonedData,
39774
+ perfRecorder: perfRecorder !== null && perfRecorder !== void 0 ? perfRecorder : ActivePerfRecorder.zeroedToNow(),
39179
39775
  };
39180
39776
  }
39181
39777
  /**
39182
39778
  * Create a `CompilationTicket` as efficiently as possible, based on a previous `NgCompiler`
39183
39779
  * instance and a new `ts.Program`.
39184
39780
  */
39185
- function incrementalFromCompilerTicket(oldCompiler, newProgram, incrementalBuildStrategy, typeCheckingProgramStrategy, modifiedResourceFiles) {
39781
+ function incrementalFromCompilerTicket(oldCompiler, newProgram, incrementalBuildStrategy, typeCheckingProgramStrategy, modifiedResourceFiles, perfRecorder) {
39186
39782
  const oldProgram = oldCompiler.getNextProgram();
39187
39783
  const oldDriver = oldCompiler.incrementalStrategy.getIncrementalDriver(oldProgram);
39188
39784
  if (oldDriver === null) {
39189
39785
  // No incremental step is possible here, since no IncrementalDriver was found for the old
39190
39786
  // program.
39191
- return freshCompilationTicket(newProgram, oldCompiler.options, incrementalBuildStrategy, typeCheckingProgramStrategy, oldCompiler.enableTemplateTypeChecker, oldCompiler.usePoisonedData);
39787
+ return freshCompilationTicket(newProgram, oldCompiler.options, incrementalBuildStrategy, typeCheckingProgramStrategy, perfRecorder, oldCompiler.enableTemplateTypeChecker, oldCompiler.usePoisonedData);
39788
+ }
39789
+ if (perfRecorder === null) {
39790
+ perfRecorder = ActivePerfRecorder.zeroedToNow();
39192
39791
  }
39193
- const newDriver = IncrementalDriver.reconcile(oldProgram, oldDriver, newProgram, modifiedResourceFiles);
39792
+ const newDriver = IncrementalDriver.reconcile(oldProgram, oldDriver, newProgram, modifiedResourceFiles, perfRecorder);
39194
39793
  return {
39195
39794
  kind: CompilationTicketKind.IncrementalTypeScript,
39196
39795
  enableTemplateTypeChecker: oldCompiler.enableTemplateTypeChecker,
@@ -39201,6 +39800,7 @@ Either add the @Injectable() decorator to '${provider.node.name
39201
39800
  newDriver,
39202
39801
  oldProgram,
39203
39802
  newProgram,
39803
+ perfRecorder,
39204
39804
  };
39205
39805
  }
39206
39806
  function resourceChangeTicket(compiler, modifiedResourceFiles) {
@@ -39208,6 +39808,7 @@ Either add the @Injectable() decorator to '${provider.node.name
39208
39808
  kind: CompilationTicketKind.IncrementalResource,
39209
39809
  compiler,
39210
39810
  modifiedResourceFiles,
39811
+ perfRecorder: ActivePerfRecorder.zeroedToNow(),
39211
39812
  };
39212
39813
  }
39213
39814
  /**
@@ -39223,7 +39824,7 @@ Either add the @Injectable() decorator to '${provider.node.name
39223
39824
  * See the README.md for more information.
39224
39825
  */
39225
39826
  class NgCompiler {
39226
- constructor(adapter, options, tsProgram, typeCheckingProgramStrategy, incrementalStrategy, incrementalDriver, enableTemplateTypeChecker, usePoisonedData, perfRecorder = NOOP_PERF_RECORDER) {
39827
+ constructor(adapter, options, tsProgram, typeCheckingProgramStrategy, incrementalStrategy, incrementalDriver, enableTemplateTypeChecker, usePoisonedData, livePerfRecorder) {
39227
39828
  this.adapter = adapter;
39228
39829
  this.options = options;
39229
39830
  this.tsProgram = tsProgram;
@@ -39232,7 +39833,7 @@ Either add the @Injectable() decorator to '${provider.node.name
39232
39833
  this.incrementalDriver = incrementalDriver;
39233
39834
  this.enableTemplateTypeChecker = enableTemplateTypeChecker;
39234
39835
  this.usePoisonedData = usePoisonedData;
39235
- this.perfRecorder = perfRecorder;
39836
+ this.livePerfRecorder = livePerfRecorder;
39236
39837
  /**
39237
39838
  * Lazily evaluated state of the compilation.
39238
39839
  *
@@ -39252,6 +39853,13 @@ Either add the @Injectable() decorator to '${provider.node.name
39252
39853
  * This is set by (and memoizes) `getNonTemplateDiagnostics`.
39253
39854
  */
39254
39855
  this.nonTemplateDiagnostics = null;
39856
+ /**
39857
+ * `NgCompiler` can be reused for multiple compilations (for resource-only changes), and each
39858
+ * new compilation uses a fresh `PerfRecorder`. Thus, classes created with a lifespan of the
39859
+ * `NgCompiler` use a `DelegatingPerfRecorder` so the `PerfRecorder` they write to can be updated
39860
+ * with each fresh compilation.
39861
+ */
39862
+ this.delegatingPerfRecorder = new DelegatingPerfRecorder(this.perfRecorder);
39255
39863
  this.constructionDiagnostics.push(...this.adapter.constructionDiagnostics);
39256
39864
  const incompatibleTypeCheckOptionsDiagnostic = verifyCompatibleTypeCheckOptions(this.options);
39257
39865
  if (incompatibleTypeCheckOptionsDiagnostic !== null) {
@@ -39262,9 +39870,7 @@ Either add the @Injectable() decorator to '${provider.node.name
39262
39870
  this.entryPoint =
39263
39871
  adapter.entryPoint !== null ? getSourceFileOrNull(tsProgram, adapter.entryPoint) : null;
39264
39872
  const moduleResolutionCache = ts$1.createModuleResolutionCache(this.adapter.getCurrentDirectory(),
39265
- // Note: this used to be an arrow-function closure. However, JS engines like v8 have some
39266
- // strange behaviors with retaining the lexical scope of the closure. Even if this function
39267
- // doesn't retain a reference to `this`, if other closures in the constructor here reference
39873
+ // doen't retain a reference to `this`, if other closures in the constructor here reference
39268
39874
  // `this` internally then a closure created here would retain them. This can cause major
39269
39875
  // memory leak issues since the `moduleResolutionCache` is a long-lived object and finds its
39270
39876
  // way into all kinds of places inside TS internal objects.
@@ -39272,55 +39878,75 @@ Either add the @Injectable() decorator to '${provider.node.name
39272
39878
  this.moduleResolver =
39273
39879
  new ModuleResolver(tsProgram, this.options, this.adapter, moduleResolutionCache);
39274
39880
  this.resourceManager = new AdapterResourceLoader(adapter, this.options);
39275
- this.cycleAnalyzer = new CycleAnalyzer(new ImportGraph(tsProgram.getTypeChecker()));
39881
+ this.cycleAnalyzer =
39882
+ new CycleAnalyzer(new ImportGraph(tsProgram.getTypeChecker(), this.delegatingPerfRecorder));
39276
39883
  this.incrementalStrategy.setIncrementalDriver(this.incrementalDriver, tsProgram);
39277
39884
  this.ignoreForDiagnostics =
39278
39885
  new Set(tsProgram.getSourceFiles().filter(sf => this.adapter.isShim(sf)));
39279
39886
  this.ignoreForEmit = this.adapter.ignoreForEmit;
39887
+ let dtsFileCount = 0;
39888
+ let nonDtsFileCount = 0;
39889
+ for (const sf of tsProgram.getSourceFiles()) {
39890
+ if (sf.isDeclarationFile) {
39891
+ dtsFileCount++;
39892
+ }
39893
+ else {
39894
+ nonDtsFileCount++;
39895
+ }
39896
+ }
39897
+ livePerfRecorder.eventCount(PerfEvent.InputDtsFile, dtsFileCount);
39898
+ livePerfRecorder.eventCount(PerfEvent.InputTsFile, nonDtsFileCount);
39280
39899
  }
39281
39900
  /**
39282
39901
  * Convert a `CompilationTicket` into an `NgCompiler` instance for the requested compilation.
39283
39902
  *
39284
39903
  * Depending on the nature of the compilation request, the `NgCompiler` instance may be reused
39285
39904
  * from a previous compilation and updated with any changes, it may be a new instance which
39286
- * incrementally reuses state from a previous compilation, or it may represent a fresh compilation
39287
- * entirely.
39905
+ * incrementally reuses state from a previous compilation, or it may represent a fresh
39906
+ * compilation entirely.
39288
39907
  */
39289
- static fromTicket(ticket, adapter, perfRecorder) {
39908
+ static fromTicket(ticket, adapter) {
39290
39909
  switch (ticket.kind) {
39291
39910
  case CompilationTicketKind.Fresh:
39292
- return new NgCompiler(adapter, ticket.options, ticket.tsProgram, ticket.typeCheckingProgramStrategy, ticket.incrementalBuildStrategy, IncrementalDriver.fresh(ticket.tsProgram), ticket.enableTemplateTypeChecker, ticket.usePoisonedData, perfRecorder);
39911
+ return new NgCompiler(adapter, ticket.options, ticket.tsProgram, ticket.typeCheckingProgramStrategy, ticket.incrementalBuildStrategy, IncrementalDriver.fresh(ticket.tsProgram), ticket.enableTemplateTypeChecker, ticket.usePoisonedData, ticket.perfRecorder);
39293
39912
  case CompilationTicketKind.IncrementalTypeScript:
39294
- return new NgCompiler(adapter, ticket.options, ticket.newProgram, ticket.typeCheckingProgramStrategy, ticket.incrementalBuildStrategy, ticket.newDriver, ticket.enableTemplateTypeChecker, ticket.usePoisonedData, perfRecorder);
39913
+ return new NgCompiler(adapter, ticket.options, ticket.newProgram, ticket.typeCheckingProgramStrategy, ticket.incrementalBuildStrategy, ticket.newDriver, ticket.enableTemplateTypeChecker, ticket.usePoisonedData, ticket.perfRecorder);
39295
39914
  case CompilationTicketKind.IncrementalResource:
39296
39915
  const compiler = ticket.compiler;
39297
- compiler.updateWithChangedResources(ticket.modifiedResourceFiles);
39916
+ compiler.updateWithChangedResources(ticket.modifiedResourceFiles, ticket.perfRecorder);
39298
39917
  return compiler;
39299
39918
  }
39300
39919
  }
39301
- updateWithChangedResources(changedResources) {
39302
- if (this.compilation === null) {
39303
- // Analysis hasn't happened yet, so no update is necessary - any changes to resources will be
39304
- // captured by the inital analysis pass itself.
39305
- return;
39306
- }
39307
- this.resourceManager.invalidate();
39308
- const classesToUpdate = new Set();
39309
- for (const resourceFile of changedResources) {
39310
- for (const templateClass of this.getComponentsWithTemplateFile(resourceFile)) {
39311
- classesToUpdate.add(templateClass);
39920
+ get perfRecorder() {
39921
+ return this.livePerfRecorder;
39922
+ }
39923
+ updateWithChangedResources(changedResources, perfRecorder) {
39924
+ this.livePerfRecorder = perfRecorder;
39925
+ this.delegatingPerfRecorder.target = perfRecorder;
39926
+ perfRecorder.inPhase(PerfPhase.ResourceUpdate, () => {
39927
+ if (this.compilation === null) {
39928
+ // Analysis hasn't happened yet, so no update is necessary - any changes to resources will
39929
+ // be captured by the inital analysis pass itself.
39930
+ return;
39312
39931
  }
39313
- for (const styleClass of this.getComponentsWithStyleFile(resourceFile)) {
39314
- classesToUpdate.add(styleClass);
39932
+ this.resourceManager.invalidate();
39933
+ const classesToUpdate = new Set();
39934
+ for (const resourceFile of changedResources) {
39935
+ for (const templateClass of this.getComponentsWithTemplateFile(resourceFile)) {
39936
+ classesToUpdate.add(templateClass);
39937
+ }
39938
+ for (const styleClass of this.getComponentsWithStyleFile(resourceFile)) {
39939
+ classesToUpdate.add(styleClass);
39940
+ }
39315
39941
  }
39316
- }
39317
- for (const clazz of classesToUpdate) {
39318
- this.compilation.traitCompiler.updateResources(clazz);
39319
- if (!ts$1.isClassDeclaration(clazz)) {
39320
- continue;
39942
+ for (const clazz of classesToUpdate) {
39943
+ this.compilation.traitCompiler.updateResources(clazz);
39944
+ if (!ts$1.isClassDeclaration(clazz)) {
39945
+ continue;
39946
+ }
39947
+ this.compilation.templateTypeChecker.invalidateClass(clazz);
39321
39948
  }
39322
- this.compilation.templateTypeChecker.invalidateClass(clazz);
39323
- }
39949
+ });
39324
39950
  }
39325
39951
  /**
39326
39952
  * Get the resource dependencies of a file.
@@ -39427,29 +40053,23 @@ Either add the @Injectable() decorator to '${provider.node.name
39427
40053
  if (this.compilation !== null) {
39428
40054
  return;
39429
40055
  }
39430
- this.compilation = this.makeCompilation();
39431
- const analyzeSpan = this.perfRecorder.start('analyze');
39432
- const promises = [];
39433
- for (const sf of this.tsProgram.getSourceFiles()) {
39434
- if (sf.isDeclarationFile) {
39435
- continue;
39436
- }
39437
- const analyzeFileSpan = this.perfRecorder.start('analyzeFile', sf);
39438
- let analysisPromise = this.compilation.traitCompiler.analyzeAsync(sf);
39439
- this.scanForMwp(sf);
39440
- if (analysisPromise === undefined) {
39441
- this.perfRecorder.stop(analyzeFileSpan);
39442
- }
39443
- else if (this.perfRecorder.enabled) {
39444
- analysisPromise = analysisPromise.then(() => this.perfRecorder.stop(analyzeFileSpan));
39445
- }
39446
- if (analysisPromise !== undefined) {
39447
- promises.push(analysisPromise);
40056
+ yield this.perfRecorder.inPhase(PerfPhase.Analysis, () => __awaiter(this, void 0, void 0, function* () {
40057
+ this.compilation = this.makeCompilation();
40058
+ const promises = [];
40059
+ for (const sf of this.tsProgram.getSourceFiles()) {
40060
+ if (sf.isDeclarationFile) {
40061
+ continue;
40062
+ }
40063
+ let analysisPromise = this.compilation.traitCompiler.analyzeAsync(sf);
40064
+ this.scanForMwp(sf);
40065
+ if (analysisPromise !== undefined) {
40066
+ promises.push(analysisPromise);
40067
+ }
39448
40068
  }
39449
- }
39450
- yield Promise.all(promises);
39451
- this.perfRecorder.stop(analyzeSpan);
39452
- this.resolveCompilation(this.compilation.traitCompiler);
40069
+ yield Promise.all(promises);
40070
+ this.perfRecorder.memory(PerfCheckpoint.Analysis);
40071
+ this.resolveCompilation(this.compilation.traitCompiler);
40072
+ }));
39453
40073
  });
39454
40074
  }
39455
40075
  /**
@@ -39459,9 +40079,7 @@ Either add the @Injectable() decorator to '${provider.node.name
39459
40079
  */
39460
40080
  listLazyRoutes(entryRoute) {
39461
40081
  if (entryRoute) {
39462
- // Note:
39463
- // This resolution step is here to match the implementation of the old `AotCompilerHost` (see
39464
- // https://github.com/angular/angular/blob/50732e156/packages/compiler-cli/src/transformers/compiler_host.ts#L175-L188).
40082
+ // htts://github.com/angular/angular/blob/50732e156/packages/compiler-cli/src/transformers/compiler_host.ts#L175-L188).
39465
40083
  //
39466
40084
  // `@angular/cli` will always call this API with an absolute path, so the resolution step is
39467
40085
  // not necessary, but keeping it backwards compatible in case someone else is using the API.
@@ -39503,7 +40121,7 @@ Either add the @Injectable() decorator to '${provider.node.name
39503
40121
  importRewriter = new NoopImportRewriter();
39504
40122
  }
39505
40123
  const before = [
39506
- ivyTransformFactory(compilation.traitCompiler, compilation.reflector, importRewriter, compilation.defaultImportTracker, compilation.isCore, this.closureCompilerEnabled),
40124
+ ivyTransformFactory(compilation.traitCompiler, compilation.reflector, importRewriter, compilation.defaultImportTracker, this.delegatingPerfRecorder, compilation.isCore, this.closureCompilerEnabled),
39507
40125
  aliasTransformFactory(compilation.traitCompiler.exportStatements),
39508
40126
  compilation.defaultImportTracker.importPreservingTransformer(),
39509
40127
  ];
@@ -39539,25 +40157,27 @@ Either add the @Injectable() decorator to '${provider.node.name
39539
40157
  return this.compilation;
39540
40158
  }
39541
40159
  analyzeSync() {
39542
- const analyzeSpan = this.perfRecorder.start('analyze');
39543
- this.compilation = this.makeCompilation();
39544
- for (const sf of this.tsProgram.getSourceFiles()) {
39545
- if (sf.isDeclarationFile) {
39546
- continue;
40160
+ this.perfRecorder.inPhase(PerfPhase.Analysis, () => {
40161
+ this.compilation = this.makeCompilation();
40162
+ for (const sf of this.tsProgram.getSourceFiles()) {
40163
+ if (sf.isDeclarationFile) {
40164
+ continue;
40165
+ }
40166
+ this.compilation.traitCompiler.analyzeSync(sf);
40167
+ this.scanForMwp(sf);
39547
40168
  }
39548
- const analyzeFileSpan = this.perfRecorder.start('analyzeFile', sf);
39549
- this.compilation.traitCompiler.analyzeSync(sf);
39550
- this.scanForMwp(sf);
39551
- this.perfRecorder.stop(analyzeFileSpan);
39552
- }
39553
- this.perfRecorder.stop(analyzeSpan);
39554
- this.resolveCompilation(this.compilation.traitCompiler);
40169
+ this.perfRecorder.memory(PerfCheckpoint.Analysis);
40170
+ this.resolveCompilation(this.compilation.traitCompiler);
40171
+ });
39555
40172
  }
39556
40173
  resolveCompilation(traitCompiler) {
39557
- traitCompiler.resolve();
39558
- // At this point, analysis is complete and the compiler can now calculate which files need to
39559
- // be emitted, so do that.
39560
- this.incrementalDriver.recordSuccessfulAnalysis(traitCompiler);
40174
+ this.perfRecorder.inPhase(PerfPhase.Resolve, () => {
40175
+ traitCompiler.resolve();
40176
+ // At this point, analysis is complete and the compiler can now calculate which files need to
40177
+ // be emitted, so do that.
40178
+ this.incrementalDriver.recordSuccessfulAnalysis(traitCompiler);
40179
+ this.perfRecorder.memory(PerfCheckpoint.Resolve);
40180
+ });
39561
40181
  }
39562
40182
  get fullTemplateTypeCheck() {
39563
40183
  // Determine the strictness level of type checking based on compiler options. As
@@ -39573,6 +40193,7 @@ Either add the @Injectable() decorator to '${provider.node.name
39573
40193
  // Also see `verifyCompatibleTypeCheckOptions` where it is verified that `fullTemplateTypeCheck`
39574
40194
  // is not disabled when `strictTemplates` is enabled.
39575
40195
  const strictTemplates = !!this.options.strictTemplates;
40196
+ const useInlineTypeConstructors = this.typeCheckingProgramStrategy.supportsInlineOperations;
39576
40197
  // First select a type-checking configuration, based on whether full template type-checking is
39577
40198
  // requested.
39578
40199
  let typeCheckingConfig;
@@ -39604,6 +40225,11 @@ Either add the @Injectable() decorator to '${provider.node.name
39604
40225
  useContextGenericType: strictTemplates,
39605
40226
  strictLiteralTypes: true,
39606
40227
  enableTemplateTypeChecker: this.enableTemplateTypeChecker,
40228
+ useInlineTypeConstructors,
40229
+ // Warnings for suboptimal type inference are only enabled if in Language Service mode
40230
+ // (providing the full TemplateTypeChecker API) and if strict mode is not enabled. In strict
40231
+ // mode, the user is in full control of type inference.
40232
+ suggestionsForSuboptimalTypeInference: this.enableTemplateTypeChecker && !strictTemplates,
39607
40233
  };
39608
40234
  }
39609
40235
  else {
@@ -39629,6 +40255,10 @@ Either add the @Injectable() decorator to '${provider.node.name
39629
40255
  useContextGenericType: false,
39630
40256
  strictLiteralTypes: false,
39631
40257
  enableTemplateTypeChecker: this.enableTemplateTypeChecker,
40258
+ useInlineTypeConstructors,
40259
+ // In "basic" template type-checking mode, no warnings are produced since most things are
40260
+ // not checked anyways.
40261
+ suggestionsForSuboptimalTypeInference: false,
39632
40262
  };
39633
40263
  }
39634
40264
  // Apply explicitly configured strictness flags on top of the default configuration
@@ -39671,7 +40301,6 @@ Either add the @Injectable() decorator to '${provider.node.name
39671
40301
  getTemplateDiagnostics() {
39672
40302
  const compilation = this.ensureAnalyzed();
39673
40303
  // Get the diagnostics.
39674
- const typeCheckSpan = this.perfRecorder.start('typeCheckDiagnostics');
39675
40304
  const diagnostics = [];
39676
40305
  for (const sf of this.tsProgram.getSourceFiles()) {
39677
40306
  if (sf.isDeclarationFile || this.adapter.isShim(sf)) {
@@ -39680,7 +40309,6 @@ Either add the @Injectable() decorator to '${provider.node.name
39680
40309
  diagnostics.push(...compilation.templateTypeChecker.getDiagnosticsForFile(sf, OptimizeFor.WholeProgram));
39681
40310
  }
39682
40311
  const program = this.typeCheckingProgramStrategy.getProgram();
39683
- this.perfRecorder.stop(typeCheckSpan);
39684
40312
  this.incrementalStrategy.setIncrementalDriver(this.incrementalDriver, program);
39685
40313
  this.nextProgram = program;
39686
40314
  return diagnostics;
@@ -39688,13 +40316,11 @@ Either add the @Injectable() decorator to '${provider.node.name
39688
40316
  getTemplateDiagnosticsForFile(sf, optimizeFor) {
39689
40317
  const compilation = this.ensureAnalyzed();
39690
40318
  // Get the diagnostics.
39691
- const typeCheckSpan = this.perfRecorder.start('typeCheckDiagnostics');
39692
40319
  const diagnostics = [];
39693
40320
  if (!sf.isDeclarationFile && !this.adapter.isShim(sf)) {
39694
40321
  diagnostics.push(...compilation.templateTypeChecker.getDiagnosticsForFile(sf, optimizeFor));
39695
40322
  }
39696
40323
  const program = this.typeCheckingProgramStrategy.getProgram();
39697
- this.perfRecorder.stop(typeCheckSpan);
39698
40324
  this.incrementalStrategy.setIncrementalDriver(this.incrementalDriver, program);
39699
40325
  this.nextProgram = program;
39700
40326
  return diagnostics;
@@ -39813,20 +40439,20 @@ Either add the @Injectable() decorator to '${provider.node.name
39813
40439
  1 /* Error */;
39814
40440
  // Set up the IvyCompilation, which manages state for the Ivy transformer.
39815
40441
  const handlers = [
39816
- new ComponentDecoratorHandler(reflector, evaluator, metaRegistry, metaReader, scopeReader, scopeRegistry, typeCheckScopeRegistry, resourceRegistry, isCore, this.resourceManager, this.adapter.rootDirs, this.options.preserveWhitespaces || false, this.options.i18nUseExternalIds !== false, this.options.enableI18nLegacyMessageIdFormat !== false, this.usePoisonedData, this.options.i18nNormalizeLineEndingsInICUs, this.moduleResolver, this.cycleAnalyzer, cycleHandlingStrategy, refEmitter, defaultImportTracker, this.incrementalDriver.depGraph, injectableRegistry, semanticDepGraphUpdater, this.closureCompilerEnabled),
40442
+ new ComponentDecoratorHandler(reflector, evaluator, metaRegistry, metaReader, scopeReader, scopeRegistry, typeCheckScopeRegistry, resourceRegistry, isCore, this.resourceManager, this.adapter.rootDirs, this.options.preserveWhitespaces || false, this.options.i18nUseExternalIds !== false, this.options.enableI18nLegacyMessageIdFormat !== false, this.usePoisonedData, this.options.i18nNormalizeLineEndingsInICUs, this.moduleResolver, this.cycleAnalyzer, cycleHandlingStrategy, refEmitter, defaultImportTracker, this.incrementalDriver.depGraph, injectableRegistry, semanticDepGraphUpdater, this.closureCompilerEnabled, this.delegatingPerfRecorder),
39817
40443
  // TODO(alxhub): understand why the cast here is necessary (something to do with `null`
39818
40444
  // not being assignable to `unknown` when wrapped in `Readonly`).
39819
40445
  // clang-format off
39820
- new DirectiveDecoratorHandler(reflector, evaluator, metaRegistry, scopeRegistry, metaReader, defaultImportTracker, injectableRegistry, isCore, semanticDepGraphUpdater, this.closureCompilerEnabled, compileUndecoratedClassesWithAngularFeatures),
40446
+ new DirectiveDecoratorHandler(reflector, evaluator, metaRegistry, scopeRegistry, metaReader, defaultImportTracker, injectableRegistry, isCore, semanticDepGraphUpdater, this.closureCompilerEnabled, compileUndecoratedClassesWithAngularFeatures, this.delegatingPerfRecorder),
39821
40447
  // clang-format on
39822
40448
  // Pipe handler must be before injectable handler in list so pipe factories are printed
39823
40449
  // before injectable factories (so injectable factories can delegate to them)
39824
- new PipeDecoratorHandler(reflector, evaluator, metaRegistry, scopeRegistry, defaultImportTracker, injectableRegistry, isCore),
39825
- new InjectableDecoratorHandler(reflector, defaultImportTracker, isCore, this.options.strictInjectionParameters || false, injectableRegistry),
39826
- new NgModuleDecoratorHandler(reflector, evaluator, metaReader, metaRegistry, scopeRegistry, referencesRegistry, isCore, routeAnalyzer, refEmitter, this.adapter.factoryTracker, defaultImportTracker, this.closureCompilerEnabled, injectableRegistry, this.options.i18nInLocale),
40450
+ new PipeDecoratorHandler(reflector, evaluator, metaRegistry, scopeRegistry, defaultImportTracker, injectableRegistry, isCore, this.delegatingPerfRecorder),
40451
+ new InjectableDecoratorHandler(reflector, defaultImportTracker, isCore, this.options.strictInjectionParameters || false, injectableRegistry, this.delegatingPerfRecorder),
40452
+ new NgModuleDecoratorHandler(reflector, evaluator, metaReader, metaRegistry, scopeRegistry, referencesRegistry, isCore, routeAnalyzer, refEmitter, this.adapter.factoryTracker, defaultImportTracker, this.closureCompilerEnabled, injectableRegistry, this.delegatingPerfRecorder, this.options.i18nInLocale),
39827
40453
  ];
39828
- const traitCompiler = new TraitCompiler(handlers, reflector, this.perfRecorder, this.incrementalDriver, this.options.compileNonExportedClasses !== false, compilationMode, dtsTransforms, semanticDepGraphUpdater);
39829
- const templateTypeChecker = new TemplateTypeCheckerImpl(this.tsProgram, this.typeCheckingProgramStrategy, traitCompiler, this.getTypeCheckingConfig(), refEmitter, reflector, this.adapter, this.incrementalDriver, scopeRegistry, typeCheckScopeRegistry);
40454
+ const traitCompiler = new TraitCompiler(handlers, reflector, this.delegatingPerfRecorder, this.incrementalDriver, this.options.compileNonExportedClasses !== false, compilationMode, dtsTransforms, semanticDepGraphUpdater);
40455
+ const templateTypeChecker = new TemplateTypeCheckerImpl(this.tsProgram, this.typeCheckingProgramStrategy, traitCompiler, this.getTypeCheckingConfig(), refEmitter, reflector, this.adapter, this.incrementalDriver, scopeRegistry, typeCheckScopeRegistry, this.delegatingPerfRecorder);
39830
40456
  return {
39831
40457
  isCore,
39832
40458
  traitCompiler,
@@ -40019,24 +40645,30 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
40019
40645
  };
40020
40646
  }
40021
40647
  function getExtendedConfigPath(configFile, extendsValue, host, fs) {
40022
- let extendedConfigPath = null;
40648
+ const result = getExtendedConfigPathWorker(configFile, extendsValue, host, fs);
40649
+ if (result !== null) {
40650
+ return result;
40651
+ }
40652
+ // Try to resolve the paths with a json extension append a json extension to the file in case if
40653
+ // it is missing and the resolution failed. This is to replicate TypeScript behaviour, see:
40654
+ // https://github.com/microsoft/TypeScript/blob/294a5a7d784a5a95a8048ee990400979a6bc3a1c/src/compiler/commandLineParser.ts#L2806
40655
+ return getExtendedConfigPathWorker(configFile, `${extendsValue}.json`, host, fs);
40656
+ }
40657
+ function getExtendedConfigPathWorker(configFile, extendsValue, host, fs) {
40023
40658
  if (extendsValue.startsWith('.') || fs.isRooted(extendsValue)) {
40024
- extendedConfigPath = host.resolve(host.dirname(configFile), extendsValue);
40025
- extendedConfigPath = host.extname(extendedConfigPath) ?
40026
- extendedConfigPath :
40027
- absoluteFrom(`${extendedConfigPath}.json`);
40659
+ const extendedConfigPath = host.resolve(host.dirname(configFile), extendsValue);
40660
+ if (host.exists(extendedConfigPath)) {
40661
+ return extendedConfigPath;
40662
+ }
40028
40663
  }
40029
40664
  else {
40030
40665
  const parseConfigHost = createParseConfigHost(host, fs);
40031
40666
  // Path isn't a rooted or relative path, resolve like a module.
40032
40667
  const { resolvedModule, } = ts$1.nodeModuleNameResolver(extendsValue, configFile, { moduleResolution: ts$1.ModuleResolutionKind.NodeJs, resolveJsonModule: true }, parseConfigHost);
40033
40668
  if (resolvedModule) {
40034
- extendedConfigPath = absoluteFrom(resolvedModule.resolvedFileName);
40669
+ return absoluteFrom(resolvedModule.resolvedFileName);
40035
40670
  }
40036
40671
  }
40037
- if (extendedConfigPath !== null && host.exists(extendedConfigPath)) {
40038
- return extendedConfigPath;
40039
- }
40040
40672
  return null;
40041
40673
  }
40042
40674
 
@@ -40745,14 +41377,20 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
40745
41377
  const ticket = resourceChangeTicket(this.compiler, modifiedResourceFiles);
40746
41378
  this.compiler = NgCompiler.fromTicket(ticket, this.adapter);
40747
41379
  }
41380
+ else {
41381
+ // The previous NgCompiler is being reused, but we still want to reset its performance
41382
+ // tracker to capture only the operations that are needed to service the current request.
41383
+ this.compiler.perfRecorder.reset();
41384
+ }
40748
41385
  return this.compiler;
40749
41386
  }
40750
41387
  let ticket;
40751
41388
  if (this.compiler === null || this.lastKnownProgram === null) {
40752
- ticket = freshCompilationTicket(program, this.options, this.incrementalStrategy, this.programStrategy, true, true);
41389
+ ticket = freshCompilationTicket(program, this.options, this.incrementalStrategy, this.programStrategy,
41390
+ /* perfRecorder */ null, true, true);
40753
41391
  }
40754
41392
  else {
40755
- ticket = incrementalFromCompilerTicket(this.compiler, program, this.incrementalStrategy, this.programStrategy, modifiedResourceFiles);
41393
+ ticket = incrementalFromCompilerTicket(this.compiler, program, this.incrementalStrategy, this.programStrategy, modifiedResourceFiles, /* perfRecorder */ null);
40756
41394
  }
40757
41395
  this.compiler = NgCompiler.fromTicket(ticket, this.adapter);
40758
41396
  this.lastKnownProgram = program;
@@ -42556,43 +43194,50 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
42556
43194
  this.ttc = this.compiler.getTemplateTypeChecker();
42557
43195
  }
42558
43196
  getRenameInfo(filePath, position) {
42559
- const templateInfo = getTemplateInfoAtPosition(filePath, position, this.compiler);
42560
- // We could not get a template at position so we assume the request came from outside the
42561
- // template.
42562
- if (templateInfo === undefined) {
42563
- return this.tsLS.getRenameInfo(filePath, position);
42564
- }
42565
- const allTargetDetails = this.getTargetDetailsAtTemplatePosition(templateInfo, position);
42566
- if (allTargetDetails === null) {
42567
- return { canRename: false, localizedErrorMessage: 'Could not find template node at position.' };
42568
- }
42569
- const { templateTarget } = allTargetDetails[0];
42570
- const templateTextAndSpan = getRenameTextAndSpanAtPosition(templateTarget, position);
42571
- if (templateTextAndSpan === null) {
42572
- return { canRename: false, localizedErrorMessage: 'Could not determine template node text.' };
42573
- }
42574
- const { text, span } = templateTextAndSpan;
42575
- return {
42576
- canRename: true,
42577
- displayName: text,
42578
- fullDisplayName: text,
42579
- triggerSpan: span,
42580
- };
43197
+ return this.compiler.perfRecorder.inPhase(PerfPhase.LsReferencesAndRenames, () => {
43198
+ const templateInfo = getTemplateInfoAtPosition(filePath, position, this.compiler);
43199
+ // We could not get a template at position so we assume the request came from outside the
43200
+ // template.
43201
+ if (templateInfo === undefined) {
43202
+ return this.tsLS.getRenameInfo(filePath, position);
43203
+ }
43204
+ const allTargetDetails = this.getTargetDetailsAtTemplatePosition(templateInfo, position);
43205
+ if (allTargetDetails === null) {
43206
+ return {
43207
+ canRename: false,
43208
+ localizedErrorMessage: 'Could not find template node at position.',
43209
+ };
43210
+ }
43211
+ const { templateTarget } = allTargetDetails[0];
43212
+ const templateTextAndSpan = getRenameTextAndSpanAtPosition(templateTarget, position);
43213
+ if (templateTextAndSpan === null) {
43214
+ return { canRename: false, localizedErrorMessage: 'Could not determine template node text.' };
43215
+ }
43216
+ const { text, span } = templateTextAndSpan;
43217
+ return {
43218
+ canRename: true,
43219
+ displayName: text,
43220
+ fullDisplayName: text,
43221
+ triggerSpan: span,
43222
+ };
43223
+ });
42581
43224
  }
42582
43225
  findRenameLocations(filePath, position) {
42583
43226
  this.ttc.generateAllTypeCheckBlocks();
42584
- const templateInfo = getTemplateInfoAtPosition(filePath, position, this.compiler);
42585
- // We could not get a template at position so we assume the request came from outside the
42586
- // template.
42587
- if (templateInfo === undefined) {
42588
- const requestNode = this.getTsNodeAtPosition(filePath, position);
42589
- if (requestNode === null) {
42590
- return undefined;
43227
+ return this.compiler.perfRecorder.inPhase(PerfPhase.LsReferencesAndRenames, () => {
43228
+ const templateInfo = getTemplateInfoAtPosition(filePath, position, this.compiler);
43229
+ // We could not get a template at position so we assume the request came from outside the
43230
+ // template.
43231
+ if (templateInfo === undefined) {
43232
+ const requestNode = this.getTsNodeAtPosition(filePath, position);
43233
+ if (requestNode === null) {
43234
+ return undefined;
43235
+ }
43236
+ const requestOrigin = { kind: RequestKind.TypeScript, requestNode };
43237
+ return this.findRenameLocationsAtTypescriptPosition(filePath, position, requestOrigin);
42591
43238
  }
42592
- const requestOrigin = { kind: RequestKind.TypeScript, requestNode };
42593
- return this.findRenameLocationsAtTypescriptPosition(filePath, position, requestOrigin);
42594
- }
42595
- return this.findRenameLocationsAtTemplatePosition(templateInfo, position);
43239
+ return this.findRenameLocationsAtTemplatePosition(templateInfo, position);
43240
+ });
42596
43241
  }
42597
43242
  findRenameLocationsAtTemplatePosition(templateInfo, position) {
42598
43243
  const allTargetDetails = this.getTargetDetailsAtTemplatePosition(templateInfo, position);
@@ -42627,52 +43272,56 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
42627
43272
  return (_a = findTightestNode(sf, position)) !== null && _a !== void 0 ? _a : null;
42628
43273
  }
42629
43274
  findRenameLocationsAtTypescriptPosition(filePath, position, requestOrigin) {
42630
- let originalNodeText;
42631
- if (requestOrigin.kind === RequestKind.TypeScript) {
42632
- originalNodeText = requestOrigin.requestNode.getText();
42633
- }
42634
- else {
42635
- const templateNodeText = getRenameTextAndSpanAtPosition(requestOrigin.requestNode, requestOrigin.position);
42636
- if (templateNodeText === null) {
42637
- return undefined;
43275
+ return this.compiler.perfRecorder.inPhase(PerfPhase.LsReferencesAndRenames, () => {
43276
+ let originalNodeText;
43277
+ if (requestOrigin.kind === RequestKind.TypeScript) {
43278
+ originalNodeText = requestOrigin.requestNode.getText();
42638
43279
  }
42639
- originalNodeText = templateNodeText.text;
42640
- }
42641
- const locations = this.tsLS.findRenameLocations(filePath, position, /*findInStrings*/ false, /*findInComments*/ false);
42642
- if (locations === undefined) {
42643
- return undefined;
42644
- }
42645
- const entries = new Map();
42646
- for (const location of locations) {
42647
- // TODO(atscott): Determine if a file is a shim file in a more robust way and make the API
42648
- // available in an appropriate location.
42649
- if (this.ttc.isTrackedTypeCheckFile(absoluteFrom(location.fileName))) {
42650
- const entry = this.convertToTemplateDocumentSpan(location, this.ttc, originalNodeText);
42651
- // There is no template node whose text matches the original rename request. Bail on
42652
- // renaming completely rather than providing incomplete results.
42653
- if (entry === null) {
43280
+ else {
43281
+ const templateNodeText = getRenameTextAndSpanAtPosition(requestOrigin.requestNode, requestOrigin.position);
43282
+ if (templateNodeText === null) {
42654
43283
  return undefined;
42655
43284
  }
42656
- entries.set(createLocationKey(entry), entry);
43285
+ originalNodeText = templateNodeText.text;
42657
43286
  }
42658
- else {
42659
- // Ensure we only allow renaming a TS result with matching text
42660
- const refNode = this.getTsNodeAtPosition(location.fileName, location.textSpan.start);
42661
- if (refNode === null || refNode.getText() !== originalNodeText) {
42662
- return undefined;
43287
+ const locations = this.tsLS.findRenameLocations(filePath, position, /*findInStrings*/ false, /*findInComments*/ false);
43288
+ if (locations === undefined) {
43289
+ return undefined;
43290
+ }
43291
+ const entries = new Map();
43292
+ for (const location of locations) {
43293
+ // TODO(atscott): Determine if a file is a shim file in a more robust way and make the API
43294
+ // available in an appropriate location.
43295
+ if (this.ttc.isTrackedTypeCheckFile(absoluteFrom(location.fileName))) {
43296
+ const entry = this.convertToTemplateDocumentSpan(location, this.ttc, originalNodeText);
43297
+ // There is no template node whose text matches the original rename request. Bail on
43298
+ // renaming completely rather than providing incomplete results.
43299
+ if (entry === null) {
43300
+ return undefined;
43301
+ }
43302
+ entries.set(createLocationKey(entry), entry);
43303
+ }
43304
+ else {
43305
+ // Ensure we only allow renaming a TS result with matching text
43306
+ const refNode = this.getTsNodeAtPosition(location.fileName, location.textSpan.start);
43307
+ if (refNode === null || refNode.getText() !== originalNodeText) {
43308
+ return undefined;
43309
+ }
43310
+ entries.set(createLocationKey(location), location);
42663
43311
  }
42664
- entries.set(createLocationKey(location), location);
42665
43312
  }
42666
- }
42667
- return Array.from(entries.values());
43313
+ return Array.from(entries.values());
43314
+ });
42668
43315
  }
42669
43316
  getReferencesAtPosition(filePath, position) {
42670
43317
  this.ttc.generateAllTypeCheckBlocks();
42671
- const templateInfo = getTemplateInfoAtPosition(filePath, position, this.compiler);
42672
- if (templateInfo === undefined) {
42673
- return this.getReferencesAtTypescriptPosition(filePath, position);
42674
- }
42675
- return this.getReferencesAtTemplatePosition(templateInfo, position);
43318
+ return this.compiler.perfRecorder.inPhase(PerfPhase.LsReferencesAndRenames, () => {
43319
+ const templateInfo = getTemplateInfoAtPosition(filePath, position, this.compiler);
43320
+ if (templateInfo === undefined) {
43321
+ return this.getReferencesAtTypescriptPosition(filePath, position);
43322
+ }
43323
+ return this.getReferencesAtTemplatePosition(templateInfo, position);
43324
+ });
42676
43325
  }
42677
43326
  getReferencesAtTemplatePosition(templateInfo, position) {
42678
43327
  const allTargetDetails = this.getTargetDetailsAtTemplatePosition(templateInfo, position);
@@ -42913,49 +43562,52 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
42913
43562
  return this.options;
42914
43563
  }
42915
43564
  getSemanticDiagnostics(fileName) {
42916
- const compiler = this.compilerFactory.getOrCreate();
42917
- const ttc = compiler.getTemplateTypeChecker();
42918
- const diagnostics = [];
42919
- if (isTypeScriptFile(fileName)) {
42920
- const program = compiler.getNextProgram();
42921
- const sourceFile = program.getSourceFile(fileName);
42922
- if (sourceFile) {
42923
- const ngDiagnostics = compiler.getDiagnosticsForFile(sourceFile, OptimizeFor.SingleFile);
42924
- // There are several kinds of diagnostics returned by `NgCompiler` for a source file:
42925
- //
42926
- // 1. Angular-related non-template diagnostics from decorated classes within that file.
42927
- // 2. Template diagnostics for components with direct inline templates (a string literal).
42928
- // 3. Template diagnostics for components with indirect inline templates (templates computed
42929
- // by expression).
42930
- // 4. Template diagnostics for components with external templates.
42931
- //
42932
- // When showing diagnostics for a TS source file, we want to only include kinds 1 and 2 -
42933
- // those diagnostics which are reported at a location within the TS file itself. Diagnostics
42934
- // for external templates will be shown when editing that template file (the `else` block)
42935
- // below.
42936
- //
42937
- // Currently, indirect inline template diagnostics (kind 3) are not shown at all by the
42938
- // Language Service, because there is no sensible location in the user's code for them. Such
42939
- // templates are an edge case, though, and should not be common.
42940
- //
42941
- // TODO(alxhub): figure out a good user experience for indirect template diagnostics and
42942
- // show them from within the Language Service.
42943
- diagnostics.push(...ngDiagnostics.filter(diag => diag.file !== undefined && diag.file.fileName === sourceFile.fileName));
43565
+ return this.withCompilerAndPerfTracing(PerfPhase.LsDiagnostics, (compiler) => {
43566
+ const ttc = compiler.getTemplateTypeChecker();
43567
+ const diagnostics = [];
43568
+ if (isTypeScriptFile(fileName)) {
43569
+ const program = compiler.getNextProgram();
43570
+ const sourceFile = program.getSourceFile(fileName);
43571
+ if (sourceFile) {
43572
+ const ngDiagnostics = compiler.getDiagnosticsForFile(sourceFile, OptimizeFor.SingleFile);
43573
+ // There are several kinds of diagnostics returned by `NgCompiler` for a source file:
43574
+ //
43575
+ // 1. Angular-related non-template diagnostics from decorated classes within that
43576
+ // file.
43577
+ // 2. Template diagnostics for components with direct inline templates (a string
43578
+ // literal).
43579
+ // 3. Template diagnostics for components with indirect inline templates (templates
43580
+ // computed
43581
+ // by expression).
43582
+ // 4. Template diagnostics for components with external templates.
43583
+ //
43584
+ // When showing diagnostics for a TS source file, we want to only include kinds 1 and
43585
+ // 2 - those diagnostics which are reported at a location within the TS file itself.
43586
+ // Diagnostics for external templates will be shown when editing that template file
43587
+ // (the `else` block) below.
43588
+ //
43589
+ // Currently, indirect inline template diagnostics (kind 3) are not shown at all by
43590
+ // the Language Service, because there is no sensible location in the user's code for
43591
+ // them. Such templates are an edge case, though, and should not be common.
43592
+ //
43593
+ // TODO(alxhub): figure out a good user experience for indirect template diagnostics
43594
+ // and show them from within the Language Service.
43595
+ diagnostics.push(...ngDiagnostics.filter(diag => diag.file !== undefined && diag.file.fileName === sourceFile.fileName));
43596
+ }
42944
43597
  }
42945
- }
42946
- else {
42947
- const components = compiler.getComponentsWithTemplateFile(fileName);
42948
- for (const component of components) {
42949
- if (ts.isClassDeclaration(component)) {
42950
- diagnostics.push(...ttc.getDiagnosticsForComponent(component));
43598
+ else {
43599
+ const components = compiler.getComponentsWithTemplateFile(fileName);
43600
+ for (const component of components) {
43601
+ if (ts.isClassDeclaration(component)) {
43602
+ diagnostics.push(...ttc.getDiagnosticsForComponent(component));
43603
+ }
42951
43604
  }
42952
43605
  }
42953
- }
42954
- this.compilerFactory.registerLastKnownProgram();
42955
- return diagnostics;
43606
+ return diagnostics;
43607
+ });
42956
43608
  }
42957
43609
  getDefinitionAndBoundSpan(fileName, position) {
42958
- return this.withCompiler((compiler) => {
43610
+ return this.withCompilerAndPerfTracing(PerfPhase.LsDefinition, (compiler) => {
42959
43611
  if (!isInAngularContext(compiler.getNextProgram(), fileName, position)) {
42960
43612
  return undefined;
42961
43613
  }
@@ -42964,7 +43616,7 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
42964
43616
  });
42965
43617
  }
42966
43618
  getTypeDefinitionAtPosition(fileName, position) {
42967
- return this.withCompiler((compiler) => {
43619
+ return this.withCompilerAndPerfTracing(PerfPhase.LsDefinition, (compiler) => {
42968
43620
  if (!isTemplateContext(compiler.getNextProgram(), fileName, position)) {
42969
43621
  return undefined;
42970
43622
  }
@@ -42973,56 +43625,57 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
42973
43625
  });
42974
43626
  }
42975
43627
  getQuickInfoAtPosition(fileName, position) {
42976
- return this.withCompiler((compiler) => {
42977
- if (!isTemplateContext(compiler.getNextProgram(), fileName, position)) {
42978
- return undefined;
42979
- }
42980
- const templateInfo = getTemplateInfoAtPosition(fileName, position, compiler);
42981
- if (templateInfo === undefined) {
42982
- return undefined;
42983
- }
42984
- const positionDetails = getTargetAtPosition(templateInfo.template, position);
42985
- if (positionDetails === null) {
42986
- return undefined;
42987
- }
42988
- // Because we can only show 1 quick info, just use the bound attribute if the target is a two
42989
- // way binding. We may consider concatenating additional display parts from the other target
42990
- // nodes or representing the two way binding in some other manner in the future.
42991
- const node = positionDetails.context.kind === TargetNodeKind.TwoWayBindingContext ?
42992
- positionDetails.context.nodes[0] :
42993
- positionDetails.context.node;
42994
- return new QuickInfoBuilder(this.tsLS, compiler, templateInfo.component, node).get();
43628
+ return this.withCompilerAndPerfTracing(PerfPhase.LsQuickInfo, (compiler) => {
43629
+ return this.getQuickInfoAtPositionImpl(fileName, position, compiler);
42995
43630
  });
42996
43631
  }
43632
+ getQuickInfoAtPositionImpl(fileName, position, compiler) {
43633
+ if (!isTemplateContext(compiler.getNextProgram(), fileName, position)) {
43634
+ return undefined;
43635
+ }
43636
+ const templateInfo = getTemplateInfoAtPosition(fileName, position, compiler);
43637
+ if (templateInfo === undefined) {
43638
+ return undefined;
43639
+ }
43640
+ const positionDetails = getTargetAtPosition(templateInfo.template, position);
43641
+ if (positionDetails === null) {
43642
+ return undefined;
43643
+ }
43644
+ // Because we can only show 1 quick info, just use the bound attribute if the target is a two
43645
+ // way binding. We may consider concatenating additional display parts from the other target
43646
+ // nodes or representing the two way binding in some other manner in the future.
43647
+ const node = positionDetails.context.kind === TargetNodeKind.TwoWayBindingContext ?
43648
+ positionDetails.context.nodes[0] :
43649
+ positionDetails.context.node;
43650
+ return new QuickInfoBuilder(this.tsLS, compiler, templateInfo.component, node).get();
43651
+ }
42997
43652
  getReferencesAtPosition(fileName, position) {
42998
- const compiler = this.compilerFactory.getOrCreate();
42999
- const results = new ReferencesAndRenameBuilder(this.strategy, this.tsLS, compiler)
43000
- .getReferencesAtPosition(fileName, position);
43001
- this.compilerFactory.registerLastKnownProgram();
43002
- return results;
43653
+ return this.withCompilerAndPerfTracing(PerfPhase.LsReferencesAndRenames, (compiler) => {
43654
+ return new ReferencesAndRenameBuilder(this.strategy, this.tsLS, compiler)
43655
+ .getReferencesAtPosition(fileName, position);
43656
+ });
43003
43657
  }
43004
43658
  getRenameInfo(fileName, position) {
43005
- var _a, _b, _c;
43006
- const compiler = this.compilerFactory.getOrCreate();
43007
- const renameInfo = new ReferencesAndRenameBuilder(this.strategy, this.tsLS, compiler)
43008
- .getRenameInfo(absoluteFrom(fileName), position);
43009
- if (!renameInfo.canRename) {
43010
- return renameInfo;
43011
- }
43012
- const quickInfo = (_a = this.getQuickInfoAtPosition(fileName, position)) !== null && _a !== void 0 ? _a : this.tsLS.getQuickInfoAtPosition(fileName, position);
43013
- const kind = (_b = quickInfo === null || quickInfo === void 0 ? void 0 : quickInfo.kind) !== null && _b !== void 0 ? _b : ts.ScriptElementKind.unknown;
43014
- const kindModifiers = (_c = quickInfo === null || quickInfo === void 0 ? void 0 : quickInfo.kindModifiers) !== null && _c !== void 0 ? _c : ts.ScriptElementKind.unknown;
43015
- return Object.assign(Object.assign({}, renameInfo), { kind, kindModifiers });
43659
+ return this.withCompilerAndPerfTracing(PerfPhase.LsReferencesAndRenames, (compiler) => {
43660
+ var _a, _b, _c;
43661
+ const renameInfo = new ReferencesAndRenameBuilder(this.strategy, this.tsLS, compiler)
43662
+ .getRenameInfo(absoluteFrom(fileName), position);
43663
+ if (!renameInfo.canRename) {
43664
+ return renameInfo;
43665
+ }
43666
+ const quickInfo = (_a = this.getQuickInfoAtPositionImpl(fileName, position, compiler)) !== null && _a !== void 0 ? _a : this.tsLS.getQuickInfoAtPosition(fileName, position);
43667
+ const kind = (_b = quickInfo === null || quickInfo === void 0 ? void 0 : quickInfo.kind) !== null && _b !== void 0 ? _b : ts.ScriptElementKind.unknown;
43668
+ const kindModifiers = (_c = quickInfo === null || quickInfo === void 0 ? void 0 : quickInfo.kindModifiers) !== null && _c !== void 0 ? _c : ts.ScriptElementKind.unknown;
43669
+ return Object.assign(Object.assign({}, renameInfo), { kind, kindModifiers });
43670
+ });
43016
43671
  }
43017
43672
  findRenameLocations(fileName, position) {
43018
- const compiler = this.compilerFactory.getOrCreate();
43019
- const results = new ReferencesAndRenameBuilder(this.strategy, this.tsLS, compiler)
43020
- .findRenameLocations(fileName, position);
43021
- this.compilerFactory.registerLastKnownProgram();
43022
- return results;
43673
+ return this.withCompilerAndPerfTracing(PerfPhase.LsReferencesAndRenames, (compiler) => {
43674
+ return new ReferencesAndRenameBuilder(this.strategy, this.tsLS, compiler)
43675
+ .findRenameLocations(fileName, position);
43676
+ });
43023
43677
  }
43024
- getCompletionBuilder(fileName, position) {
43025
- const compiler = this.compilerFactory.getOrCreate();
43678
+ getCompletionBuilder(fileName, position, compiler) {
43026
43679
  const templateInfo = getTemplateInfoAtPosition(fileName, position, compiler);
43027
43680
  if (templateInfo === undefined) {
43028
43681
  return null;
@@ -43039,23 +43692,26 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
43039
43692
  return new CompletionBuilder(this.tsLS, compiler, templateInfo.component, node, positionDetails, isTypeScriptFile(fileName));
43040
43693
  }
43041
43694
  getCompletionsAtPosition(fileName, position, options) {
43042
- return this.withCompiler((compiler) => {
43043
- if (!isTemplateContext(compiler.getNextProgram(), fileName, position)) {
43044
- return undefined;
43045
- }
43046
- const builder = this.getCompletionBuilder(fileName, position);
43047
- if (builder === null) {
43048
- return undefined;
43049
- }
43050
- return builder.getCompletionsAtPosition(options);
43695
+ return this.withCompilerAndPerfTracing(PerfPhase.LsCompletions, (compiler) => {
43696
+ return this.getCompletionsAtPositionImpl(fileName, position, options, compiler);
43051
43697
  });
43052
43698
  }
43699
+ getCompletionsAtPositionImpl(fileName, position, options, compiler) {
43700
+ if (!isTemplateContext(compiler.getNextProgram(), fileName, position)) {
43701
+ return undefined;
43702
+ }
43703
+ const builder = this.getCompletionBuilder(fileName, position, compiler);
43704
+ if (builder === null) {
43705
+ return undefined;
43706
+ }
43707
+ return builder.getCompletionsAtPosition(options);
43708
+ }
43053
43709
  getCompletionEntryDetails(fileName, position, entryName, formatOptions, preferences) {
43054
- return this.withCompiler((compiler) => {
43710
+ return this.withCompilerAndPerfTracing(PerfPhase.LsCompletions, (compiler) => {
43055
43711
  if (!isTemplateContext(compiler.getNextProgram(), fileName, position)) {
43056
43712
  return undefined;
43057
43713
  }
43058
- const builder = this.getCompletionBuilder(fileName, position);
43714
+ const builder = this.getCompletionBuilder(fileName, position, compiler);
43059
43715
  if (builder === null) {
43060
43716
  return undefined;
43061
43717
  }
@@ -43063,11 +43719,11 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
43063
43719
  });
43064
43720
  }
43065
43721
  getCompletionEntrySymbol(fileName, position, entryName) {
43066
- return this.withCompiler((compiler) => {
43722
+ return this.withCompilerAndPerfTracing(PerfPhase.LsCompletions, (compiler) => {
43067
43723
  if (!isTemplateContext(compiler.getNextProgram(), fileName, position)) {
43068
43724
  return undefined;
43069
43725
  }
43070
- const builder = this.getCompletionBuilder(fileName, position);
43726
+ const builder = this.getCompletionBuilder(fileName, position, compiler);
43071
43727
  if (builder === null) {
43072
43728
  return undefined;
43073
43729
  }
@@ -43077,7 +43733,7 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
43077
43733
  });
43078
43734
  }
43079
43735
  getTcb(fileName, position) {
43080
- return this.withCompiler(compiler => {
43736
+ return this.withCompilerAndPerfTracing(PerfPhase.LsTcb, compiler => {
43081
43737
  const templateInfo = getTemplateInfoAtPosition(fileName, position, compiler);
43082
43738
  if (templateInfo === undefined) {
43083
43739
  return undefined;
@@ -43117,10 +43773,31 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
43117
43773
  };
43118
43774
  });
43119
43775
  }
43120
- withCompiler(p) {
43776
+ /**
43777
+ * Provides an instance of the `NgCompiler` and traces perf results. Perf results are logged only
43778
+ * if the log level is verbose or higher. This method is intended to be called once per public
43779
+ * method call.
43780
+ *
43781
+ * Here is an example of the log output.
43782
+ *
43783
+ * Perf 245 [16:16:39.353] LanguageService#getQuickInfoAtPosition(): {"events":{},"phases":{
43784
+ * "Unaccounted":379,"TtcSymbol":4},"memory":{}}
43785
+ *
43786
+ * Passing name of caller instead of using `arguments.caller` because 'caller', 'callee', and
43787
+ * 'arguments' properties may not be accessed in strict mode.
43788
+ *
43789
+ * @param phase the `PerfPhase` to execute the `p` callback in
43790
+ * @param p callback to be run synchronously with an instance of the `NgCompiler` as argument
43791
+ * @return the result of running the `p` callback
43792
+ */
43793
+ withCompilerAndPerfTracing(phase, p) {
43121
43794
  const compiler = this.compilerFactory.getOrCreate();
43122
- const result = p(compiler);
43795
+ const result = compiler.perfRecorder.inPhase(phase, () => p(compiler));
43123
43796
  this.compilerFactory.registerLastKnownProgram();
43797
+ const logger = this.project.projectService.logger;
43798
+ if (logger.hasLevel(ts.server.LogLevel.verbose)) {
43799
+ logger.perftrc(`LanguageService#${PerfPhase[phase]}: ${JSON.stringify(compiler.perfRecorder.finalize())}`);
43800
+ }
43124
43801
  return result;
43125
43802
  }
43126
43803
  getCompilerOptionsDiagnostics() {
@@ -43128,22 +43805,23 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
43128
43805
  if (!(project instanceof ts.server.ConfiguredProject)) {
43129
43806
  return [];
43130
43807
  }
43131
- const diagnostics = [];
43132
- const configSourceFile = ts.readJsonConfigFile(project.getConfigFilePath(), (path) => project.readFile(path));
43133
- if (!this.options.strictTemplates && !this.options.fullTemplateTypeCheck) {
43134
- diagnostics.push({
43135
- messageText: 'Some language features are not available. ' +
43136
- 'To access all features, enable `strictTemplates` in `angularCompilerOptions`.',
43137
- category: ts.DiagnosticCategory.Suggestion,
43138
- code: ngErrorCode(ErrorCode.SUGGEST_STRICT_TEMPLATES),
43139
- file: configSourceFile,
43140
- start: undefined,
43141
- length: undefined,
43142
- });
43143
- }
43144
- const compiler = this.compilerFactory.getOrCreate();
43145
- diagnostics.push(...compiler.getOptionDiagnostics());
43146
- return diagnostics;
43808
+ return this.withCompilerAndPerfTracing(PerfPhase.LsDiagnostics, (compiler) => {
43809
+ const diagnostics = [];
43810
+ const configSourceFile = ts.readJsonConfigFile(project.getConfigFilePath(), (path) => project.readFile(path));
43811
+ if (!this.options.strictTemplates && !this.options.fullTemplateTypeCheck) {
43812
+ diagnostics.push({
43813
+ messageText: 'Some language features are not available. ' +
43814
+ 'To access all features, enable `strictTemplates` in `angularCompilerOptions`.',
43815
+ category: ts.DiagnosticCategory.Suggestion,
43816
+ code: ngErrorCode(ErrorCode.SUGGEST_STRICT_TEMPLATES),
43817
+ file: configSourceFile,
43818
+ start: undefined,
43819
+ length: undefined,
43820
+ });
43821
+ }
43822
+ diagnostics.push(...compiler.getOptionDiagnostics());
43823
+ return diagnostics;
43824
+ });
43147
43825
  }
43148
43826
  watchConfigFile(project) {
43149
43827
  // TODO: Check the case when the project is disposed. An InferredProject