@angular/language-service 11.2.7 → 11.2.11

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.7
2
+ * @license Angular v11.2.11
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;
@@ -11518,7 +11533,7 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
11518
11533
  return visitor.visitElement(this, context);
11519
11534
  }
11520
11535
  }
11521
- class Comment {
11536
+ class Comment$1 {
11522
11537
  constructor(value, sourceSpan) {
11523
11538
  this.value = value;
11524
11539
  this.sourceSpan = sourceSpan;
@@ -12548,7 +12563,7 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
12548
12563
  const text = this._advanceIf(TokenType.RAW_TEXT);
12549
12564
  this._advanceIf(TokenType.COMMENT_END);
12550
12565
  const value = text != null ? text.parts[0].trim() : null;
12551
- this._addToParent(new Comment(value, token.sourceSpan));
12566
+ this._addToParent(new Comment$1(value, token.sourceSpan));
12552
12567
  }
12553
12568
  _consumeExpansion(token) {
12554
12569
  const switchValue = this._advance();
@@ -13756,7 +13771,11 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
13756
13771
  if (isEmptyExpression(value)) {
13757
13772
  return null;
13758
13773
  }
13759
- 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
+ }
13760
13779
  const { property, hasOverrideFlag, suffix: bindingSuffix } = parseProperty(name);
13761
13780
  suffix = typeof suffix === 'string' && suffix.length !== 0 ? suffix : bindingSuffix;
13762
13781
  const entry = { name: property, suffix: suffix, value, sourceSpan, hasOverrideFlag };
@@ -14101,8 +14120,12 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
14101
14120
  return Identifiers$1.stylePropInterpolateV;
14102
14121
  }
14103
14122
  }
14104
- function normalizePropName(prop) {
14105
- 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('--');
14106
14129
  }
14107
14130
 
14108
14131
  /**
@@ -16220,26 +16243,33 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
16220
16243
  EVENT: { start: '(', end: ')' },
16221
16244
  };
16222
16245
  const TEMPLATE_ATTR_PREFIX$1 = '*';
16223
- function htmlAstToRender3Ast(htmlNodes, bindingParser) {
16224
- const transformer = new HtmlAstToIvyAst(bindingParser);
16246
+ function htmlAstToRender3Ast(htmlNodes, bindingParser, options) {
16247
+ const transformer = new HtmlAstToIvyAst(bindingParser, options);
16225
16248
  const ivyNodes = visitAll$1(transformer, htmlNodes);
16226
16249
  // Errors might originate in either the binding parser or the html to ivy transformer
16227
16250
  const allErrors = bindingParser.errors.concat(transformer.errors);
16228
- return {
16251
+ const result = {
16229
16252
  nodes: ivyNodes,
16230
16253
  errors: allErrors,
16231
16254
  styleUrls: transformer.styleUrls,
16232
16255
  styles: transformer.styles,
16233
- ngContentSelectors: transformer.ngContentSelectors,
16256
+ ngContentSelectors: transformer.ngContentSelectors
16234
16257
  };
16258
+ if (options.collectCommentNodes) {
16259
+ result.commentNodes = transformer.commentNodes;
16260
+ }
16261
+ return result;
16235
16262
  }
16236
16263
  class HtmlAstToIvyAst {
16237
- constructor(bindingParser) {
16264
+ constructor(bindingParser, options) {
16238
16265
  this.bindingParser = bindingParser;
16266
+ this.options = options;
16239
16267
  this.errors = [];
16240
16268
  this.styles = [];
16241
16269
  this.styleUrls = [];
16242
16270
  this.ngContentSelectors = [];
16271
+ // This array will be populated if `Render3ParseOptions['collectCommentNodes']` is true
16272
+ this.commentNodes = [];
16243
16273
  this.inI18nBlock = false;
16244
16274
  }
16245
16275
  // HTML visitor
@@ -16408,6 +16438,9 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
16408
16438
  return null;
16409
16439
  }
16410
16440
  visitComment(comment) {
16441
+ if (this.options.collectCommentNodes) {
16442
+ this.commentNodes.push(new Comment(comment.value || '', comment.sourceSpan));
16443
+ }
16411
16444
  return null;
16412
16445
  }
16413
16446
  // convert view engine `ParsedProperty` to a format suitable for IVY
@@ -16599,7 +16632,7 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
16599
16632
  return node instanceof Text$2 && node.value.trim().length == 0;
16600
16633
  }
16601
16634
  function isCommentNode(node) {
16602
- return node instanceof Comment;
16635
+ return node instanceof Comment$1;
16603
16636
  }
16604
16637
  function textContents(node) {
16605
16638
  if (node.children.length !== 1 || !(node.children[0] instanceof Text$2)) {
@@ -19176,7 +19209,7 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
19176
19209
  const parseResult = htmlParser.parse(template, templateUrl, Object.assign(Object.assign({ leadingTriviaChars: LEADING_TRIVIA_CHARS }, options), { tokenizeExpansionForms: true }));
19177
19210
  if (!options.alwaysAttemptHtmlToR3AstConversion && parseResult.errors &&
19178
19211
  parseResult.errors.length > 0) {
19179
- return {
19212
+ const parsedTemplate = {
19180
19213
  interpolationConfig,
19181
19214
  preserveWhitespaces,
19182
19215
  template,
@@ -19188,6 +19221,10 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
19188
19221
  styles: [],
19189
19222
  ngContentSelectors: []
19190
19223
  };
19224
+ if (options.collectCommentNodes) {
19225
+ parsedTemplate.commentNodes = [];
19226
+ }
19227
+ return parsedTemplate;
19191
19228
  }
19192
19229
  let rootNodes = parseResult.rootNodes;
19193
19230
  // process i18n meta information (scan attributes, generate ids)
@@ -19198,7 +19235,7 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
19198
19235
  const i18nMetaResult = i18nMetaVisitor.visitAllWithErrors(rootNodes);
19199
19236
  if (!options.alwaysAttemptHtmlToR3AstConversion && i18nMetaResult.errors &&
19200
19237
  i18nMetaResult.errors.length > 0) {
19201
- return {
19238
+ const parsedTemplate = {
19202
19239
  interpolationConfig,
19203
19240
  preserveWhitespaces,
19204
19241
  template,
@@ -19210,6 +19247,10 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
19210
19247
  styles: [],
19211
19248
  ngContentSelectors: []
19212
19249
  };
19250
+ if (options.collectCommentNodes) {
19251
+ parsedTemplate.commentNodes = [];
19252
+ }
19253
+ return parsedTemplate;
19213
19254
  }
19214
19255
  rootNodes = i18nMetaResult.rootNodes;
19215
19256
  if (!preserveWhitespaces) {
@@ -19222,9 +19263,9 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
19222
19263
  rootNodes = visitAll$1(new I18nMetaVisitor(interpolationConfig, /* keepI18nAttrs */ false), rootNodes);
19223
19264
  }
19224
19265
  }
19225
- const { nodes, errors, styleUrls, styles, ngContentSelectors } = htmlAstToRender3Ast(rootNodes, bindingParser);
19266
+ const { nodes, errors, styleUrls, styles, ngContentSelectors, commentNodes } = htmlAstToRender3Ast(rootNodes, bindingParser, { collectCommentNodes: !!options.collectCommentNodes });
19226
19267
  errors.push(...parseResult.errors, ...i18nMetaResult.errors);
19227
- return {
19268
+ const parsedTemplate = {
19228
19269
  interpolationConfig,
19229
19270
  preserveWhitespaces,
19230
19271
  errors: errors.length > 0 ? errors : null,
@@ -19236,6 +19277,10 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
19236
19277
  styles,
19237
19278
  ngContentSelectors
19238
19279
  };
19280
+ if (options.collectCommentNodes) {
19281
+ parsedTemplate.commentNodes = commentNodes;
19282
+ }
19283
+ return parsedTemplate;
19239
19284
  }
19240
19285
  const elementRegistry = new DomElementSchemaRegistry();
19241
19286
  /**
@@ -20354,7 +20399,7 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
20354
20399
  * Use of this source code is governed by an MIT-style license that can be
20355
20400
  * found in the LICENSE file at https://angular.io/license
20356
20401
  */
20357
- const VERSION$1 = new Version('11.2.7');
20402
+ const VERSION$1 = new Version('11.2.11');
20358
20403
 
20359
20404
  /**
20360
20405
  * @license
@@ -21011,7 +21056,7 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
21011
21056
  */
21012
21057
  function createDirectiveDefinitionMap(meta) {
21013
21058
  const definitionMap = new DefinitionMap();
21014
- definitionMap.set('version', literal('11.2.7'));
21059
+ definitionMap.set('version', literal('11.2.11'));
21015
21060
  // e.g. `type: MyDirective`
21016
21061
  definitionMap.set('type', meta.internalType);
21017
21062
  // e.g. `selector: 'some-dir'`
@@ -21232,7 +21277,7 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
21232
21277
  */
21233
21278
  function createPipeDefinitionMap(meta) {
21234
21279
  const definitionMap = new DefinitionMap();
21235
- definitionMap.set('version', literal('11.2.7'));
21280
+ definitionMap.set('version', literal('11.2.11'));
21236
21281
  definitionMap.set('ngImport', importExpr(Identifiers$1.core));
21237
21282
  // e.g. `type: MyPipe`
21238
21283
  definitionMap.set('type', meta.internalType);
@@ -21264,7 +21309,7 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
21264
21309
  * Use of this source code is governed by an MIT-style license that can be
21265
21310
  * found in the LICENSE file at https://angular.io/license
21266
21311
  */
21267
- const VERSION$2 = new Version('11.2.7');
21312
+ const VERSION$2 = new Version('11.2.11');
21268
21313
 
21269
21314
  /**
21270
21315
  * @license
@@ -21414,6 +21459,11 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
21414
21459
  * Raised when a directive/pipe is part of the declarations of two or more NgModules.
21415
21460
  */
21416
21461
  ErrorCode[ErrorCode["NGMODULE_DECLARATION_NOT_UNIQUE"] = 6007] = "NGMODULE_DECLARATION_NOT_UNIQUE";
21462
+ /**
21463
+ * Not actually raised by the compiler, but reserved for documentation of a View Engine error when
21464
+ * a View Engine build depends on an Ivy-compiled NgModule.
21465
+ */
21466
+ ErrorCode[ErrorCode["NGMODULE_VE_DEPENDENCY_ON_IVY_LIB"] = 6999] = "NGMODULE_VE_DEPENDENCY_ON_IVY_LIB";
21417
21467
  /**
21418
21468
  * An element name failed validation against the DOM schema.
21419
21469
  */
@@ -21659,6 +21709,16 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
21659
21709
  function isAssignment(node) {
21660
21710
  return ts$1.isBinaryExpression(node) && node.operatorToken.kind === ts$1.SyntaxKind.EqualsToken;
21661
21711
  }
21712
+ /**
21713
+ * Obtains the non-redirected source file for `sf`.
21714
+ */
21715
+ function toUnredirectedSourceFile(sf) {
21716
+ const redirectInfo = sf.redirectInfo;
21717
+ if (redirectInfo === undefined) {
21718
+ return sf;
21719
+ }
21720
+ return redirectInfo.unredirected;
21721
+ }
21662
21722
 
21663
21723
  /**
21664
21724
  * @license
@@ -22167,6 +22227,22 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
22167
22227
  * Use of this source code is governed by an MIT-style license that can be
22168
22228
  * found in the LICENSE file at https://angular.io/license
22169
22229
  */
22230
+ const DefaultImportDeclaration = Symbol('DefaultImportDeclaration');
22231
+ /**
22232
+ * Attaches a default import declaration to `expr` to indicate the dependency of `expr` on the
22233
+ * default import.
22234
+ */
22235
+ function attachDefaultImportDeclaration(expr, importDecl) {
22236
+ expr[DefaultImportDeclaration] = importDecl;
22237
+ }
22238
+ /**
22239
+ * Obtains the default import declaration that `expr` depends on, or `null` if there is no such
22240
+ * dependency.
22241
+ */
22242
+ function getDefaultImportDeclaration(expr) {
22243
+ var _a;
22244
+ return (_a = expr[DefaultImportDeclaration]) !== null && _a !== void 0 ? _a : null;
22245
+ }
22170
22246
  /**
22171
22247
  * TypeScript has trouble with generating default imports inside of transformers for some module
22172
22248
  * formats. The issue is that for the statement:
@@ -22199,47 +22275,25 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
22199
22275
  */
22200
22276
  class DefaultImportTracker {
22201
22277
  constructor() {
22202
- /**
22203
- * A `Map` which tracks the `Map` of default import `ts.Identifier`s to their
22204
- * `ts.ImportDeclaration`s. These declarations are not guaranteed to be used.
22205
- */
22206
- this.sourceFileToImportMap = new Map();
22207
22278
  /**
22208
22279
  * A `Map` which tracks the `Set` of `ts.ImportDeclaration`s for default imports that were used in
22209
22280
  * a given `ts.SourceFile` and need to be preserved.
22210
22281
  */
22211
22282
  this.sourceFileToUsedImports = new Map();
22212
22283
  }
22213
- recordImportedIdentifier(id, decl) {
22214
- const sf = getSourceFile(id);
22215
- if (!this.sourceFileToImportMap.has(sf)) {
22216
- this.sourceFileToImportMap.set(sf, new Map());
22217
- }
22218
- this.sourceFileToImportMap.get(sf).set(id, decl);
22219
- }
22220
- recordUsedIdentifier(id) {
22221
- const sf = getSourceFile(id);
22222
- if (!this.sourceFileToImportMap.has(sf)) {
22223
- // The identifier's source file has no registered default imports at all.
22224
- return;
22225
- }
22226
- const identiferToDeclaration = this.sourceFileToImportMap.get(sf);
22227
- if (!identiferToDeclaration.has(id)) {
22228
- // The identifier isn't from a registered default import.
22229
- return;
22230
- }
22231
- const decl = identiferToDeclaration.get(id);
22284
+ recordUsedImport(importDecl) {
22285
+ const sf = getSourceFile(importDecl);
22232
22286
  // Add the default import declaration to the set of used import declarations for the file.
22233
22287
  if (!this.sourceFileToUsedImports.has(sf)) {
22234
22288
  this.sourceFileToUsedImports.set(sf, new Set());
22235
22289
  }
22236
- this.sourceFileToUsedImports.get(sf).add(decl);
22290
+ this.sourceFileToUsedImports.get(sf).add(importDecl);
22237
22291
  }
22238
22292
  /**
22239
22293
  * Get a `ts.TransformerFactory` which will preserve default imports that were previously marked
22240
22294
  * as used.
22241
22295
  *
22242
- * This transformer must run after any other transformers which call `recordUsedIdentifier`.
22296
+ * This transformer must run after any other transformers which call `recordUsedImport`.
22243
22297
  */
22244
22298
  importPreservingTransformer() {
22245
22299
  return (context) => {
@@ -22294,7 +22348,6 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
22294
22348
  });
22295
22349
  // Save memory - there's no need to keep these around once the transform has run for the given
22296
22350
  // file.
22297
- this.sourceFileToImportMap.delete(originalSf);
22298
22351
  this.sourceFileToUsedImports.delete(originalSf);
22299
22352
  return ts$1.updateSourceFileNode(sf, statements);
22300
22353
  }
@@ -25492,6 +25545,417 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
25492
25545
  }
25493
25546
  }
25494
25547
 
25548
+ /**
25549
+ * @license
25550
+ * Copyright Google LLC All Rights Reserved.
25551
+ *
25552
+ * Use of this source code is governed by an MIT-style license that can be
25553
+ * found in the LICENSE file at https://angular.io/license
25554
+ */
25555
+ /**
25556
+ * A phase of compilation for which time is tracked in a distinct bucket.
25557
+ */
25558
+ var PerfPhase;
25559
+ (function (PerfPhase) {
25560
+ /**
25561
+ * The "default" phase which tracks time not spent in any other phase.
25562
+ */
25563
+ PerfPhase[PerfPhase["Unaccounted"] = 0] = "Unaccounted";
25564
+ /**
25565
+ * Time spent setting up the compiler, before a TypeScript program is created.
25566
+ *
25567
+ * This includes operations like configuring the `ts.CompilerHost` and any wrappers.
25568
+ */
25569
+ PerfPhase[PerfPhase["Setup"] = 1] = "Setup";
25570
+ /**
25571
+ * Time spent in `ts.createProgram`, including reading and parsing `ts.SourceFile`s in the
25572
+ * `ts.CompilerHost`.
25573
+ *
25574
+ * This might be an incremental program creation operation.
25575
+ */
25576
+ PerfPhase[PerfPhase["TypeScriptProgramCreate"] = 2] = "TypeScriptProgramCreate";
25577
+ /**
25578
+ * Time spent reconciling the contents of an old `ts.Program` with the new incremental one.
25579
+ *
25580
+ * Only present in incremental compilations.
25581
+ */
25582
+ PerfPhase[PerfPhase["Reconciliation"] = 3] = "Reconciliation";
25583
+ /**
25584
+ * Time spent updating an `NgCompiler` instance with a resource-only change.
25585
+ *
25586
+ * Only present in incremental compilations where the change was resource-only.
25587
+ */
25588
+ PerfPhase[PerfPhase["ResourceUpdate"] = 4] = "ResourceUpdate";
25589
+ /**
25590
+ * Time spent calculating the plain TypeScript diagnostics (structural and semantic).
25591
+ */
25592
+ PerfPhase[PerfPhase["TypeScriptDiagnostics"] = 5] = "TypeScriptDiagnostics";
25593
+ /**
25594
+ * Time spent in Angular analysis of individual classes in the program.
25595
+ */
25596
+ PerfPhase[PerfPhase["Analysis"] = 6] = "Analysis";
25597
+ /**
25598
+ * Time spent in Angular global analysis (synthesis of analysis information into a complete
25599
+ * understanding of the program).
25600
+ */
25601
+ PerfPhase[PerfPhase["Resolve"] = 7] = "Resolve";
25602
+ /**
25603
+ * Time spent building the import graph of the program in order to perform cycle detection.
25604
+ */
25605
+ PerfPhase[PerfPhase["CycleDetection"] = 8] = "CycleDetection";
25606
+ /**
25607
+ * Time spent generating the text of Type Check Blocks in order to perform template type checking.
25608
+ */
25609
+ PerfPhase[PerfPhase["TcbGeneration"] = 9] = "TcbGeneration";
25610
+ /**
25611
+ * Time spent updating the `ts.Program` with new Type Check Block code.
25612
+ */
25613
+ PerfPhase[PerfPhase["TcbUpdateProgram"] = 10] = "TcbUpdateProgram";
25614
+ /**
25615
+ * Time spent by TypeScript performing its emit operations, including downleveling and writing
25616
+ * output files.
25617
+ */
25618
+ PerfPhase[PerfPhase["TypeScriptEmit"] = 11] = "TypeScriptEmit";
25619
+ /**
25620
+ * Time spent by Angular performing code transformations of ASTs as they're about to be emitted.
25621
+ *
25622
+ * This includes the actual code generation step for templates, and occurs during the emit phase
25623
+ * (but is tracked separately from `TypeScriptEmit` time).
25624
+ */
25625
+ PerfPhase[PerfPhase["Compile"] = 12] = "Compile";
25626
+ /**
25627
+ * Time spent performing a `TemplateTypeChecker` autocompletion operation.
25628
+ */
25629
+ PerfPhase[PerfPhase["TtcAutocompletion"] = 13] = "TtcAutocompletion";
25630
+ /**
25631
+ * Time spent computing template type-checking diagnostics.
25632
+ */
25633
+ PerfPhase[PerfPhase["TtcDiagnostics"] = 14] = "TtcDiagnostics";
25634
+ /**
25635
+ * Time spent getting a `Symbol` from the `TemplateTypeChecker`.
25636
+ */
25637
+ PerfPhase[PerfPhase["TtcSymbol"] = 15] = "TtcSymbol";
25638
+ /**
25639
+ * Time spent by the Angular Language Service calculating a "get references" or a renaming
25640
+ * operation.
25641
+ */
25642
+ PerfPhase[PerfPhase["LsReferencesAndRenames"] = 16] = "LsReferencesAndRenames";
25643
+ /**
25644
+ * Time spent by the Angular Language Service calculating a "quick info" operation.
25645
+ */
25646
+ PerfPhase[PerfPhase["LsQuickInfo"] = 17] = "LsQuickInfo";
25647
+ /**
25648
+ * Time spent by the Angular Language Service calculating a "get type definition" or "get
25649
+ * definition" operation.
25650
+ */
25651
+ PerfPhase[PerfPhase["LsDefinition"] = 18] = "LsDefinition";
25652
+ /**
25653
+ * Time spent by the Angular Language Service calculating a "get completions" (AKA autocomplete)
25654
+ * operation.
25655
+ */
25656
+ PerfPhase[PerfPhase["LsCompletions"] = 19] = "LsCompletions";
25657
+ /**
25658
+ * Time spent by the Angular Language Service calculating a "view template typecheck block"
25659
+ * operation.
25660
+ */
25661
+ PerfPhase[PerfPhase["LsTcb"] = 20] = "LsTcb";
25662
+ /**
25663
+ * Time spent by the Angular Language Service calculating diagnostics.
25664
+ */
25665
+ PerfPhase[PerfPhase["LsDiagnostics"] = 21] = "LsDiagnostics";
25666
+ /**
25667
+ * Time spent by the Angular Language Service calculating a "get component locations for template"
25668
+ * operation.
25669
+ */
25670
+ PerfPhase[PerfPhase["LsComponentLocations"] = 22] = "LsComponentLocations";
25671
+ /**
25672
+ * Tracks the number of `PerfPhase`s, and must appear at the end of the list.
25673
+ */
25674
+ PerfPhase[PerfPhase["LAST"] = 23] = "LAST";
25675
+ })(PerfPhase || (PerfPhase = {}));
25676
+ /**
25677
+ * Represents some occurrence during compilation, and is tracked with a counter.
25678
+ */
25679
+ var PerfEvent;
25680
+ (function (PerfEvent) {
25681
+ /**
25682
+ * Counts the number of `.d.ts` files in the program.
25683
+ */
25684
+ PerfEvent[PerfEvent["InputDtsFile"] = 0] = "InputDtsFile";
25685
+ /**
25686
+ * Counts the number of non-`.d.ts` files in the program.
25687
+ */
25688
+ PerfEvent[PerfEvent["InputTsFile"] = 1] = "InputTsFile";
25689
+ /**
25690
+ * An `@Component` class was analyzed.
25691
+ */
25692
+ PerfEvent[PerfEvent["AnalyzeComponent"] = 2] = "AnalyzeComponent";
25693
+ /**
25694
+ * An `@Directive` class was analyzed.
25695
+ */
25696
+ PerfEvent[PerfEvent["AnalyzeDirective"] = 3] = "AnalyzeDirective";
25697
+ /**
25698
+ * An `@Injectable` class was analyzed.
25699
+ */
25700
+ PerfEvent[PerfEvent["AnalyzeInjectable"] = 4] = "AnalyzeInjectable";
25701
+ /**
25702
+ * An `@NgModule` class was analyzed.
25703
+ */
25704
+ PerfEvent[PerfEvent["AnalyzeNgModule"] = 5] = "AnalyzeNgModule";
25705
+ /**
25706
+ * An `@Pipe` class was analyzed.
25707
+ */
25708
+ PerfEvent[PerfEvent["AnalyzePipe"] = 6] = "AnalyzePipe";
25709
+ /**
25710
+ * A trait was analyzed.
25711
+ *
25712
+ * In theory, this should be the sum of the `Analyze` counters for each decorator type.
25713
+ */
25714
+ PerfEvent[PerfEvent["TraitAnalyze"] = 7] = "TraitAnalyze";
25715
+ /**
25716
+ * A trait had a prior analysis available from an incremental program, and did not need to be
25717
+ * re-analyzed.
25718
+ */
25719
+ PerfEvent[PerfEvent["TraitReuseAnalysis"] = 8] = "TraitReuseAnalysis";
25720
+ /**
25721
+ * A `ts.SourceFile` directly changed between the prior program and a new incremental compilation.
25722
+ */
25723
+ PerfEvent[PerfEvent["SourceFilePhysicalChange"] = 9] = "SourceFilePhysicalChange";
25724
+ /**
25725
+ * A `ts.SourceFile` did not physically changed, but according to the file dependency graph, has
25726
+ * logically changed between the prior program and a new incremental compilation.
25727
+ */
25728
+ PerfEvent[PerfEvent["SourceFileLogicalChange"] = 10] = "SourceFileLogicalChange";
25729
+ /**
25730
+ * A `ts.SourceFile` has not logically changed and all of its analysis results were thus available
25731
+ * for reuse.
25732
+ */
25733
+ PerfEvent[PerfEvent["SourceFileReuseAnalysis"] = 11] = "SourceFileReuseAnalysis";
25734
+ /**
25735
+ * A Type Check Block (TCB) was generated.
25736
+ */
25737
+ PerfEvent[PerfEvent["GenerateTcb"] = 12] = "GenerateTcb";
25738
+ /**
25739
+ * A Type Check Block (TCB) could not be generated because inlining was disabled, and the block
25740
+ * would've required inlining.
25741
+ */
25742
+ PerfEvent[PerfEvent["SkipGenerateTcbNoInline"] = 13] = "SkipGenerateTcbNoInline";
25743
+ /**
25744
+ * A `.ngtypecheck.ts` file could be reused from the previous program and did not need to be
25745
+ * regenerated.
25746
+ */
25747
+ PerfEvent[PerfEvent["ReuseTypeCheckFile"] = 14] = "ReuseTypeCheckFile";
25748
+ /**
25749
+ * The template type-checking program required changes and had to be updated in an incremental
25750
+ * step.
25751
+ */
25752
+ PerfEvent[PerfEvent["UpdateTypeCheckProgram"] = 15] = "UpdateTypeCheckProgram";
25753
+ /**
25754
+ * The compiler was able to prove that a `ts.SourceFile` did not need to be re-emitted.
25755
+ */
25756
+ PerfEvent[PerfEvent["EmitSkipSourceFile"] = 16] = "EmitSkipSourceFile";
25757
+ /**
25758
+ * A `ts.SourceFile` was emitted.
25759
+ */
25760
+ PerfEvent[PerfEvent["EmitSourceFile"] = 17] = "EmitSourceFile";
25761
+ /**
25762
+ * Tracks the number of `PrefEvent`s, and must appear at the end of the list.
25763
+ */
25764
+ PerfEvent[PerfEvent["LAST"] = 18] = "LAST";
25765
+ })(PerfEvent || (PerfEvent = {}));
25766
+ /**
25767
+ * Represents a checkpoint during compilation at which the memory usage of the compiler should be
25768
+ * recorded.
25769
+ */
25770
+ var PerfCheckpoint;
25771
+ (function (PerfCheckpoint) {
25772
+ /**
25773
+ * The point at which the `PerfRecorder` was created, and ideally tracks memory used before any
25774
+ * compilation structures are created.
25775
+ */
25776
+ PerfCheckpoint[PerfCheckpoint["Initial"] = 0] = "Initial";
25777
+ /**
25778
+ * The point just after the `ts.Program` has been created.
25779
+ */
25780
+ PerfCheckpoint[PerfCheckpoint["TypeScriptProgramCreate"] = 1] = "TypeScriptProgramCreate";
25781
+ /**
25782
+ * The point just before Angular analysis starts.
25783
+ *
25784
+ * In the main usage pattern for the compiler, TypeScript diagnostics have been calculated at this
25785
+ * point, so the `ts.TypeChecker` has fully ingested the current program, all `ts.Type` structures
25786
+ * and `ts.Symbol`s have been created.
25787
+ */
25788
+ PerfCheckpoint[PerfCheckpoint["PreAnalysis"] = 2] = "PreAnalysis";
25789
+ /**
25790
+ * The point just after Angular analysis completes.
25791
+ */
25792
+ PerfCheckpoint[PerfCheckpoint["Analysis"] = 3] = "Analysis";
25793
+ /**
25794
+ * The point just after Angular resolution is complete.
25795
+ */
25796
+ PerfCheckpoint[PerfCheckpoint["Resolve"] = 4] = "Resolve";
25797
+ /**
25798
+ * The point just after Type Check Blocks (TCBs) have been generated.
25799
+ */
25800
+ PerfCheckpoint[PerfCheckpoint["TtcGeneration"] = 5] = "TtcGeneration";
25801
+ /**
25802
+ * The point just after the template type-checking program has been updated with any new TCBs.
25803
+ */
25804
+ PerfCheckpoint[PerfCheckpoint["TtcUpdateProgram"] = 6] = "TtcUpdateProgram";
25805
+ /**
25806
+ * The point just before emit begins.
25807
+ *
25808
+ * In the main usage pattern for the compiler, all template type-checking diagnostics have been
25809
+ * requested at this point.
25810
+ */
25811
+ PerfCheckpoint[PerfCheckpoint["PreEmit"] = 7] = "PreEmit";
25812
+ /**
25813
+ * The point just after the program has been fully emitted.
25814
+ */
25815
+ PerfCheckpoint[PerfCheckpoint["Emit"] = 8] = "Emit";
25816
+ /**
25817
+ * Tracks the number of `PerfCheckpoint`s, and must appear at the end of the list.
25818
+ */
25819
+ PerfCheckpoint[PerfCheckpoint["LAST"] = 9] = "LAST";
25820
+ })(PerfCheckpoint || (PerfCheckpoint = {}));
25821
+
25822
+ /**
25823
+ * @license
25824
+ * Copyright Google LLC All Rights Reserved.
25825
+ *
25826
+ * Use of this source code is governed by an MIT-style license that can be
25827
+ * found in the LICENSE file at https://angular.io/license
25828
+ */
25829
+ function mark() {
25830
+ return process.hrtime();
25831
+ }
25832
+ function timeSinceInMicros(mark) {
25833
+ const delta = process.hrtime(mark);
25834
+ return (delta[0] * 1000000) + Math.floor(delta[1] / 1000);
25835
+ }
25836
+
25837
+ /**
25838
+ * @license
25839
+ * Copyright Google LLC All Rights Reserved.
25840
+ *
25841
+ * Use of this source code is governed by an MIT-style license that can be
25842
+ * found in the LICENSE file at https://angular.io/license
25843
+ */
25844
+ /**
25845
+ * A `PerfRecorder` that actively tracks performance statistics.
25846
+ */
25847
+ class ActivePerfRecorder {
25848
+ constructor(zeroTime) {
25849
+ this.zeroTime = zeroTime;
25850
+ this.currentPhase = PerfPhase.Unaccounted;
25851
+ this.currentPhaseEntered = this.zeroTime;
25852
+ this.counters = Array(PerfEvent.LAST).fill(0);
25853
+ this.phaseTime = Array(PerfPhase.LAST).fill(0);
25854
+ this.bytes = Array(PerfCheckpoint.LAST).fill(0);
25855
+ // Take an initial memory snapshot before any other compilation work begins.
25856
+ this.memory(PerfCheckpoint.Initial);
25857
+ }
25858
+ /**
25859
+ * Creates an `ActivePerfRecoder` with its zero point set to the current time.
25860
+ */
25861
+ static zeroedToNow() {
25862
+ return new ActivePerfRecorder(mark());
25863
+ }
25864
+ reset() {
25865
+ this.counters = Array(PerfEvent.LAST).fill(0);
25866
+ this.phaseTime = Array(PerfPhase.LAST).fill(0);
25867
+ this.bytes = Array(PerfCheckpoint.LAST).fill(0);
25868
+ this.zeroTime = mark();
25869
+ this.currentPhase = PerfPhase.Unaccounted;
25870
+ this.currentPhaseEntered = this.zeroTime;
25871
+ }
25872
+ memory(after) {
25873
+ this.bytes[after] = process.memoryUsage().heapUsed;
25874
+ }
25875
+ phase(phase) {
25876
+ const previous = this.currentPhase;
25877
+ this.phaseTime[this.currentPhase] += timeSinceInMicros(this.currentPhaseEntered);
25878
+ this.currentPhase = phase;
25879
+ this.currentPhaseEntered = mark();
25880
+ return previous;
25881
+ }
25882
+ inPhase(phase, fn) {
25883
+ const previousPhase = this.phase(phase);
25884
+ try {
25885
+ return fn();
25886
+ }
25887
+ finally {
25888
+ this.phase(previousPhase);
25889
+ }
25890
+ }
25891
+ eventCount(counter, incrementBy = 1) {
25892
+ this.counters[counter] += incrementBy;
25893
+ }
25894
+ /**
25895
+ * Return the current performance metrics as a serializable object.
25896
+ */
25897
+ finalize() {
25898
+ // Track the last segment of time spent in `this.currentPhase` in the time array.
25899
+ this.phase(PerfPhase.Unaccounted);
25900
+ const results = {
25901
+ events: {},
25902
+ phases: {},
25903
+ memory: {},
25904
+ };
25905
+ for (let i = 0; i < this.phaseTime.length; i++) {
25906
+ if (this.phaseTime[i] > 0) {
25907
+ results.phases[PerfPhase[i]] = this.phaseTime[i];
25908
+ }
25909
+ }
25910
+ for (let i = 0; i < this.phaseTime.length; i++) {
25911
+ if (this.counters[i] > 0) {
25912
+ results.events[PerfEvent[i]] = this.counters[i];
25913
+ }
25914
+ }
25915
+ for (let i = 0; i < this.bytes.length; i++) {
25916
+ if (this.bytes[i] > 0) {
25917
+ results.memory[PerfCheckpoint[i]] = this.bytes[i];
25918
+ }
25919
+ }
25920
+ return results;
25921
+ }
25922
+ }
25923
+ /**
25924
+ * A `PerfRecorder` that delegates to a target `PerfRecorder` which can be updated later.
25925
+ *
25926
+ * `DelegatingPerfRecorder` is useful when a compiler class that needs a `PerfRecorder` can outlive
25927
+ * the current compilation. This is true for most compiler classes as resource-only changes reuse
25928
+ * the same `NgCompiler` for a new compilation.
25929
+ */
25930
+ class DelegatingPerfRecorder {
25931
+ constructor(target) {
25932
+ this.target = target;
25933
+ }
25934
+ eventCount(counter, incrementBy) {
25935
+ this.target.eventCount(counter, incrementBy);
25936
+ }
25937
+ phase(phase) {
25938
+ return this.target.phase(phase);
25939
+ }
25940
+ inPhase(phase, fn) {
25941
+ // Note: this doesn't delegate to `this.target.inPhase` but instead is implemented manually here
25942
+ // to avoid adding an additional frame of noise to the stack when debugging.
25943
+ const previousPhase = this.target.phase(phase);
25944
+ try {
25945
+ return fn();
25946
+ }
25947
+ finally {
25948
+ this.target.phase(previousPhase);
25949
+ }
25950
+ }
25951
+ memory(after) {
25952
+ this.target.memory(after);
25953
+ }
25954
+ reset() {
25955
+ this.target.reset();
25956
+ }
25957
+ }
25958
+
25495
25959
  /**
25496
25960
  * @license
25497
25961
  * Copyright Google LLC All Rights Reserved.
@@ -25742,6 +26206,8 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
25742
26206
  for (const priorRecord of priorWork) {
25743
26207
  this.adopt(priorRecord);
25744
26208
  }
26209
+ this.perf.eventCount(PerfEvent.SourceFileReuseAnalysis);
26210
+ this.perf.eventCount(PerfEvent.TraitReuseAnalysis, priorWork.length);
25745
26211
  // Skip the rest of analysis, as this file's prior traits are being reused.
25746
26212
  return;
25747
26213
  }
@@ -25945,6 +26411,7 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
25945
26411
  if (trait.state !== TraitState.Pending) {
25946
26412
  throw new Error(`Attempt to analyze trait of ${clazz.name.text} in state ${TraitState[trait.state]} (expected DETECTED)`);
25947
26413
  }
26414
+ this.perf.eventCount(PerfEvent.TraitAnalyze);
25948
26415
  // Attempt analysis. This could fail with a `FatalDiagnosticError`; catch it if it does.
25949
26416
  let result;
25950
26417
  try {
@@ -26083,7 +26550,6 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
26083
26550
  // Cannot compile a trait that is not resolved, or had any errors in its declaration.
26084
26551
  continue;
26085
26552
  }
26086
- const compileSpan = this.perf.start('compileClass', original);
26087
26553
  // `trait.resolution` is non-null asserted here because TypeScript does not recognize that
26088
26554
  // `Readonly<unknown>` is nullable (as `unknown` itself is nullable) due to the way that
26089
26555
  // `Readonly` works.
@@ -26097,7 +26563,6 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
26097
26563
  trait.handler.compileFull(clazz, trait.analysis, trait.resolution, constantPool);
26098
26564
  }
26099
26565
  const compileMatchRes = compileRes;
26100
- this.perf.stop(compileSpan);
26101
26566
  if (Array.isArray(compileMatchRes)) {
26102
26567
  for (const result of compileMatchRes) {
26103
26568
  if (!res.some(r => r.name === result.name)) {
@@ -26262,7 +26727,7 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
26262
26727
  this.imports = imports;
26263
26728
  this.downlevelTaggedTemplates = options.downlevelTaggedTemplates === true;
26264
26729
  this.downlevelVariableDeclarations = options.downlevelVariableDeclarations === true;
26265
- this.recordWrappedNodeExpr = options.recordWrappedNodeExpr || (() => { });
26730
+ this.recordWrappedNode = options.recordWrappedNode || (() => { });
26266
26731
  }
26267
26732
  visitDeclareVarStmt(stmt, context) {
26268
26733
  var _a;
@@ -26490,7 +26955,7 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
26490
26955
  throw new Error('Method not implemented.');
26491
26956
  }
26492
26957
  visitWrappedNodeExpr(ast, _context) {
26493
- this.recordWrappedNodeExpr(ast.node);
26958
+ this.recordWrappedNode(ast);
26494
26959
  return ast.node;
26495
26960
  }
26496
26961
  visitTypeofExpr(ast, context) {
@@ -27349,11 +27814,11 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
27349
27814
  */
27350
27815
  const NO_DECORATORS = new Set();
27351
27816
  const CLOSURE_FILE_OVERVIEW_REGEXP = /\s+@fileoverview\s+/i;
27352
- function ivyTransformFactory(compilation, reflector, importRewriter, defaultImportRecorder, isCore, isClosureCompilerEnabled) {
27353
- const recordWrappedNodeExpr = createRecorderFn(defaultImportRecorder);
27817
+ function ivyTransformFactory(compilation, reflector, importRewriter, defaultImportTracker, perf, isCore, isClosureCompilerEnabled) {
27818
+ const recordWrappedNode = createRecorderFn(defaultImportTracker);
27354
27819
  return (context) => {
27355
27820
  return (file) => {
27356
- return transformIvySourceFile(compilation, context, reflector, importRewriter, file, isCore, isClosureCompilerEnabled, recordWrappedNodeExpr);
27821
+ return perf.inPhase(PerfPhase.Compile, () => transformIvySourceFile(compilation, context, reflector, importRewriter, file, isCore, isClosureCompilerEnabled, recordWrappedNode));
27357
27822
  };
27358
27823
  };
27359
27824
  }
@@ -27384,13 +27849,13 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
27384
27849
  * compilation results (provided as an argument).
27385
27850
  */
27386
27851
  class IvyTransformationVisitor extends Visitor {
27387
- constructor(compilation, classCompilationMap, reflector, importManager, recordWrappedNodeExpr, isClosureCompilerEnabled, isCore) {
27852
+ constructor(compilation, classCompilationMap, reflector, importManager, recordWrappedNode, isClosureCompilerEnabled, isCore) {
27388
27853
  super();
27389
27854
  this.compilation = compilation;
27390
27855
  this.classCompilationMap = classCompilationMap;
27391
27856
  this.reflector = reflector;
27392
27857
  this.importManager = importManager;
27393
- this.recordWrappedNodeExpr = recordWrappedNodeExpr;
27858
+ this.recordWrappedNode = recordWrappedNode;
27394
27859
  this.isClosureCompilerEnabled = isClosureCompilerEnabled;
27395
27860
  this.isCore = isCore;
27396
27861
  }
@@ -27405,7 +27870,7 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
27405
27870
  const members = [...node.members];
27406
27871
  for (const field of this.classCompilationMap.get(node)) {
27407
27872
  // Translate the initializer for the field into TS nodes.
27408
- const exprNode = translateExpression(field.initializer, this.importManager, { recordWrappedNodeExpr: this.recordWrappedNodeExpr });
27873
+ const exprNode = translateExpression(field.initializer, this.importManager, { recordWrappedNode: this.recordWrappedNode });
27409
27874
  // Create a static property declaration for the new field.
27410
27875
  const property = ts$1.createProperty(undefined, [ts$1.createToken(ts$1.SyntaxKind.StaticKeyword)], field.name, undefined, undefined, exprNode);
27411
27876
  if (this.isClosureCompilerEnabled) {
@@ -27417,7 +27882,7 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
27417
27882
  /* hasTrailingNewLine */ false);
27418
27883
  }
27419
27884
  field.statements
27420
- .map(stmt => translateStatement(stmt, this.importManager, { recordWrappedNodeExpr: this.recordWrappedNodeExpr }))
27885
+ .map(stmt => translateStatement(stmt, this.importManager, { recordWrappedNode: this.recordWrappedNode }))
27421
27886
  .forEach(stmt => statements.push(stmt));
27422
27887
  members.push(property);
27423
27888
  }
@@ -27521,7 +27986,7 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
27521
27986
  /**
27522
27987
  * A transformer which operates on ts.SourceFiles and applies changes from an `IvyCompilation`.
27523
27988
  */
27524
- function transformIvySourceFile(compilation, context, reflector, importRewriter, file, isCore, isClosureCompilerEnabled, recordWrappedNodeExpr) {
27989
+ function transformIvySourceFile(compilation, context, reflector, importRewriter, file, isCore, isClosureCompilerEnabled, recordWrappedNode) {
27525
27990
  const constantPool = new ConstantPool(isClosureCompilerEnabled);
27526
27991
  const importManager = new ImportManager(importRewriter);
27527
27992
  // The transformation process consists of 2 steps:
@@ -27538,13 +28003,13 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
27538
28003
  visit(file, compilationVisitor, context);
27539
28004
  // Step 2. Scan through the AST again and perform transformations based on Ivy compilation
27540
28005
  // results obtained at Step 1.
27541
- const transformationVisitor = new IvyTransformationVisitor(compilation, compilationVisitor.classCompilationMap, reflector, importManager, recordWrappedNodeExpr, isClosureCompilerEnabled, isCore);
28006
+ const transformationVisitor = new IvyTransformationVisitor(compilation, compilationVisitor.classCompilationMap, reflector, importManager, recordWrappedNode, isClosureCompilerEnabled, isCore);
27542
28007
  let sf = visit(file, transformationVisitor, context);
27543
28008
  // Generate the constant statements first, as they may involve adding additional imports
27544
28009
  // to the ImportManager.
27545
28010
  const downlevelTranslatedCode = getLocalizeCompileTarget(context) < ts$1.ScriptTarget.ES2015;
27546
28011
  const constants = constantPool.statements.map(stmt => translateStatement(stmt, importManager, {
27547
- recordWrappedNodeExpr,
28012
+ recordWrappedNode,
27548
28013
  downlevelTaggedTemplates: downlevelTranslatedCode,
27549
28014
  downlevelVariableDeclarations: downlevelTranslatedCode,
27550
28015
  }));
@@ -27618,10 +28083,11 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
27618
28083
  function isFromAngularCore(decorator) {
27619
28084
  return decorator.import !== null && decorator.import.from === '@angular/core';
27620
28085
  }
27621
- function createRecorderFn(defaultImportRecorder) {
27622
- return expr => {
27623
- if (ts$1.isIdentifier(expr)) {
27624
- defaultImportRecorder.recordUsedIdentifier(expr);
28086
+ function createRecorderFn(defaultImportTracker) {
28087
+ return node => {
28088
+ const importDecl = getDefaultImportDeclaration(node);
28089
+ if (importDecl !== null) {
28090
+ defaultImportTracker.recordUsedImport(importDecl);
27625
28091
  }
27626
28092
  };
27627
28093
  }
@@ -27716,7 +28182,7 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
27716
28182
  * Use of this source code is governed by an MIT-style license that can be
27717
28183
  * found in the LICENSE file at https://angular.io/license
27718
28184
  */
27719
- function getConstructorDependencies(clazz, reflector, defaultImportRecorder, isCore) {
28185
+ function getConstructorDependencies(clazz, reflector, isCore) {
27720
28186
  const deps = [];
27721
28187
  const errors = [];
27722
28188
  let ctorParams = reflector.getConstructorParameters(clazz);
@@ -27729,7 +28195,7 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
27729
28195
  }
27730
28196
  }
27731
28197
  ctorParams.forEach((param, idx) => {
27732
- let token = valueReferenceToExpression(param.typeValueReference, defaultImportRecorder);
28198
+ let token = valueReferenceToExpression(param.typeValueReference);
27733
28199
  let attribute = null;
27734
28200
  let optional = false, self = false, skipSelf = false, host = false;
27735
28201
  let resolved = R3ResolvedDependencyType.Token;
@@ -27796,16 +28262,16 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
27796
28262
  return { deps: null, errors };
27797
28263
  }
27798
28264
  }
27799
- function valueReferenceToExpression(valueRef, defaultImportRecorder) {
28265
+ function valueReferenceToExpression(valueRef) {
27800
28266
  if (valueRef.kind === 2 /* UNAVAILABLE */) {
27801
28267
  return null;
27802
28268
  }
27803
28269
  else if (valueRef.kind === 0 /* LOCAL */) {
27804
- if (defaultImportRecorder !== null && valueRef.defaultImportStatement !== null &&
27805
- ts$1.isIdentifier(valueRef.expression)) {
27806
- defaultImportRecorder.recordImportedIdentifier(valueRef.expression, valueRef.defaultImportStatement);
28270
+ const expr = new WrappedNodeExpr(valueRef.expression);
28271
+ if (valueRef.defaultImportStatement !== null) {
28272
+ attachDefaultImportDeclaration(expr, valueRef.defaultImportStatement);
27807
28273
  }
27808
- return new WrappedNodeExpr(valueRef.expression);
28274
+ return expr;
27809
28275
  }
27810
28276
  else {
27811
28277
  let importExpr = new ExternalExpr({ moduleName: valueRef.moduleName, name: valueRef.importedName });
@@ -27836,8 +28302,8 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
27836
28302
  return 'invalid';
27837
28303
  }
27838
28304
  }
27839
- function getValidConstructorDependencies(clazz, reflector, defaultImportRecorder, isCore) {
27840
- return validateConstructorDependencies(clazz, getConstructorDependencies(clazz, reflector, defaultImportRecorder, isCore));
28305
+ function getValidConstructorDependencies(clazz, reflector, isCore) {
28306
+ return validateConstructorDependencies(clazz, getConstructorDependencies(clazz, reflector, isCore));
27841
28307
  }
27842
28308
  /**
27843
28309
  * Validate that `ConstructorDeps` does not have any invalid dependencies and convert them into the
@@ -28345,7 +28811,7 @@ Either add the @Injectable() decorator to '${provider.node.name
28345
28811
  * If no such metadata is present, this function returns `null`. Otherwise, the call is returned
28346
28812
  * as a `Statement` for inclusion along with the class.
28347
28813
  */
28348
- function generateSetClassMetadataCall(clazz, reflection, defaultImportRecorder, isCore, annotateForClosureCompiler) {
28814
+ function generateSetClassMetadataCall(clazz, reflection, isCore, annotateForClosureCompiler) {
28349
28815
  if (!reflection.isClass(clazz)) {
28350
28816
  return null;
28351
28817
  }
@@ -28372,7 +28838,7 @@ Either add the @Injectable() decorator to '${provider.node.name
28372
28838
  let metaCtorParameters = new LiteralExpr(null);
28373
28839
  const classCtorParameters = reflection.getConstructorParameters(clazz);
28374
28840
  if (classCtorParameters !== null) {
28375
- const ctorParameters = classCtorParameters.map(param => ctorParameterToMetadata(param, defaultImportRecorder, isCore));
28841
+ const ctorParameters = classCtorParameters.map(param => ctorParameterToMetadata(param, isCore));
28376
28842
  metaCtorParameters = new FunctionExpr([], [
28377
28843
  new ReturnStatement(new LiteralArrayExpr(ctorParameters)),
28378
28844
  ]);
@@ -28409,11 +28875,11 @@ Either add the @Injectable() decorator to '${provider.node.name
28409
28875
  /**
28410
28876
  * Convert a reflected constructor parameter to metadata.
28411
28877
  */
28412
- function ctorParameterToMetadata(param, defaultImportRecorder, isCore) {
28878
+ function ctorParameterToMetadata(param, isCore) {
28413
28879
  // Parameters sometimes have a type that can be referenced. If so, then use it, otherwise
28414
28880
  // its type is undefined.
28415
28881
  const type = param.typeValueReference.kind !== 2 /* UNAVAILABLE */ ?
28416
- valueReferenceToExpression(param.typeValueReference, defaultImportRecorder) :
28882
+ valueReferenceToExpression(param.typeValueReference) :
28417
28883
  new LiteralExpr(undefined);
28418
28884
  const mapEntries = [
28419
28885
  { key: 'type', value: type, quoted: false },
@@ -28600,18 +29066,18 @@ Either add the @Injectable() decorator to '${provider.node.name
28600
29066
  return isSymbolEqual(current, previous);
28601
29067
  }
28602
29068
  class DirectiveDecoratorHandler {
28603
- constructor(reflector, evaluator, metaRegistry, scopeRegistry, metaReader, defaultImportRecorder, injectableRegistry, isCore, semanticDepGraphUpdater, annotateForClosureCompiler, compileUndecoratedClassesWithAngularFeatures) {
29069
+ constructor(reflector, evaluator, metaRegistry, scopeRegistry, metaReader, injectableRegistry, isCore, semanticDepGraphUpdater, annotateForClosureCompiler, compileUndecoratedClassesWithAngularFeatures, perf) {
28604
29070
  this.reflector = reflector;
28605
29071
  this.evaluator = evaluator;
28606
29072
  this.metaRegistry = metaRegistry;
28607
29073
  this.scopeRegistry = scopeRegistry;
28608
29074
  this.metaReader = metaReader;
28609
- this.defaultImportRecorder = defaultImportRecorder;
28610
29075
  this.injectableRegistry = injectableRegistry;
28611
29076
  this.isCore = isCore;
28612
29077
  this.semanticDepGraphUpdater = semanticDepGraphUpdater;
28613
29078
  this.annotateForClosureCompiler = annotateForClosureCompiler;
28614
29079
  this.compileUndecoratedClassesWithAngularFeatures = compileUndecoratedClassesWithAngularFeatures;
29080
+ this.perf = perf;
28615
29081
  this.precedence = HandlerPrecedence.PRIMARY;
28616
29082
  this.name = DirectiveDecoratorHandler.name;
28617
29083
  }
@@ -28638,7 +29104,8 @@ Either add the @Injectable() decorator to '${provider.node.name
28638
29104
  if (this.compileUndecoratedClassesWithAngularFeatures === false && decorator === null) {
28639
29105
  return { diagnostics: [getUndecoratedClassWithAngularFeaturesDiagnostic(node)] };
28640
29106
  }
28641
- const directiveResult = extractDirectiveMetadata(node, decorator, this.reflector, this.evaluator, this.defaultImportRecorder, this.isCore, flags, this.annotateForClosureCompiler);
29107
+ this.perf.eventCount(PerfEvent.AnalyzeDirective);
29108
+ const directiveResult = extractDirectiveMetadata(node, decorator, this.reflector, this.evaluator, this.isCore, flags, this.annotateForClosureCompiler);
28642
29109
  if (directiveResult === undefined) {
28643
29110
  return {};
28644
29111
  }
@@ -28652,7 +29119,7 @@ Either add the @Injectable() decorator to '${provider.node.name
28652
29119
  inputs: directiveResult.inputs,
28653
29120
  outputs: directiveResult.outputs,
28654
29121
  meta: analysis,
28655
- metadataStmt: generateSetClassMetadataCall(node, this.reflector, this.defaultImportRecorder, this.isCore, this.annotateForClosureCompiler),
29122
+ metadataStmt: generateSetClassMetadataCall(node, this.reflector, this.isCore, this.annotateForClosureCompiler),
28656
29123
  baseClass: readBaseClass$1(node, this.reflector, this.evaluator),
28657
29124
  typeCheckMeta: extractDirectiveTypeCheckMeta(node, directiveResult.inputs, this.reflector),
28658
29125
  providersRequiringFactory,
@@ -28735,7 +29202,7 @@ Either add the @Injectable() decorator to '${provider.node.name
28735
29202
  * appear in the declarations of an `NgModule` and additional verification is done when processing
28736
29203
  * the module.
28737
29204
  */
28738
- function extractDirectiveMetadata(clazz, decorator, reflector, evaluator, defaultImportRecorder, isCore, flags, annotateForClosureCompiler, defaultSelector = null) {
29205
+ function extractDirectiveMetadata(clazz, decorator, reflector, evaluator, isCore, flags, annotateForClosureCompiler, defaultSelector = null) {
28739
29206
  let directive;
28740
29207
  if (decorator === null || decorator.args === null || decorator.args.length === 0) {
28741
29208
  directive = new Map();
@@ -28813,7 +29280,7 @@ Either add the @Injectable() decorator to '${provider.node.name
28813
29280
  }
28814
29281
  exportAs = resolved.split(',').map(part => part.trim());
28815
29282
  }
28816
- const rawCtorDeps = getConstructorDependencies(clazz, reflector, defaultImportRecorder, isCore);
29283
+ const rawCtorDeps = getConstructorDependencies(clazz, reflector, isCore);
28817
29284
  let ctorDeps;
28818
29285
  // Non-abstract directives (those with a selector) require valid constructor dependencies, whereas
28819
29286
  // abstract directives are allowed to have invalid dependencies, given that a subclass may call
@@ -29235,7 +29702,7 @@ Either add the @Injectable() decorator to '${provider.node.name
29235
29702
  * Compiles @NgModule annotations to ngModuleDef fields.
29236
29703
  */
29237
29704
  class NgModuleDecoratorHandler {
29238
- constructor(reflector, evaluator, metaReader, metaRegistry, scopeRegistry, referencesRegistry, isCore, routeAnalyzer, refEmitter, factoryTracker, defaultImportRecorder, annotateForClosureCompiler, injectableRegistry, localeId) {
29705
+ constructor(reflector, evaluator, metaReader, metaRegistry, scopeRegistry, referencesRegistry, isCore, routeAnalyzer, refEmitter, factoryTracker, annotateForClosureCompiler, injectableRegistry, perf, localeId) {
29239
29706
  this.reflector = reflector;
29240
29707
  this.evaluator = evaluator;
29241
29708
  this.metaReader = metaReader;
@@ -29246,9 +29713,9 @@ Either add the @Injectable() decorator to '${provider.node.name
29246
29713
  this.routeAnalyzer = routeAnalyzer;
29247
29714
  this.refEmitter = refEmitter;
29248
29715
  this.factoryTracker = factoryTracker;
29249
- this.defaultImportRecorder = defaultImportRecorder;
29250
29716
  this.annotateForClosureCompiler = annotateForClosureCompiler;
29251
29717
  this.injectableRegistry = injectableRegistry;
29718
+ this.perf = perf;
29252
29719
  this.localeId = localeId;
29253
29720
  this.precedence = HandlerPrecedence.PRIMARY;
29254
29721
  this.name = NgModuleDecoratorHandler.name;
@@ -29270,6 +29737,7 @@ Either add the @Injectable() decorator to '${provider.node.name
29270
29737
  }
29271
29738
  }
29272
29739
  analyze(node, decorator) {
29740
+ this.perf.eventCount(PerfEvent.AnalyzeNgModule);
29273
29741
  const name = node.name.text;
29274
29742
  if (decorator.args === null || decorator.args.length > 1) {
29275
29743
  throw new FatalDiagnosticError(ErrorCode.DECORATOR_ARITY_WRONG, Decorator.nodeForError(decorator), `Incorrect number of arguments to @NgModule decorator`);
@@ -29422,7 +29890,7 @@ Either add the @Injectable() decorator to '${provider.node.name
29422
29890
  schemas: schemas,
29423
29891
  mod: ngModuleDef,
29424
29892
  inj: ngInjectorDef,
29425
- deps: getValidConstructorDependencies(node, this.reflector, this.defaultImportRecorder, this.isCore),
29893
+ deps: getValidConstructorDependencies(node, this.reflector, this.isCore),
29426
29894
  declarations: declarationRefs,
29427
29895
  rawDeclarations,
29428
29896
  imports: importRefs,
@@ -29431,7 +29899,7 @@ Either add the @Injectable() decorator to '${provider.node.name
29431
29899
  providersRequiringFactory: rawProviders ?
29432
29900
  resolveProvidersRequiringFactory(rawProviders, this.reflector, this.evaluator) :
29433
29901
  null,
29434
- metadataStmt: generateSetClassMetadataCall(node, this.reflector, this.defaultImportRecorder, this.isCore, this.annotateForClosureCompiler),
29902
+ metadataStmt: generateSetClassMetadataCall(node, this.reflector, this.isCore, this.annotateForClosureCompiler),
29435
29903
  factorySymbolName: node.name.text,
29436
29904
  },
29437
29905
  };
@@ -29762,7 +30230,7 @@ Either add the @Injectable() decorator to '${provider.node.name
29762
30230
  * `DecoratorHandler` which handles the `@Component` annotation.
29763
30231
  */
29764
30232
  class ComponentDecoratorHandler {
29765
- 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) {
30233
+ constructor(reflector, evaluator, metaRegistry, metaReader, scopeReader, scopeRegistry, typeCheckScopeRegistry, resourceRegistry, isCore, resourceLoader, rootDirs, defaultPreserveWhitespaces, i18nUseExternalIds, enableI18nLegacyMessageIdFormat, usePoisonedData, i18nNormalizeLineEndingsInICUs, moduleResolver, cycleAnalyzer, cycleHandlingStrategy, refEmitter, depTracker, injectableRegistry, semanticDepGraphUpdater, annotateForClosureCompiler, perf) {
29766
30234
  this.reflector = reflector;
29767
30235
  this.evaluator = evaluator;
29768
30236
  this.metaRegistry = metaRegistry;
@@ -29783,11 +30251,11 @@ Either add the @Injectable() decorator to '${provider.node.name
29783
30251
  this.cycleAnalyzer = cycleAnalyzer;
29784
30252
  this.cycleHandlingStrategy = cycleHandlingStrategy;
29785
30253
  this.refEmitter = refEmitter;
29786
- this.defaultImportRecorder = defaultImportRecorder;
29787
30254
  this.depTracker = depTracker;
29788
30255
  this.injectableRegistry = injectableRegistry;
29789
30256
  this.semanticDepGraphUpdater = semanticDepGraphUpdater;
29790
30257
  this.annotateForClosureCompiler = annotateForClosureCompiler;
30258
+ this.perf = perf;
29791
30259
  this.literalCache = new Map();
29792
30260
  this.elementSchemaRegistry = new DomElementSchemaRegistry();
29793
30261
  /**
@@ -29833,9 +30301,16 @@ Either add the @Injectable() decorator to '${provider.node.name
29833
30301
  const meta = this._resolveLiteral(decorator);
29834
30302
  const component = reflectObjectLiteral(meta);
29835
30303
  const containingFile = node.getSourceFile().fileName;
29836
- const resolveStyleUrl = (styleUrl, nodeForError, resourceType) => {
29837
- const resourceUrl = this._resolveResourceOrThrow(styleUrl, containingFile, nodeForError, resourceType);
29838
- return this.resourceLoader.preload(resourceUrl);
30304
+ const resolveStyleUrl = (styleUrl) => {
30305
+ try {
30306
+ const resourceUrl = this.resourceLoader.resolve(styleUrl, containingFile);
30307
+ return this.resourceLoader.preload(resourceUrl);
30308
+ }
30309
+ catch (_a) {
30310
+ // Don't worry about failures to preload. We can handle this problem during analysis by
30311
+ // producing a diagnostic.
30312
+ return undefined;
30313
+ }
29839
30314
  };
29840
30315
  // A Promise that waits for the template and all <link>ed styles within it to be preloaded.
29841
30316
  const templateAndTemplateStyleResources = this._preloadAndParseTemplate(node, decorator, component, containingFile)
@@ -29843,9 +30318,7 @@ Either add the @Injectable() decorator to '${provider.node.name
29843
30318
  if (template === null) {
29844
30319
  return undefined;
29845
30320
  }
29846
- const nodeForError = getTemplateDeclarationNodeForError(template.declaration);
29847
- return Promise
29848
- .all(template.styleUrls.map(styleUrl => resolveStyleUrl(styleUrl, nodeForError, 1 /* StylesheetFromTemplate */)))
30321
+ return Promise.all(template.styleUrls.map(styleUrl => resolveStyleUrl(styleUrl)))
29849
30322
  .then(() => undefined);
29850
30323
  });
29851
30324
  // Extract all the styleUrls in the decorator.
@@ -29860,18 +30333,21 @@ Either add the @Injectable() decorator to '${provider.node.name
29860
30333
  return Promise
29861
30334
  .all([
29862
30335
  templateAndTemplateStyleResources,
29863
- ...componentStyleUrls.map(styleUrl => resolveStyleUrl(styleUrl.url, styleUrl.nodeForError, 2 /* StylesheetFromDecorator */))
30336
+ ...componentStyleUrls.map(styleUrl => resolveStyleUrl(styleUrl.url))
29864
30337
  ])
29865
30338
  .then(() => undefined);
29866
30339
  }
29867
30340
  }
29868
30341
  analyze(node, decorator, flags = HandlerFlags.NONE) {
29869
30342
  var _a;
30343
+ this.perf.eventCount(PerfEvent.AnalyzeComponent);
29870
30344
  const containingFile = node.getSourceFile().fileName;
29871
30345
  this.literalCache.delete(decorator);
30346
+ let diagnostics;
30347
+ let isPoisoned = false;
29872
30348
  // @Component inherits @Directive, so begin by extracting the @Directive metadata and building
29873
30349
  // on it.
29874
- const directiveResult = extractDirectiveMetadata(node, decorator, this.reflector, this.evaluator, this.defaultImportRecorder, this.isCore, flags, this.annotateForClosureCompiler, this.elementSchemaRegistry.getDefaultComponentElementName());
30350
+ const directiveResult = extractDirectiveMetadata(node, decorator, this.reflector, this.evaluator, this.isCore, flags, this.annotateForClosureCompiler, this.elementSchemaRegistry.getDefaultComponentElementName());
29875
30351
  if (directiveResult === undefined) {
29876
30352
  // `extractDirectiveMetadata` returns undefined when the @Directive has `jit: true`. In this
29877
30353
  // case, compilation of the decorator is skipped. Returning an empty object signifies
@@ -29936,14 +30412,23 @@ Either add the @Injectable() decorator to '${provider.node.name
29936
30412
  ...this._extractComponentStyleUrls(component), ...this._extractTemplateStyleUrls(template)
29937
30413
  ];
29938
30414
  for (const styleUrl of styleUrls) {
29939
- const resourceType = styleUrl.source === 2 /* StylesheetFromDecorator */ ?
29940
- 2 /* StylesheetFromDecorator */ :
29941
- 1 /* StylesheetFromTemplate */;
29942
- const resourceUrl = this._resolveResourceOrThrow(styleUrl.url, containingFile, styleUrl.nodeForError, resourceType);
29943
- const resourceStr = this.resourceLoader.load(resourceUrl);
29944
- styles.push(resourceStr);
29945
- if (this.depTracker !== null) {
29946
- this.depTracker.addResourceDependency(node.getSourceFile(), absoluteFrom(resourceUrl));
30415
+ try {
30416
+ const resourceUrl = this.resourceLoader.resolve(styleUrl.url, containingFile);
30417
+ const resourceStr = this.resourceLoader.load(resourceUrl);
30418
+ styles.push(resourceStr);
30419
+ if (this.depTracker !== null) {
30420
+ this.depTracker.addResourceDependency(node.getSourceFile(), absoluteFrom(resourceUrl));
30421
+ }
30422
+ }
30423
+ catch (_b) {
30424
+ if (diagnostics === undefined) {
30425
+ diagnostics = [];
30426
+ }
30427
+ const resourceType = styleUrl.source === 2 /* StylesheetFromDecorator */ ?
30428
+ 2 /* StylesheetFromDecorator */ :
30429
+ 1 /* StylesheetFromTemplate */;
30430
+ diagnostics.push(this.makeResourceNotFoundError(styleUrl.url, styleUrl.nodeForError, resourceType)
30431
+ .toDiagnostic());
29947
30432
  }
29948
30433
  }
29949
30434
  let inlineStyles = null;
@@ -29976,7 +30461,7 @@ Either add the @Injectable() decorator to '${provider.node.name
29976
30461
  // analyzed and the full compilation scope for the component can be realized.
29977
30462
  animations, viewProviders: wrappedViewProviders, i18nUseExternalIds: this.i18nUseExternalIds, relativeContextFilePath }),
29978
30463
  typeCheckMeta: extractDirectiveTypeCheckMeta(node, inputs, this.reflector),
29979
- metadataStmt: generateSetClassMetadataCall(node, this.reflector, this.defaultImportRecorder, this.isCore, this.annotateForClosureCompiler),
30464
+ metadataStmt: generateSetClassMetadataCall(node, this.reflector, this.isCore, this.annotateForClosureCompiler),
29980
30465
  template,
29981
30466
  providersRequiringFactory,
29982
30467
  viewProvidersRequiringFactory,
@@ -29986,8 +30471,9 @@ Either add the @Injectable() decorator to '${provider.node.name
29986
30471
  styles: styleResources,
29987
30472
  template: templateResource,
29988
30473
  },
29989
- isPoisoned: false,
30474
+ isPoisoned,
29990
30475
  },
30476
+ diagnostics,
29991
30477
  };
29992
30478
  if (changeDetection !== null) {
29993
30479
  output.analysis.meta.changeDetection = changeDetection;
@@ -30216,12 +30702,15 @@ Either add the @Injectable() decorator to '${provider.node.name
30216
30702
  let styles = [];
30217
30703
  if (analysis.styleUrls !== null) {
30218
30704
  for (const styleUrl of analysis.styleUrls) {
30219
- const resourceType = styleUrl.source === 2 /* StylesheetFromDecorator */ ?
30220
- 2 /* StylesheetFromDecorator */ :
30221
- 1 /* StylesheetFromTemplate */;
30222
- const resolvedStyleUrl = this._resolveResourceOrThrow(styleUrl.url, containingFile, styleUrl.nodeForError, resourceType);
30223
- const styleText = this.resourceLoader.load(resolvedStyleUrl);
30224
- styles.push(styleText);
30705
+ try {
30706
+ const resolvedStyleUrl = this.resourceLoader.resolve(styleUrl.url, containingFile);
30707
+ const styleText = this.resourceLoader.load(resolvedStyleUrl);
30708
+ styles.push(styleText);
30709
+ }
30710
+ catch (e) {
30711
+ // Resource resolve failures should already be in the diagnostics list from the analyze
30712
+ // stage. We do not need to do anything with them when updating resources.
30713
+ }
30225
30714
  }
30226
30715
  }
30227
30716
  if (analysis.inlineStyles !== null) {
@@ -30343,8 +30832,15 @@ Either add the @Injectable() decorator to '${provider.node.name
30343
30832
  const styleUrlsExpr = component.get('styleUrls');
30344
30833
  if (styleUrlsExpr !== undefined && ts$1.isArrayLiteralExpression(styleUrlsExpr)) {
30345
30834
  for (const expression of stringLiteralElements(styleUrlsExpr)) {
30346
- const resourceUrl = this._resolveResourceOrThrow(expression.text, containingFile, expression, 2 /* StylesheetFromDecorator */);
30347
- styles.add({ path: absoluteFrom(resourceUrl), expression });
30835
+ try {
30836
+ const resourceUrl = this.resourceLoader.resolve(expression.text, containingFile);
30837
+ styles.add({ path: absoluteFrom(resourceUrl), expression });
30838
+ }
30839
+ catch (_a) {
30840
+ // Errors in style resource extraction do not need to be handled here. We will produce
30841
+ // diagnostics for each one that fails in the analysis, after we evaluate the `styleUrls`
30842
+ // expression to determine _all_ style resources, not just the string literals.
30843
+ }
30348
30844
  }
30349
30845
  }
30350
30846
  const stylesExpr = component.get('styles');
@@ -30363,20 +30859,25 @@ Either add the @Injectable() decorator to '${provider.node.name
30363
30859
  if (typeof templateUrl !== 'string') {
30364
30860
  throw createValueHasWrongTypeError(templateUrlExpr, templateUrl, 'templateUrl must be a string');
30365
30861
  }
30366
- const resourceUrl = this._resolveResourceOrThrow(templateUrl, containingFile, templateUrlExpr, 0 /* Template */);
30367
- const templatePromise = this.resourceLoader.preload(resourceUrl);
30368
- // If the preload worked, then actually load and parse the template, and wait for any style
30369
- // URLs to resolve.
30370
- if (templatePromise !== undefined) {
30371
- return templatePromise.then(() => {
30372
- const templateDecl = this.parseTemplateDeclaration(decorator, component, containingFile);
30373
- const template = this.extractTemplate(node, templateDecl);
30374
- this.preanalyzeTemplateCache.set(node, template);
30375
- return template;
30376
- });
30862
+ try {
30863
+ const resourceUrl = this.resourceLoader.resolve(templateUrl, containingFile);
30864
+ const templatePromise = this.resourceLoader.preload(resourceUrl);
30865
+ // If the preload worked, then actually load and parse the template, and wait for any style
30866
+ // URLs to resolve.
30867
+ if (templatePromise !== undefined) {
30868
+ return templatePromise.then(() => {
30869
+ const templateDecl = this.parseTemplateDeclaration(decorator, component, containingFile);
30870
+ const template = this.extractTemplate(node, templateDecl);
30871
+ this.preanalyzeTemplateCache.set(node, template);
30872
+ return template;
30873
+ });
30874
+ }
30875
+ else {
30876
+ return Promise.resolve(null);
30877
+ }
30377
30878
  }
30378
- else {
30379
- return Promise.resolve(null);
30879
+ catch (e) {
30880
+ throw this.makeResourceNotFoundError(templateUrl, templateUrlExpr, 0 /* Template */);
30380
30881
  }
30381
30882
  }
30382
30883
  else {
@@ -30508,16 +31009,21 @@ Either add the @Injectable() decorator to '${provider.node.name
30508
31009
  if (typeof templateUrl !== 'string') {
30509
31010
  throw createValueHasWrongTypeError(templateUrlExpr, templateUrl, 'templateUrl must be a string');
30510
31011
  }
30511
- const resourceUrl = this._resolveResourceOrThrow(templateUrl, containingFile, templateUrlExpr, 0 /* Template */);
30512
- return {
30513
- isInline: false,
30514
- interpolationConfig,
30515
- preserveWhitespaces,
30516
- templateUrl,
30517
- templateUrlExpression: templateUrlExpr,
30518
- resolvedTemplateUrl: resourceUrl,
30519
- sourceMapUrl: sourceMapUrl(resourceUrl),
30520
- };
31012
+ try {
31013
+ const resourceUrl = this.resourceLoader.resolve(templateUrl, containingFile);
31014
+ return {
31015
+ isInline: false,
31016
+ interpolationConfig,
31017
+ preserveWhitespaces,
31018
+ templateUrl,
31019
+ templateUrlExpression: templateUrlExpr,
31020
+ resolvedTemplateUrl: resourceUrl,
31021
+ sourceMapUrl: sourceMapUrl(resourceUrl),
31022
+ };
31023
+ }
31024
+ catch (e) {
31025
+ throw this.makeResourceNotFoundError(templateUrl, templateUrlExpr, 0 /* Template */);
31026
+ }
30521
31027
  }
30522
31028
  else if (component.has('template')) {
30523
31029
  return {
@@ -30570,30 +31076,20 @@ Either add the @Injectable() decorator to '${provider.node.name
30570
31076
  }
30571
31077
  this.cycleAnalyzer.recordSyntheticImport(origin, imported);
30572
31078
  }
30573
- /**
30574
- * Resolve the url of a resource relative to the file that contains the reference to it.
30575
- *
30576
- * Throws a FatalDiagnosticError when unable to resolve the file.
30577
- */
30578
- _resolveResourceOrThrow(file, basePath, nodeForError, resourceType) {
30579
- try {
30580
- return this.resourceLoader.resolve(file, basePath);
30581
- }
30582
- catch (e) {
30583
- let errorText;
30584
- switch (resourceType) {
30585
- case 0 /* Template */:
30586
- errorText = `Could not find template file '${file}'.`;
30587
- break;
30588
- case 1 /* StylesheetFromTemplate */:
30589
- errorText = `Could not find stylesheet file '${file}' linked from the template.`;
30590
- break;
30591
- case 2 /* StylesheetFromDecorator */:
30592
- errorText = `Could not find stylesheet file '${file}'.`;
30593
- break;
30594
- }
30595
- throw new FatalDiagnosticError(ErrorCode.COMPONENT_RESOURCE_NOT_FOUND, nodeForError, errorText);
31079
+ makeResourceNotFoundError(file, nodeForError, resourceType) {
31080
+ let errorText;
31081
+ switch (resourceType) {
31082
+ case 0 /* Template */:
31083
+ errorText = `Could not find template file '${file}'.`;
31084
+ break;
31085
+ case 1 /* StylesheetFromTemplate */:
31086
+ errorText = `Could not find stylesheet file '${file}' linked from the template.`;
31087
+ break;
31088
+ case 2 /* StylesheetFromDecorator */:
31089
+ errorText = `Could not find stylesheet file '${file}'.`;
31090
+ break;
30596
31091
  }
31092
+ return new FatalDiagnosticError(ErrorCode.COMPONENT_RESOURCE_NOT_FOUND, nodeForError, errorText);
30597
31093
  }
30598
31094
  _extractTemplateStyleUrls(template) {
30599
31095
  if (template.styleUrls === null) {
@@ -30661,7 +31157,7 @@ Either add the @Injectable() decorator to '${provider.node.name
30661
31157
  * Adapts the `compileIvyInjectable` compiler for `@Injectable` decorators to the Ivy compiler.
30662
31158
  */
30663
31159
  class InjectableDecoratorHandler {
30664
- constructor(reflector, defaultImportRecorder, isCore, strictCtorDeps, injectableRegistry,
31160
+ constructor(reflector, isCore, strictCtorDeps, injectableRegistry, perf,
30665
31161
  /**
30666
31162
  * What to do if the injectable already contains a ɵprov property.
30667
31163
  *
@@ -30670,10 +31166,10 @@ Either add the @Injectable() decorator to '${provider.node.name
30670
31166
  */
30671
31167
  errorOnDuplicateProv = true) {
30672
31168
  this.reflector = reflector;
30673
- this.defaultImportRecorder = defaultImportRecorder;
30674
31169
  this.isCore = isCore;
30675
31170
  this.strictCtorDeps = strictCtorDeps;
30676
31171
  this.injectableRegistry = injectableRegistry;
31172
+ this.perf = perf;
30677
31173
  this.errorOnDuplicateProv = errorOnDuplicateProv;
30678
31174
  this.precedence = HandlerPrecedence.SHARED;
30679
31175
  this.name = InjectableDecoratorHandler.name;
@@ -30695,13 +31191,14 @@ Either add the @Injectable() decorator to '${provider.node.name
30695
31191
  }
30696
31192
  }
30697
31193
  analyze(node, decorator) {
31194
+ this.perf.eventCount(PerfEvent.AnalyzeInjectable);
30698
31195
  const meta = extractInjectableMetadata(node, decorator, this.reflector);
30699
31196
  const decorators = this.reflector.getDecoratorsOfDeclaration(node);
30700
31197
  return {
30701
31198
  analysis: {
30702
31199
  meta,
30703
- ctorDeps: extractInjectableCtorDeps(node, meta, decorator, this.reflector, this.defaultImportRecorder, this.isCore, this.strictCtorDeps),
30704
- metadataStmt: generateSetClassMetadataCall(node, this.reflector, this.defaultImportRecorder, this.isCore),
31200
+ ctorDeps: extractInjectableCtorDeps(node, meta, decorator, this.reflector, this.isCore, this.strictCtorDeps),
31201
+ metadataStmt: generateSetClassMetadataCall(node, this.reflector, this.isCore),
30705
31202
  // Avoid generating multiple factories if a class has
30706
31203
  // more Angular decorators, apart from Injectable.
30707
31204
  needsFactory: !decorators ||
@@ -30843,7 +31340,7 @@ Either add the @Injectable() decorator to '${provider.node.name
30843
31340
  throw new FatalDiagnosticError(ErrorCode.DECORATOR_ARITY_WRONG, decorator.args[2], 'Too many arguments to @Injectable');
30844
31341
  }
30845
31342
  }
30846
- function extractInjectableCtorDeps(clazz, meta, decorator, reflector, defaultImportRecorder, isCore, strictCtorDeps) {
31343
+ function extractInjectableCtorDeps(clazz, meta, decorator, reflector, isCore, strictCtorDeps) {
30847
31344
  if (decorator.args === null) {
30848
31345
  throw new FatalDiagnosticError(ErrorCode.DECORATOR_NOT_CALLED, Decorator.nodeForError(decorator), '@Injectable must be called');
30849
31346
  }
@@ -30858,15 +31355,16 @@ Either add the @Injectable() decorator to '${provider.node.name
30858
31355
  // constructor signature does not work for DI then a factory definition (ɵfac) that throws is
30859
31356
  // generated.
30860
31357
  if (strictCtorDeps) {
30861
- ctorDeps = getValidConstructorDependencies(clazz, reflector, defaultImportRecorder, isCore);
31358
+ ctorDeps = getValidConstructorDependencies(clazz, reflector, isCore);
30862
31359
  }
30863
31360
  else {
30864
- ctorDeps = unwrapConstructorDependencies(getConstructorDependencies(clazz, reflector, defaultImportRecorder, isCore));
31361
+ ctorDeps =
31362
+ unwrapConstructorDependencies(getConstructorDependencies(clazz, reflector, isCore));
30865
31363
  }
30866
31364
  return ctorDeps;
30867
31365
  }
30868
31366
  else if (decorator.args.length === 1) {
30869
- const rawCtorDeps = getConstructorDependencies(clazz, reflector, defaultImportRecorder, isCore);
31367
+ const rawCtorDeps = getConstructorDependencies(clazz, reflector, isCore);
30870
31368
  if (strictCtorDeps && meta.useValue === undefined && meta.useExisting === undefined &&
30871
31369
  meta.useClass === undefined && meta.useFactory === undefined) {
30872
31370
  // Since use* was not provided, validate the deps according to strictCtorDeps.
@@ -30950,14 +31448,14 @@ Either add the @Injectable() decorator to '${provider.node.name
30950
31448
  }
30951
31449
  }
30952
31450
  class PipeDecoratorHandler {
30953
- constructor(reflector, evaluator, metaRegistry, scopeRegistry, defaultImportRecorder, injectableRegistry, isCore) {
31451
+ constructor(reflector, evaluator, metaRegistry, scopeRegistry, injectableRegistry, isCore, perf) {
30954
31452
  this.reflector = reflector;
30955
31453
  this.evaluator = evaluator;
30956
31454
  this.metaRegistry = metaRegistry;
30957
31455
  this.scopeRegistry = scopeRegistry;
30958
- this.defaultImportRecorder = defaultImportRecorder;
30959
31456
  this.injectableRegistry = injectableRegistry;
30960
31457
  this.isCore = isCore;
31458
+ this.perf = perf;
30961
31459
  this.precedence = HandlerPrecedence.PRIMARY;
30962
31460
  this.name = PipeDecoratorHandler.name;
30963
31461
  }
@@ -30978,6 +31476,7 @@ Either add the @Injectable() decorator to '${provider.node.name
30978
31476
  }
30979
31477
  }
30980
31478
  analyze(clazz, decorator) {
31479
+ this.perf.eventCount(PerfEvent.AnalyzePipe);
30981
31480
  const name = clazz.name.text;
30982
31481
  const type = wrapTypeReference(this.reflector, clazz);
30983
31482
  const internalType = new WrappedNodeExpr(this.reflector.getInternalNameOfClass(clazz));
@@ -31017,10 +31516,10 @@ Either add the @Injectable() decorator to '${provider.node.name
31017
31516
  internalType,
31018
31517
  typeArgumentCount: this.reflector.getGenericArityOfClass(clazz) || 0,
31019
31518
  pipeName,
31020
- deps: getValidConstructorDependencies(clazz, this.reflector, this.defaultImportRecorder, this.isCore),
31519
+ deps: getValidConstructorDependencies(clazz, this.reflector, this.isCore),
31021
31520
  pure,
31022
31521
  },
31023
- metadataStmt: generateSetClassMetadataCall(clazz, this.reflector, this.defaultImportRecorder, this.isCore),
31522
+ metadataStmt: generateSetClassMetadataCall(clazz, this.reflector, this.isCore),
31024
31523
  },
31025
31524
  };
31026
31525
  }
@@ -31156,8 +31655,9 @@ Either add the @Injectable() decorator to '${provider.node.name
31156
31655
  * dependencies within the same program are tracked; imports into packages on NPM are not.
31157
31656
  */
31158
31657
  class ImportGraph {
31159
- constructor(checker) {
31658
+ constructor(checker, perf) {
31160
31659
  this.checker = checker;
31660
+ this.perf = perf;
31161
31661
  this.map = new Map();
31162
31662
  }
31163
31663
  /**
@@ -31233,25 +31733,27 @@ Either add the @Injectable() decorator to '${provider.node.name
31233
31733
  }
31234
31734
  }
31235
31735
  scanImports(sf) {
31236
- const imports = new Set();
31237
- // Look through the source file for import and export statements.
31238
- for (const stmt of sf.statements) {
31239
- if ((!ts$1.isImportDeclaration(stmt) && !ts$1.isExportDeclaration(stmt)) ||
31240
- stmt.moduleSpecifier === undefined) {
31241
- continue;
31242
- }
31243
- const symbol = this.checker.getSymbolAtLocation(stmt.moduleSpecifier);
31244
- if (symbol === undefined || symbol.valueDeclaration === undefined) {
31245
- // No symbol could be found to skip over this import/export.
31246
- continue;
31247
- }
31248
- const moduleFile = symbol.valueDeclaration;
31249
- if (ts$1.isSourceFile(moduleFile) && isLocalFile(moduleFile)) {
31250
- // Record this local import.
31251
- imports.add(moduleFile);
31736
+ return this.perf.inPhase(PerfPhase.CycleDetection, () => {
31737
+ const imports = new Set();
31738
+ // Look through the source file for import and export statements.
31739
+ for (const stmt of sf.statements) {
31740
+ if ((!ts$1.isImportDeclaration(stmt) && !ts$1.isExportDeclaration(stmt)) ||
31741
+ stmt.moduleSpecifier === undefined) {
31742
+ continue;
31743
+ }
31744
+ const symbol = this.checker.getSymbolAtLocation(stmt.moduleSpecifier);
31745
+ if (symbol === undefined || symbol.valueDeclaration === undefined) {
31746
+ // No symbol could be found to skip over this import/export.
31747
+ continue;
31748
+ }
31749
+ const moduleFile = symbol.valueDeclaration;
31750
+ if (ts$1.isSourceFile(moduleFile) && isLocalFile(moduleFile)) {
31751
+ // Record this local import.
31752
+ imports.add(moduleFile);
31753
+ }
31252
31754
  }
31253
- }
31254
- return imports;
31755
+ return imports;
31756
+ });
31255
31757
  }
31256
31758
  }
31257
31759
  function isLocalFile(sf) {
@@ -31613,103 +32115,108 @@ Either add the @Injectable() decorator to '${provider.node.name
31613
32115
  * The previous build's `BuildState` is reconciled with the new program's changes, and the results
31614
32116
  * are merged into the new build's `PendingBuildState`.
31615
32117
  */
31616
- static reconcile(oldProgram, oldDriver, newProgram, modifiedResourceFiles) {
31617
- // Initialize the state of the current build based on the previous one.
31618
- let state;
31619
- if (oldDriver.state.kind === BuildStateKind.Pending) {
31620
- // The previous build never made it past the pending state. Reuse it as the starting state for
31621
- // this build.
31622
- state = oldDriver.state;
31623
- }
31624
- else {
31625
- let priorGraph = null;
31626
- if (oldDriver.state.lastGood !== null) {
31627
- priorGraph = oldDriver.state.lastGood.semanticDepGraph;
31628
- }
31629
- // The previous build was successfully analyzed. `pendingEmit` is the only state carried
31630
- // forward into this build.
31631
- state = {
31632
- kind: BuildStateKind.Pending,
31633
- pendingEmit: oldDriver.state.pendingEmit,
31634
- pendingTypeCheckEmit: oldDriver.state.pendingTypeCheckEmit,
31635
- changedResourcePaths: new Set(),
31636
- changedTsPaths: new Set(),
31637
- lastGood: oldDriver.state.lastGood,
31638
- semanticDepGraphUpdater: new SemanticDepGraphUpdater(priorGraph),
31639
- };
31640
- }
31641
- // Merge the freshly modified resource files with any prior ones.
31642
- if (modifiedResourceFiles !== null) {
31643
- for (const resFile of modifiedResourceFiles) {
31644
- state.changedResourcePaths.add(absoluteFrom(resFile));
31645
- }
31646
- }
31647
- // Next, process the files in the new program, with a couple of goals:
31648
- // 1) Determine which TS files have changed, if any, and merge them into `changedTsFiles`.
31649
- // 2) Produce a list of TS files which no longer exist in the program (they've been deleted
31650
- // since the previous compilation). These need to be removed from the state tracking to avoid
31651
- // leaking memory.
31652
- // All files in the old program, for easy detection of changes.
31653
- const oldFiles = new Set(oldProgram.getSourceFiles());
31654
- // Assume all the old files were deleted to begin with. Only TS files are tracked.
31655
- const deletedTsPaths = new Set(tsOnlyFiles(oldProgram).map(sf => sf.fileName));
31656
- for (const newFile of newProgram.getSourceFiles()) {
31657
- if (!newFile.isDeclarationFile) {
31658
- // This file exists in the new program, so remove it from `deletedTsPaths`.
31659
- deletedTsPaths.delete(newFile.fileName);
31660
- }
31661
- if (oldFiles.has(newFile)) {
31662
- // This file hasn't changed; no need to look at it further.
31663
- continue;
31664
- }
31665
- // The file has changed since the last successful build. The appropriate reaction depends on
31666
- // what kind of file it is.
31667
- if (!newFile.isDeclarationFile) {
31668
- // It's a .ts file, so track it as a change.
31669
- state.changedTsPaths.add(newFile.fileName);
32118
+ static reconcile(oldProgram, oldDriver, newProgram, modifiedResourceFiles, perf) {
32119
+ return perf.inPhase(PerfPhase.Reconciliation, () => {
32120
+ // Initialize the state of the current build based on the previous one.
32121
+ let state;
32122
+ if (oldDriver.state.kind === BuildStateKind.Pending) {
32123
+ // The previous build never made it past the pending state. Reuse it as the starting state
32124
+ // for this build.
32125
+ state = oldDriver.state;
31670
32126
  }
31671
32127
  else {
31672
- // It's a .d.ts file. Currently the compiler does not do a great job of tracking
31673
- // dependencies on .d.ts files, so bail out of incremental builds here and do a full build.
31674
- // This usually only happens if something in node_modules changes.
31675
- return IncrementalDriver.fresh(newProgram);
31676
- }
31677
- }
31678
- // The next step is to remove any deleted files from the state.
31679
- for (const filePath of deletedTsPaths) {
31680
- state.pendingEmit.delete(filePath);
31681
- state.pendingTypeCheckEmit.delete(filePath);
31682
- // Even if the file doesn't exist in the current compilation, it still might have been changed
31683
- // in a previous one, so delete it from the set of changed TS files, just in case.
31684
- state.changedTsPaths.delete(filePath);
31685
- }
31686
- // Now, changedTsPaths contains physically changed TS paths. Use the previous program's logical
31687
- // dependency graph to determine logically changed files.
31688
- const depGraph = new FileDependencyGraph();
31689
- // If a previous compilation exists, use its dependency graph to determine the set of logically
31690
- // changed files.
31691
- let logicalChanges = null;
31692
- if (state.lastGood !== null) {
31693
- // Extract the set of logically changed files. At the same time, this operation populates the
31694
- // current (fresh) dependency graph with information about those files which have not
31695
- // logically changed.
31696
- logicalChanges = depGraph.updateWithPhysicalChanges(state.lastGood.depGraph, state.changedTsPaths, deletedTsPaths, state.changedResourcePaths);
31697
- for (const fileName of state.changedTsPaths) {
31698
- logicalChanges.add(fileName);
31699
- }
31700
- // Any logically changed files need to be re-emitted. Most of the time this would happen
31701
- // regardless because the new dependency graph would _also_ identify the file as stale.
31702
- // However there are edge cases such as removing a component from an NgModule without adding
31703
- // it to another one, where the previous graph identifies the file as logically changed, but
31704
- // the new graph (which does not have that edge) fails to identify that the file should be
31705
- // re-emitted.
31706
- for (const change of logicalChanges) {
31707
- state.pendingEmit.add(change);
31708
- state.pendingTypeCheckEmit.add(change);
32128
+ let priorGraph = null;
32129
+ if (oldDriver.state.lastGood !== null) {
32130
+ priorGraph = oldDriver.state.lastGood.semanticDepGraph;
32131
+ }
32132
+ // The previous build was successfully analyzed. `pendingEmit` is the only state carried
32133
+ // forward into this build.
32134
+ state = {
32135
+ kind: BuildStateKind.Pending,
32136
+ pendingEmit: oldDriver.state.pendingEmit,
32137
+ pendingTypeCheckEmit: oldDriver.state.pendingTypeCheckEmit,
32138
+ changedResourcePaths: new Set(),
32139
+ changedTsPaths: new Set(),
32140
+ lastGood: oldDriver.state.lastGood,
32141
+ semanticDepGraphUpdater: new SemanticDepGraphUpdater(priorGraph),
32142
+ };
31709
32143
  }
31710
- }
31711
- // `state` now reflects the initial pending state of the current compilation.
31712
- return new IncrementalDriver(state, depGraph, logicalChanges);
32144
+ // Merge the freshly modified resource files with any prior ones.
32145
+ if (modifiedResourceFiles !== null) {
32146
+ for (const resFile of modifiedResourceFiles) {
32147
+ state.changedResourcePaths.add(absoluteFrom(resFile));
32148
+ }
32149
+ }
32150
+ // Next, process the files in the new program, with a couple of goals:
32151
+ // 1) Determine which TS files have changed, if any, and merge them into `changedTsFiles`.
32152
+ // 2) Produce a list of TS files which no longer exist in the program (they've been deleted
32153
+ // since the previous compilation). These need to be removed from the state tracking to
32154
+ // avoid leaking memory.
32155
+ // All files in the old program, for easy detection of changes.
32156
+ const oldFiles = new Set(oldProgram.getSourceFiles().map(toUnredirectedSourceFile));
32157
+ // Assume all the old files were deleted to begin with. Only TS files are tracked.
32158
+ const deletedTsPaths = new Set(tsOnlyFiles(oldProgram).map(sf => sf.fileName));
32159
+ for (const possiblyRedirectedNewFile of newProgram.getSourceFiles()) {
32160
+ const newFile = toUnredirectedSourceFile(possiblyRedirectedNewFile);
32161
+ if (!newFile.isDeclarationFile) {
32162
+ // This file exists in the new program, so remove it from `deletedTsPaths`.
32163
+ deletedTsPaths.delete(newFile.fileName);
32164
+ }
32165
+ if (oldFiles.has(newFile)) {
32166
+ // This file hasn't changed; no need to look at it further.
32167
+ continue;
32168
+ }
32169
+ // The file has changed since the last successful build. The appropriate reaction depends on
32170
+ // what kind of file it is.
32171
+ if (!newFile.isDeclarationFile) {
32172
+ // It's a .ts file, so track it as a change.
32173
+ state.changedTsPaths.add(newFile.fileName);
32174
+ }
32175
+ else {
32176
+ // It's a .d.ts file. Currently the compiler does not do a great job of tracking
32177
+ // dependencies on .d.ts files, so bail out of incremental builds here and do a full
32178
+ // build. This usually only happens if something in node_modules changes.
32179
+ return IncrementalDriver.fresh(newProgram);
32180
+ }
32181
+ }
32182
+ // The next step is to remove any deleted files from the state.
32183
+ for (const filePath of deletedTsPaths) {
32184
+ state.pendingEmit.delete(filePath);
32185
+ state.pendingTypeCheckEmit.delete(filePath);
32186
+ // Even if the file doesn't exist in the current compilation, it still might have been
32187
+ // changed in a previous one, so delete it from the set of changed TS files, just in case.
32188
+ state.changedTsPaths.delete(filePath);
32189
+ }
32190
+ perf.eventCount(PerfEvent.SourceFilePhysicalChange, state.changedTsPaths.size);
32191
+ // Now, changedTsPaths contains physically changed TS paths. Use the previous program's
32192
+ // logical dependency graph to determine logically changed files.
32193
+ const depGraph = new FileDependencyGraph();
32194
+ // If a previous compilation exists, use its dependency graph to determine the set of
32195
+ // logically changed files.
32196
+ let logicalChanges = null;
32197
+ if (state.lastGood !== null) {
32198
+ // Extract the set of logically changed files. At the same time, this operation populates
32199
+ // the current (fresh) dependency graph with information about those files which have not
32200
+ // logically changed.
32201
+ logicalChanges = depGraph.updateWithPhysicalChanges(state.lastGood.depGraph, state.changedTsPaths, deletedTsPaths, state.changedResourcePaths);
32202
+ perf.eventCount(PerfEvent.SourceFileLogicalChange, logicalChanges.size);
32203
+ for (const fileName of state.changedTsPaths) {
32204
+ logicalChanges.add(fileName);
32205
+ }
32206
+ // Any logically changed files need to be re-emitted. Most of the time this would happen
32207
+ // regardless because the new dependency graph would _also_ identify the file as stale.
32208
+ // However there are edge cases such as removing a component from an NgModule without adding
32209
+ // it to another one, where the previous graph identifies the file as logically changed, but
32210
+ // the new graph (which does not have that edge) fails to identify that the file should be
32211
+ // re-emitted.
32212
+ for (const change of logicalChanges) {
32213
+ state.pendingEmit.add(change);
32214
+ state.pendingTypeCheckEmit.add(change);
32215
+ }
32216
+ }
32217
+ // `state` now reflects the initial pending state of the current compilation.
32218
+ return new IncrementalDriver(state, depGraph, logicalChanges);
32219
+ });
31713
32220
  }
31714
32221
  static fresh(program) {
31715
32222
  // Initialize the set of files which need to be emitted to the set of all TS files in the
@@ -32394,29 +32901,6 @@ Either add the @Injectable() decorator to '${provider.node.name
32394
32901
  node.modifiers.some(mod => mod.kind === ts$1.SyntaxKind.StaticKeyword);
32395
32902
  }
32396
32903
 
32397
- const NOOP_PERF_RECORDER = {
32398
- enabled: false,
32399
- mark: (name, node, category, detail) => { },
32400
- start: (name, node, category, detail) => {
32401
- return 0;
32402
- },
32403
- stop: (span) => { },
32404
- };
32405
-
32406
- /**
32407
- * @license
32408
- * Copyright Google LLC All Rights Reserved.
32409
- *
32410
- * Use of this source code is governed by an MIT-style license that can be
32411
- * found in the LICENSE file at https://angular.io/license
32412
- */
32413
- var PerfLogEventType;
32414
- (function (PerfLogEventType) {
32415
- PerfLogEventType[PerfLogEventType["SPAN_OPEN"] = 0] = "SPAN_OPEN";
32416
- PerfLogEventType[PerfLogEventType["SPAN_CLOSE"] = 1] = "SPAN_CLOSE";
32417
- PerfLogEventType[PerfLogEventType["MARK"] = 2] = "MARK";
32418
- })(PerfLogEventType || (PerfLogEventType = {}));
32419
-
32420
32904
  /**
32421
32905
  * @license
32422
32906
  * Copyright Google LLC All Rights Reserved.
@@ -32454,7 +32938,7 @@ Either add the @Injectable() decorator to '${provider.node.name
32454
32938
  resolve(url, fromFile) {
32455
32939
  let resolvedUrl = null;
32456
32940
  if (this.adapter.resourceNameToFileName) {
32457
- resolvedUrl = this.adapter.resourceNameToFileName(url, fromFile);
32941
+ resolvedUrl = this.adapter.resourceNameToFileName(url, fromFile, (url, fromFile) => this.fallbackResolve(url, fromFile));
32458
32942
  }
32459
32943
  else {
32460
32944
  resolvedUrl = this.fallbackResolve(url, fromFile);
@@ -34242,56 +34726,63 @@ Either add the @Injectable() decorator to '${provider.node.name
34242
34726
  this.data = data;
34243
34727
  this.shimPath = shimPath;
34244
34728
  /**
34245
- * Cache of `GlobalCompletion`s for various levels of the template, including the root template
34246
- * (`null`).
34729
+ * Cache of completions for various levels of the template, including the root template (`null`).
34730
+ * Memoizes `getTemplateContextCompletions`.
34247
34731
  */
34248
- this.globalCompletionCache = new Map();
34732
+ this.templateContextCache = new Map();
34249
34733
  this.expressionCompletionCache = new Map();
34250
- }
34251
- /**
34252
- * Get global completions within the given template context - either a `TmplAstTemplate` embedded
34253
- * view, or `null` for the root template context.
34254
- */
34255
- getGlobalCompletions(context) {
34256
- if (this.globalCompletionCache.has(context)) {
34257
- return this.globalCompletionCache.get(context);
34258
- }
34259
34734
  // Find the component completion expression within the TCB. This looks like: `ctx. /* ... */;`
34260
34735
  const globalRead = findFirstMatchingNode(this.tcb, {
34261
34736
  filter: ts$1.isPropertyAccessExpression,
34262
34737
  withExpressionIdentifier: ExpressionIdentifier.COMPONENT_COMPLETION
34263
34738
  });
34264
- if (globalRead === null) {
34265
- return null;
34266
- }
34267
- const completion = {
34268
- componentContext: {
34739
+ if (globalRead !== null) {
34740
+ this.componentContext = {
34269
34741
  shimPath: this.shimPath,
34270
34742
  // `globalRead.name` is an empty `ts.Identifier`, so its start position immediately follows
34271
34743
  // the `.` in `ctx.`. TS autocompletion APIs can then be used to access completion results
34272
34744
  // for the component context.
34273
34745
  positionInShimFile: globalRead.name.getStart(),
34274
- },
34275
- templateContext: new Map(),
34276
- };
34277
- // The bound template already has details about the references and variables in scope in the
34278
- // `context` template - they just need to be converted to `Completion`s.
34279
- for (const node of this.data.boundTarget.getEntitiesInTemplateScope(context)) {
34280
- if (node instanceof Reference) {
34281
- completion.templateContext.set(node.name, {
34282
- kind: CompletionKind.Reference,
34283
- node,
34284
- });
34285
- }
34286
- else {
34287
- completion.templateContext.set(node.name, {
34288
- kind: CompletionKind.Variable,
34289
- node,
34290
- });
34746
+ };
34747
+ }
34748
+ else {
34749
+ this.componentContext = null;
34750
+ }
34751
+ }
34752
+ /**
34753
+ * Get global completions within the given template context and AST node.
34754
+ *
34755
+ * @param context the given template context - either a `TmplAstTemplate` embedded view, or `null`
34756
+ * for the root
34757
+ * template context.
34758
+ * @param node the given AST node
34759
+ */
34760
+ getGlobalCompletions(context, node) {
34761
+ if (this.componentContext === null) {
34762
+ return null;
34763
+ }
34764
+ const templateContext = this.getTemplateContextCompletions(context);
34765
+ if (templateContext === null) {
34766
+ return null;
34767
+ }
34768
+ let nodeContext = null;
34769
+ if (node instanceof EmptyExpr) {
34770
+ const nodeLocation = findFirstMatchingNode(this.tcb, {
34771
+ filter: ts$1.isIdentifier,
34772
+ withSpan: node.sourceSpan,
34773
+ });
34774
+ if (nodeLocation !== null) {
34775
+ nodeContext = {
34776
+ shimPath: this.shimPath,
34777
+ positionInShimFile: nodeLocation.getStart(),
34778
+ };
34291
34779
  }
34292
34780
  }
34293
- this.globalCompletionCache.set(context, completion);
34294
- return completion;
34781
+ return {
34782
+ componentContext: this.componentContext,
34783
+ templateContext,
34784
+ nodeContext,
34785
+ };
34295
34786
  }
34296
34787
  getExpressionCompletionLocation(expr) {
34297
34788
  if (this.expressionCompletionCache.has(expr)) {
@@ -34336,6 +34827,34 @@ Either add the @Injectable() decorator to '${provider.node.name
34336
34827
  this.expressionCompletionCache.set(expr, res);
34337
34828
  return res;
34338
34829
  }
34830
+ /**
34831
+ * Get global completions within the given template context - either a `TmplAstTemplate` embedded
34832
+ * view, or `null` for the root context.
34833
+ */
34834
+ getTemplateContextCompletions(context) {
34835
+ if (this.templateContextCache.has(context)) {
34836
+ return this.templateContextCache.get(context);
34837
+ }
34838
+ const templateContext = new Map();
34839
+ // The bound template already has details about the references and variables in scope in the
34840
+ // `context` template - they just need to be converted to `Completion`s.
34841
+ for (const node of this.data.boundTarget.getEntitiesInTemplateScope(context)) {
34842
+ if (node instanceof Reference) {
34843
+ templateContext.set(node.name, {
34844
+ kind: CompletionKind.Reference,
34845
+ node,
34846
+ });
34847
+ }
34848
+ else {
34849
+ templateContext.set(node.name, {
34850
+ kind: CompletionKind.Variable,
34851
+ node,
34852
+ });
34853
+ }
34854
+ }
34855
+ this.templateContextCache.set(context, templateContext);
34856
+ return templateContext;
34857
+ }
34339
34858
  }
34340
34859
 
34341
34860
  /**
@@ -34665,12 +35184,6 @@ Either add the @Injectable() decorator to '${provider.node.name
34665
35184
  }
34666
35185
  return false;
34667
35186
  }
34668
- function checkIfGenericTypesAreUnbound(node) {
34669
- if (node.typeParameters === undefined) {
34670
- return true;
34671
- }
34672
- return node.typeParameters.every(param => param.constraint === undefined);
34673
- }
34674
35187
  function isAccessExpression(node) {
34675
35188
  return ts$1.isPropertyAccessExpression(node) || ts$1.isElementAccessExpression(node);
34676
35189
  }
@@ -34890,6 +35403,136 @@ Either add the @Injectable() decorator to '${provider.node.name
34890
35403
  }
34891
35404
  }
34892
35405
 
35406
+ /**
35407
+ * @license
35408
+ * Copyright Google LLC All Rights Reserved.
35409
+ *
35410
+ * Use of this source code is governed by an MIT-style license that can be
35411
+ * found in the LICENSE file at https://angular.io/license
35412
+ */
35413
+ /**
35414
+ * Indicates whether a particular component requires an inline type check block.
35415
+ *
35416
+ * This is not a boolean state as inlining might only be required to get the best possible
35417
+ * type-checking, but the component could theoretically still be checked without it.
35418
+ */
35419
+ var TcbInliningRequirement;
35420
+ (function (TcbInliningRequirement) {
35421
+ /**
35422
+ * There is no way to type check this component without inlining.
35423
+ */
35424
+ TcbInliningRequirement[TcbInliningRequirement["MustInline"] = 0] = "MustInline";
35425
+ /**
35426
+ * Inlining should be used due to the component's generic bounds, but a non-inlining fallback
35427
+ * method can be used if that's not possible.
35428
+ */
35429
+ TcbInliningRequirement[TcbInliningRequirement["ShouldInlineForGenericBounds"] = 1] = "ShouldInlineForGenericBounds";
35430
+ /**
35431
+ * There is no requirement for this component's TCB to be inlined.
35432
+ */
35433
+ TcbInliningRequirement[TcbInliningRequirement["None"] = 2] = "None";
35434
+ })(TcbInliningRequirement || (TcbInliningRequirement = {}));
35435
+ function requiresInlineTypeCheckBlock(node, usedPipes, reflector) {
35436
+ // In order to qualify for a declared TCB (not inline) two conditions must be met:
35437
+ // 1) the class must be exported
35438
+ // 2) it must not have contextual generic type bounds
35439
+ if (!checkIfClassIsExported(node)) {
35440
+ // Condition 1 is false, the class is not exported.
35441
+ return TcbInliningRequirement.MustInline;
35442
+ }
35443
+ else if (!checkIfGenericTypeBoundsAreContextFree(node, reflector)) {
35444
+ // Condition 2 is false, the class has constrained generic types. It should be checked with an
35445
+ // inline TCB if possible, but can potentially use fallbacks to avoid inlining if not.
35446
+ return TcbInliningRequirement.ShouldInlineForGenericBounds;
35447
+ }
35448
+ else if (Array.from(usedPipes.values())
35449
+ .some(pipeRef => !checkIfClassIsExported(pipeRef.node))) {
35450
+ // If one of the pipes used by the component is not exported, a non-inline TCB will not be able
35451
+ // to import it, so this requires an inline TCB.
35452
+ return TcbInliningRequirement.MustInline;
35453
+ }
35454
+ else {
35455
+ return TcbInliningRequirement.None;
35456
+ }
35457
+ }
35458
+ /** Maps a shim position back to a template location. */
35459
+ function getTemplateMapping(shimSf, position, resolver, isDiagnosticRequest) {
35460
+ const node = getTokenAtPosition(shimSf, position);
35461
+ const sourceLocation = findSourceLocation(node, shimSf, isDiagnosticRequest);
35462
+ if (sourceLocation === null) {
35463
+ return null;
35464
+ }
35465
+ const mapping = resolver.getSourceMapping(sourceLocation.id);
35466
+ const span = resolver.toParseSourceSpan(sourceLocation.id, sourceLocation.span);
35467
+ if (span === null) {
35468
+ return null;
35469
+ }
35470
+ // TODO(atscott): Consider adding a context span by walking up from `node` until we get a
35471
+ // different span.
35472
+ return { sourceLocation, templateSourceMapping: mapping, span };
35473
+ }
35474
+ function findTypeCheckBlock(file, id, isDiagnosticRequest) {
35475
+ for (const stmt of file.statements) {
35476
+ if (ts$1.isFunctionDeclaration(stmt) && getTemplateId$1(stmt, file, isDiagnosticRequest) === id) {
35477
+ return stmt;
35478
+ }
35479
+ }
35480
+ return null;
35481
+ }
35482
+ /**
35483
+ * Traverses up the AST starting from the given node to extract the source location from comments
35484
+ * that have been emitted into the TCB. If the node does not exist within a TCB, or if an ignore
35485
+ * marker comment is found up the tree (and this is part of a diagnostic request), this function
35486
+ * returns null.
35487
+ */
35488
+ function findSourceLocation(node, sourceFile, isDiagnosticsRequest) {
35489
+ // Search for comments until the TCB's function declaration is encountered.
35490
+ while (node !== undefined && !ts$1.isFunctionDeclaration(node)) {
35491
+ if (hasIgnoreForDiagnosticsMarker(node, sourceFile) && isDiagnosticsRequest) {
35492
+ // There's an ignore marker on this node, so the diagnostic should not be reported.
35493
+ return null;
35494
+ }
35495
+ const span = readSpanComment(node, sourceFile);
35496
+ if (span !== null) {
35497
+ // Once the positional information has been extracted, search further up the TCB to extract
35498
+ // the unique id that is attached with the TCB's function declaration.
35499
+ const id = getTemplateId$1(node, sourceFile, isDiagnosticsRequest);
35500
+ if (id === null) {
35501
+ return null;
35502
+ }
35503
+ return { id, span };
35504
+ }
35505
+ node = node.parent;
35506
+ }
35507
+ return null;
35508
+ }
35509
+ function getTemplateId$1(node, sourceFile, isDiagnosticRequest) {
35510
+ // Walk up to the function declaration of the TCB, the file information is attached there.
35511
+ while (!ts$1.isFunctionDeclaration(node)) {
35512
+ if (hasIgnoreForDiagnosticsMarker(node, sourceFile) && isDiagnosticRequest) {
35513
+ // There's an ignore marker on this node, so the diagnostic should not be reported.
35514
+ return null;
35515
+ }
35516
+ node = node.parent;
35517
+ // Bail once we have reached the root.
35518
+ if (node === undefined) {
35519
+ return null;
35520
+ }
35521
+ }
35522
+ const start = node.getFullStart();
35523
+ return ts$1.forEachLeadingCommentRange(sourceFile.text, start, (pos, end, kind) => {
35524
+ if (kind !== ts$1.SyntaxKind.MultiLineCommentTrivia) {
35525
+ return null;
35526
+ }
35527
+ const commentText = sourceFile.text.substring(pos + 2, end - 2);
35528
+ return commentText;
35529
+ }) || null;
35530
+ }
35531
+ function checkIfGenericTypeBoundsAreContextFree(node, reflector) {
35532
+ // Generic type parameters are considered context free if they can be emitted into any context.
35533
+ return new TypeParameterEmitter(node.typeParameters, reflector).canEmit();
35534
+ }
35535
+
34893
35536
  /**
34894
35537
  * @license
34895
35538
  * Copyright Google LLC All Rights Reserved.
@@ -35054,10 +35697,6 @@ Either add the @Injectable() decorator to '${provider.node.name
35054
35697
  // emitted into a different context.
35055
35698
  return !checkIfGenericTypeBoundsAreContextFree(node, host);
35056
35699
  }
35057
- function checkIfGenericTypeBoundsAreContextFree(node, reflector) {
35058
- // Generic type parameters are considered context free if they can be emitted into any context.
35059
- return new TypeParameterEmitter(node.typeParameters, reflector).canEmit();
35060
- }
35061
35700
  /**
35062
35701
  * Add a default `= any` to type parameters that don't have a default value already.
35063
35702
  *
@@ -35366,109 +36005,6 @@ Either add the @Injectable() decorator to '${provider.node.name
35366
36005
  return Object.assign(Object.assign({}, makeDiagnostic(code, node, messageText, relatedInformation)), { componentFile: node.getSourceFile(), templateId });
35367
36006
  }
35368
36007
 
35369
- /**
35370
- * @license
35371
- * Copyright Google LLC All Rights Reserved.
35372
- *
35373
- * Use of this source code is governed by an MIT-style license that can be
35374
- * found in the LICENSE file at https://angular.io/license
35375
- */
35376
- function requiresInlineTypeCheckBlock(node, usedPipes) {
35377
- // In order to qualify for a declared TCB (not inline) two conditions must be met:
35378
- // 1) the class must be exported
35379
- // 2) it must not have constrained generic types
35380
- if (!checkIfClassIsExported(node)) {
35381
- // Condition 1 is false, the class is not exported.
35382
- return true;
35383
- }
35384
- else if (!checkIfGenericTypesAreUnbound(node)) {
35385
- // Condition 2 is false, the class has constrained generic types
35386
- return true;
35387
- }
35388
- else if (Array.from(usedPipes.values())
35389
- .some(pipeRef => !checkIfClassIsExported(pipeRef.node))) {
35390
- // If one of the pipes used by the component is not exported, a non-inline TCB will not be able
35391
- // to import it, so this requires an inline TCB.
35392
- return true;
35393
- }
35394
- else {
35395
- return false;
35396
- }
35397
- }
35398
- /** Maps a shim position back to a template location. */
35399
- function getTemplateMapping(shimSf, position, resolver, isDiagnosticRequest) {
35400
- const node = getTokenAtPosition(shimSf, position);
35401
- const sourceLocation = findSourceLocation(node, shimSf, isDiagnosticRequest);
35402
- if (sourceLocation === null) {
35403
- return null;
35404
- }
35405
- const mapping = resolver.getSourceMapping(sourceLocation.id);
35406
- const span = resolver.toParseSourceSpan(sourceLocation.id, sourceLocation.span);
35407
- if (span === null) {
35408
- return null;
35409
- }
35410
- // TODO(atscott): Consider adding a context span by walking up from `node` until we get a
35411
- // different span.
35412
- return { sourceLocation, templateSourceMapping: mapping, span };
35413
- }
35414
- function findTypeCheckBlock(file, id, isDiagnosticRequest) {
35415
- for (const stmt of file.statements) {
35416
- if (ts$1.isFunctionDeclaration(stmt) && getTemplateId$1(stmt, file, isDiagnosticRequest) === id) {
35417
- return stmt;
35418
- }
35419
- }
35420
- return null;
35421
- }
35422
- /**
35423
- * Traverses up the AST starting from the given node to extract the source location from comments
35424
- * that have been emitted into the TCB. If the node does not exist within a TCB, or if an ignore
35425
- * marker comment is found up the tree (and this is part of a diagnostic request), this function
35426
- * returns null.
35427
- */
35428
- function findSourceLocation(node, sourceFile, isDiagnosticsRequest) {
35429
- // Search for comments until the TCB's function declaration is encountered.
35430
- while (node !== undefined && !ts$1.isFunctionDeclaration(node)) {
35431
- if (hasIgnoreForDiagnosticsMarker(node, sourceFile) && isDiagnosticsRequest) {
35432
- // There's an ignore marker on this node, so the diagnostic should not be reported.
35433
- return null;
35434
- }
35435
- const span = readSpanComment(node, sourceFile);
35436
- if (span !== null) {
35437
- // Once the positional information has been extracted, search further up the TCB to extract
35438
- // the unique id that is attached with the TCB's function declaration.
35439
- const id = getTemplateId$1(node, sourceFile, isDiagnosticsRequest);
35440
- if (id === null) {
35441
- return null;
35442
- }
35443
- return { id, span };
35444
- }
35445
- node = node.parent;
35446
- }
35447
- return null;
35448
- }
35449
- function getTemplateId$1(node, sourceFile, isDiagnosticRequest) {
35450
- // Walk up to the function declaration of the TCB, the file information is attached there.
35451
- while (!ts$1.isFunctionDeclaration(node)) {
35452
- if (hasIgnoreForDiagnosticsMarker(node, sourceFile) && isDiagnosticRequest) {
35453
- // There's an ignore marker on this node, so the diagnostic should not be reported.
35454
- return null;
35455
- }
35456
- node = node.parent;
35457
- // Bail once we have reached the root.
35458
- if (node === undefined) {
35459
- return null;
35460
- }
35461
- }
35462
- const start = node.getFullStart();
35463
- return ts$1.forEachLeadingCommentRange(sourceFile.text, start, (pos, end, kind) => {
35464
- if (kind !== ts$1.SyntaxKind.MultiLineCommentTrivia) {
35465
- return null;
35466
- }
35467
- const commentText = sourceFile.text.substring(pos + 2, end - 2);
35468
- return commentText;
35469
- }) || null;
35470
- }
35471
-
35472
36008
  /**
35473
36009
  * @license
35474
36010
  * Copyright Google LLC All Rights Reserved.
@@ -35613,7 +36149,9 @@ Either add the @Injectable() decorator to '${provider.node.name
35613
36149
  }
35614
36150
  // The `EmptyExpr` doesn't have a dedicated method on `AstVisitor`, so it's special cased here.
35615
36151
  if (ast instanceof EmptyExpr) {
35616
- return UNDEFINED;
36152
+ const res = ts$1.factory.createIdentifier('undefined');
36153
+ addParseSpanInfo(res, ast.sourceSpan);
36154
+ return res;
35617
36155
  }
35618
36156
  // First attempt to let any custom resolution logic provide a translation for the given node.
35619
36157
  const resolved = this.maybeResolve(ast);
@@ -35977,6 +36515,32 @@ Either add the @Injectable() decorator to '${provider.node.name
35977
36515
  * Use of this source code is governed by an MIT-style license that can be
35978
36516
  * found in the LICENSE file at https://angular.io/license
35979
36517
  */
36518
+ /**
36519
+ * Controls how generics for the component context class will be handled during TCB generation.
36520
+ */
36521
+ var TcbGenericContextBehavior;
36522
+ (function (TcbGenericContextBehavior) {
36523
+ /**
36524
+ * References to generic parameter bounds will be emitted via the `TypeParameterEmitter`.
36525
+ *
36526
+ * The caller must verify that all parameter bounds are emittable in order to use this mode.
36527
+ */
36528
+ TcbGenericContextBehavior[TcbGenericContextBehavior["UseEmitter"] = 0] = "UseEmitter";
36529
+ /**
36530
+ * Generic parameter declarations will be copied directly from the `ts.ClassDeclaration` of the
36531
+ * component class.
36532
+ *
36533
+ * The caller must only use the generated TCB code in a context where such copies will still be
36534
+ * valid, such as an inline type check block.
36535
+ */
36536
+ TcbGenericContextBehavior[TcbGenericContextBehavior["CopyClassNodes"] = 1] = "CopyClassNodes";
36537
+ /**
36538
+ * Any generic parameters for the component context class will be set to `any`.
36539
+ *
36540
+ * Produces a less useful type, but is always safe to use.
36541
+ */
36542
+ TcbGenericContextBehavior[TcbGenericContextBehavior["FallbackToAny"] = 2] = "FallbackToAny";
36543
+ })(TcbGenericContextBehavior || (TcbGenericContextBehavior = {}));
35980
36544
  /**
35981
36545
  * Given a `ts.ClassDeclaration` for a component, and metadata regarding that component, compose a
35982
36546
  * "type check block" function.
@@ -35998,15 +36562,39 @@ Either add the @Injectable() decorator to '${provider.node.name
35998
36562
  * and bindings.
35999
36563
  * @param oobRecorder used to record errors regarding template elements which could not be correctly
36000
36564
  * translated into types during TCB generation.
36565
+ * @param genericContextBehavior controls how generic parameters (especially parameters with generic
36566
+ * bounds) will be referenced from the generated TCB code.
36001
36567
  */
36002
- function generateTypeCheckBlock(env, ref, name, meta, domSchemaChecker, oobRecorder) {
36568
+ function generateTypeCheckBlock(env, ref, name, meta, domSchemaChecker, oobRecorder, genericContextBehavior) {
36003
36569
  const tcb = new Context$1(env, domSchemaChecker, oobRecorder, meta.id, meta.boundTarget, meta.pipes, meta.schemas);
36004
36570
  const scope = Scope$1.forNodes(tcb, null, tcb.boundTarget.target.template, /* guard */ null);
36005
36571
  const ctxRawType = env.referenceType(ref);
36006
36572
  if (!ts$1.isTypeReferenceNode(ctxRawType)) {
36007
36573
  throw new Error(`Expected TypeReferenceNode when referencing the ctx param for ${ref.debugName}`);
36008
36574
  }
36009
- const paramList = [tcbCtxParam(ref.node, ctxRawType.typeName, env.config.useContextGenericType)];
36575
+ let typeParameters = undefined;
36576
+ let typeArguments = undefined;
36577
+ if (ref.node.typeParameters !== undefined) {
36578
+ if (!env.config.useContextGenericType) {
36579
+ genericContextBehavior = TcbGenericContextBehavior.FallbackToAny;
36580
+ }
36581
+ switch (genericContextBehavior) {
36582
+ case TcbGenericContextBehavior.UseEmitter:
36583
+ // Guaranteed to emit type parameters since we checked that the class has them above.
36584
+ typeParameters = new TypeParameterEmitter(ref.node.typeParameters, env.reflector)
36585
+ .emit(typeRef => env.referenceType(typeRef));
36586
+ typeArguments = typeParameters.map(param => ts$1.factory.createTypeReferenceNode(param.name));
36587
+ break;
36588
+ case TcbGenericContextBehavior.CopyClassNodes:
36589
+ typeParameters = [...ref.node.typeParameters];
36590
+ typeArguments = typeParameters.map(param => ts$1.factory.createTypeReferenceNode(param.name));
36591
+ break;
36592
+ case TcbGenericContextBehavior.FallbackToAny:
36593
+ typeArguments = ref.node.typeParameters.map(() => ts$1.factory.createKeywordTypeNode(ts$1.SyntaxKind.AnyKeyword));
36594
+ break;
36595
+ }
36596
+ }
36597
+ const paramList = [tcbCtxParam(ref.node, ctxRawType.typeName, typeArguments)];
36010
36598
  const scopeStatements = scope.render();
36011
36599
  const innerBody = ts$1.createBlock([
36012
36600
  ...env.getPreludeStatements(),
@@ -36020,7 +36608,7 @@ Either add the @Injectable() decorator to '${provider.node.name
36020
36608
  /* modifiers */ undefined,
36021
36609
  /* asteriskToken */ undefined,
36022
36610
  /* name */ name,
36023
- /* typeParameters */ env.config.useContextGenericType ? ref.node.typeParameters : undefined,
36611
+ /* typeParameters */ env.config.useContextGenericType ? typeParameters : undefined,
36024
36612
  /* parameters */ paramList,
36025
36613
  /* type */ undefined,
36026
36614
  /* body */ body);
@@ -37389,26 +37977,11 @@ Either add the @Injectable() decorator to '${provider.node.name
37389
37977
  }
37390
37978
  }
37391
37979
  /**
37392
- * Create the `ctx` parameter to the top-level TCB function.
37393
- *
37394
- * This is a parameter with a type equivalent to the component type, with all generic type
37395
- * parameters listed (without their generic bounds).
37980
+ * Create the `ctx` parameter to the top-level TCB function, with the given generic type arguments.
37396
37981
  */
37397
- function tcbCtxParam(node, name, useGenericType) {
37398
- let typeArguments = undefined;
37399
- // Check if the component is generic, and pass generic type parameters if so.
37400
- if (node.typeParameters !== undefined) {
37401
- if (useGenericType) {
37402
- typeArguments =
37403
- node.typeParameters.map(param => ts$1.createTypeReferenceNode(param.name, undefined));
37404
- }
37405
- else {
37406
- typeArguments =
37407
- node.typeParameters.map(() => ts$1.createKeywordTypeNode(ts$1.SyntaxKind.AnyKeyword));
37408
- }
37409
- }
37410
- const type = ts$1.createTypeReferenceNode(name, typeArguments);
37411
- return ts$1.createParameter(
37982
+ function tcbCtxParam(node, name, typeArguments) {
37983
+ const type = ts$1.factory.createTypeReferenceNode(name, typeArguments);
37984
+ return ts$1.factory.createParameterDeclaration(
37412
37985
  /* decorators */ undefined,
37413
37986
  /* modifiers */ undefined,
37414
37987
  /* dotDotDotToken */ undefined,
@@ -37719,9 +38292,9 @@ Either add the @Injectable() decorator to '${provider.node.name
37719
38292
  this.nextTcbId = 1;
37720
38293
  this.tcbStatements = [];
37721
38294
  }
37722
- addTypeCheckBlock(ref, meta, domSchemaChecker, oobRecorder) {
38295
+ addTypeCheckBlock(ref, meta, domSchemaChecker, oobRecorder, genericContextBehavior) {
37723
38296
  const fnId = ts$1.createIdentifier(`_tcb${this.nextTcbId++}`);
37724
- const fn = generateTypeCheckBlock(this, ref, fnId, meta, domSchemaChecker, oobRecorder);
38297
+ const fn = generateTypeCheckBlock(this, ref, fnId, meta, domSchemaChecker, oobRecorder, genericContextBehavior);
37725
38298
  this.tcbStatements.push(fn);
37726
38299
  }
37727
38300
  render(removeComments) {
@@ -37780,7 +38353,7 @@ Either add the @Injectable() decorator to '${provider.node.name
37780
38353
  * type checked.
37781
38354
  */
37782
38355
  class TypeCheckContextImpl {
37783
- constructor(config, compilerHost, componentMappingStrategy, refEmitter, reflector, host, inlining) {
38356
+ constructor(config, compilerHost, componentMappingStrategy, refEmitter, reflector, host, inlining, perf) {
37784
38357
  this.config = config;
37785
38358
  this.compilerHost = compilerHost;
37786
38359
  this.componentMappingStrategy = componentMappingStrategy;
@@ -37788,6 +38361,7 @@ Either add the @Injectable() decorator to '${provider.node.name
37788
38361
  this.reflector = reflector;
37789
38362
  this.host = host;
37790
38363
  this.inlining = inlining;
38364
+ this.perf = perf;
37791
38365
  this.fileMap = new Map();
37792
38366
  /**
37793
38367
  * A `Map` of `ts.SourceFile`s that the context has seen to the operations (additions of methods
@@ -37852,15 +38426,17 @@ Either add the @Injectable() decorator to '${provider.node.name
37852
38426
  boundTarget,
37853
38427
  templateDiagnostics,
37854
38428
  });
37855
- const tcbRequiresInline = requiresInlineTypeCheckBlock(ref.node, pipes);
38429
+ const inliningRequirement = requiresInlineTypeCheckBlock(ref.node, pipes, this.reflector);
37856
38430
  // If inlining is not supported, but is required for either the TCB or one of its directive
37857
38431
  // dependencies, then exit here with an error.
37858
- if (this.inlining === InliningMode.Error && tcbRequiresInline) {
38432
+ if (this.inlining === InliningMode.Error &&
38433
+ inliningRequirement === TcbInliningRequirement.MustInline) {
37859
38434
  // This template cannot be supported because the underlying strategy does not support inlining
37860
38435
  // and inlining would be required.
37861
38436
  // Record diagnostics to indicate the issues with this template.
37862
38437
  shimData.oobRecorder.requiresInlineTcb(templateId, ref.node);
37863
38438
  // Checking this template would be unsupported, so don't try.
38439
+ this.perf.eventCount(PerfEvent.SkipGenerateTcbNoInline);
37864
38440
  return;
37865
38441
  }
37866
38442
  const meta = {
@@ -37869,14 +38445,24 @@ Either add the @Injectable() decorator to '${provider.node.name
37869
38445
  pipes,
37870
38446
  schemas,
37871
38447
  };
37872
- if (tcbRequiresInline) {
38448
+ this.perf.eventCount(PerfEvent.GenerateTcb);
38449
+ if (inliningRequirement !== TcbInliningRequirement.None &&
38450
+ this.inlining === InliningMode.InlineOps) {
37873
38451
  // This class didn't meet the requirements for external type checking, so generate an inline
37874
38452
  // TCB for the class.
37875
38453
  this.addInlineTypeCheckBlock(fileData, shimData, ref, meta);
37876
38454
  }
38455
+ else if (inliningRequirement === TcbInliningRequirement.ShouldInlineForGenericBounds &&
38456
+ this.inlining === InliningMode.Error) {
38457
+ // It's suggested that this TCB should be generated inline due to the component's generic
38458
+ // bounds, but inlining is not supported by the current environment. Use a non-inline type
38459
+ // check block, but fall back to `any` generic parameters since the generic bounds can't be
38460
+ // referenced in that context. This will infer a less useful type for the component, but allow
38461
+ // for type-checking it in an environment where that would not be possible otherwise.
38462
+ shimData.file.addTypeCheckBlock(ref, meta, shimData.domSchemaChecker, shimData.oobRecorder, TcbGenericContextBehavior.FallbackToAny);
38463
+ }
37877
38464
  else {
37878
- // The class can be type-checked externally as normal.
37879
- shimData.file.addTypeCheckBlock(ref, meta, shimData.domSchemaChecker, shimData.oobRecorder);
38465
+ shimData.file.addTypeCheckBlock(ref, meta, shimData.domSchemaChecker, shimData.oobRecorder, TcbGenericContextBehavior.UseEmitter);
37880
38466
  }
37881
38467
  }
37882
38468
  /**
@@ -37966,7 +38552,7 @@ Either add the @Injectable() decorator to '${provider.node.name
37966
38552
  this.opMap.set(sf, []);
37967
38553
  }
37968
38554
  const ops = this.opMap.get(sf);
37969
- ops.push(new TcbOp$1(ref, tcbMeta, this.config, this.reflector, shimData.domSchemaChecker, shimData.oobRecorder));
38555
+ ops.push(new InlineTcbOp(ref, tcbMeta, this.config, this.reflector, shimData.domSchemaChecker, shimData.oobRecorder));
37970
38556
  fileData.hasInlines = true;
37971
38557
  }
37972
38558
  pendingShimForComponent(node) {
@@ -38009,9 +38595,9 @@ Either add the @Injectable() decorator to '${provider.node.name
38009
38595
  }
38010
38596
  }
38011
38597
  /**
38012
- * A type check block operation which produces type check code for a particular component.
38598
+ * A type check block operation which produces inline type check code for a particular component.
38013
38599
  */
38014
- class TcbOp$1 {
38600
+ class InlineTcbOp {
38015
38601
  constructor(ref, meta, config, reflector, domSchemaChecker, oobRecorder) {
38016
38602
  this.ref = ref;
38017
38603
  this.meta = meta;
@@ -38029,7 +38615,9 @@ Either add the @Injectable() decorator to '${provider.node.name
38029
38615
  execute(im, sf, refEmitter, printer) {
38030
38616
  const env = new Environment(this.config, im, refEmitter, this.reflector, sf);
38031
38617
  const fnName = ts$1.createIdentifier(`_tcb_${this.ref.node.pos}`);
38032
- const fn = generateTypeCheckBlock(env, this.ref, fnName, this.meta, this.domSchemaChecker, this.oobRecorder);
38618
+ // Inline TCBs should copy any generic type parameter nodes directly, as the TCB code is inlined
38619
+ // into the class in a context where that will always be legal.
38620
+ const fn = generateTypeCheckBlock(env, this.ref, fnName, this.meta, this.domSchemaChecker, this.oobRecorder, TcbGenericContextBehavior.CopyClassNodes);
38033
38621
  return printer.printNode(ts$1.EmitHint.Unspecified, fn, sf);
38034
38622
  }
38035
38623
  }
@@ -38685,7 +39273,7 @@ Either add the @Injectable() decorator to '${provider.node.name
38685
39273
  * `ProgramTypeCheckAdapter` for generation of template type-checking code.
38686
39274
  */
38687
39275
  class TemplateTypeCheckerImpl {
38688
- constructor(originalProgram, typeCheckingStrategy, typeCheckAdapter, config, refEmitter, reflector, compilerHost, priorBuild, componentScopeReader, typeCheckScopeRegistry) {
39276
+ constructor(originalProgram, typeCheckingStrategy, typeCheckAdapter, config, refEmitter, reflector, compilerHost, priorBuild, componentScopeReader, typeCheckScopeRegistry, perf) {
38689
39277
  this.originalProgram = originalProgram;
38690
39278
  this.typeCheckingStrategy = typeCheckingStrategy;
38691
39279
  this.typeCheckAdapter = typeCheckAdapter;
@@ -38696,6 +39284,7 @@ Either add the @Injectable() decorator to '${provider.node.name
38696
39284
  this.priorBuild = priorBuild;
38697
39285
  this.componentScopeReader = componentScopeReader;
38698
39286
  this.typeCheckScopeRegistry = typeCheckScopeRegistry;
39287
+ this.perf = perf;
38699
39288
  this.state = new Map();
38700
39289
  /**
38701
39290
  * Stores the `CompletionEngine` which powers autocompletion for each component class.
@@ -38807,65 +39396,69 @@ Either add the @Injectable() decorator to '${provider.node.name
38807
39396
  this.ensureAllShimsForOneFile(sf);
38808
39397
  break;
38809
39398
  }
38810
- const sfPath = absoluteFromSourceFile(sf);
38811
- const fileRecord = this.state.get(sfPath);
38812
- const typeCheckProgram = this.typeCheckingStrategy.getProgram();
38813
- const diagnostics = [];
38814
- if (fileRecord.hasInlines) {
38815
- const inlineSf = getSourceFileOrError(typeCheckProgram, sfPath);
38816
- diagnostics.push(...typeCheckProgram.getSemanticDiagnostics(inlineSf).map(diag => convertDiagnostic(diag, fileRecord.sourceManager)));
38817
- }
38818
- for (const [shimPath, shimRecord] of fileRecord.shimData) {
39399
+ return this.perf.inPhase(PerfPhase.TtcDiagnostics, () => {
39400
+ const sfPath = absoluteFromSourceFile(sf);
39401
+ const fileRecord = this.state.get(sfPath);
39402
+ const typeCheckProgram = this.typeCheckingStrategy.getProgram();
39403
+ const diagnostics = [];
39404
+ if (fileRecord.hasInlines) {
39405
+ const inlineSf = getSourceFileOrError(typeCheckProgram, sfPath);
39406
+ diagnostics.push(...typeCheckProgram.getSemanticDiagnostics(inlineSf).map(diag => convertDiagnostic(diag, fileRecord.sourceManager)));
39407
+ }
39408
+ for (const [shimPath, shimRecord] of fileRecord.shimData) {
39409
+ const shimSf = getSourceFileOrError(typeCheckProgram, shimPath);
39410
+ diagnostics.push(...typeCheckProgram.getSemanticDiagnostics(shimSf).map(diag => convertDiagnostic(diag, fileRecord.sourceManager)));
39411
+ diagnostics.push(...shimRecord.genesisDiagnostics);
39412
+ for (const templateData of shimRecord.templates.values()) {
39413
+ diagnostics.push(...templateData.templateDiagnostics);
39414
+ }
39415
+ }
39416
+ return diagnostics.filter((diag) => diag !== null);
39417
+ });
39418
+ }
39419
+ getDiagnosticsForComponent(component) {
39420
+ this.ensureShimForComponent(component);
39421
+ return this.perf.inPhase(PerfPhase.TtcDiagnostics, () => {
39422
+ const sf = component.getSourceFile();
39423
+ const sfPath = absoluteFromSourceFile(sf);
39424
+ const shimPath = this.typeCheckingStrategy.shimPathForComponent(component);
39425
+ const fileRecord = this.getFileData(sfPath);
39426
+ if (!fileRecord.shimData.has(shimPath)) {
39427
+ return [];
39428
+ }
39429
+ const templateId = fileRecord.sourceManager.getTemplateId(component);
39430
+ const shimRecord = fileRecord.shimData.get(shimPath);
39431
+ const typeCheckProgram = this.typeCheckingStrategy.getProgram();
39432
+ const diagnostics = [];
39433
+ if (shimRecord.hasInlines) {
39434
+ const inlineSf = getSourceFileOrError(typeCheckProgram, sfPath);
39435
+ diagnostics.push(...typeCheckProgram.getSemanticDiagnostics(inlineSf).map(diag => convertDiagnostic(diag, fileRecord.sourceManager)));
39436
+ }
38819
39437
  const shimSf = getSourceFileOrError(typeCheckProgram, shimPath);
38820
39438
  diagnostics.push(...typeCheckProgram.getSemanticDiagnostics(shimSf).map(diag => convertDiagnostic(diag, fileRecord.sourceManager)));
38821
39439
  diagnostics.push(...shimRecord.genesisDiagnostics);
38822
39440
  for (const templateData of shimRecord.templates.values()) {
38823
39441
  diagnostics.push(...templateData.templateDiagnostics);
38824
39442
  }
38825
- }
38826
- return diagnostics.filter((diag) => diag !== null);
38827
- }
38828
- getDiagnosticsForComponent(component) {
38829
- this.ensureShimForComponent(component);
38830
- const sf = component.getSourceFile();
38831
- const sfPath = absoluteFromSourceFile(sf);
38832
- const shimPath = this.typeCheckingStrategy.shimPathForComponent(component);
38833
- const fileRecord = this.getFileData(sfPath);
38834
- if (!fileRecord.shimData.has(shimPath)) {
38835
- return [];
38836
- }
38837
- const templateId = fileRecord.sourceManager.getTemplateId(component);
38838
- const shimRecord = fileRecord.shimData.get(shimPath);
38839
- const typeCheckProgram = this.typeCheckingStrategy.getProgram();
38840
- const diagnostics = [];
38841
- if (shimRecord.hasInlines) {
38842
- const inlineSf = getSourceFileOrError(typeCheckProgram, sfPath);
38843
- diagnostics.push(...typeCheckProgram.getSemanticDiagnostics(inlineSf).map(diag => convertDiagnostic(diag, fileRecord.sourceManager)));
38844
- }
38845
- const shimSf = getSourceFileOrError(typeCheckProgram, shimPath);
38846
- diagnostics.push(...typeCheckProgram.getSemanticDiagnostics(shimSf).map(diag => convertDiagnostic(diag, fileRecord.sourceManager)));
38847
- diagnostics.push(...shimRecord.genesisDiagnostics);
38848
- for (const templateData of shimRecord.templates.values()) {
38849
- diagnostics.push(...templateData.templateDiagnostics);
38850
- }
38851
- return diagnostics.filter((diag) => diag !== null && diag.templateId === templateId);
39443
+ return diagnostics.filter((diag) => diag !== null && diag.templateId === templateId);
39444
+ });
38852
39445
  }
38853
39446
  getTypeCheckBlock(component) {
38854
39447
  return this.getLatestComponentState(component).tcb;
38855
39448
  }
38856
- getGlobalCompletions(context, component) {
39449
+ getGlobalCompletions(context, component, node) {
38857
39450
  const engine = this.getOrCreateCompletionEngine(component);
38858
39451
  if (engine === null) {
38859
39452
  return null;
38860
39453
  }
38861
- return engine.getGlobalCompletions(context);
39454
+ return this.perf.inPhase(PerfPhase.TtcAutocompletion, () => engine.getGlobalCompletions(context, node));
38862
39455
  }
38863
39456
  getExpressionCompletionLocation(ast, component) {
38864
39457
  const engine = this.getOrCreateCompletionEngine(component);
38865
39458
  if (engine === null) {
38866
39459
  return null;
38867
39460
  }
38868
- return engine.getExpressionCompletionLocation(ast);
39461
+ return this.perf.inPhase(PerfPhase.TtcAutocompletion, () => engine.getExpressionCompletionLocation(ast));
38869
39462
  }
38870
39463
  invalidateClass(clazz) {
38871
39464
  this.completionCache.delete(clazz);
@@ -38906,43 +39499,48 @@ Either add the @Injectable() decorator to '${provider.node.name
38906
39499
  if (previousResults === null || !previousResults.isComplete) {
38907
39500
  return;
38908
39501
  }
39502
+ this.perf.eventCount(PerfEvent.ReuseTypeCheckFile);
38909
39503
  this.state.set(sfPath, previousResults);
38910
39504
  }
38911
39505
  ensureAllShimsForAllFiles() {
38912
39506
  if (this.isComplete) {
38913
39507
  return;
38914
39508
  }
38915
- const host = new WholeProgramTypeCheckingHost(this);
38916
- const ctx = this.newContext(host);
38917
- for (const sf of this.originalProgram.getSourceFiles()) {
38918
- if (sf.isDeclarationFile || isShim(sf)) {
38919
- continue;
39509
+ this.perf.inPhase(PerfPhase.TcbGeneration, () => {
39510
+ const host = new WholeProgramTypeCheckingHost(this);
39511
+ const ctx = this.newContext(host);
39512
+ for (const sf of this.originalProgram.getSourceFiles()) {
39513
+ if (sf.isDeclarationFile || isShim(sf)) {
39514
+ continue;
39515
+ }
39516
+ this.maybeAdoptPriorResultsForFile(sf);
39517
+ const sfPath = absoluteFromSourceFile(sf);
39518
+ const fileData = this.getFileData(sfPath);
39519
+ if (fileData.isComplete) {
39520
+ continue;
39521
+ }
39522
+ this.typeCheckAdapter.typeCheck(sf, ctx);
39523
+ fileData.isComplete = true;
38920
39524
  }
39525
+ this.updateFromContext(ctx);
39526
+ this.isComplete = true;
39527
+ });
39528
+ }
39529
+ ensureAllShimsForOneFile(sf) {
39530
+ this.perf.inPhase(PerfPhase.TcbGeneration, () => {
38921
39531
  this.maybeAdoptPriorResultsForFile(sf);
38922
39532
  const sfPath = absoluteFromSourceFile(sf);
38923
39533
  const fileData = this.getFileData(sfPath);
38924
39534
  if (fileData.isComplete) {
38925
- continue;
39535
+ // All data for this file is present and accounted for already.
39536
+ return;
38926
39537
  }
39538
+ const host = new SingleFileTypeCheckingHost(sfPath, fileData, this.typeCheckingStrategy, this);
39539
+ const ctx = this.newContext(host);
38927
39540
  this.typeCheckAdapter.typeCheck(sf, ctx);
38928
39541
  fileData.isComplete = true;
38929
- }
38930
- this.updateFromContext(ctx);
38931
- this.isComplete = true;
38932
- }
38933
- ensureAllShimsForOneFile(sf) {
38934
- this.maybeAdoptPriorResultsForFile(sf);
38935
- const sfPath = absoluteFromSourceFile(sf);
38936
- const fileData = this.getFileData(sfPath);
38937
- if (fileData.isComplete) {
38938
- // All data for this file is present and accounted for already.
38939
- return;
38940
- }
38941
- const host = new SingleFileTypeCheckingHost(sfPath, fileData, this.typeCheckingStrategy, this);
38942
- const ctx = this.newContext(host);
38943
- this.typeCheckAdapter.typeCheck(sf, ctx);
38944
- fileData.isComplete = true;
38945
- this.updateFromContext(ctx);
39542
+ this.updateFromContext(ctx);
39543
+ });
38946
39544
  }
38947
39545
  ensureShimForComponent(component) {
38948
39546
  const sf = component.getSourceFile();
@@ -38962,7 +39560,7 @@ Either add the @Injectable() decorator to '${provider.node.name
38962
39560
  newContext(host) {
38963
39561
  const inlining = this.typeCheckingStrategy.supportsInlineOperations ? InliningMode.InlineOps :
38964
39562
  InliningMode.Error;
38965
- return new TypeCheckContextImpl(this.config, this.compilerHost, this.typeCheckingStrategy, this.refEmitter, this.reflector, host, inlining);
39563
+ return new TypeCheckContextImpl(this.config, this.compilerHost, this.typeCheckingStrategy, this.refEmitter, this.reflector, host, inlining, this.perf);
38966
39564
  }
38967
39565
  /**
38968
39566
  * Remove any shim data that depends on inline operations applied to the type-checking program.
@@ -38987,8 +39585,14 @@ Either add the @Injectable() decorator to '${provider.node.name
38987
39585
  }
38988
39586
  updateFromContext(ctx) {
38989
39587
  const updates = ctx.finalize();
38990
- this.typeCheckingStrategy.updateFiles(updates, UpdateMode.Incremental);
38991
- this.priorBuild.recordSuccessfulTypeCheck(this.state);
39588
+ return this.perf.inPhase(PerfPhase.TcbUpdateProgram, () => {
39589
+ if (updates.size > 0) {
39590
+ this.perf.eventCount(PerfEvent.UpdateTypeCheckProgram);
39591
+ }
39592
+ this.typeCheckingStrategy.updateFiles(updates, UpdateMode.Incremental);
39593
+ this.priorBuild.recordSuccessfulTypeCheck(this.state);
39594
+ this.perf.memory(PerfCheckpoint.TtcUpdateProgram);
39595
+ });
38992
39596
  }
38993
39597
  getFileData(path) {
38994
39598
  if (!this.state.has(path)) {
@@ -39006,7 +39610,7 @@ Either add the @Injectable() decorator to '${provider.node.name
39006
39610
  if (builder === null) {
39007
39611
  return null;
39008
39612
  }
39009
- return builder.getSymbol(node);
39613
+ return this.perf.inPhase(PerfPhase.TtcSymbol, () => builder.getSymbol(node));
39010
39614
  }
39011
39615
  getOrCreateSymbolBuilder(component) {
39012
39616
  if (this.symbolBuilderCache.has(component)) {
@@ -39265,7 +39869,7 @@ Either add the @Injectable() decorator to '${provider.node.name
39265
39869
  /**
39266
39870
  * Create a `CompilationTicket` for a brand new compilation, using no prior state.
39267
39871
  */
39268
- function freshCompilationTicket(tsProgram, options, incrementalBuildStrategy, typeCheckingProgramStrategy, enableTemplateTypeChecker, usePoisonedData) {
39872
+ function freshCompilationTicket(tsProgram, options, incrementalBuildStrategy, typeCheckingProgramStrategy, perfRecorder, enableTemplateTypeChecker, usePoisonedData) {
39269
39873
  return {
39270
39874
  kind: CompilationTicketKind.Fresh,
39271
39875
  tsProgram,
@@ -39274,21 +39878,25 @@ Either add the @Injectable() decorator to '${provider.node.name
39274
39878
  typeCheckingProgramStrategy,
39275
39879
  enableTemplateTypeChecker,
39276
39880
  usePoisonedData,
39881
+ perfRecorder: perfRecorder !== null && perfRecorder !== void 0 ? perfRecorder : ActivePerfRecorder.zeroedToNow(),
39277
39882
  };
39278
39883
  }
39279
39884
  /**
39280
39885
  * Create a `CompilationTicket` as efficiently as possible, based on a previous `NgCompiler`
39281
39886
  * instance and a new `ts.Program`.
39282
39887
  */
39283
- function incrementalFromCompilerTicket(oldCompiler, newProgram, incrementalBuildStrategy, typeCheckingProgramStrategy, modifiedResourceFiles) {
39888
+ function incrementalFromCompilerTicket(oldCompiler, newProgram, incrementalBuildStrategy, typeCheckingProgramStrategy, modifiedResourceFiles, perfRecorder) {
39284
39889
  const oldProgram = oldCompiler.getNextProgram();
39285
39890
  const oldDriver = oldCompiler.incrementalStrategy.getIncrementalDriver(oldProgram);
39286
39891
  if (oldDriver === null) {
39287
39892
  // No incremental step is possible here, since no IncrementalDriver was found for the old
39288
39893
  // program.
39289
- return freshCompilationTicket(newProgram, oldCompiler.options, incrementalBuildStrategy, typeCheckingProgramStrategy, oldCompiler.enableTemplateTypeChecker, oldCompiler.usePoisonedData);
39894
+ return freshCompilationTicket(newProgram, oldCompiler.options, incrementalBuildStrategy, typeCheckingProgramStrategy, perfRecorder, oldCompiler.enableTemplateTypeChecker, oldCompiler.usePoisonedData);
39895
+ }
39896
+ if (perfRecorder === null) {
39897
+ perfRecorder = ActivePerfRecorder.zeroedToNow();
39290
39898
  }
39291
- const newDriver = IncrementalDriver.reconcile(oldProgram, oldDriver, newProgram, modifiedResourceFiles);
39899
+ const newDriver = IncrementalDriver.reconcile(oldProgram, oldDriver, newProgram, modifiedResourceFiles, perfRecorder);
39292
39900
  return {
39293
39901
  kind: CompilationTicketKind.IncrementalTypeScript,
39294
39902
  enableTemplateTypeChecker: oldCompiler.enableTemplateTypeChecker,
@@ -39299,6 +39907,7 @@ Either add the @Injectable() decorator to '${provider.node.name
39299
39907
  newDriver,
39300
39908
  oldProgram,
39301
39909
  newProgram,
39910
+ perfRecorder,
39302
39911
  };
39303
39912
  }
39304
39913
  function resourceChangeTicket(compiler, modifiedResourceFiles) {
@@ -39306,6 +39915,7 @@ Either add the @Injectable() decorator to '${provider.node.name
39306
39915
  kind: CompilationTicketKind.IncrementalResource,
39307
39916
  compiler,
39308
39917
  modifiedResourceFiles,
39918
+ perfRecorder: ActivePerfRecorder.zeroedToNow(),
39309
39919
  };
39310
39920
  }
39311
39921
  /**
@@ -39321,7 +39931,7 @@ Either add the @Injectable() decorator to '${provider.node.name
39321
39931
  * See the README.md for more information.
39322
39932
  */
39323
39933
  class NgCompiler {
39324
- constructor(adapter, options, tsProgram, typeCheckingProgramStrategy, incrementalStrategy, incrementalDriver, enableTemplateTypeChecker, usePoisonedData, perfRecorder = NOOP_PERF_RECORDER) {
39934
+ constructor(adapter, options, tsProgram, typeCheckingProgramStrategy, incrementalStrategy, incrementalDriver, enableTemplateTypeChecker, usePoisonedData, livePerfRecorder) {
39325
39935
  this.adapter = adapter;
39326
39936
  this.options = options;
39327
39937
  this.tsProgram = tsProgram;
@@ -39330,7 +39940,7 @@ Either add the @Injectable() decorator to '${provider.node.name
39330
39940
  this.incrementalDriver = incrementalDriver;
39331
39941
  this.enableTemplateTypeChecker = enableTemplateTypeChecker;
39332
39942
  this.usePoisonedData = usePoisonedData;
39333
- this.perfRecorder = perfRecorder;
39943
+ this.livePerfRecorder = livePerfRecorder;
39334
39944
  /**
39335
39945
  * Lazily evaluated state of the compilation.
39336
39946
  *
@@ -39350,6 +39960,13 @@ Either add the @Injectable() decorator to '${provider.node.name
39350
39960
  * This is set by (and memoizes) `getNonTemplateDiagnostics`.
39351
39961
  */
39352
39962
  this.nonTemplateDiagnostics = null;
39963
+ /**
39964
+ * `NgCompiler` can be reused for multiple compilations (for resource-only changes), and each
39965
+ * new compilation uses a fresh `PerfRecorder`. Thus, classes created with a lifespan of the
39966
+ * `NgCompiler` use a `DelegatingPerfRecorder` so the `PerfRecorder` they write to can be updated
39967
+ * with each fresh compilation.
39968
+ */
39969
+ this.delegatingPerfRecorder = new DelegatingPerfRecorder(this.perfRecorder);
39353
39970
  this.constructionDiagnostics.push(...this.adapter.constructionDiagnostics);
39354
39971
  const incompatibleTypeCheckOptionsDiagnostic = verifyCompatibleTypeCheckOptions(this.options);
39355
39972
  if (incompatibleTypeCheckOptionsDiagnostic !== null) {
@@ -39360,9 +39977,7 @@ Either add the @Injectable() decorator to '${provider.node.name
39360
39977
  this.entryPoint =
39361
39978
  adapter.entryPoint !== null ? getSourceFileOrNull(tsProgram, adapter.entryPoint) : null;
39362
39979
  const moduleResolutionCache = ts$1.createModuleResolutionCache(this.adapter.getCurrentDirectory(),
39363
- // Note: this used to be an arrow-function closure. However, JS engines like v8 have some
39364
- // strange behaviors with retaining the lexical scope of the closure. Even if this function
39365
- // doesn't retain a reference to `this`, if other closures in the constructor here reference
39980
+ // doen't retain a reference to `this`, if other closures in the constructor here reference
39366
39981
  // `this` internally then a closure created here would retain them. This can cause major
39367
39982
  // memory leak issues since the `moduleResolutionCache` is a long-lived object and finds its
39368
39983
  // way into all kinds of places inside TS internal objects.
@@ -39370,55 +39985,75 @@ Either add the @Injectable() decorator to '${provider.node.name
39370
39985
  this.moduleResolver =
39371
39986
  new ModuleResolver(tsProgram, this.options, this.adapter, moduleResolutionCache);
39372
39987
  this.resourceManager = new AdapterResourceLoader(adapter, this.options);
39373
- this.cycleAnalyzer = new CycleAnalyzer(new ImportGraph(tsProgram.getTypeChecker()));
39988
+ this.cycleAnalyzer =
39989
+ new CycleAnalyzer(new ImportGraph(tsProgram.getTypeChecker(), this.delegatingPerfRecorder));
39374
39990
  this.incrementalStrategy.setIncrementalDriver(this.incrementalDriver, tsProgram);
39375
39991
  this.ignoreForDiagnostics =
39376
39992
  new Set(tsProgram.getSourceFiles().filter(sf => this.adapter.isShim(sf)));
39377
39993
  this.ignoreForEmit = this.adapter.ignoreForEmit;
39994
+ let dtsFileCount = 0;
39995
+ let nonDtsFileCount = 0;
39996
+ for (const sf of tsProgram.getSourceFiles()) {
39997
+ if (sf.isDeclarationFile) {
39998
+ dtsFileCount++;
39999
+ }
40000
+ else {
40001
+ nonDtsFileCount++;
40002
+ }
40003
+ }
40004
+ livePerfRecorder.eventCount(PerfEvent.InputDtsFile, dtsFileCount);
40005
+ livePerfRecorder.eventCount(PerfEvent.InputTsFile, nonDtsFileCount);
39378
40006
  }
39379
40007
  /**
39380
40008
  * Convert a `CompilationTicket` into an `NgCompiler` instance for the requested compilation.
39381
40009
  *
39382
40010
  * Depending on the nature of the compilation request, the `NgCompiler` instance may be reused
39383
40011
  * from a previous compilation and updated with any changes, it may be a new instance which
39384
- * incrementally reuses state from a previous compilation, or it may represent a fresh compilation
39385
- * entirely.
40012
+ * incrementally reuses state from a previous compilation, or it may represent a fresh
40013
+ * compilation entirely.
39386
40014
  */
39387
- static fromTicket(ticket, adapter, perfRecorder) {
40015
+ static fromTicket(ticket, adapter) {
39388
40016
  switch (ticket.kind) {
39389
40017
  case CompilationTicketKind.Fresh:
39390
- return new NgCompiler(adapter, ticket.options, ticket.tsProgram, ticket.typeCheckingProgramStrategy, ticket.incrementalBuildStrategy, IncrementalDriver.fresh(ticket.tsProgram), ticket.enableTemplateTypeChecker, ticket.usePoisonedData, perfRecorder);
40018
+ return new NgCompiler(adapter, ticket.options, ticket.tsProgram, ticket.typeCheckingProgramStrategy, ticket.incrementalBuildStrategy, IncrementalDriver.fresh(ticket.tsProgram), ticket.enableTemplateTypeChecker, ticket.usePoisonedData, ticket.perfRecorder);
39391
40019
  case CompilationTicketKind.IncrementalTypeScript:
39392
- return new NgCompiler(adapter, ticket.options, ticket.newProgram, ticket.typeCheckingProgramStrategy, ticket.incrementalBuildStrategy, ticket.newDriver, ticket.enableTemplateTypeChecker, ticket.usePoisonedData, perfRecorder);
40020
+ return new NgCompiler(adapter, ticket.options, ticket.newProgram, ticket.typeCheckingProgramStrategy, ticket.incrementalBuildStrategy, ticket.newDriver, ticket.enableTemplateTypeChecker, ticket.usePoisonedData, ticket.perfRecorder);
39393
40021
  case CompilationTicketKind.IncrementalResource:
39394
40022
  const compiler = ticket.compiler;
39395
- compiler.updateWithChangedResources(ticket.modifiedResourceFiles);
40023
+ compiler.updateWithChangedResources(ticket.modifiedResourceFiles, ticket.perfRecorder);
39396
40024
  return compiler;
39397
40025
  }
39398
40026
  }
39399
- updateWithChangedResources(changedResources) {
39400
- if (this.compilation === null) {
39401
- // Analysis hasn't happened yet, so no update is necessary - any changes to resources will be
39402
- // captured by the inital analysis pass itself.
39403
- return;
39404
- }
39405
- this.resourceManager.invalidate();
39406
- const classesToUpdate = new Set();
39407
- for (const resourceFile of changedResources) {
39408
- for (const templateClass of this.getComponentsWithTemplateFile(resourceFile)) {
39409
- classesToUpdate.add(templateClass);
40027
+ get perfRecorder() {
40028
+ return this.livePerfRecorder;
40029
+ }
40030
+ updateWithChangedResources(changedResources, perfRecorder) {
40031
+ this.livePerfRecorder = perfRecorder;
40032
+ this.delegatingPerfRecorder.target = perfRecorder;
40033
+ perfRecorder.inPhase(PerfPhase.ResourceUpdate, () => {
40034
+ if (this.compilation === null) {
40035
+ // Analysis hasn't happened yet, so no update is necessary - any changes to resources will
40036
+ // be captured by the inital analysis pass itself.
40037
+ return;
39410
40038
  }
39411
- for (const styleClass of this.getComponentsWithStyleFile(resourceFile)) {
39412
- classesToUpdate.add(styleClass);
40039
+ this.resourceManager.invalidate();
40040
+ const classesToUpdate = new Set();
40041
+ for (const resourceFile of changedResources) {
40042
+ for (const templateClass of this.getComponentsWithTemplateFile(resourceFile)) {
40043
+ classesToUpdate.add(templateClass);
40044
+ }
40045
+ for (const styleClass of this.getComponentsWithStyleFile(resourceFile)) {
40046
+ classesToUpdate.add(styleClass);
40047
+ }
39413
40048
  }
39414
- }
39415
- for (const clazz of classesToUpdate) {
39416
- this.compilation.traitCompiler.updateResources(clazz);
39417
- if (!ts$1.isClassDeclaration(clazz)) {
39418
- continue;
40049
+ for (const clazz of classesToUpdate) {
40050
+ this.compilation.traitCompiler.updateResources(clazz);
40051
+ if (!ts$1.isClassDeclaration(clazz)) {
40052
+ continue;
40053
+ }
40054
+ this.compilation.templateTypeChecker.invalidateClass(clazz);
39419
40055
  }
39420
- this.compilation.templateTypeChecker.invalidateClass(clazz);
39421
- }
40056
+ });
39422
40057
  }
39423
40058
  /**
39424
40059
  * Get the resource dependencies of a file.
@@ -39525,29 +40160,23 @@ Either add the @Injectable() decorator to '${provider.node.name
39525
40160
  if (this.compilation !== null) {
39526
40161
  return;
39527
40162
  }
39528
- this.compilation = this.makeCompilation();
39529
- const analyzeSpan = this.perfRecorder.start('analyze');
39530
- const promises = [];
39531
- for (const sf of this.tsProgram.getSourceFiles()) {
39532
- if (sf.isDeclarationFile) {
39533
- continue;
39534
- }
39535
- const analyzeFileSpan = this.perfRecorder.start('analyzeFile', sf);
39536
- let analysisPromise = this.compilation.traitCompiler.analyzeAsync(sf);
39537
- this.scanForMwp(sf);
39538
- if (analysisPromise === undefined) {
39539
- this.perfRecorder.stop(analyzeFileSpan);
39540
- }
39541
- else if (this.perfRecorder.enabled) {
39542
- analysisPromise = analysisPromise.then(() => this.perfRecorder.stop(analyzeFileSpan));
39543
- }
39544
- if (analysisPromise !== undefined) {
39545
- promises.push(analysisPromise);
40163
+ yield this.perfRecorder.inPhase(PerfPhase.Analysis, () => __awaiter(this, void 0, void 0, function* () {
40164
+ this.compilation = this.makeCompilation();
40165
+ const promises = [];
40166
+ for (const sf of this.tsProgram.getSourceFiles()) {
40167
+ if (sf.isDeclarationFile) {
40168
+ continue;
40169
+ }
40170
+ let analysisPromise = this.compilation.traitCompiler.analyzeAsync(sf);
40171
+ this.scanForMwp(sf);
40172
+ if (analysisPromise !== undefined) {
40173
+ promises.push(analysisPromise);
40174
+ }
39546
40175
  }
39547
- }
39548
- yield Promise.all(promises);
39549
- this.perfRecorder.stop(analyzeSpan);
39550
- this.resolveCompilation(this.compilation.traitCompiler);
40176
+ yield Promise.all(promises);
40177
+ this.perfRecorder.memory(PerfCheckpoint.Analysis);
40178
+ this.resolveCompilation(this.compilation.traitCompiler);
40179
+ }));
39551
40180
  });
39552
40181
  }
39553
40182
  /**
@@ -39557,9 +40186,7 @@ Either add the @Injectable() decorator to '${provider.node.name
39557
40186
  */
39558
40187
  listLazyRoutes(entryRoute) {
39559
40188
  if (entryRoute) {
39560
- // Note:
39561
- // This resolution step is here to match the implementation of the old `AotCompilerHost` (see
39562
- // https://github.com/angular/angular/blob/50732e156/packages/compiler-cli/src/transformers/compiler_host.ts#L175-L188).
40189
+ // htts://github.com/angular/angular/blob/50732e156/packages/compiler-cli/src/transformers/compiler_host.ts#L175-L188).
39563
40190
  //
39564
40191
  // `@angular/cli` will always call this API with an absolute path, so the resolution step is
39565
40192
  // not necessary, but keeping it backwards compatible in case someone else is using the API.
@@ -39600,10 +40227,11 @@ Either add the @Injectable() decorator to '${provider.node.name
39600
40227
  else {
39601
40228
  importRewriter = new NoopImportRewriter();
39602
40229
  }
40230
+ const defaultImportTracker = new DefaultImportTracker();
39603
40231
  const before = [
39604
- ivyTransformFactory(compilation.traitCompiler, compilation.reflector, importRewriter, compilation.defaultImportTracker, compilation.isCore, this.closureCompilerEnabled),
40232
+ ivyTransformFactory(compilation.traitCompiler, compilation.reflector, importRewriter, defaultImportTracker, this.delegatingPerfRecorder, compilation.isCore, this.closureCompilerEnabled),
39605
40233
  aliasTransformFactory(compilation.traitCompiler.exportStatements),
39606
- compilation.defaultImportTracker.importPreservingTransformer(),
40234
+ defaultImportTracker.importPreservingTransformer(),
39607
40235
  ];
39608
40236
  const afterDeclarations = [];
39609
40237
  if (compilation.dtsTransforms !== null) {
@@ -39637,25 +40265,27 @@ Either add the @Injectable() decorator to '${provider.node.name
39637
40265
  return this.compilation;
39638
40266
  }
39639
40267
  analyzeSync() {
39640
- const analyzeSpan = this.perfRecorder.start('analyze');
39641
- this.compilation = this.makeCompilation();
39642
- for (const sf of this.tsProgram.getSourceFiles()) {
39643
- if (sf.isDeclarationFile) {
39644
- continue;
40268
+ this.perfRecorder.inPhase(PerfPhase.Analysis, () => {
40269
+ this.compilation = this.makeCompilation();
40270
+ for (const sf of this.tsProgram.getSourceFiles()) {
40271
+ if (sf.isDeclarationFile) {
40272
+ continue;
40273
+ }
40274
+ this.compilation.traitCompiler.analyzeSync(sf);
40275
+ this.scanForMwp(sf);
39645
40276
  }
39646
- const analyzeFileSpan = this.perfRecorder.start('analyzeFile', sf);
39647
- this.compilation.traitCompiler.analyzeSync(sf);
39648
- this.scanForMwp(sf);
39649
- this.perfRecorder.stop(analyzeFileSpan);
39650
- }
39651
- this.perfRecorder.stop(analyzeSpan);
39652
- this.resolveCompilation(this.compilation.traitCompiler);
40277
+ this.perfRecorder.memory(PerfCheckpoint.Analysis);
40278
+ this.resolveCompilation(this.compilation.traitCompiler);
40279
+ });
39653
40280
  }
39654
40281
  resolveCompilation(traitCompiler) {
39655
- traitCompiler.resolve();
39656
- // At this point, analysis is complete and the compiler can now calculate which files need to
39657
- // be emitted, so do that.
39658
- this.incrementalDriver.recordSuccessfulAnalysis(traitCompiler);
40282
+ this.perfRecorder.inPhase(PerfPhase.Resolve, () => {
40283
+ traitCompiler.resolve();
40284
+ // At this point, analysis is complete and the compiler can now calculate which files need to
40285
+ // be emitted, so do that.
40286
+ this.incrementalDriver.recordSuccessfulAnalysis(traitCompiler);
40287
+ this.perfRecorder.memory(PerfCheckpoint.Resolve);
40288
+ });
39659
40289
  }
39660
40290
  get fullTemplateTypeCheck() {
39661
40291
  // Determine the strictness level of type checking based on compiler options. As
@@ -39779,7 +40409,6 @@ Either add the @Injectable() decorator to '${provider.node.name
39779
40409
  getTemplateDiagnostics() {
39780
40410
  const compilation = this.ensureAnalyzed();
39781
40411
  // Get the diagnostics.
39782
- const typeCheckSpan = this.perfRecorder.start('typeCheckDiagnostics');
39783
40412
  const diagnostics = [];
39784
40413
  for (const sf of this.tsProgram.getSourceFiles()) {
39785
40414
  if (sf.isDeclarationFile || this.adapter.isShim(sf)) {
@@ -39788,7 +40417,6 @@ Either add the @Injectable() decorator to '${provider.node.name
39788
40417
  diagnostics.push(...compilation.templateTypeChecker.getDiagnosticsForFile(sf, OptimizeFor.WholeProgram));
39789
40418
  }
39790
40419
  const program = this.typeCheckingProgramStrategy.getProgram();
39791
- this.perfRecorder.stop(typeCheckSpan);
39792
40420
  this.incrementalStrategy.setIncrementalDriver(this.incrementalDriver, program);
39793
40421
  this.nextProgram = program;
39794
40422
  return diagnostics;
@@ -39796,13 +40424,11 @@ Either add the @Injectable() decorator to '${provider.node.name
39796
40424
  getTemplateDiagnosticsForFile(sf, optimizeFor) {
39797
40425
  const compilation = this.ensureAnalyzed();
39798
40426
  // Get the diagnostics.
39799
- const typeCheckSpan = this.perfRecorder.start('typeCheckDiagnostics');
39800
40427
  const diagnostics = [];
39801
40428
  if (!sf.isDeclarationFile && !this.adapter.isShim(sf)) {
39802
40429
  diagnostics.push(...compilation.templateTypeChecker.getDiagnosticsForFile(sf, optimizeFor));
39803
40430
  }
39804
40431
  const program = this.typeCheckingProgramStrategy.getProgram();
39805
- this.perfRecorder.stop(typeCheckSpan);
39806
40432
  this.incrementalStrategy.setIncrementalDriver(this.incrementalDriver, program);
39807
40433
  this.nextProgram = program;
39808
40434
  return diagnostics;
@@ -39910,7 +40536,6 @@ Either add the @Injectable() decorator to '${provider.node.name
39910
40536
  const dtsTransforms = new DtsTransformRegistry();
39911
40537
  const mwpScanner = new ModuleWithProvidersScanner(reflector, evaluator, refEmitter);
39912
40538
  const isCore = isAngularCorePackage(this.tsProgram);
39913
- const defaultImportTracker = new DefaultImportTracker();
39914
40539
  const resourceRegistry = new ResourceRegistry();
39915
40540
  const compilationMode = this.options.compilationMode === 'partial' ? CompilationMode.PARTIAL : CompilationMode.FULL;
39916
40541
  // Cycles are handled in full compilation mode by "remote scoping".
@@ -39921,20 +40546,20 @@ Either add the @Injectable() decorator to '${provider.node.name
39921
40546
  1 /* Error */;
39922
40547
  // Set up the IvyCompilation, which manages state for the Ivy transformer.
39923
40548
  const handlers = [
39924
- 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),
40549
+ 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, this.incrementalDriver.depGraph, injectableRegistry, semanticDepGraphUpdater, this.closureCompilerEnabled, this.delegatingPerfRecorder),
39925
40550
  // TODO(alxhub): understand why the cast here is necessary (something to do with `null`
39926
40551
  // not being assignable to `unknown` when wrapped in `Readonly`).
39927
40552
  // clang-format off
39928
- new DirectiveDecoratorHandler(reflector, evaluator, metaRegistry, scopeRegistry, metaReader, defaultImportTracker, injectableRegistry, isCore, semanticDepGraphUpdater, this.closureCompilerEnabled, compileUndecoratedClassesWithAngularFeatures),
40553
+ new DirectiveDecoratorHandler(reflector, evaluator, metaRegistry, scopeRegistry, metaReader, injectableRegistry, isCore, semanticDepGraphUpdater, this.closureCompilerEnabled, compileUndecoratedClassesWithAngularFeatures, this.delegatingPerfRecorder),
39929
40554
  // clang-format on
39930
40555
  // Pipe handler must be before injectable handler in list so pipe factories are printed
39931
40556
  // before injectable factories (so injectable factories can delegate to them)
39932
- new PipeDecoratorHandler(reflector, evaluator, metaRegistry, scopeRegistry, defaultImportTracker, injectableRegistry, isCore),
39933
- new InjectableDecoratorHandler(reflector, defaultImportTracker, isCore, this.options.strictInjectionParameters || false, injectableRegistry),
39934
- new NgModuleDecoratorHandler(reflector, evaluator, metaReader, metaRegistry, scopeRegistry, referencesRegistry, isCore, routeAnalyzer, refEmitter, this.adapter.factoryTracker, defaultImportTracker, this.closureCompilerEnabled, injectableRegistry, this.options.i18nInLocale),
40557
+ new PipeDecoratorHandler(reflector, evaluator, metaRegistry, scopeRegistry, injectableRegistry, isCore, this.delegatingPerfRecorder),
40558
+ new InjectableDecoratorHandler(reflector, isCore, this.options.strictInjectionParameters || false, injectableRegistry, this.delegatingPerfRecorder),
40559
+ new NgModuleDecoratorHandler(reflector, evaluator, metaReader, metaRegistry, scopeRegistry, referencesRegistry, isCore, routeAnalyzer, refEmitter, this.adapter.factoryTracker, this.closureCompilerEnabled, injectableRegistry, this.delegatingPerfRecorder, this.options.i18nInLocale),
39935
40560
  ];
39936
- const traitCompiler = new TraitCompiler(handlers, reflector, this.perfRecorder, this.incrementalDriver, this.options.compileNonExportedClasses !== false, compilationMode, dtsTransforms, semanticDepGraphUpdater);
39937
- const templateTypeChecker = new TemplateTypeCheckerImpl(this.tsProgram, this.typeCheckingProgramStrategy, traitCompiler, this.getTypeCheckingConfig(), refEmitter, reflector, this.adapter, this.incrementalDriver, scopeRegistry, typeCheckScopeRegistry);
40561
+ const traitCompiler = new TraitCompiler(handlers, reflector, this.delegatingPerfRecorder, this.incrementalDriver, this.options.compileNonExportedClasses !== false, compilationMode, dtsTransforms, semanticDepGraphUpdater);
40562
+ const templateTypeChecker = new TemplateTypeCheckerImpl(this.tsProgram, this.typeCheckingProgramStrategy, traitCompiler, this.getTypeCheckingConfig(), refEmitter, reflector, this.adapter, this.incrementalDriver, scopeRegistry, typeCheckScopeRegistry, this.delegatingPerfRecorder);
39938
40563
  return {
39939
40564
  isCore,
39940
40565
  traitCompiler,
@@ -39946,7 +40571,6 @@ Either add the @Injectable() decorator to '${provider.node.name
39946
40571
  mwpScanner,
39947
40572
  metaReader,
39948
40573
  typeCheckScopeRegistry,
39949
- defaultImportTracker,
39950
40574
  aliasingHost,
39951
40575
  refEmitter,
39952
40576
  templateTypeChecker,
@@ -40127,24 +40751,30 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
40127
40751
  };
40128
40752
  }
40129
40753
  function getExtendedConfigPath(configFile, extendsValue, host, fs) {
40130
- let extendedConfigPath = null;
40754
+ const result = getExtendedConfigPathWorker(configFile, extendsValue, host, fs);
40755
+ if (result !== null) {
40756
+ return result;
40757
+ }
40758
+ // Try to resolve the paths with a json extension append a json extension to the file in case if
40759
+ // it is missing and the resolution failed. This is to replicate TypeScript behaviour, see:
40760
+ // https://github.com/microsoft/TypeScript/blob/294a5a7d784a5a95a8048ee990400979a6bc3a1c/src/compiler/commandLineParser.ts#L2806
40761
+ return getExtendedConfigPathWorker(configFile, `${extendsValue}.json`, host, fs);
40762
+ }
40763
+ function getExtendedConfigPathWorker(configFile, extendsValue, host, fs) {
40131
40764
  if (extendsValue.startsWith('.') || fs.isRooted(extendsValue)) {
40132
- extendedConfigPath = host.resolve(host.dirname(configFile), extendsValue);
40133
- extendedConfigPath = host.extname(extendedConfigPath) ?
40134
- extendedConfigPath :
40135
- absoluteFrom(`${extendedConfigPath}.json`);
40765
+ const extendedConfigPath = host.resolve(host.dirname(configFile), extendsValue);
40766
+ if (host.exists(extendedConfigPath)) {
40767
+ return extendedConfigPath;
40768
+ }
40136
40769
  }
40137
40770
  else {
40138
40771
  const parseConfigHost = createParseConfigHost(host, fs);
40139
40772
  // Path isn't a rooted or relative path, resolve like a module.
40140
40773
  const { resolvedModule, } = ts$1.nodeModuleNameResolver(extendsValue, configFile, { moduleResolution: ts$1.ModuleResolutionKind.NodeJs, resolveJsonModule: true }, parseConfigHost);
40141
40774
  if (resolvedModule) {
40142
- extendedConfigPath = absoluteFrom(resolvedModule.resolvedFileName);
40775
+ return absoluteFrom(resolvedModule.resolvedFileName);
40143
40776
  }
40144
40777
  }
40145
- if (extendedConfigPath !== null && host.exists(extendedConfigPath)) {
40146
- return extendedConfigPath;
40147
- }
40148
40778
  return null;
40149
40779
  }
40150
40780
 
@@ -40515,7 +41145,7 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
40515
41145
  */
40516
41146
  function toAttributeString(attribute) {
40517
41147
  var _a, _b;
40518
- if (attribute instanceof BoundEvent) {
41148
+ if (attribute instanceof BoundEvent || attribute instanceof BoundAttribute) {
40519
41149
  return `[${attribute.name}]`;
40520
41150
  }
40521
41151
  else {
@@ -40693,6 +41323,7 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
40693
41323
  * Use of this source code is governed by an MIT-style license that can be
40694
41324
  * found in the LICENSE file at https://angular.io/license
40695
41325
  */
41326
+ const PRE_COMPILED_STYLE_EXTENSIONS = ['.scss', '.sass', '.less', '.styl'];
40696
41327
  class LanguageServiceAdapter {
40697
41328
  constructor(project) {
40698
41329
  this.project = project;
@@ -40709,6 +41340,22 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
40709
41340
  this.lastReadResourceVersion = new Map();
40710
41341
  this.rootDirs = getRootDirs(this, project.getCompilationSettings());
40711
41342
  }
41343
+ resourceNameToFileName(url, fromFile, fallbackResolve) {
41344
+ var _a;
41345
+ // If we are trying to resolve a `.css` file, see if we can find a pre-compiled file with the
41346
+ // same name instead. That way, we can provide go-to-definition for the pre-compiled files which
41347
+ // would generally be the desired behavior.
41348
+ if (url.endsWith('.css')) {
41349
+ const styleUrl = path.resolve(fromFile, '..', url);
41350
+ for (const ext of PRE_COMPILED_STYLE_EXTENSIONS) {
41351
+ const precompiledFileUrl = styleUrl.replace(/\.css$/, ext);
41352
+ if (this.fileExists(precompiledFileUrl)) {
41353
+ return precompiledFileUrl;
41354
+ }
41355
+ }
41356
+ }
41357
+ return (_a = fallbackResolve === null || fallbackResolve === void 0 ? void 0 : fallbackResolve(url, fromFile)) !== null && _a !== void 0 ? _a : null;
41358
+ }
40712
41359
  isShim(sf) {
40713
41360
  return isShim(sf);
40714
41361
  }
@@ -40853,14 +41500,20 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
40853
41500
  const ticket = resourceChangeTicket(this.compiler, modifiedResourceFiles);
40854
41501
  this.compiler = NgCompiler.fromTicket(ticket, this.adapter);
40855
41502
  }
41503
+ else {
41504
+ // The previous NgCompiler is being reused, but we still want to reset its performance
41505
+ // tracker to capture only the operations that are needed to service the current request.
41506
+ this.compiler.perfRecorder.reset();
41507
+ }
40856
41508
  return this.compiler;
40857
41509
  }
40858
41510
  let ticket;
40859
41511
  if (this.compiler === null || this.lastKnownProgram === null) {
40860
- ticket = freshCompilationTicket(program, this.options, this.incrementalStrategy, this.programStrategy, true, true);
41512
+ ticket = freshCompilationTicket(program, this.options, this.incrementalStrategy, this.programStrategy,
41513
+ /* perfRecorder */ null, true, true);
40861
41514
  }
40862
41515
  else {
40863
- ticket = incrementalFromCompilerTicket(this.compiler, program, this.incrementalStrategy, this.programStrategy, modifiedResourceFiles);
41516
+ ticket = incrementalFromCompilerTicket(this.compiler, program, this.incrementalStrategy, this.programStrategy, modifiedResourceFiles, /* perfRecorder */ null);
40864
41517
  }
40865
41518
  this.compiler = NgCompiler.fromTicket(ticket, this.adapter);
40866
41519
  this.lastKnownProgram = program;
@@ -41762,17 +42415,17 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
41762
42415
  * Get completions for a property expression in a global context (e.g. `{{y|}}`).
41763
42416
  */
41764
42417
  getGlobalPropertyExpressionCompletion(options) {
41765
- const completions = this.templateTypeChecker.getGlobalCompletions(this.template, this.component);
42418
+ const completions = this.templateTypeChecker.getGlobalCompletions(this.template, this.component, this.node);
41766
42419
  if (completions === null) {
41767
42420
  return undefined;
41768
42421
  }
41769
- const { componentContext, templateContext } = completions;
42422
+ const { componentContext, templateContext, nodeContext: astContext } = completions;
41770
42423
  const replacementSpan = makeReplacementSpanFromAst(this.node);
41771
42424
  // Merge TS completion results with results from the template scope.
41772
42425
  let entries = [];
41773
- const tsLsCompletions = this.tsLS.getCompletionsAtPosition(componentContext.shimPath, componentContext.positionInShimFile, options);
41774
- if (tsLsCompletions !== undefined) {
41775
- for (const tsCompletion of tsLsCompletions.entries) {
42426
+ const componentCompletions = this.tsLS.getCompletionsAtPosition(componentContext.shimPath, componentContext.positionInShimFile, options);
42427
+ if (componentCompletions !== undefined) {
42428
+ for (const tsCompletion of componentCompletions.entries) {
41776
42429
  // Skip completions that are shadowed by a template entity definition.
41777
42430
  if (templateContext.has(tsCompletion.name)) {
41778
42431
  continue;
@@ -41783,6 +42436,20 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
41783
42436
  replacementSpan }));
41784
42437
  }
41785
42438
  }
42439
+ // Merge TS completion results with results from the ast context.
42440
+ if (astContext !== null) {
42441
+ const nodeCompletions = this.tsLS.getCompletionsAtPosition(astContext.shimPath, astContext.positionInShimFile, options);
42442
+ if (nodeCompletions !== undefined) {
42443
+ for (const tsCompletion of nodeCompletions.entries) {
42444
+ if (this.isValidNodeContextCompletion(tsCompletion)) {
42445
+ entries.push(Object.assign(Object.assign({}, tsCompletion), {
42446
+ // Substitute the TS completion's `replacementSpan` (which uses offsets within the
42447
+ // TCB) with the `replacementSpan` within the template source.
42448
+ replacementSpan }));
42449
+ }
42450
+ }
42451
+ }
42452
+ }
41786
42453
  for (const [name, entity] of templateContext) {
41787
42454
  entries.push({
41788
42455
  name,
@@ -41808,7 +42475,7 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
41808
42475
  * `{{y|}}`).
41809
42476
  */
41810
42477
  getGlobalPropertyExpressionCompletionDetails(entryName, formatOptions, preferences) {
41811
- const completions = this.templateTypeChecker.getGlobalCompletions(this.template, this.component);
42478
+ const completions = this.templateTypeChecker.getGlobalCompletions(this.template, this.component, this.node);
41812
42479
  if (completions === null) {
41813
42480
  return undefined;
41814
42481
  }
@@ -41841,7 +42508,7 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
41841
42508
  * `{{y|}}`).
41842
42509
  */
41843
42510
  getGlobalPropertyExpressionCompletionSymbol(entryName) {
41844
- const completions = this.templateTypeChecker.getGlobalCompletions(this.template, this.component);
42511
+ const completions = this.templateTypeChecker.getGlobalCompletions(this.template, this.component, this.node);
41845
42512
  if (completions === null) {
41846
42513
  return undefined;
41847
42514
  }
@@ -42113,6 +42780,24 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
42113
42780
  isNewIdentifierLocation: false,
42114
42781
  };
42115
42782
  }
42783
+ /**
42784
+ * From the AST node of the cursor position, include completion of string literals, number
42785
+ * literals, `true`, `false`, `null`, and `undefined`.
42786
+ */
42787
+ isValidNodeContextCompletion(completion) {
42788
+ if (completion.kind === ts$1.ScriptElementKind.string) {
42789
+ // 'string' kind includes both string literals and number literals
42790
+ return true;
42791
+ }
42792
+ if (completion.kind === ts$1.ScriptElementKind.keyword) {
42793
+ return completion.name === 'true' || completion.name === 'false' ||
42794
+ completion.name === 'null';
42795
+ }
42796
+ if (completion.kind === ts$1.ScriptElementKind.variableElement) {
42797
+ return completion.name === 'undefined';
42798
+ }
42799
+ return false;
42800
+ }
42116
42801
  }
42117
42802
  function makeReplacementSpanFromParseSourceSpan(span) {
42118
42803
  return {
@@ -42664,43 +43349,50 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
42664
43349
  this.ttc = this.compiler.getTemplateTypeChecker();
42665
43350
  }
42666
43351
  getRenameInfo(filePath, position) {
42667
- const templateInfo = getTemplateInfoAtPosition(filePath, position, this.compiler);
42668
- // We could not get a template at position so we assume the request came from outside the
42669
- // template.
42670
- if (templateInfo === undefined) {
42671
- return this.tsLS.getRenameInfo(filePath, position);
42672
- }
42673
- const allTargetDetails = this.getTargetDetailsAtTemplatePosition(templateInfo, position);
42674
- if (allTargetDetails === null) {
42675
- return { canRename: false, localizedErrorMessage: 'Could not find template node at position.' };
42676
- }
42677
- const { templateTarget } = allTargetDetails[0];
42678
- const templateTextAndSpan = getRenameTextAndSpanAtPosition(templateTarget, position);
42679
- if (templateTextAndSpan === null) {
42680
- return { canRename: false, localizedErrorMessage: 'Could not determine template node text.' };
42681
- }
42682
- const { text, span } = templateTextAndSpan;
42683
- return {
42684
- canRename: true,
42685
- displayName: text,
42686
- fullDisplayName: text,
42687
- triggerSpan: span,
42688
- };
43352
+ return this.compiler.perfRecorder.inPhase(PerfPhase.LsReferencesAndRenames, () => {
43353
+ const templateInfo = getTemplateInfoAtPosition(filePath, position, this.compiler);
43354
+ // We could not get a template at position so we assume the request came from outside the
43355
+ // template.
43356
+ if (templateInfo === undefined) {
43357
+ return this.tsLS.getRenameInfo(filePath, position);
43358
+ }
43359
+ const allTargetDetails = this.getTargetDetailsAtTemplatePosition(templateInfo, position);
43360
+ if (allTargetDetails === null) {
43361
+ return {
43362
+ canRename: false,
43363
+ localizedErrorMessage: 'Could not find template node at position.',
43364
+ };
43365
+ }
43366
+ const { templateTarget } = allTargetDetails[0];
43367
+ const templateTextAndSpan = getRenameTextAndSpanAtPosition(templateTarget, position);
43368
+ if (templateTextAndSpan === null) {
43369
+ return { canRename: false, localizedErrorMessage: 'Could not determine template node text.' };
43370
+ }
43371
+ const { text, span } = templateTextAndSpan;
43372
+ return {
43373
+ canRename: true,
43374
+ displayName: text,
43375
+ fullDisplayName: text,
43376
+ triggerSpan: span,
43377
+ };
43378
+ });
42689
43379
  }
42690
43380
  findRenameLocations(filePath, position) {
42691
43381
  this.ttc.generateAllTypeCheckBlocks();
42692
- const templateInfo = getTemplateInfoAtPosition(filePath, position, this.compiler);
42693
- // We could not get a template at position so we assume the request came from outside the
42694
- // template.
42695
- if (templateInfo === undefined) {
42696
- const requestNode = this.getTsNodeAtPosition(filePath, position);
42697
- if (requestNode === null) {
42698
- return undefined;
43382
+ return this.compiler.perfRecorder.inPhase(PerfPhase.LsReferencesAndRenames, () => {
43383
+ const templateInfo = getTemplateInfoAtPosition(filePath, position, this.compiler);
43384
+ // We could not get a template at position so we assume the request came from outside the
43385
+ // template.
43386
+ if (templateInfo === undefined) {
43387
+ const requestNode = this.getTsNodeAtPosition(filePath, position);
43388
+ if (requestNode === null) {
43389
+ return undefined;
43390
+ }
43391
+ const requestOrigin = { kind: RequestKind.TypeScript, requestNode };
43392
+ return this.findRenameLocationsAtTypescriptPosition(filePath, position, requestOrigin);
42699
43393
  }
42700
- const requestOrigin = { kind: RequestKind.TypeScript, requestNode };
42701
- return this.findRenameLocationsAtTypescriptPosition(filePath, position, requestOrigin);
42702
- }
42703
- return this.findRenameLocationsAtTemplatePosition(templateInfo, position);
43394
+ return this.findRenameLocationsAtTemplatePosition(templateInfo, position);
43395
+ });
42704
43396
  }
42705
43397
  findRenameLocationsAtTemplatePosition(templateInfo, position) {
42706
43398
  const allTargetDetails = this.getTargetDetailsAtTemplatePosition(templateInfo, position);
@@ -42735,52 +43427,56 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
42735
43427
  return (_a = findTightestNode(sf, position)) !== null && _a !== void 0 ? _a : null;
42736
43428
  }
42737
43429
  findRenameLocationsAtTypescriptPosition(filePath, position, requestOrigin) {
42738
- let originalNodeText;
42739
- if (requestOrigin.kind === RequestKind.TypeScript) {
42740
- originalNodeText = requestOrigin.requestNode.getText();
42741
- }
42742
- else {
42743
- const templateNodeText = getRenameTextAndSpanAtPosition(requestOrigin.requestNode, requestOrigin.position);
42744
- if (templateNodeText === null) {
42745
- return undefined;
43430
+ return this.compiler.perfRecorder.inPhase(PerfPhase.LsReferencesAndRenames, () => {
43431
+ let originalNodeText;
43432
+ if (requestOrigin.kind === RequestKind.TypeScript) {
43433
+ originalNodeText = requestOrigin.requestNode.getText();
42746
43434
  }
42747
- originalNodeText = templateNodeText.text;
42748
- }
42749
- const locations = this.tsLS.findRenameLocations(filePath, position, /*findInStrings*/ false, /*findInComments*/ false);
42750
- if (locations === undefined) {
42751
- return undefined;
42752
- }
42753
- const entries = new Map();
42754
- for (const location of locations) {
42755
- // TODO(atscott): Determine if a file is a shim file in a more robust way and make the API
42756
- // available in an appropriate location.
42757
- if (this.ttc.isTrackedTypeCheckFile(absoluteFrom(location.fileName))) {
42758
- const entry = this.convertToTemplateDocumentSpan(location, this.ttc, originalNodeText);
42759
- // There is no template node whose text matches the original rename request. Bail on
42760
- // renaming completely rather than providing incomplete results.
42761
- if (entry === null) {
43435
+ else {
43436
+ const templateNodeText = getRenameTextAndSpanAtPosition(requestOrigin.requestNode, requestOrigin.position);
43437
+ if (templateNodeText === null) {
42762
43438
  return undefined;
42763
43439
  }
42764
- entries.set(createLocationKey(entry), entry);
43440
+ originalNodeText = templateNodeText.text;
42765
43441
  }
42766
- else {
42767
- // Ensure we only allow renaming a TS result with matching text
42768
- const refNode = this.getTsNodeAtPosition(location.fileName, location.textSpan.start);
42769
- if (refNode === null || refNode.getText() !== originalNodeText) {
42770
- return undefined;
43442
+ const locations = this.tsLS.findRenameLocations(filePath, position, /*findInStrings*/ false, /*findInComments*/ false);
43443
+ if (locations === undefined) {
43444
+ return undefined;
43445
+ }
43446
+ const entries = new Map();
43447
+ for (const location of locations) {
43448
+ // TODO(atscott): Determine if a file is a shim file in a more robust way and make the API
43449
+ // available in an appropriate location.
43450
+ if (this.ttc.isTrackedTypeCheckFile(absoluteFrom(location.fileName))) {
43451
+ const entry = this.convertToTemplateDocumentSpan(location, this.ttc, originalNodeText);
43452
+ // There is no template node whose text matches the original rename request. Bail on
43453
+ // renaming completely rather than providing incomplete results.
43454
+ if (entry === null) {
43455
+ return undefined;
43456
+ }
43457
+ entries.set(createLocationKey(entry), entry);
43458
+ }
43459
+ else {
43460
+ // Ensure we only allow renaming a TS result with matching text
43461
+ const refNode = this.getTsNodeAtPosition(location.fileName, location.textSpan.start);
43462
+ if (refNode === null || refNode.getText() !== originalNodeText) {
43463
+ return undefined;
43464
+ }
43465
+ entries.set(createLocationKey(location), location);
42771
43466
  }
42772
- entries.set(createLocationKey(location), location);
42773
43467
  }
42774
- }
42775
- return Array.from(entries.values());
43468
+ return Array.from(entries.values());
43469
+ });
42776
43470
  }
42777
43471
  getReferencesAtPosition(filePath, position) {
42778
43472
  this.ttc.generateAllTypeCheckBlocks();
42779
- const templateInfo = getTemplateInfoAtPosition(filePath, position, this.compiler);
42780
- if (templateInfo === undefined) {
42781
- return this.getReferencesAtTypescriptPosition(filePath, position);
42782
- }
42783
- return this.getReferencesAtTemplatePosition(templateInfo, position);
43473
+ return this.compiler.perfRecorder.inPhase(PerfPhase.LsReferencesAndRenames, () => {
43474
+ const templateInfo = getTemplateInfoAtPosition(filePath, position, this.compiler);
43475
+ if (templateInfo === undefined) {
43476
+ return this.getReferencesAtTypescriptPosition(filePath, position);
43477
+ }
43478
+ return this.getReferencesAtTemplatePosition(templateInfo, position);
43479
+ });
42784
43480
  }
42785
43481
  getReferencesAtTemplatePosition(templateInfo, position) {
42786
43482
  const allTargetDetails = this.getTargetDetailsAtTemplatePosition(templateInfo, position);
@@ -43021,49 +43717,52 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
43021
43717
  return this.options;
43022
43718
  }
43023
43719
  getSemanticDiagnostics(fileName) {
43024
- const compiler = this.compilerFactory.getOrCreate();
43025
- const ttc = compiler.getTemplateTypeChecker();
43026
- const diagnostics = [];
43027
- if (isTypeScriptFile(fileName)) {
43028
- const program = compiler.getNextProgram();
43029
- const sourceFile = program.getSourceFile(fileName);
43030
- if (sourceFile) {
43031
- const ngDiagnostics = compiler.getDiagnosticsForFile(sourceFile, OptimizeFor.SingleFile);
43032
- // There are several kinds of diagnostics returned by `NgCompiler` for a source file:
43033
- //
43034
- // 1. Angular-related non-template diagnostics from decorated classes within that file.
43035
- // 2. Template diagnostics for components with direct inline templates (a string literal).
43036
- // 3. Template diagnostics for components with indirect inline templates (templates computed
43037
- // by expression).
43038
- // 4. Template diagnostics for components with external templates.
43039
- //
43040
- // When showing diagnostics for a TS source file, we want to only include kinds 1 and 2 -
43041
- // those diagnostics which are reported at a location within the TS file itself. Diagnostics
43042
- // for external templates will be shown when editing that template file (the `else` block)
43043
- // below.
43044
- //
43045
- // Currently, indirect inline template diagnostics (kind 3) are not shown at all by the
43046
- // Language Service, because there is no sensible location in the user's code for them. Such
43047
- // templates are an edge case, though, and should not be common.
43048
- //
43049
- // TODO(alxhub): figure out a good user experience for indirect template diagnostics and
43050
- // show them from within the Language Service.
43051
- diagnostics.push(...ngDiagnostics.filter(diag => diag.file !== undefined && diag.file.fileName === sourceFile.fileName));
43720
+ return this.withCompilerAndPerfTracing(PerfPhase.LsDiagnostics, (compiler) => {
43721
+ const ttc = compiler.getTemplateTypeChecker();
43722
+ const diagnostics = [];
43723
+ if (isTypeScriptFile(fileName)) {
43724
+ const program = compiler.getNextProgram();
43725
+ const sourceFile = program.getSourceFile(fileName);
43726
+ if (sourceFile) {
43727
+ const ngDiagnostics = compiler.getDiagnosticsForFile(sourceFile, OptimizeFor.SingleFile);
43728
+ // There are several kinds of diagnostics returned by `NgCompiler` for a source file:
43729
+ //
43730
+ // 1. Angular-related non-template diagnostics from decorated classes within that
43731
+ // file.
43732
+ // 2. Template diagnostics for components with direct inline templates (a string
43733
+ // literal).
43734
+ // 3. Template diagnostics for components with indirect inline templates (templates
43735
+ // computed
43736
+ // by expression).
43737
+ // 4. Template diagnostics for components with external templates.
43738
+ //
43739
+ // When showing diagnostics for a TS source file, we want to only include kinds 1 and
43740
+ // 2 - those diagnostics which are reported at a location within the TS file itself.
43741
+ // Diagnostics for external templates will be shown when editing that template file
43742
+ // (the `else` block) below.
43743
+ //
43744
+ // Currently, indirect inline template diagnostics (kind 3) are not shown at all by
43745
+ // the Language Service, because there is no sensible location in the user's code for
43746
+ // them. Such templates are an edge case, though, and should not be common.
43747
+ //
43748
+ // TODO(alxhub): figure out a good user experience for indirect template diagnostics
43749
+ // and show them from within the Language Service.
43750
+ diagnostics.push(...ngDiagnostics.filter(diag => diag.file !== undefined && diag.file.fileName === sourceFile.fileName));
43751
+ }
43052
43752
  }
43053
- }
43054
- else {
43055
- const components = compiler.getComponentsWithTemplateFile(fileName);
43056
- for (const component of components) {
43057
- if (ts.isClassDeclaration(component)) {
43058
- diagnostics.push(...ttc.getDiagnosticsForComponent(component));
43753
+ else {
43754
+ const components = compiler.getComponentsWithTemplateFile(fileName);
43755
+ for (const component of components) {
43756
+ if (ts.isClassDeclaration(component)) {
43757
+ diagnostics.push(...ttc.getDiagnosticsForComponent(component));
43758
+ }
43059
43759
  }
43060
43760
  }
43061
- }
43062
- this.compilerFactory.registerLastKnownProgram();
43063
- return diagnostics;
43761
+ return diagnostics;
43762
+ });
43064
43763
  }
43065
43764
  getDefinitionAndBoundSpan(fileName, position) {
43066
- return this.withCompiler((compiler) => {
43765
+ return this.withCompilerAndPerfTracing(PerfPhase.LsDefinition, (compiler) => {
43067
43766
  if (!isInAngularContext(compiler.getNextProgram(), fileName, position)) {
43068
43767
  return undefined;
43069
43768
  }
@@ -43072,7 +43771,7 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
43072
43771
  });
43073
43772
  }
43074
43773
  getTypeDefinitionAtPosition(fileName, position) {
43075
- return this.withCompiler((compiler) => {
43774
+ return this.withCompilerAndPerfTracing(PerfPhase.LsDefinition, (compiler) => {
43076
43775
  if (!isTemplateContext(compiler.getNextProgram(), fileName, position)) {
43077
43776
  return undefined;
43078
43777
  }
@@ -43081,56 +43780,57 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
43081
43780
  });
43082
43781
  }
43083
43782
  getQuickInfoAtPosition(fileName, position) {
43084
- return this.withCompiler((compiler) => {
43085
- if (!isTemplateContext(compiler.getNextProgram(), fileName, position)) {
43086
- return undefined;
43087
- }
43088
- const templateInfo = getTemplateInfoAtPosition(fileName, position, compiler);
43089
- if (templateInfo === undefined) {
43090
- return undefined;
43091
- }
43092
- const positionDetails = getTargetAtPosition(templateInfo.template, position);
43093
- if (positionDetails === null) {
43094
- return undefined;
43095
- }
43096
- // Because we can only show 1 quick info, just use the bound attribute if the target is a two
43097
- // way binding. We may consider concatenating additional display parts from the other target
43098
- // nodes or representing the two way binding in some other manner in the future.
43099
- const node = positionDetails.context.kind === TargetNodeKind.TwoWayBindingContext ?
43100
- positionDetails.context.nodes[0] :
43101
- positionDetails.context.node;
43102
- return new QuickInfoBuilder(this.tsLS, compiler, templateInfo.component, node).get();
43783
+ return this.withCompilerAndPerfTracing(PerfPhase.LsQuickInfo, (compiler) => {
43784
+ return this.getQuickInfoAtPositionImpl(fileName, position, compiler);
43103
43785
  });
43104
43786
  }
43787
+ getQuickInfoAtPositionImpl(fileName, position, compiler) {
43788
+ if (!isTemplateContext(compiler.getNextProgram(), fileName, position)) {
43789
+ return undefined;
43790
+ }
43791
+ const templateInfo = getTemplateInfoAtPosition(fileName, position, compiler);
43792
+ if (templateInfo === undefined) {
43793
+ return undefined;
43794
+ }
43795
+ const positionDetails = getTargetAtPosition(templateInfo.template, position);
43796
+ if (positionDetails === null) {
43797
+ return undefined;
43798
+ }
43799
+ // Because we can only show 1 quick info, just use the bound attribute if the target is a two
43800
+ // way binding. We may consider concatenating additional display parts from the other target
43801
+ // nodes or representing the two way binding in some other manner in the future.
43802
+ const node = positionDetails.context.kind === TargetNodeKind.TwoWayBindingContext ?
43803
+ positionDetails.context.nodes[0] :
43804
+ positionDetails.context.node;
43805
+ return new QuickInfoBuilder(this.tsLS, compiler, templateInfo.component, node).get();
43806
+ }
43105
43807
  getReferencesAtPosition(fileName, position) {
43106
- const compiler = this.compilerFactory.getOrCreate();
43107
- const results = new ReferencesAndRenameBuilder(this.strategy, this.tsLS, compiler)
43108
- .getReferencesAtPosition(fileName, position);
43109
- this.compilerFactory.registerLastKnownProgram();
43110
- return results;
43808
+ return this.withCompilerAndPerfTracing(PerfPhase.LsReferencesAndRenames, (compiler) => {
43809
+ return new ReferencesAndRenameBuilder(this.strategy, this.tsLS, compiler)
43810
+ .getReferencesAtPosition(fileName, position);
43811
+ });
43111
43812
  }
43112
43813
  getRenameInfo(fileName, position) {
43113
- var _a, _b, _c;
43114
- const compiler = this.compilerFactory.getOrCreate();
43115
- const renameInfo = new ReferencesAndRenameBuilder(this.strategy, this.tsLS, compiler)
43116
- .getRenameInfo(absoluteFrom(fileName), position);
43117
- if (!renameInfo.canRename) {
43118
- return renameInfo;
43119
- }
43120
- const quickInfo = (_a = this.getQuickInfoAtPosition(fileName, position)) !== null && _a !== void 0 ? _a : this.tsLS.getQuickInfoAtPosition(fileName, position);
43121
- const kind = (_b = quickInfo === null || quickInfo === void 0 ? void 0 : quickInfo.kind) !== null && _b !== void 0 ? _b : ts.ScriptElementKind.unknown;
43122
- const kindModifiers = (_c = quickInfo === null || quickInfo === void 0 ? void 0 : quickInfo.kindModifiers) !== null && _c !== void 0 ? _c : ts.ScriptElementKind.unknown;
43123
- return Object.assign(Object.assign({}, renameInfo), { kind, kindModifiers });
43814
+ return this.withCompilerAndPerfTracing(PerfPhase.LsReferencesAndRenames, (compiler) => {
43815
+ var _a, _b, _c;
43816
+ const renameInfo = new ReferencesAndRenameBuilder(this.strategy, this.tsLS, compiler)
43817
+ .getRenameInfo(absoluteFrom(fileName), position);
43818
+ if (!renameInfo.canRename) {
43819
+ return renameInfo;
43820
+ }
43821
+ const quickInfo = (_a = this.getQuickInfoAtPositionImpl(fileName, position, compiler)) !== null && _a !== void 0 ? _a : this.tsLS.getQuickInfoAtPosition(fileName, position);
43822
+ const kind = (_b = quickInfo === null || quickInfo === void 0 ? void 0 : quickInfo.kind) !== null && _b !== void 0 ? _b : ts.ScriptElementKind.unknown;
43823
+ const kindModifiers = (_c = quickInfo === null || quickInfo === void 0 ? void 0 : quickInfo.kindModifiers) !== null && _c !== void 0 ? _c : ts.ScriptElementKind.unknown;
43824
+ return Object.assign(Object.assign({}, renameInfo), { kind, kindModifiers });
43825
+ });
43124
43826
  }
43125
43827
  findRenameLocations(fileName, position) {
43126
- const compiler = this.compilerFactory.getOrCreate();
43127
- const results = new ReferencesAndRenameBuilder(this.strategy, this.tsLS, compiler)
43128
- .findRenameLocations(fileName, position);
43129
- this.compilerFactory.registerLastKnownProgram();
43130
- return results;
43828
+ return this.withCompilerAndPerfTracing(PerfPhase.LsReferencesAndRenames, (compiler) => {
43829
+ return new ReferencesAndRenameBuilder(this.strategy, this.tsLS, compiler)
43830
+ .findRenameLocations(fileName, position);
43831
+ });
43131
43832
  }
43132
- getCompletionBuilder(fileName, position) {
43133
- const compiler = this.compilerFactory.getOrCreate();
43833
+ getCompletionBuilder(fileName, position, compiler) {
43134
43834
  const templateInfo = getTemplateInfoAtPosition(fileName, position, compiler);
43135
43835
  if (templateInfo === undefined) {
43136
43836
  return null;
@@ -43147,23 +43847,26 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
43147
43847
  return new CompletionBuilder(this.tsLS, compiler, templateInfo.component, node, positionDetails, isTypeScriptFile(fileName));
43148
43848
  }
43149
43849
  getCompletionsAtPosition(fileName, position, options) {
43150
- return this.withCompiler((compiler) => {
43151
- if (!isTemplateContext(compiler.getNextProgram(), fileName, position)) {
43152
- return undefined;
43153
- }
43154
- const builder = this.getCompletionBuilder(fileName, position);
43155
- if (builder === null) {
43156
- return undefined;
43157
- }
43158
- return builder.getCompletionsAtPosition(options);
43850
+ return this.withCompilerAndPerfTracing(PerfPhase.LsCompletions, (compiler) => {
43851
+ return this.getCompletionsAtPositionImpl(fileName, position, options, compiler);
43159
43852
  });
43160
43853
  }
43854
+ getCompletionsAtPositionImpl(fileName, position, options, compiler) {
43855
+ if (!isTemplateContext(compiler.getNextProgram(), fileName, position)) {
43856
+ return undefined;
43857
+ }
43858
+ const builder = this.getCompletionBuilder(fileName, position, compiler);
43859
+ if (builder === null) {
43860
+ return undefined;
43861
+ }
43862
+ return builder.getCompletionsAtPosition(options);
43863
+ }
43161
43864
  getCompletionEntryDetails(fileName, position, entryName, formatOptions, preferences) {
43162
- return this.withCompiler((compiler) => {
43865
+ return this.withCompilerAndPerfTracing(PerfPhase.LsCompletions, (compiler) => {
43163
43866
  if (!isTemplateContext(compiler.getNextProgram(), fileName, position)) {
43164
43867
  return undefined;
43165
43868
  }
43166
- const builder = this.getCompletionBuilder(fileName, position);
43869
+ const builder = this.getCompletionBuilder(fileName, position, compiler);
43167
43870
  if (builder === null) {
43168
43871
  return undefined;
43169
43872
  }
@@ -43171,11 +43874,11 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
43171
43874
  });
43172
43875
  }
43173
43876
  getCompletionEntrySymbol(fileName, position, entryName) {
43174
- return this.withCompiler((compiler) => {
43877
+ return this.withCompilerAndPerfTracing(PerfPhase.LsCompletions, (compiler) => {
43175
43878
  if (!isTemplateContext(compiler.getNextProgram(), fileName, position)) {
43176
43879
  return undefined;
43177
43880
  }
43178
- const builder = this.getCompletionBuilder(fileName, position);
43881
+ const builder = this.getCompletionBuilder(fileName, position, compiler);
43179
43882
  if (builder === null) {
43180
43883
  return undefined;
43181
43884
  }
@@ -43185,7 +43888,7 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
43185
43888
  });
43186
43889
  }
43187
43890
  getTcb(fileName, position) {
43188
- return this.withCompiler(compiler => {
43891
+ return this.withCompilerAndPerfTracing(PerfPhase.LsTcb, compiler => {
43189
43892
  const templateInfo = getTemplateInfoAtPosition(fileName, position, compiler);
43190
43893
  if (templateInfo === undefined) {
43191
43894
  return undefined;
@@ -43225,10 +43928,31 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
43225
43928
  };
43226
43929
  });
43227
43930
  }
43228
- withCompiler(p) {
43931
+ /**
43932
+ * Provides an instance of the `NgCompiler` and traces perf results. Perf results are logged only
43933
+ * if the log level is verbose or higher. This method is intended to be called once per public
43934
+ * method call.
43935
+ *
43936
+ * Here is an example of the log output.
43937
+ *
43938
+ * Perf 245 [16:16:39.353] LanguageService#getQuickInfoAtPosition(): {"events":{},"phases":{
43939
+ * "Unaccounted":379,"TtcSymbol":4},"memory":{}}
43940
+ *
43941
+ * Passing name of caller instead of using `arguments.caller` because 'caller', 'callee', and
43942
+ * 'arguments' properties may not be accessed in strict mode.
43943
+ *
43944
+ * @param phase the `PerfPhase` to execute the `p` callback in
43945
+ * @param p callback to be run synchronously with an instance of the `NgCompiler` as argument
43946
+ * @return the result of running the `p` callback
43947
+ */
43948
+ withCompilerAndPerfTracing(phase, p) {
43229
43949
  const compiler = this.compilerFactory.getOrCreate();
43230
- const result = p(compiler);
43950
+ const result = compiler.perfRecorder.inPhase(phase, () => p(compiler));
43231
43951
  this.compilerFactory.registerLastKnownProgram();
43952
+ const logger = this.project.projectService.logger;
43953
+ if (logger.hasLevel(ts.server.LogLevel.verbose)) {
43954
+ logger.perftrc(`LanguageService#${PerfPhase[phase]}: ${JSON.stringify(compiler.perfRecorder.finalize())}`);
43955
+ }
43232
43956
  return result;
43233
43957
  }
43234
43958
  getCompilerOptionsDiagnostics() {
@@ -43236,22 +43960,23 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
43236
43960
  if (!(project instanceof ts.server.ConfiguredProject)) {
43237
43961
  return [];
43238
43962
  }
43239
- const diagnostics = [];
43240
- const configSourceFile = ts.readJsonConfigFile(project.getConfigFilePath(), (path) => project.readFile(path));
43241
- if (!this.options.strictTemplates && !this.options.fullTemplateTypeCheck) {
43242
- diagnostics.push({
43243
- messageText: 'Some language features are not available. ' +
43244
- 'To access all features, enable `strictTemplates` in `angularCompilerOptions`.',
43245
- category: ts.DiagnosticCategory.Suggestion,
43246
- code: ngErrorCode(ErrorCode.SUGGEST_STRICT_TEMPLATES),
43247
- file: configSourceFile,
43248
- start: undefined,
43249
- length: undefined,
43250
- });
43251
- }
43252
- const compiler = this.compilerFactory.getOrCreate();
43253
- diagnostics.push(...compiler.getOptionDiagnostics());
43254
- return diagnostics;
43963
+ return this.withCompilerAndPerfTracing(PerfPhase.LsDiagnostics, (compiler) => {
43964
+ const diagnostics = [];
43965
+ const configSourceFile = ts.readJsonConfigFile(project.getConfigFilePath(), (path) => project.readFile(path));
43966
+ if (!this.options.strictTemplates && !this.options.fullTemplateTypeCheck) {
43967
+ diagnostics.push({
43968
+ messageText: 'Some language features are not available. ' +
43969
+ 'To access all features, enable `strictTemplates` in `angularCompilerOptions`.',
43970
+ category: ts.DiagnosticCategory.Suggestion,
43971
+ code: ngErrorCode(ErrorCode.SUGGEST_STRICT_TEMPLATES),
43972
+ file: configSourceFile,
43973
+ start: undefined,
43974
+ length: undefined,
43975
+ });
43976
+ }
43977
+ diagnostics.push(...compiler.getOptionDiagnostics());
43978
+ return diagnostics;
43979
+ });
43255
43980
  }
43256
43981
  watchConfigFile(project) {
43257
43982
  // TODO: Check the case when the project is disposed. An InferredProject