@angular/language-service 11.2.4 → 11.2.5

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.4
2
+ * @license Angular v11.2.5
3
3
  * Copyright Google LLC All Rights Reserved.
4
4
  * License: MIT
5
5
  */
@@ -5903,10 +5903,6 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
5903
5903
  Identifiers$1.CopyDefinitionFeature = { name: 'ɵɵCopyDefinitionFeature', moduleName: CORE$1 };
5904
5904
  Identifiers$1.ProvidersFeature = { name: 'ɵɵProvidersFeature', moduleName: CORE$1 };
5905
5905
  Identifiers$1.listener = { name: 'ɵɵlistener', moduleName: CORE$1 };
5906
- Identifiers$1.getFactoryOf = {
5907
- name: 'ɵɵgetFactoryOf',
5908
- moduleName: CORE$1,
5909
- };
5910
5906
  Identifiers$1.getInheritedFactory = {
5911
5907
  name: 'ɵɵgetInheritedFactory',
5912
5908
  moduleName: CORE$1,
@@ -7886,7 +7882,6 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
7886
7882
  (function (R3FactoryDelegateType) {
7887
7883
  R3FactoryDelegateType[R3FactoryDelegateType["Class"] = 0] = "Class";
7888
7884
  R3FactoryDelegateType[R3FactoryDelegateType["Function"] = 1] = "Function";
7889
- R3FactoryDelegateType[R3FactoryDelegateType["Factory"] = 2] = "Factory";
7890
7885
  })(R3FactoryDelegateType || (R3FactoryDelegateType = {}));
7891
7886
  var R3FactoryTarget;
7892
7887
  (function (R3FactoryTarget) {
@@ -7974,19 +7969,7 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
7974
7969
  body.push(ifStmt(t, [ctorStmt], [r.set(nonCtorExpr).toStmt()]));
7975
7970
  return r;
7976
7971
  }
7977
- if (isDelegatedMetadata(meta) && meta.delegateType === R3FactoryDelegateType.Factory) {
7978
- const delegateFactory = variable(`ɵ${meta.name}_BaseFactory`);
7979
- const getFactoryOf = importExpr(Identifiers$1.getFactoryOf);
7980
- if (meta.delegate.isEquivalent(meta.internalType)) {
7981
- throw new Error(`Illegal state: compiling factory that delegates to itself`);
7982
- }
7983
- const delegateFactoryStmt = delegateFactory.set(getFactoryOf.callFn([meta.delegate])).toDeclStmt(INFERRED_TYPE, [
7984
- StmtModifier.Exported, StmtModifier.Final
7985
- ]);
7986
- statements.push(delegateFactoryStmt);
7987
- retExpr = makeConditionalFactory(delegateFactory.callFn([]));
7988
- }
7989
- else if (isDelegatedMetadata(meta)) {
7972
+ if (isDelegatedMetadata(meta)) {
7990
7973
  // This type is created with a delegated factory. If a type parameter is not specified, call
7991
7974
  // the factory instead.
7992
7975
  const delegateArgs = injectDependencies(meta.delegateDeps, meta.injectFn, meta.target === R3FactoryTarget.Pipe);
@@ -8995,21 +8978,6 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
8995
8978
  }
8996
8979
  }
8997
8980
 
8998
- /**
8999
- * @license
9000
- * Copyright Google LLC All Rights Reserved.
9001
- *
9002
- * Use of this source code is governed by an MIT-style license that can be
9003
- * found in the LICENSE file at https://angular.io/license
9004
- */
9005
- function mapLiteral(obj, quoted = false) {
9006
- return literalMap(Object.keys(obj).map(key => ({
9007
- key,
9008
- quoted,
9009
- value: obj[key],
9010
- })));
9011
- }
9012
-
9013
8981
  /**
9014
8982
  * @license
9015
8983
  * Copyright Google LLC All Rights Reserved.
@@ -9100,18 +9068,7 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
9100
9068
  return iifeCall.toStmt();
9101
9069
  }
9102
9070
  function compileInjector(meta) {
9103
- const result = compileFactoryFunction({
9104
- name: meta.name,
9105
- type: meta.type,
9106
- internalType: meta.internalType,
9107
- typeArgumentCount: 0,
9108
- deps: meta.deps,
9109
- injectFn: Identifiers$1.inject,
9110
- target: R3FactoryTarget.NgModule,
9111
- });
9112
- const definitionMap = {
9113
- factory: result.factory,
9114
- };
9071
+ const definitionMap = {};
9115
9072
  if (meta.providers !== null) {
9116
9073
  definitionMap.providers = meta.providers;
9117
9074
  }
@@ -9120,7 +9077,7 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
9120
9077
  }
9121
9078
  const expression = importExpr(Identifiers$1.defineInjector).callFn([mapToMapExpression(definitionMap)]);
9122
9079
  const type = new ExpressionType(importExpr(Identifiers$1.InjectorDef, [new ExpressionType(meta.type.type)]));
9123
- return { expression, type, statements: result.statements };
9080
+ return { expression, type };
9124
9081
  }
9125
9082
  function tupleTypeOf(exp) {
9126
9083
  const types = exp.map(ref => typeofExpr(ref.type));
@@ -15685,6 +15642,21 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
15685
15642
  }
15686
15643
  }
15687
15644
 
15645
+ /**
15646
+ * @license
15647
+ * Copyright Google LLC All Rights Reserved.
15648
+ *
15649
+ * Use of this source code is governed by an MIT-style license that can be
15650
+ * found in the LICENSE file at https://angular.io/license
15651
+ */
15652
+ function mapLiteral(obj, quoted = false) {
15653
+ return literalMap(Object.keys(obj).map(key => ({
15654
+ key,
15655
+ quoted,
15656
+ value: obj[key],
15657
+ })));
15658
+ }
15659
+
15688
15660
  /**
15689
15661
  * @license
15690
15662
  * Copyright Google LLC All Rights Reserved.
@@ -20052,12 +20024,11 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
20052
20024
  name: facade.name,
20053
20025
  type: wrapReference(facade.type),
20054
20026
  internalType: new WrappedNodeExpr(facade.type),
20055
- deps: convertR3DependencyMetadataArray(facade.deps),
20056
20027
  providers: new WrappedNodeExpr(facade.providers),
20057
20028
  imports: facade.imports.map(i => new WrappedNodeExpr(i)),
20058
20029
  };
20059
20030
  const res = compileInjector(meta);
20060
- return this.jitExpression(res.expression, angularCoreEnv, sourceMapUrl, res.statements);
20031
+ return this.jitExpression(res.expression, angularCoreEnv, sourceMapUrl, []);
20061
20032
  }
20062
20033
  compileNgModule(angularCoreEnv, sourceMapUrl, facade) {
20063
20034
  const meta = {
@@ -20385,7 +20356,7 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
20385
20356
  * Use of this source code is governed by an MIT-style license that can be
20386
20357
  * found in the LICENSE file at https://angular.io/license
20387
20358
  */
20388
- const VERSION$1 = new Version('11.2.4');
20359
+ const VERSION$1 = new Version('11.2.5');
20389
20360
 
20390
20361
  /**
20391
20362
  * @license
@@ -21042,7 +21013,7 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
21042
21013
  */
21043
21014
  function createDirectiveDefinitionMap(meta) {
21044
21015
  const definitionMap = new DefinitionMap();
21045
- definitionMap.set('version', literal('11.2.4'));
21016
+ definitionMap.set('version', literal('11.2.5'));
21046
21017
  // e.g. `type: MyDirective`
21047
21018
  definitionMap.set('type', meta.internalType);
21048
21019
  // e.g. `selector: 'some-dir'`
@@ -21263,7 +21234,7 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
21263
21234
  */
21264
21235
  function createPipeDefinitionMap(meta) {
21265
21236
  const definitionMap = new DefinitionMap();
21266
- definitionMap.set('version', literal('11.2.4'));
21237
+ definitionMap.set('version', literal('11.2.5'));
21267
21238
  definitionMap.set('ngImport', importExpr(Identifiers$1.core));
21268
21239
  // e.g. `type: MyPipe`
21269
21240
  definitionMap.set('type', meta.internalType);
@@ -21295,7 +21266,7 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
21295
21266
  * Use of this source code is governed by an MIT-style license that can be
21296
21267
  * found in the LICENSE file at https://angular.io/license
21297
21268
  */
21298
- const VERSION$2 = new Version('11.2.4');
21269
+ const VERSION$2 = new Version('11.2.5');
21299
21270
 
21300
21271
  /**
21301
21272
  * @license
@@ -21786,13 +21757,19 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
21786
21757
  // such a case, the reference's `identities` property would be `[foo]`, which would result in an
21787
21758
  // invalid emission of a free-standing `foo` identifier, rather than `exports.foo`.
21788
21759
  if (!isDeclaration(ref.node) && refSf === context) {
21789
- return new WrappedNodeExpr(ref.node);
21760
+ return {
21761
+ expression: new WrappedNodeExpr(ref.node),
21762
+ importedFile: null,
21763
+ };
21790
21764
  }
21791
21765
  // A Reference can have multiple identities in different files, so it may already have an
21792
21766
  // Identifier in the requested context file.
21793
21767
  const identifier = ref.getIdentityIn(context);
21794
21768
  if (identifier !== null) {
21795
- return new WrappedNodeExpr(identifier);
21769
+ return {
21770
+ expression: new WrappedNodeExpr(identifier),
21771
+ importedFile: null,
21772
+ };
21796
21773
  }
21797
21774
  else {
21798
21775
  return null;
@@ -21835,22 +21812,17 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
21835
21812
  }
21836
21813
  // Try to find the exported name of the declaration, if one is available.
21837
21814
  const { specifier, resolutionContext } = ref.bestGuessOwningModule;
21838
- const symbolName = this.resolveImportName(specifier, ref.node, resolutionContext);
21839
- if (symbolName === null) {
21815
+ const exports = this.getExportsOfModule(specifier, resolutionContext);
21816
+ if (exports === null || !exports.exportMap.has(ref.node)) {
21840
21817
  // TODO(alxhub): make this error a ts.Diagnostic pointing at whatever caused this import to be
21841
21818
  // triggered.
21842
21819
  throw new Error(`Symbol ${ref.debugName} declared in ${getSourceFile(ref.node).fileName} is not exported from ${specifier} (import into ${context.fileName})`);
21843
21820
  }
21844
- return new ExternalExpr(new ExternalReference(specifier, symbolName));
21845
- }
21846
- resolveImportName(moduleName, target, fromFile) {
21847
- const exports = this.getExportsOfModule(moduleName, fromFile);
21848
- if (exports !== null && exports.has(target)) {
21849
- return exports.get(target);
21850
- }
21851
- else {
21852
- return null;
21853
- }
21821
+ const symbolName = exports.exportMap.get(ref.node);
21822
+ return {
21823
+ expression: new ExternalExpr(new ExternalReference(specifier, symbolName)),
21824
+ importedFile: exports.module,
21825
+ };
21854
21826
  }
21855
21827
  getExportsOfModule(moduleName, fromFile) {
21856
21828
  if (!this.moduleExportsCache.has(moduleName)) {
@@ -21872,7 +21844,7 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
21872
21844
  exports.forEach((declaration, name) => {
21873
21845
  exportMap.set(declaration.node, name);
21874
21846
  });
21875
- return exportMap;
21847
+ return { module: entryPointFile, exportMap };
21876
21848
  }
21877
21849
  }
21878
21850
  /**
@@ -21913,7 +21885,10 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
21913
21885
  // With both files expressed as LogicalProjectPaths, getting the module specifier as a relative
21914
21886
  // path is now straightforward.
21915
21887
  const moduleName = LogicalProjectPath.relativePathBetween(originPath, destPath);
21916
- return new ExternalExpr({ moduleName, name });
21888
+ return {
21889
+ expression: new ExternalExpr({ moduleName, name }),
21890
+ importedFile: destSf,
21891
+ };
21917
21892
  }
21918
21893
  }
21919
21894
  /**
@@ -21931,7 +21906,7 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
21931
21906
  const relativePath = relative(dirname(absoluteFromSourceFile(context)), absoluteFromSourceFile(destSf));
21932
21907
  const moduleName = toRelativeImport(stripExtension(relativePath));
21933
21908
  const name = findExportedNameOfNode(ref.node, destSf, this.reflector);
21934
- return new ExternalExpr({ moduleName, name });
21909
+ return { expression: new ExternalExpr({ moduleName, name }), importedFile: destSf };
21935
21910
  }
21936
21911
  }
21937
21912
  /**
@@ -21950,7 +21925,10 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
21950
21925
  return null;
21951
21926
  }
21952
21927
  const moduleName = this.unifiedModulesHost.fileNameToModuleName(destSf.fileName, context.fileName);
21953
- return new ExternalExpr({ moduleName, name });
21928
+ return {
21929
+ expression: new ExternalExpr({ moduleName, name }),
21930
+ importedFile: destSf,
21931
+ };
21954
21932
  }
21955
21933
  }
21956
21934
 
@@ -22082,10 +22060,10 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
22082
22060
  */
22083
22061
  class AliasStrategy {
22084
22062
  emit(ref, context, importMode) {
22085
- if (importMode & ImportFlags.NoAliasing) {
22063
+ if (importMode & ImportFlags.NoAliasing || ref.alias === null) {
22086
22064
  return null;
22087
22065
  }
22088
- return ref.alias;
22066
+ return { expression: ref.alias, importedFile: 'unknown' };
22089
22067
  }
22090
22068
  }
22091
22069
 
@@ -22476,6 +22454,382 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
22476
22454
  }
22477
22455
  }
22478
22456
 
22457
+ /**
22458
+ * @license
22459
+ * Copyright Google LLC All Rights Reserved.
22460
+ *
22461
+ * Use of this source code is governed by an MIT-style license that can be
22462
+ * found in the LICENSE file at https://angular.io/license
22463
+ */
22464
+ /**
22465
+ * Represents a symbol that is recognizable across incremental rebuilds, which enables the captured
22466
+ * metadata to be compared to the prior compilation. This allows for semantic understanding of
22467
+ * the changes that have been made in a rebuild, which potentially enables more reuse of work
22468
+ * from the prior compilation.
22469
+ */
22470
+ class SemanticSymbol {
22471
+ constructor(
22472
+ /**
22473
+ * The declaration for this symbol.
22474
+ */
22475
+ decl) {
22476
+ this.decl = decl;
22477
+ this.path = absoluteFromSourceFile(decl.getSourceFile());
22478
+ this.identifier = getSymbolIdentifier(decl);
22479
+ }
22480
+ }
22481
+ function getSymbolIdentifier(decl) {
22482
+ if (!ts$1.isSourceFile(decl.parent)) {
22483
+ return null;
22484
+ }
22485
+ // If this is a top-level class declaration, the class name is used as unique identifier.
22486
+ // Other scenarios are currently not supported and causes the symbol not to be identified
22487
+ // across rebuilds, unless the declaration node has not changed.
22488
+ return decl.name.text;
22489
+ }
22490
+
22491
+ /**
22492
+ * @license
22493
+ * Copyright Google LLC All Rights Reserved.
22494
+ *
22495
+ * Use of this source code is governed by an MIT-style license that can be
22496
+ * found in the LICENSE file at https://angular.io/license
22497
+ */
22498
+ /**
22499
+ * Represents a declaration for which no semantic symbol has been registered. For example,
22500
+ * declarations from external dependencies have not been explicitly registered and are represented
22501
+ * by this symbol. This allows the unresolved symbol to still be compared to a symbol from a prior
22502
+ * compilation.
22503
+ */
22504
+ class OpaqueSymbol extends SemanticSymbol {
22505
+ isPublicApiAffected() {
22506
+ return false;
22507
+ }
22508
+ isTypeCheckApiAffected() {
22509
+ return false;
22510
+ }
22511
+ }
22512
+ /**
22513
+ * The semantic dependency graph of a single compilation.
22514
+ */
22515
+ class SemanticDepGraph {
22516
+ constructor() {
22517
+ this.files = new Map();
22518
+ this.symbolByDecl = new Map();
22519
+ }
22520
+ /**
22521
+ * Registers a symbol in the graph. The symbol is given a unique identifier if possible, such that
22522
+ * its equivalent symbol can be obtained from a prior graph even if its declaration node has
22523
+ * changed across rebuilds. Symbols without an identifier are only able to find themselves in a
22524
+ * prior graph if their declaration node is identical.
22525
+ */
22526
+ registerSymbol(symbol) {
22527
+ this.symbolByDecl.set(symbol.decl, symbol);
22528
+ if (symbol.identifier !== null) {
22529
+ // If the symbol has a unique identifier, record it in the file that declares it. This enables
22530
+ // the symbol to be requested by its unique name.
22531
+ if (!this.files.has(symbol.path)) {
22532
+ this.files.set(symbol.path, new Map());
22533
+ }
22534
+ this.files.get(symbol.path).set(symbol.identifier, symbol);
22535
+ }
22536
+ }
22537
+ /**
22538
+ * Attempts to resolve a symbol in this graph that represents the given symbol from another graph.
22539
+ * If no matching symbol could be found, null is returned.
22540
+ *
22541
+ * @param symbol The symbol from another graph for which its equivalent in this graph should be
22542
+ * found.
22543
+ */
22544
+ getEquivalentSymbol(symbol) {
22545
+ // First lookup the symbol by its declaration. It is typical for the declaration to not have
22546
+ // changed across rebuilds, so this is likely to find the symbol. Using the declaration also
22547
+ // allows to diff symbols for which no unique identifier could be determined.
22548
+ let previousSymbol = this.getSymbolByDecl(symbol.decl);
22549
+ if (previousSymbol === null && symbol.identifier !== null) {
22550
+ // The declaration could not be resolved to a symbol in a prior compilation, which may
22551
+ // happen because the file containing the declaration has changed. In that case we want to
22552
+ // lookup the symbol based on its unique identifier, as that allows us to still compare the
22553
+ // changed declaration to the prior compilation.
22554
+ previousSymbol = this.getSymbolByName(symbol.path, symbol.identifier);
22555
+ }
22556
+ return previousSymbol;
22557
+ }
22558
+ /**
22559
+ * Attempts to find the symbol by its identifier.
22560
+ */
22561
+ getSymbolByName(path, identifier) {
22562
+ if (!this.files.has(path)) {
22563
+ return null;
22564
+ }
22565
+ const file = this.files.get(path);
22566
+ if (!file.has(identifier)) {
22567
+ return null;
22568
+ }
22569
+ return file.get(identifier);
22570
+ }
22571
+ /**
22572
+ * Attempts to resolve the declaration to its semantic symbol.
22573
+ */
22574
+ getSymbolByDecl(decl) {
22575
+ if (!this.symbolByDecl.has(decl)) {
22576
+ return null;
22577
+ }
22578
+ return this.symbolByDecl.get(decl);
22579
+ }
22580
+ }
22581
+ /**
22582
+ * Implements the logic to go from a previous dependency graph to a new one, along with information
22583
+ * on which files have been affected.
22584
+ */
22585
+ class SemanticDepGraphUpdater {
22586
+ constructor(
22587
+ /**
22588
+ * The semantic dependency graph of the most recently succeeded compilation, or null if this
22589
+ * is the initial build.
22590
+ */
22591
+ priorGraph) {
22592
+ this.priorGraph = priorGraph;
22593
+ this.newGraph = new SemanticDepGraph();
22594
+ /**
22595
+ * Contains opaque symbols that were created for declarations for which there was no symbol
22596
+ * registered, which happens for e.g. external declarations.
22597
+ */
22598
+ this.opaqueSymbols = new Map();
22599
+ }
22600
+ /**
22601
+ * Registers the symbol in the new graph that is being created.
22602
+ */
22603
+ registerSymbol(symbol) {
22604
+ this.newGraph.registerSymbol(symbol);
22605
+ }
22606
+ /**
22607
+ * Takes all facts that have been gathered to create a new semantic dependency graph. In this
22608
+ * process, the semantic impact of the changes is determined which results in a set of files that
22609
+ * need to be emitted and/or type-checked.
22610
+ */
22611
+ finalize() {
22612
+ if (this.priorGraph === null) {
22613
+ // If no prior dependency graph is available then this was the initial build, in which case
22614
+ // we don't need to determine the semantic impact as everything is already considered
22615
+ // logically changed.
22616
+ return {
22617
+ needsEmit: new Set(),
22618
+ needsTypeCheckEmit: new Set(),
22619
+ newGraph: this.newGraph,
22620
+ };
22621
+ }
22622
+ const needsEmit = this.determineInvalidatedFiles(this.priorGraph);
22623
+ const needsTypeCheckEmit = this.determineInvalidatedTypeCheckFiles(this.priorGraph);
22624
+ return {
22625
+ needsEmit,
22626
+ needsTypeCheckEmit,
22627
+ newGraph: this.newGraph,
22628
+ };
22629
+ }
22630
+ determineInvalidatedFiles(priorGraph) {
22631
+ const isPublicApiAffected = new Set();
22632
+ // The first phase is to collect all symbols which have their public API affected. Any symbols
22633
+ // that cannot be matched up with a symbol from the prior graph are considered affected.
22634
+ for (const symbol of this.newGraph.symbolByDecl.values()) {
22635
+ const previousSymbol = priorGraph.getEquivalentSymbol(symbol);
22636
+ if (previousSymbol === null || symbol.isPublicApiAffected(previousSymbol)) {
22637
+ isPublicApiAffected.add(symbol);
22638
+ }
22639
+ }
22640
+ // The second phase is to find all symbols for which the emit result is affected, either because
22641
+ // their used declarations have changed or any of those used declarations has had its public API
22642
+ // affected as determined in the first phase.
22643
+ const needsEmit = new Set();
22644
+ for (const symbol of this.newGraph.symbolByDecl.values()) {
22645
+ if (symbol.isEmitAffected === undefined) {
22646
+ continue;
22647
+ }
22648
+ const previousSymbol = priorGraph.getEquivalentSymbol(symbol);
22649
+ if (previousSymbol === null || symbol.isEmitAffected(previousSymbol, isPublicApiAffected)) {
22650
+ needsEmit.add(symbol.path);
22651
+ }
22652
+ }
22653
+ return needsEmit;
22654
+ }
22655
+ determineInvalidatedTypeCheckFiles(priorGraph) {
22656
+ const isTypeCheckApiAffected = new Set();
22657
+ // The first phase is to collect all symbols which have their public API affected. Any symbols
22658
+ // that cannot be matched up with a symbol from the prior graph are considered affected.
22659
+ for (const symbol of this.newGraph.symbolByDecl.values()) {
22660
+ const previousSymbol = priorGraph.getEquivalentSymbol(symbol);
22661
+ if (previousSymbol === null || symbol.isTypeCheckApiAffected(previousSymbol)) {
22662
+ isTypeCheckApiAffected.add(symbol);
22663
+ }
22664
+ }
22665
+ // The second phase is to find all symbols for which the emit result is affected, either because
22666
+ // their used declarations have changed or any of those used declarations has had its public API
22667
+ // affected as determined in the first phase.
22668
+ const needsTypeCheckEmit = new Set();
22669
+ for (const symbol of this.newGraph.symbolByDecl.values()) {
22670
+ if (symbol.isTypeCheckBlockAffected === undefined) {
22671
+ continue;
22672
+ }
22673
+ const previousSymbol = priorGraph.getEquivalentSymbol(symbol);
22674
+ if (previousSymbol === null ||
22675
+ symbol.isTypeCheckBlockAffected(previousSymbol, isTypeCheckApiAffected)) {
22676
+ needsTypeCheckEmit.add(symbol.path);
22677
+ }
22678
+ }
22679
+ return needsTypeCheckEmit;
22680
+ }
22681
+ /**
22682
+ * Creates a `SemanticReference` for the reference to `decl` using the expression `expr`. See
22683
+ * the documentation of `SemanticReference` for details.
22684
+ */
22685
+ getSemanticReference(decl, expr) {
22686
+ return {
22687
+ symbol: this.getSymbol(decl),
22688
+ importPath: getImportPath(expr),
22689
+ };
22690
+ }
22691
+ /**
22692
+ * Gets the `SemanticSymbol` that was registered for `decl` during the current compilation, or
22693
+ * returns an opaque symbol that represents `decl`.
22694
+ */
22695
+ getSymbol(decl) {
22696
+ const symbol = this.newGraph.getSymbolByDecl(decl);
22697
+ if (symbol === null) {
22698
+ // No symbol has been recorded for the provided declaration, which would be the case if the
22699
+ // declaration is external. Return an opaque symbol in that case, to allow the external
22700
+ // declaration to be compared to a prior compilation.
22701
+ return this.getOpaqueSymbol(decl);
22702
+ }
22703
+ return symbol;
22704
+ }
22705
+ /**
22706
+ * Gets or creates an `OpaqueSymbol` for the provided class declaration.
22707
+ */
22708
+ getOpaqueSymbol(decl) {
22709
+ if (this.opaqueSymbols.has(decl)) {
22710
+ return this.opaqueSymbols.get(decl);
22711
+ }
22712
+ const symbol = new OpaqueSymbol(decl);
22713
+ this.opaqueSymbols.set(decl, symbol);
22714
+ return symbol;
22715
+ }
22716
+ }
22717
+ function getImportPath(expr) {
22718
+ if (expr instanceof ExternalExpr) {
22719
+ return `${expr.value.moduleName}\$${expr.value.name}`;
22720
+ }
22721
+ else {
22722
+ return null;
22723
+ }
22724
+ }
22725
+
22726
+ /**
22727
+ * Determines whether the provided symbols represent the same declaration.
22728
+ */
22729
+ function isSymbolEqual(a, b) {
22730
+ if (a.decl === b.decl) {
22731
+ // If the declaration is identical then it must represent the same symbol.
22732
+ return true;
22733
+ }
22734
+ if (a.identifier === null || b.identifier === null) {
22735
+ // Unidentifiable symbols are assumed to be different.
22736
+ return false;
22737
+ }
22738
+ return a.path === b.path && a.identifier === b.identifier;
22739
+ }
22740
+ /**
22741
+ * Determines whether the provided references to a semantic symbol are still equal, i.e. represent
22742
+ * the same symbol and are imported by the same path.
22743
+ */
22744
+ function isReferenceEqual(a, b) {
22745
+ if (!isSymbolEqual(a.symbol, b.symbol)) {
22746
+ // If the reference's target symbols are different, the reference itself is different.
22747
+ return false;
22748
+ }
22749
+ // The reference still corresponds with the same symbol, now check that the path by which it is
22750
+ // imported has not changed.
22751
+ return a.importPath === b.importPath;
22752
+ }
22753
+ function referenceEquality(a, b) {
22754
+ return a === b;
22755
+ }
22756
+ /**
22757
+ * Determines if the provided arrays are equal to each other, using the provided equality tester
22758
+ * that is called for all entries in the array.
22759
+ */
22760
+ function isArrayEqual(a, b, equalityTester = referenceEquality) {
22761
+ if (a === null || b === null) {
22762
+ return a === b;
22763
+ }
22764
+ if (a.length !== b.length) {
22765
+ return false;
22766
+ }
22767
+ return !a.some((item, index) => !equalityTester(item, b[index]));
22768
+ }
22769
+ /**
22770
+ * Determines if the provided sets are equal to each other, using the provided equality tester.
22771
+ * Sets that only differ in ordering are considered equal.
22772
+ */
22773
+ function isSetEqual(a, b, equalityTester = referenceEquality) {
22774
+ if (a === null || b === null) {
22775
+ return a === b;
22776
+ }
22777
+ if (a.size !== b.size) {
22778
+ return false;
22779
+ }
22780
+ for (const itemA of a) {
22781
+ let found = false;
22782
+ for (const itemB of b) {
22783
+ if (equalityTester(itemA, itemB)) {
22784
+ found = true;
22785
+ break;
22786
+ }
22787
+ }
22788
+ if (!found) {
22789
+ return false;
22790
+ }
22791
+ }
22792
+ return true;
22793
+ }
22794
+
22795
+ /**
22796
+ * @license
22797
+ * Copyright Google LLC All Rights Reserved.
22798
+ *
22799
+ * Use of this source code is governed by an MIT-style license that can be
22800
+ * found in the LICENSE file at https://angular.io/license
22801
+ */
22802
+ /**
22803
+ * Converts the type parameters of the given class into their semantic representation. If the class
22804
+ * does not have any type parameters, then `null` is returned.
22805
+ */
22806
+ function extractSemanticTypeParameters(node) {
22807
+ if (!ts$1.isClassDeclaration(node) || node.typeParameters === undefined) {
22808
+ return null;
22809
+ }
22810
+ return node.typeParameters.map(typeParam => ({ hasGenericTypeBound: typeParam.constraint !== undefined }));
22811
+ }
22812
+ /**
22813
+ * Compares the list of type parameters to determine if they can be considered equal.
22814
+ */
22815
+ function areTypeParametersEqual(current, previous) {
22816
+ // First compare all type parameters one-to-one; any differences mean that the list of type
22817
+ // parameters has changed.
22818
+ if (!isArrayEqual(current, previous, isTypeParameterEqual)) {
22819
+ return false;
22820
+ }
22821
+ // If there is a current list of type parameters and if any of them has a generic type constraint,
22822
+ // then the meaning of that type parameter may have changed without us being aware; as such we
22823
+ // have to assume that the type parameters have in fact changed.
22824
+ if (current !== null && current.some(typeParam => typeParam.hasGenericTypeBound)) {
22825
+ return false;
22826
+ }
22827
+ return true;
22828
+ }
22829
+ function isTypeParameterEqual(a, b) {
22830
+ return a.hasGenericTypeBound === b.hasGenericTypeBound;
22831
+ }
22832
+
22479
22833
  /**
22480
22834
  * @license
22481
22835
  * Copyright Google LLC All Rights Reserved.
@@ -25267,17 +25621,19 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
25267
25621
  constructor(handler, detected) {
25268
25622
  this.state = TraitState.Pending;
25269
25623
  this.analysis = null;
25624
+ this.symbol = null;
25270
25625
  this.resolution = null;
25271
25626
  this.analysisDiagnostics = null;
25272
25627
  this.resolveDiagnostics = null;
25273
25628
  this.handler = handler;
25274
25629
  this.detected = detected;
25275
25630
  }
25276
- toAnalyzed(analysis, diagnostics) {
25631
+ toAnalyzed(analysis, diagnostics, symbol) {
25277
25632
  // Only pending traits can be analyzed.
25278
25633
  this.assertTransitionLegal(TraitState.Pending, TraitState.Analyzed);
25279
25634
  this.analysis = analysis;
25280
25635
  this.analysisDiagnostics = diagnostics;
25636
+ this.symbol = symbol;
25281
25637
  this.state = TraitState.Analyzed;
25282
25638
  return this;
25283
25639
  }
@@ -25338,7 +25694,7 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
25338
25694
  * class (like adding fields or type declarations).
25339
25695
  */
25340
25696
  class TraitCompiler {
25341
- constructor(handlers, reflector, perf, incrementalBuild, compileNonExportedClasses, compilationMode, dtsTransforms) {
25697
+ constructor(handlers, reflector, perf, incrementalBuild, compileNonExportedClasses, compilationMode, dtsTransforms, semanticDepGraphUpdater) {
25342
25698
  this.handlers = handlers;
25343
25699
  this.reflector = reflector;
25344
25700
  this.perf = perf;
@@ -25346,6 +25702,7 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
25346
25702
  this.compileNonExportedClasses = compileNonExportedClasses;
25347
25703
  this.compilationMode = compilationMode;
25348
25704
  this.dtsTransforms = dtsTransforms;
25705
+ this.semanticDepGraphUpdater = semanticDepGraphUpdater;
25349
25706
  /**
25350
25707
  * Maps class declarations to their `ClassRecord`, which tracks the Ivy traits being applied to
25351
25708
  * those classes.
@@ -25436,7 +25793,8 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
25436
25793
  const handler = this.handlersByName.get(priorTrait.handler.name);
25437
25794
  let trait = Trait.pending(handler, priorTrait.detected);
25438
25795
  if (priorTrait.state === TraitState.Analyzed || priorTrait.state === TraitState.Resolved) {
25439
- trait = trait.toAnalyzed(priorTrait.analysis, priorTrait.analysisDiagnostics);
25796
+ const symbol = this.makeSymbolForTrait(handler, record.node, priorTrait.analysis);
25797
+ trait = trait.toAnalyzed(priorTrait.analysis, priorTrait.analysisDiagnostics, symbol);
25440
25798
  if (trait.analysis !== null && trait.handler.register !== undefined) {
25441
25799
  trait.handler.register(record.node, trait.analysis);
25442
25800
  }
@@ -25531,6 +25889,20 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
25531
25889
  }
25532
25890
  return foundTraits.length > 0 ? foundTraits : null;
25533
25891
  }
25892
+ makeSymbolForTrait(handler, decl, analysis) {
25893
+ if (analysis === null) {
25894
+ return null;
25895
+ }
25896
+ const symbol = handler.symbol(decl, analysis);
25897
+ if (symbol !== null && this.semanticDepGraphUpdater !== null) {
25898
+ const isPrimary = handler.precedence === HandlerPrecedence.PRIMARY;
25899
+ if (!isPrimary) {
25900
+ throw new Error(`AssertionError: ${handler.name} returned a symbol but is not a primary handler.`);
25901
+ }
25902
+ this.semanticDepGraphUpdater.registerSymbol(symbol);
25903
+ }
25904
+ return symbol;
25905
+ }
25534
25906
  analyzeClass(clazz, preanalyzeQueue) {
25535
25907
  const traits = this.scanClassForTraits(clazz);
25536
25908
  if (traits === null) {
@@ -25548,7 +25920,7 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
25548
25920
  }
25549
25921
  catch (err) {
25550
25922
  if (err instanceof FatalDiagnosticError) {
25551
- trait.toAnalyzed(null, [err.toDiagnostic()]);
25923
+ trait.toAnalyzed(null, [err.toDiagnostic()], null);
25552
25924
  return;
25553
25925
  }
25554
25926
  else {
@@ -25565,7 +25937,7 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
25565
25937
  }
25566
25938
  }
25567
25939
  analyzeTrait(clazz, trait, flags) {
25568
- var _a, _b;
25940
+ var _a, _b, _c;
25569
25941
  if (trait.state !== TraitState.Pending) {
25570
25942
  throw new Error(`Attempt to analyze trait of ${clazz.name.text} in state ${TraitState[trait.state]} (expected DETECTED)`);
25571
25943
  }
@@ -25576,17 +25948,18 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
25576
25948
  }
25577
25949
  catch (err) {
25578
25950
  if (err instanceof FatalDiagnosticError) {
25579
- trait.toAnalyzed(null, [err.toDiagnostic()]);
25951
+ trait.toAnalyzed(null, [err.toDiagnostic()], null);
25580
25952
  return;
25581
25953
  }
25582
25954
  else {
25583
25955
  throw err;
25584
25956
  }
25585
25957
  }
25958
+ const symbol = this.makeSymbolForTrait(trait.handler, clazz, (_a = result.analysis) !== null && _a !== void 0 ? _a : null);
25586
25959
  if (result.analysis !== undefined && trait.handler.register !== undefined) {
25587
25960
  trait.handler.register(clazz, result.analysis);
25588
25961
  }
25589
- trait = trait.toAnalyzed((_a = result.analysis) !== null && _a !== void 0 ? _a : null, (_b = result.diagnostics) !== null && _b !== void 0 ? _b : null);
25962
+ trait = trait.toAnalyzed((_b = result.analysis) !== null && _b !== void 0 ? _b : null, (_c = result.diagnostics) !== null && _c !== void 0 ? _c : null, symbol);
25590
25963
  }
25591
25964
  resolve() {
25592
25965
  var _a, _b;
@@ -25614,7 +25987,7 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
25614
25987
  }
25615
25988
  let result;
25616
25989
  try {
25617
- result = handler.resolve(clazz, trait.analysis);
25990
+ result = handler.resolve(clazz, trait.analysis, trait.symbol);
25618
25991
  }
25619
25992
  catch (err) {
25620
25993
  if (err instanceof FatalDiagnosticError) {
@@ -27545,12 +27918,12 @@ define(['exports', 'typescript/lib/tsserverlibrary', 'os', 'typescript', 'fs', '
27545
27918
  return new FatalDiagnosticError(ErrorCode.PARAM_MISSING_TOKEN, param.nameNode, chain, hints);
27546
27919
  }
27547
27920
  function toR3Reference(valueRef, typeRef, valueContext, typeContext, refEmitter) {
27548
- const value = refEmitter.emit(valueRef, valueContext);
27549
- const type = refEmitter.emit(typeRef, typeContext, ImportFlags.ForceNewImport | ImportFlags.AllowTypeImports);
27550
- if (value === null || type === null) {
27551
- throw new Error(`Could not refer to ${ts$1.SyntaxKind[valueRef.node.kind]}`);
27552
- }
27553
- return { value, type };
27921
+ return {
27922
+ value: refEmitter.emit(valueRef, valueContext).expression,
27923
+ type: refEmitter
27924
+ .emit(typeRef, typeContext, ImportFlags.ForceNewImport | ImportFlags.AllowTypeImports)
27925
+ .expression,
27926
+ };
27554
27927
  }
27555
27928
  function isAngularCore(decorator) {
27556
27929
  return decorator.import !== null && decorator.import.from === '@angular/core';
@@ -28118,8 +28491,112 @@ Either add the @Injectable() decorator to '${provider.node.name
28118
28491
  'ngOnChanges', 'ngOnInit', 'ngOnDestroy', 'ngDoCheck', 'ngAfterViewInit', 'ngAfterViewChecked',
28119
28492
  'ngAfterContentInit', 'ngAfterContentChecked'
28120
28493
  ]);
28494
+ /**
28495
+ * Represents an Angular directive. Components are represented by `ComponentSymbol`, which inherits
28496
+ * from this symbol.
28497
+ */
28498
+ class DirectiveSymbol extends SemanticSymbol {
28499
+ constructor(decl, selector, inputs, outputs, exportAs, typeCheckMeta, typeParameters) {
28500
+ super(decl);
28501
+ this.selector = selector;
28502
+ this.inputs = inputs;
28503
+ this.outputs = outputs;
28504
+ this.exportAs = exportAs;
28505
+ this.typeCheckMeta = typeCheckMeta;
28506
+ this.typeParameters = typeParameters;
28507
+ this.baseClass = null;
28508
+ }
28509
+ isPublicApiAffected(previousSymbol) {
28510
+ // Note: since components and directives have exactly the same items contributing to their
28511
+ // public API, it is okay for a directive to change into a component and vice versa without
28512
+ // the API being affected.
28513
+ if (!(previousSymbol instanceof DirectiveSymbol)) {
28514
+ return true;
28515
+ }
28516
+ // Directives and components have a public API of:
28517
+ // 1. Their selector.
28518
+ // 2. The binding names of their inputs and outputs; a change in ordering is also considered
28519
+ // to be a change in public API.
28520
+ // 3. The list of exportAs names and its ordering.
28521
+ return this.selector !== previousSymbol.selector ||
28522
+ !isArrayEqual(this.inputs.propertyNames, previousSymbol.inputs.propertyNames) ||
28523
+ !isArrayEqual(this.outputs.propertyNames, previousSymbol.outputs.propertyNames) ||
28524
+ !isArrayEqual(this.exportAs, previousSymbol.exportAs);
28525
+ }
28526
+ isTypeCheckApiAffected(previousSymbol) {
28527
+ // If the public API of the directive has changed, then so has its type-check API.
28528
+ if (this.isPublicApiAffected(previousSymbol)) {
28529
+ return true;
28530
+ }
28531
+ if (!(previousSymbol instanceof DirectiveSymbol)) {
28532
+ return true;
28533
+ }
28534
+ // The type-check block also depends on the class property names, as writes property bindings
28535
+ // directly into the backing fields.
28536
+ if (!isArrayEqual(Array.from(this.inputs), Array.from(previousSymbol.inputs), isInputMappingEqual) ||
28537
+ !isArrayEqual(Array.from(this.outputs), Array.from(previousSymbol.outputs), isInputMappingEqual)) {
28538
+ return true;
28539
+ }
28540
+ // The type parameters of a directive are emitted into the type constructors in the type-check
28541
+ // block of a component, so if the type parameters are not considered equal then consider the
28542
+ // type-check API of this directive to be affected.
28543
+ if (!areTypeParametersEqual(this.typeParameters, previousSymbol.typeParameters)) {
28544
+ return true;
28545
+ }
28546
+ // The type-check metadata is used during TCB code generation, so any changes should invalidate
28547
+ // prior type-check files.
28548
+ if (!isTypeCheckMetaEqual(this.typeCheckMeta, previousSymbol.typeCheckMeta)) {
28549
+ return true;
28550
+ }
28551
+ // Changing the base class of a directive means that its inputs/outputs etc may have changed,
28552
+ // so the type-check block of components that use this directive needs to be regenerated.
28553
+ if (!isBaseClassEqual(this.baseClass, previousSymbol.baseClass)) {
28554
+ return true;
28555
+ }
28556
+ return false;
28557
+ }
28558
+ }
28559
+ function isInputMappingEqual(current, previous) {
28560
+ return current[0] === previous[0] && current[1] === previous[1];
28561
+ }
28562
+ function isTypeCheckMetaEqual(current, previous) {
28563
+ if (current.hasNgTemplateContextGuard !== previous.hasNgTemplateContextGuard) {
28564
+ return false;
28565
+ }
28566
+ if (current.isGeneric !== previous.isGeneric) {
28567
+ // Note: changes in the number of type parameters is also considered in `areTypeParametersEqual`
28568
+ // so this check is technically not needed; it is done anyway for completeness in terms of
28569
+ // whether the `DirectiveTypeCheckMeta` struct itself compares equal or not.
28570
+ return false;
28571
+ }
28572
+ if (!isArrayEqual(current.ngTemplateGuards, previous.ngTemplateGuards, isTemplateGuardEqual)) {
28573
+ return false;
28574
+ }
28575
+ if (!isSetEqual(current.coercedInputFields, previous.coercedInputFields)) {
28576
+ return false;
28577
+ }
28578
+ if (!isSetEqual(current.restrictedInputFields, previous.restrictedInputFields)) {
28579
+ return false;
28580
+ }
28581
+ if (!isSetEqual(current.stringLiteralInputFields, previous.stringLiteralInputFields)) {
28582
+ return false;
28583
+ }
28584
+ if (!isSetEqual(current.undeclaredInputFields, previous.undeclaredInputFields)) {
28585
+ return false;
28586
+ }
28587
+ return true;
28588
+ }
28589
+ function isTemplateGuardEqual(current, previous) {
28590
+ return current.inputName === previous.inputName && current.type === previous.type;
28591
+ }
28592
+ function isBaseClassEqual(current, previous) {
28593
+ if (current === null || previous === null) {
28594
+ return current === previous;
28595
+ }
28596
+ return isSymbolEqual(current, previous);
28597
+ }
28121
28598
  class DirectiveDecoratorHandler {
28122
- constructor(reflector, evaluator, metaRegistry, scopeRegistry, metaReader, defaultImportRecorder, injectableRegistry, isCore, annotateForClosureCompiler, compileUndecoratedClassesWithAngularFeatures) {
28599
+ constructor(reflector, evaluator, metaRegistry, scopeRegistry, metaReader, defaultImportRecorder, injectableRegistry, isCore, semanticDepGraphUpdater, annotateForClosureCompiler, compileUndecoratedClassesWithAngularFeatures) {
28123
28600
  this.reflector = reflector;
28124
28601
  this.evaluator = evaluator;
28125
28602
  this.metaRegistry = metaRegistry;
@@ -28128,6 +28605,7 @@ Either add the @Injectable() decorator to '${provider.node.name
28128
28605
  this.defaultImportRecorder = defaultImportRecorder;
28129
28606
  this.injectableRegistry = injectableRegistry;
28130
28607
  this.isCore = isCore;
28608
+ this.semanticDepGraphUpdater = semanticDepGraphUpdater;
28131
28609
  this.annotateForClosureCompiler = annotateForClosureCompiler;
28132
28610
  this.compileUndecoratedClassesWithAngularFeatures = compileUndecoratedClassesWithAngularFeatures;
28133
28611
  this.precedence = HandlerPrecedence.PRIMARY;
@@ -28179,6 +28657,10 @@ Either add the @Injectable() decorator to '${provider.node.name
28179
28657
  }
28180
28658
  };
28181
28659
  }
28660
+ symbol(node, analysis) {
28661
+ const typeParameters = extractSemanticTypeParameters(node);
28662
+ return new DirectiveSymbol(node, analysis.meta.selector, analysis.inputs, analysis.outputs, analysis.meta.exportAs, analysis.typeCheckMeta, typeParameters);
28663
+ }
28182
28664
  register(node, analysis) {
28183
28665
  // Register this directive's information with the `MetadataRegistry`. This ensures that
28184
28666
  // the information about the directive is available during the compile() phase.
@@ -28186,7 +28668,10 @@ Either add the @Injectable() decorator to '${provider.node.name
28186
28668
  this.metaRegistry.registerDirectiveMetadata(Object.assign(Object.assign({ ref, name: node.name.text, selector: analysis.meta.selector, exportAs: analysis.meta.exportAs, inputs: analysis.inputs, outputs: analysis.outputs, queries: analysis.meta.queries.map(query => query.propertyName), isComponent: false, baseClass: analysis.baseClass }, analysis.typeCheckMeta), { isPoisoned: analysis.isPoisoned, isStructural: analysis.isStructural }));
28187
28669
  this.injectableRegistry.registerInjectable(node);
28188
28670
  }
28189
- resolve(node, analysis) {
28671
+ resolve(node, analysis, symbol) {
28672
+ if (this.semanticDepGraphUpdater !== null && analysis.baseClass instanceof Reference$1) {
28673
+ symbol.baseClass = this.semanticDepGraphUpdater.getSymbol(analysis.baseClass.node);
28674
+ }
28190
28675
  const diagnostics = [];
28191
28676
  if (analysis.providersRequiringFactory !== null &&
28192
28677
  analysis.meta.providers instanceof WrappedNodeExpr) {
@@ -28679,6 +29164,525 @@ Either add the @Injectable() decorator to '${provider.node.name
28679
29164
  'ViewChildren',
28680
29165
  ]);
28681
29166
 
29167
+ /**
29168
+ * @license
29169
+ * Copyright Google LLC All Rights Reserved.
29170
+ *
29171
+ * Use of this source code is governed by an MIT-style license that can be
29172
+ * found in the LICENSE file at https://angular.io/license
29173
+ */
29174
+ /**
29175
+ * Represents an Angular NgModule.
29176
+ */
29177
+ class NgModuleSymbol extends SemanticSymbol {
29178
+ constructor() {
29179
+ super(...arguments);
29180
+ this.remotelyScopedComponents = [];
29181
+ }
29182
+ isPublicApiAffected(previousSymbol) {
29183
+ if (!(previousSymbol instanceof NgModuleSymbol)) {
29184
+ return true;
29185
+ }
29186
+ // NgModules don't have a public API that could affect emit of Angular decorated classes.
29187
+ return false;
29188
+ }
29189
+ isEmitAffected(previousSymbol) {
29190
+ if (!(previousSymbol instanceof NgModuleSymbol)) {
29191
+ return true;
29192
+ }
29193
+ // compare our remotelyScopedComponents to the previous symbol
29194
+ if (previousSymbol.remotelyScopedComponents.length !== this.remotelyScopedComponents.length) {
29195
+ return true;
29196
+ }
29197
+ for (const currEntry of this.remotelyScopedComponents) {
29198
+ const prevEntry = previousSymbol.remotelyScopedComponents.find(prevEntry => {
29199
+ return isSymbolEqual(prevEntry.component, currEntry.component);
29200
+ });
29201
+ if (prevEntry === undefined) {
29202
+ // No previous entry was found, which means that this component became remotely scoped and
29203
+ // hence this NgModule needs to be re-emitted.
29204
+ return true;
29205
+ }
29206
+ if (!isArrayEqual(currEntry.usedDirectives, prevEntry.usedDirectives, isReferenceEqual)) {
29207
+ // The list of used directives or their order has changed. Since this NgModule emits
29208
+ // references to the list of used directives, it should be re-emitted to update this list.
29209
+ // Note: the NgModule does not have to be re-emitted when any of the directives has had
29210
+ // their public API changed, as the NgModule only emits a reference to the symbol by its
29211
+ // name. Therefore, testing for symbol equality is sufficient.
29212
+ return true;
29213
+ }
29214
+ if (!isArrayEqual(currEntry.usedPipes, prevEntry.usedPipes, isReferenceEqual)) {
29215
+ return true;
29216
+ }
29217
+ }
29218
+ return false;
29219
+ }
29220
+ isTypeCheckApiAffected(previousSymbol) {
29221
+ if (!(previousSymbol instanceof NgModuleSymbol)) {
29222
+ return true;
29223
+ }
29224
+ return false;
29225
+ }
29226
+ addRemotelyScopedComponent(component, usedDirectives, usedPipes) {
29227
+ this.remotelyScopedComponents.push({ component, usedDirectives, usedPipes });
29228
+ }
29229
+ }
29230
+ /**
29231
+ * Compiles @NgModule annotations to ngModuleDef fields.
29232
+ */
29233
+ class NgModuleDecoratorHandler {
29234
+ constructor(reflector, evaluator, metaReader, metaRegistry, scopeRegistry, referencesRegistry, isCore, routeAnalyzer, refEmitter, factoryTracker, defaultImportRecorder, annotateForClosureCompiler, injectableRegistry, localeId) {
29235
+ this.reflector = reflector;
29236
+ this.evaluator = evaluator;
29237
+ this.metaReader = metaReader;
29238
+ this.metaRegistry = metaRegistry;
29239
+ this.scopeRegistry = scopeRegistry;
29240
+ this.referencesRegistry = referencesRegistry;
29241
+ this.isCore = isCore;
29242
+ this.routeAnalyzer = routeAnalyzer;
29243
+ this.refEmitter = refEmitter;
29244
+ this.factoryTracker = factoryTracker;
29245
+ this.defaultImportRecorder = defaultImportRecorder;
29246
+ this.annotateForClosureCompiler = annotateForClosureCompiler;
29247
+ this.injectableRegistry = injectableRegistry;
29248
+ this.localeId = localeId;
29249
+ this.precedence = HandlerPrecedence.PRIMARY;
29250
+ this.name = NgModuleDecoratorHandler.name;
29251
+ }
29252
+ detect(node, decorators) {
29253
+ if (!decorators) {
29254
+ return undefined;
29255
+ }
29256
+ const decorator = findAngularDecorator(decorators, 'NgModule', this.isCore);
29257
+ if (decorator !== undefined) {
29258
+ return {
29259
+ trigger: decorator.node,
29260
+ decorator: decorator,
29261
+ metadata: decorator,
29262
+ };
29263
+ }
29264
+ else {
29265
+ return undefined;
29266
+ }
29267
+ }
29268
+ analyze(node, decorator) {
29269
+ const name = node.name.text;
29270
+ if (decorator.args === null || decorator.args.length > 1) {
29271
+ throw new FatalDiagnosticError(ErrorCode.DECORATOR_ARITY_WRONG, Decorator.nodeForError(decorator), `Incorrect number of arguments to @NgModule decorator`);
29272
+ }
29273
+ // @NgModule can be invoked without arguments. In case it is, pretend as if a blank object
29274
+ // literal was specified. This simplifies the code below.
29275
+ const meta = decorator.args.length === 1 ? unwrapExpression(decorator.args[0]) :
29276
+ ts$1.createObjectLiteral([]);
29277
+ if (!ts$1.isObjectLiteralExpression(meta)) {
29278
+ throw new FatalDiagnosticError(ErrorCode.DECORATOR_ARG_NOT_LITERAL, meta, '@NgModule argument must be an object literal');
29279
+ }
29280
+ const ngModule = reflectObjectLiteral(meta);
29281
+ if (ngModule.has('jit')) {
29282
+ // The only allowed value is true, so there's no need to expand further.
29283
+ return {};
29284
+ }
29285
+ const moduleResolvers = combineResolvers([
29286
+ ref => this._extractModuleFromModuleWithProvidersFn(ref.node),
29287
+ forwardRefResolver,
29288
+ ]);
29289
+ const diagnostics = [];
29290
+ // Extract the module declarations, imports, and exports.
29291
+ let declarationRefs = [];
29292
+ let rawDeclarations = null;
29293
+ if (ngModule.has('declarations')) {
29294
+ rawDeclarations = ngModule.get('declarations');
29295
+ const declarationMeta = this.evaluator.evaluate(rawDeclarations, forwardRefResolver);
29296
+ declarationRefs =
29297
+ this.resolveTypeList(rawDeclarations, declarationMeta, name, 'declarations');
29298
+ // Look through the declarations to make sure they're all a part of the current compilation.
29299
+ for (const ref of declarationRefs) {
29300
+ if (ref.node.getSourceFile().isDeclarationFile) {
29301
+ const errorNode = ref.getOriginForDiagnostics(rawDeclarations);
29302
+ diagnostics.push(makeDiagnostic(ErrorCode.NGMODULE_INVALID_DECLARATION, errorNode, `Cannot declare '${ref.node.name
29303
+ .text}' in an NgModule as it's not a part of the current compilation.`, [makeRelatedInformation(ref.node.name, `'${ref.node.name.text}' is declared here.`)]));
29304
+ }
29305
+ }
29306
+ }
29307
+ if (diagnostics.length > 0) {
29308
+ return { diagnostics };
29309
+ }
29310
+ let importRefs = [];
29311
+ let rawImports = null;
29312
+ if (ngModule.has('imports')) {
29313
+ rawImports = ngModule.get('imports');
29314
+ const importsMeta = this.evaluator.evaluate(rawImports, moduleResolvers);
29315
+ importRefs = this.resolveTypeList(rawImports, importsMeta, name, 'imports');
29316
+ }
29317
+ let exportRefs = [];
29318
+ let rawExports = null;
29319
+ if (ngModule.has('exports')) {
29320
+ rawExports = ngModule.get('exports');
29321
+ const exportsMeta = this.evaluator.evaluate(rawExports, moduleResolvers);
29322
+ exportRefs = this.resolveTypeList(rawExports, exportsMeta, name, 'exports');
29323
+ this.referencesRegistry.add(node, ...exportRefs);
29324
+ }
29325
+ let bootstrapRefs = [];
29326
+ if (ngModule.has('bootstrap')) {
29327
+ const expr = ngModule.get('bootstrap');
29328
+ const bootstrapMeta = this.evaluator.evaluate(expr, forwardRefResolver);
29329
+ bootstrapRefs = this.resolveTypeList(expr, bootstrapMeta, name, 'bootstrap');
29330
+ }
29331
+ const schemas = [];
29332
+ if (ngModule.has('schemas')) {
29333
+ const rawExpr = ngModule.get('schemas');
29334
+ const result = this.evaluator.evaluate(rawExpr);
29335
+ if (!Array.isArray(result)) {
29336
+ throw createValueHasWrongTypeError(rawExpr, result, `NgModule.schemas must be an array`);
29337
+ }
29338
+ for (const schemaRef of result) {
29339
+ if (!(schemaRef instanceof Reference$1)) {
29340
+ throw createValueHasWrongTypeError(rawExpr, result, 'NgModule.schemas must be an array of schemas');
29341
+ }
29342
+ const id = schemaRef.getIdentityIn(schemaRef.node.getSourceFile());
29343
+ if (id === null || schemaRef.ownedByModuleGuess !== '@angular/core') {
29344
+ throw createValueHasWrongTypeError(rawExpr, result, 'NgModule.schemas must be an array of schemas');
29345
+ }
29346
+ // Since `id` is the `ts.Identifer` within the schema ref's declaration file, it's safe to
29347
+ // use `id.text` here to figure out which schema is in use. Even if the actual reference was
29348
+ // renamed when the user imported it, these names will match.
29349
+ switch (id.text) {
29350
+ case 'CUSTOM_ELEMENTS_SCHEMA':
29351
+ schemas.push(CUSTOM_ELEMENTS_SCHEMA);
29352
+ break;
29353
+ case 'NO_ERRORS_SCHEMA':
29354
+ schemas.push(NO_ERRORS_SCHEMA);
29355
+ break;
29356
+ default:
29357
+ throw createValueHasWrongTypeError(rawExpr, schemaRef, `'${schemaRef.debugName}' is not a valid NgModule schema`);
29358
+ }
29359
+ }
29360
+ }
29361
+ const id = ngModule.has('id') ? new WrappedNodeExpr(ngModule.get('id')) : null;
29362
+ const valueContext = node.getSourceFile();
29363
+ let typeContext = valueContext;
29364
+ const typeNode = this.reflector.getDtsDeclaration(node);
29365
+ if (typeNode !== null) {
29366
+ typeContext = typeNode.getSourceFile();
29367
+ }
29368
+ const bootstrap = bootstrapRefs.map(bootstrap => this._toR3Reference(bootstrap, valueContext, typeContext));
29369
+ const declarations = declarationRefs.map(decl => this._toR3Reference(decl, valueContext, typeContext));
29370
+ const imports = importRefs.map(imp => this._toR3Reference(imp, valueContext, typeContext));
29371
+ const exports = exportRefs.map(exp => this._toR3Reference(exp, valueContext, typeContext));
29372
+ const isForwardReference = (ref) => isExpressionForwardReference(ref.value, node.name, valueContext);
29373
+ const containsForwardDecls = bootstrap.some(isForwardReference) ||
29374
+ declarations.some(isForwardReference) || imports.some(isForwardReference) ||
29375
+ exports.some(isForwardReference);
29376
+ const type = wrapTypeReference(this.reflector, node);
29377
+ const internalType = new WrappedNodeExpr(this.reflector.getInternalNameOfClass(node));
29378
+ const adjacentType = new WrappedNodeExpr(this.reflector.getAdjacentNameOfClass(node));
29379
+ const ngModuleDef = {
29380
+ type,
29381
+ internalType,
29382
+ adjacentType,
29383
+ bootstrap,
29384
+ declarations,
29385
+ exports,
29386
+ imports,
29387
+ containsForwardDecls,
29388
+ id,
29389
+ emitInline: false,
29390
+ // TODO: to be implemented as a part of FW-1004.
29391
+ schemas: [],
29392
+ };
29393
+ const rawProviders = ngModule.has('providers') ? ngModule.get('providers') : null;
29394
+ const wrapperProviders = rawProviders !== null ?
29395
+ new WrappedNodeExpr(this.annotateForClosureCompiler ? wrapFunctionExpressionsInParens(rawProviders) :
29396
+ rawProviders) :
29397
+ null;
29398
+ // At this point, only add the module's imports as the injectors' imports. Any exported modules
29399
+ // are added during `resolve`, as we need scope information to be able to filter out directives
29400
+ // and pipes from the module exports.
29401
+ const injectorImports = [];
29402
+ if (ngModule.has('imports')) {
29403
+ injectorImports.push(new WrappedNodeExpr(ngModule.get('imports')));
29404
+ }
29405
+ if (this.routeAnalyzer !== null) {
29406
+ this.routeAnalyzer.add(node.getSourceFile(), name, rawImports, rawExports, rawProviders);
29407
+ }
29408
+ const ngInjectorDef = {
29409
+ name,
29410
+ type,
29411
+ internalType,
29412
+ providers: wrapperProviders,
29413
+ imports: injectorImports,
29414
+ };
29415
+ return {
29416
+ analysis: {
29417
+ id,
29418
+ schemas: schemas,
29419
+ mod: ngModuleDef,
29420
+ inj: ngInjectorDef,
29421
+ deps: getValidConstructorDependencies(node, this.reflector, this.defaultImportRecorder, this.isCore),
29422
+ declarations: declarationRefs,
29423
+ rawDeclarations,
29424
+ imports: importRefs,
29425
+ exports: exportRefs,
29426
+ providers: rawProviders,
29427
+ providersRequiringFactory: rawProviders ?
29428
+ resolveProvidersRequiringFactory(rawProviders, this.reflector, this.evaluator) :
29429
+ null,
29430
+ metadataStmt: generateSetClassMetadataCall(node, this.reflector, this.defaultImportRecorder, this.isCore, this.annotateForClosureCompiler),
29431
+ factorySymbolName: node.name.text,
29432
+ },
29433
+ };
29434
+ }
29435
+ symbol(node) {
29436
+ return new NgModuleSymbol(node);
29437
+ }
29438
+ register(node, analysis) {
29439
+ // Register this module's information with the LocalModuleScopeRegistry. This ensures that
29440
+ // during the compile() phase, the module's metadata is available for selector scope
29441
+ // computation.
29442
+ this.metaRegistry.registerNgModuleMetadata({
29443
+ ref: new Reference$1(node),
29444
+ schemas: analysis.schemas,
29445
+ declarations: analysis.declarations,
29446
+ imports: analysis.imports,
29447
+ exports: analysis.exports,
29448
+ rawDeclarations: analysis.rawDeclarations,
29449
+ });
29450
+ if (this.factoryTracker !== null) {
29451
+ this.factoryTracker.track(node.getSourceFile(), {
29452
+ name: analysis.factorySymbolName,
29453
+ hasId: analysis.id !== null,
29454
+ });
29455
+ }
29456
+ this.injectableRegistry.registerInjectable(node);
29457
+ }
29458
+ resolve(node, analysis) {
29459
+ const scope = this.scopeRegistry.getScopeOfModule(node);
29460
+ const diagnostics = [];
29461
+ const scopeDiagnostics = this.scopeRegistry.getDiagnosticsOfModule(node);
29462
+ if (scopeDiagnostics !== null) {
29463
+ diagnostics.push(...scopeDiagnostics);
29464
+ }
29465
+ if (analysis.providersRequiringFactory !== null) {
29466
+ const providerDiagnostics = getProviderDiagnostics(analysis.providersRequiringFactory, analysis.providers, this.injectableRegistry);
29467
+ diagnostics.push(...providerDiagnostics);
29468
+ }
29469
+ const data = {
29470
+ injectorImports: [],
29471
+ };
29472
+ if (scope !== null && !scope.compilation.isPoisoned) {
29473
+ // Using the scope information, extend the injector's imports using the modules that are
29474
+ // specified as module exports.
29475
+ const context = getSourceFile(node);
29476
+ for (const exportRef of analysis.exports) {
29477
+ if (isNgModule(exportRef.node, scope.compilation)) {
29478
+ data.injectorImports.push(this.refEmitter.emit(exportRef, context).expression);
29479
+ }
29480
+ }
29481
+ for (const decl of analysis.declarations) {
29482
+ const metadata = this.metaReader.getDirectiveMetadata(decl);
29483
+ if (metadata !== null && metadata.selector === null) {
29484
+ throw new FatalDiagnosticError(ErrorCode.DIRECTIVE_MISSING_SELECTOR, decl.node, `Directive ${decl.node.name.text} has no selector, please add it!`);
29485
+ }
29486
+ }
29487
+ }
29488
+ if (diagnostics.length > 0) {
29489
+ return { diagnostics };
29490
+ }
29491
+ if (scope === null || scope.compilation.isPoisoned || scope.exported.isPoisoned ||
29492
+ scope.reexports === null) {
29493
+ return { data };
29494
+ }
29495
+ else {
29496
+ return {
29497
+ data,
29498
+ reexports: scope.reexports,
29499
+ };
29500
+ }
29501
+ }
29502
+ compileFull(node, { inj, mod, deps, metadataStmt, declarations }, resolution) {
29503
+ // Merge the injector imports (which are 'exports' that were later found to be NgModules)
29504
+ // computed during resolution with the ones from analysis.
29505
+ const ngInjectorDef = compileInjector(Object.assign(Object.assign({}, inj), { imports: [...inj.imports, ...resolution.injectorImports] }));
29506
+ const ngModuleDef = compileNgModule(mod);
29507
+ const ngModuleStatements = ngModuleDef.additionalStatements;
29508
+ if (metadataStmt !== null) {
29509
+ ngModuleStatements.push(metadataStmt);
29510
+ }
29511
+ const context = getSourceFile(node);
29512
+ for (const decl of declarations) {
29513
+ const remoteScope = this.scopeRegistry.getRemoteScope(decl.node);
29514
+ if (remoteScope !== null) {
29515
+ const directives = remoteScope.directives.map(directive => this.refEmitter.emit(directive, context).expression);
29516
+ const pipes = remoteScope.pipes.map(pipe => this.refEmitter.emit(pipe, context).expression);
29517
+ const directiveArray = new LiteralArrayExpr(directives);
29518
+ const pipesArray = new LiteralArrayExpr(pipes);
29519
+ const declExpr = this.refEmitter.emit(decl, context).expression;
29520
+ const setComponentScope = new ExternalExpr(Identifiers$1.setComponentScope);
29521
+ const callExpr = new InvokeFunctionExpr(setComponentScope, [declExpr, directiveArray, pipesArray]);
29522
+ ngModuleStatements.push(callExpr.toStmt());
29523
+ }
29524
+ }
29525
+ const res = [
29526
+ compileNgFactoryDefField({
29527
+ name: inj.name,
29528
+ type: inj.type,
29529
+ internalType: inj.internalType,
29530
+ typeArgumentCount: 0,
29531
+ deps,
29532
+ injectFn: Identifiers.inject,
29533
+ target: R3FactoryTarget.NgModule,
29534
+ }),
29535
+ {
29536
+ name: 'ɵmod',
29537
+ initializer: ngModuleDef.expression,
29538
+ statements: ngModuleStatements,
29539
+ type: ngModuleDef.type,
29540
+ },
29541
+ {
29542
+ name: 'ɵinj',
29543
+ initializer: ngInjectorDef.expression,
29544
+ statements: [],
29545
+ type: ngInjectorDef.type,
29546
+ },
29547
+ ];
29548
+ if (this.localeId) {
29549
+ res.push({
29550
+ name: 'ɵloc',
29551
+ initializer: new LiteralExpr(this.localeId),
29552
+ statements: [],
29553
+ type: STRING_TYPE
29554
+ });
29555
+ }
29556
+ return res;
29557
+ }
29558
+ _toR3Reference(valueRef, valueContext, typeContext) {
29559
+ if (valueRef.hasOwningModuleGuess) {
29560
+ return toR3Reference(valueRef, valueRef, valueContext, valueContext, this.refEmitter);
29561
+ }
29562
+ else {
29563
+ let typeRef = valueRef;
29564
+ let typeNode = this.reflector.getDtsDeclaration(typeRef.node);
29565
+ if (typeNode !== null && isNamedClassDeclaration(typeNode)) {
29566
+ typeRef = new Reference$1(typeNode);
29567
+ }
29568
+ return toR3Reference(valueRef, typeRef, valueContext, typeContext, this.refEmitter);
29569
+ }
29570
+ }
29571
+ /**
29572
+ * Given a `FunctionDeclaration`, `MethodDeclaration` or `FunctionExpression`, check if it is
29573
+ * typed as a `ModuleWithProviders` and return an expression referencing the module if available.
29574
+ */
29575
+ _extractModuleFromModuleWithProvidersFn(node) {
29576
+ const type = node.type || null;
29577
+ return type &&
29578
+ (this._reflectModuleFromTypeParam(type, node) || this._reflectModuleFromLiteralType(type));
29579
+ }
29580
+ /**
29581
+ * Retrieve an `NgModule` identifier (T) from the specified `type`, if it is of the form:
29582
+ * `ModuleWithProviders<T>`
29583
+ * @param type The type to reflect on.
29584
+ * @returns the identifier of the NgModule type if found, or null otherwise.
29585
+ */
29586
+ _reflectModuleFromTypeParam(type, node) {
29587
+ // Examine the type of the function to see if it's a ModuleWithProviders reference.
29588
+ if (!ts$1.isTypeReferenceNode(type)) {
29589
+ return null;
29590
+ }
29591
+ const typeName = type &&
29592
+ (ts$1.isIdentifier(type.typeName) && type.typeName ||
29593
+ ts$1.isQualifiedName(type.typeName) && type.typeName.right) ||
29594
+ null;
29595
+ if (typeName === null) {
29596
+ return null;
29597
+ }
29598
+ // Look at the type itself to see where it comes from.
29599
+ const id = this.reflector.getImportOfIdentifier(typeName);
29600
+ // If it's not named ModuleWithProviders, bail.
29601
+ if (id === null || id.name !== 'ModuleWithProviders') {
29602
+ return null;
29603
+ }
29604
+ // If it's not from @angular/core, bail.
29605
+ if (!this.isCore && id.from !== '@angular/core') {
29606
+ return null;
29607
+ }
29608
+ // If there's no type parameter specified, bail.
29609
+ if (type.typeArguments === undefined || type.typeArguments.length !== 1) {
29610
+ const parent = ts$1.isMethodDeclaration(node) && ts$1.isClassDeclaration(node.parent) ? node.parent : null;
29611
+ const symbolName = (parent && parent.name ? parent.name.getText() + '.' : '') +
29612
+ (node.name ? node.name.getText() : 'anonymous');
29613
+ throw new FatalDiagnosticError(ErrorCode.NGMODULE_MODULE_WITH_PROVIDERS_MISSING_GENERIC, type, `${symbolName} returns a ModuleWithProviders type without a generic type argument. ` +
29614
+ `Please add a generic type argument to the ModuleWithProviders type. If this ` +
29615
+ `occurrence is in library code you don't control, please contact the library authors.`);
29616
+ }
29617
+ const arg = type.typeArguments[0];
29618
+ return typeNodeToValueExpr(arg);
29619
+ }
29620
+ /**
29621
+ * Retrieve an `NgModule` identifier (T) from the specified `type`, if it is of the form:
29622
+ * `A|B|{ngModule: T}|C`.
29623
+ * @param type The type to reflect on.
29624
+ * @returns the identifier of the NgModule type if found, or null otherwise.
29625
+ */
29626
+ _reflectModuleFromLiteralType(type) {
29627
+ if (!ts$1.isIntersectionTypeNode(type)) {
29628
+ return null;
29629
+ }
29630
+ for (const t of type.types) {
29631
+ if (ts$1.isTypeLiteralNode(t)) {
29632
+ for (const m of t.members) {
29633
+ const ngModuleType = ts$1.isPropertySignature(m) && ts$1.isIdentifier(m.name) &&
29634
+ m.name.text === 'ngModule' && m.type ||
29635
+ null;
29636
+ const ngModuleExpression = ngModuleType && typeNodeToValueExpr(ngModuleType);
29637
+ if (ngModuleExpression) {
29638
+ return ngModuleExpression;
29639
+ }
29640
+ }
29641
+ }
29642
+ }
29643
+ return null;
29644
+ }
29645
+ // Verify that a "Declaration" reference is a `ClassDeclaration` reference.
29646
+ isClassDeclarationReference(ref) {
29647
+ return this.reflector.isClass(ref.node);
29648
+ }
29649
+ /**
29650
+ * Compute a list of `Reference`s from a resolved metadata value.
29651
+ */
29652
+ resolveTypeList(expr, resolvedList, className, arrayName) {
29653
+ const refList = [];
29654
+ if (!Array.isArray(resolvedList)) {
29655
+ throw createValueHasWrongTypeError(expr, resolvedList, `Expected array when reading the NgModule.${arrayName} of ${className}`);
29656
+ }
29657
+ resolvedList.forEach((entry, idx) => {
29658
+ // Unwrap ModuleWithProviders for modules that are locally declared (and thus static
29659
+ // resolution was able to descend into the function and return an object literal, a Map).
29660
+ if (entry instanceof Map && entry.has('ngModule')) {
29661
+ entry = entry.get('ngModule');
29662
+ }
29663
+ if (Array.isArray(entry)) {
29664
+ // Recurse into nested arrays.
29665
+ refList.push(...this.resolveTypeList(expr, entry, className, arrayName));
29666
+ }
29667
+ else if (entry instanceof Reference$1) {
29668
+ if (!this.isClassDeclarationReference(entry)) {
29669
+ throw createValueHasWrongTypeError(entry.node, entry, `Value at position ${idx} in the NgModule.${arrayName} of ${className} is not a class`);
29670
+ }
29671
+ refList.push(entry);
29672
+ }
29673
+ else {
29674
+ // TODO(alxhub): Produce a better diagnostic here - the array index may be an inner array.
29675
+ throw createValueHasWrongTypeError(expr, entry, `Value at position ${idx} in the NgModule.${arrayName} of ${className} is not a reference`);
29676
+ }
29677
+ });
29678
+ return refList;
29679
+ }
29680
+ }
29681
+ function isNgModule(node, compilation) {
29682
+ return !compilation.directives.some(directive => directive.ref.node === node) &&
29683
+ !compilation.pipes.some(pipe => pipe.ref.node === node);
29684
+ }
29685
+
28682
29686
  /**
28683
29687
  * @license
28684
29688
  * Copyright Google LLC All Rights Reserved.
@@ -28688,11 +29692,73 @@ Either add the @Injectable() decorator to '${provider.node.name
28688
29692
  */
28689
29693
  const EMPTY_MAP = new Map();
28690
29694
  const EMPTY_ARRAY = [];
29695
+ /**
29696
+ * Represents an Angular component.
29697
+ */
29698
+ class ComponentSymbol extends DirectiveSymbol {
29699
+ constructor() {
29700
+ super(...arguments);
29701
+ this.usedDirectives = [];
29702
+ this.usedPipes = [];
29703
+ this.isRemotelyScoped = false;
29704
+ }
29705
+ isEmitAffected(previousSymbol, publicApiAffected) {
29706
+ if (!(previousSymbol instanceof ComponentSymbol)) {
29707
+ return true;
29708
+ }
29709
+ // Create an equality function that considers symbols equal if they represent the same
29710
+ // declaration, but only if the symbol in the current compilation does not have its public API
29711
+ // affected.
29712
+ const isSymbolUnaffected = (current, previous) => isReferenceEqual(current, previous) && !publicApiAffected.has(current.symbol);
29713
+ // The emit of a component is affected if either of the following is true:
29714
+ // 1. The component used to be remotely scoped but no longer is, or vice versa.
29715
+ // 2. The list of used directives has changed or any of those directives have had their public
29716
+ // API changed. If the used directives have been reordered but not otherwise affected then
29717
+ // the component must still be re-emitted, as this may affect directive instantiation order.
29718
+ // 3. The list of used pipes has changed, or any of those pipes have had their public API
29719
+ // changed.
29720
+ return this.isRemotelyScoped !== previousSymbol.isRemotelyScoped ||
29721
+ !isArrayEqual(this.usedDirectives, previousSymbol.usedDirectives, isSymbolUnaffected) ||
29722
+ !isArrayEqual(this.usedPipes, previousSymbol.usedPipes, isSymbolUnaffected);
29723
+ }
29724
+ isTypeCheckBlockAffected(previousSymbol, typeCheckApiAffected) {
29725
+ if (!(previousSymbol instanceof ComponentSymbol)) {
29726
+ return true;
29727
+ }
29728
+ // To verify that a used directive is not affected we need to verify that its full inheritance
29729
+ // chain is not present in `typeCheckApiAffected`.
29730
+ const isInheritanceChainAffected = (symbol) => {
29731
+ let currentSymbol = symbol;
29732
+ while (currentSymbol instanceof DirectiveSymbol) {
29733
+ if (typeCheckApiAffected.has(currentSymbol)) {
29734
+ return true;
29735
+ }
29736
+ currentSymbol = currentSymbol.baseClass;
29737
+ }
29738
+ return false;
29739
+ };
29740
+ // Create an equality function that considers directives equal if they represent the same
29741
+ // declaration and if the symbol and all symbols it inherits from in the current compilation
29742
+ // do not have their type-check API affected.
29743
+ const isDirectiveUnaffected = (current, previous) => isReferenceEqual(current, previous) && !isInheritanceChainAffected(current.symbol);
29744
+ // Create an equality function that considers pipes equal if they represent the same
29745
+ // declaration and if the symbol in the current compilation does not have its type-check
29746
+ // API affected.
29747
+ const isPipeUnaffected = (current, previous) => isReferenceEqual(current, previous) && !typeCheckApiAffected.has(current.symbol);
29748
+ // The emit of a type-check block of a component is affected if either of the following is true:
29749
+ // 1. The list of used directives has changed or any of those directives have had their
29750
+ // type-check API changed.
29751
+ // 2. The list of used pipes has changed, or any of those pipes have had their type-check API
29752
+ // changed.
29753
+ return !isArrayEqual(this.usedDirectives, previousSymbol.usedDirectives, isDirectiveUnaffected) ||
29754
+ !isArrayEqual(this.usedPipes, previousSymbol.usedPipes, isPipeUnaffected);
29755
+ }
29756
+ }
28691
29757
  /**
28692
29758
  * `DecoratorHandler` which handles the `@Component` annotation.
28693
29759
  */
28694
29760
  class ComponentDecoratorHandler {
28695
- constructor(reflector, evaluator, metaRegistry, metaReader, scopeReader, scopeRegistry, typeCheckScopeRegistry, resourceRegistry, isCore, resourceLoader, rootDirs, defaultPreserveWhitespaces, i18nUseExternalIds, enableI18nLegacyMessageIdFormat, usePoisonedData, i18nNormalizeLineEndingsInICUs, moduleResolver, cycleAnalyzer, cycleHandlingStrategy, refEmitter, defaultImportRecorder, depTracker, injectableRegistry, annotateForClosureCompiler) {
29761
+ constructor(reflector, evaluator, metaRegistry, metaReader, scopeReader, scopeRegistry, typeCheckScopeRegistry, resourceRegistry, isCore, resourceLoader, rootDirs, defaultPreserveWhitespaces, i18nUseExternalIds, enableI18nLegacyMessageIdFormat, usePoisonedData, i18nNormalizeLineEndingsInICUs, moduleResolver, cycleAnalyzer, cycleHandlingStrategy, refEmitter, defaultImportRecorder, depTracker, injectableRegistry, semanticDepGraphUpdater, annotateForClosureCompiler) {
28696
29762
  this.reflector = reflector;
28697
29763
  this.evaluator = evaluator;
28698
29764
  this.metaRegistry = metaRegistry;
@@ -28716,6 +29782,7 @@ Either add the @Injectable() decorator to '${provider.node.name
28716
29782
  this.defaultImportRecorder = defaultImportRecorder;
28717
29783
  this.depTracker = depTracker;
28718
29784
  this.injectableRegistry = injectableRegistry;
29785
+ this.semanticDepGraphUpdater = semanticDepGraphUpdater;
28719
29786
  this.annotateForClosureCompiler = annotateForClosureCompiler;
28720
29787
  this.literalCache = new Map();
28721
29788
  this.elementSchemaRegistry = new DomElementSchemaRegistry();
@@ -28923,6 +29990,10 @@ Either add the @Injectable() decorator to '${provider.node.name
28923
29990
  }
28924
29991
  return output;
28925
29992
  }
29993
+ symbol(node, analysis) {
29994
+ const typeParameters = extractSemanticTypeParameters(node);
29995
+ return new ComponentSymbol(node, analysis.meta.selector, analysis.inputs, analysis.outputs, analysis.meta.exportAs, analysis.typeCheckMeta, typeParameters);
29996
+ }
28926
29997
  register(node, analysis) {
28927
29998
  // Register this component's information with the `MetadataRegistry`. This ensures that
28928
29999
  // the information about the component is available during the compile() phase.
@@ -28977,7 +30048,10 @@ Either add the @Injectable() decorator to '${provider.node.name
28977
30048
  const binder = new R3TargetBinder(scope.matcher);
28978
30049
  ctx.addTemplate(new Reference$1(node), binder, meta.template.diagNodes, scope.pipes, scope.schemas, meta.template.sourceMapping, meta.template.file, meta.template.errors);
28979
30050
  }
28980
- resolve(node, analysis) {
30051
+ resolve(node, analysis, symbol) {
30052
+ if (this.semanticDepGraphUpdater !== null && analysis.baseClass instanceof Reference$1) {
30053
+ symbol.baseClass = this.semanticDepGraphUpdater.getSymbol(analysis.baseClass.node);
30054
+ }
28981
30055
  if (analysis.isPoisoned && !this.usePoisonedData) {
28982
30056
  return {};
28983
30057
  }
@@ -29007,9 +30081,11 @@ Either add the @Injectable() decorator to '${provider.node.name
29007
30081
  const binder = new R3TargetBinder(matcher);
29008
30082
  const bound = binder.bind({ template: metadata.template.nodes });
29009
30083
  const usedDirectives = bound.getUsedDirectives().map(directive => {
30084
+ const type = this.refEmitter.emit(directive.ref, context);
29010
30085
  return {
29011
30086
  ref: directive.ref,
29012
- type: this.refEmitter.emit(directive.ref, context),
30087
+ type: type.expression,
30088
+ importedFile: type.importedFile,
29013
30089
  selector: directive.selector,
29014
30090
  inputs: directive.inputs.propertyNames,
29015
30091
  outputs: directive.outputs.propertyNames,
@@ -29023,36 +30099,43 @@ Either add the @Injectable() decorator to '${provider.node.name
29023
30099
  continue;
29024
30100
  }
29025
30101
  const pipe = pipes.get(pipeName);
30102
+ const type = this.refEmitter.emit(pipe, context);
29026
30103
  usedPipes.push({
29027
30104
  ref: pipe,
29028
30105
  pipeName,
29029
- expression: this.refEmitter.emit(pipe, context),
30106
+ expression: type.expression,
30107
+ importedFile: type.importedFile,
29030
30108
  });
29031
30109
  }
30110
+ if (this.semanticDepGraphUpdater !== null) {
30111
+ symbol.usedDirectives = usedDirectives.map(dir => this.semanticDepGraphUpdater.getSemanticReference(dir.ref.node, dir.type));
30112
+ symbol.usedPipes = usedPipes.map(pipe => this.semanticDepGraphUpdater.getSemanticReference(pipe.ref.node, pipe.expression));
30113
+ }
29032
30114
  // Scan through the directives/pipes actually used in the template and check whether any
29033
30115
  // import which needs to be generated would create a cycle.
29034
30116
  const cyclesFromDirectives = new Map();
29035
30117
  for (const usedDirective of usedDirectives) {
29036
- const cycle = this._checkForCyclicImport(usedDirective.ref, usedDirective.type, context);
30118
+ const cycle = this._checkForCyclicImport(usedDirective.importedFile, usedDirective.type, context);
29037
30119
  if (cycle !== null) {
29038
30120
  cyclesFromDirectives.set(usedDirective, cycle);
29039
30121
  }
29040
30122
  }
29041
30123
  const cyclesFromPipes = new Map();
29042
30124
  for (const usedPipe of usedPipes) {
29043
- const cycle = this._checkForCyclicImport(usedPipe.ref, usedPipe.expression, context);
30125
+ const cycle = this._checkForCyclicImport(usedPipe.importedFile, usedPipe.expression, context);
29044
30126
  if (cycle !== null) {
29045
30127
  cyclesFromPipes.set(usedPipe, cycle);
29046
30128
  }
29047
30129
  }
29048
- if (cyclesFromDirectives.size === 0 && cyclesFromPipes.size === 0) {
30130
+ const cycleDetected = cyclesFromDirectives.size !== 0 || cyclesFromPipes.size !== 0;
30131
+ if (!cycleDetected) {
29049
30132
  // No cycle was detected. Record the imports that need to be created in the cycle detector
29050
30133
  // so that future cyclic import checks consider their production.
29051
- for (const { type } of usedDirectives) {
29052
- this._recordSyntheticImport(type, context);
30134
+ for (const { type, importedFile } of usedDirectives) {
30135
+ this._recordSyntheticImport(importedFile, type, context);
29053
30136
  }
29054
- for (const { expression } of usedPipes) {
29055
- this._recordSyntheticImport(expression, context);
30137
+ for (const { expression, importedFile } of usedPipes) {
30138
+ this._recordSyntheticImport(importedFile, expression, context);
29056
30139
  }
29057
30140
  // Check whether the directive/pipe arrays in ɵcmp need to be wrapped in closures.
29058
30141
  // This is required if any directive/pipe reference is to a declaration in the same file
@@ -29071,6 +30154,17 @@ Either add the @Injectable() decorator to '${provider.node.name
29071
30154
  // create a cycle. Instead, mark this component as requiring remote scoping, so that the
29072
30155
  // NgModule file will take care of setting the directives for the component.
29073
30156
  this.scopeRegistry.setComponentRemoteScope(node, usedDirectives.map(dir => dir.ref), usedPipes.map(pipe => pipe.ref));
30157
+ symbol.isRemotelyScoped = true;
30158
+ // If a semantic graph is being tracked, record the fact that this component is remotely
30159
+ // scoped with the declaring NgModule symbol as the NgModule's emit becomes dependent on
30160
+ // the directive/pipe usages of this component.
30161
+ if (this.semanticDepGraphUpdater !== null) {
30162
+ const moduleSymbol = this.semanticDepGraphUpdater.getSymbol(scope.ngModule);
30163
+ if (!(moduleSymbol instanceof NgModuleSymbol)) {
30164
+ throw new Error(`AssertionError: Expected ${scope.ngModule.name} to be an NgModuleSymbol.`);
30165
+ }
30166
+ moduleSymbol.addRemotelyScopedComponent(symbol, symbol.usedDirectives, symbol.usedPipes);
30167
+ }
29074
30168
  }
29075
30169
  else {
29076
30170
  // We are not able to handle this cycle so throw an error.
@@ -29436,7 +30530,15 @@ Either add the @Injectable() decorator to '${provider.node.name
29436
30530
  throw new FatalDiagnosticError(ErrorCode.COMPONENT_MISSING_TEMPLATE, Decorator.nodeForError(decorator), 'component is missing a template');
29437
30531
  }
29438
30532
  }
29439
- _expressionToImportedFile(expr, origin) {
30533
+ _resolveImportedFile(importedFile, expr, origin) {
30534
+ // If `importedFile` is not 'unknown' then it accurately reflects the source file that is
30535
+ // being imported.
30536
+ if (importedFile !== 'unknown') {
30537
+ return importedFile;
30538
+ }
30539
+ // Otherwise `expr` has to be inspected to determine the file that is being imported. If `expr`
30540
+ // is not an `ExternalExpr` then it does not correspond with an import, so return null in that
30541
+ // case.
29440
30542
  if (!(expr instanceof ExternalExpr)) {
29441
30543
  return null;
29442
30544
  }
@@ -29449,16 +30551,16 @@ Either add the @Injectable() decorator to '${provider.node.name
29449
30551
  *
29450
30552
  * @returns a `Cycle` object if a cycle would be created, otherwise `null`.
29451
30553
  */
29452
- _checkForCyclicImport(ref, expr, origin) {
29453
- const importedFile = this._expressionToImportedFile(expr, origin);
29454
- if (importedFile === null) {
30554
+ _checkForCyclicImport(importedFile, expr, origin) {
30555
+ const imported = this._resolveImportedFile(importedFile, expr, origin);
30556
+ if (imported === null) {
29455
30557
  return null;
29456
30558
  }
29457
30559
  // Check whether the import is legal.
29458
- return this.cycleAnalyzer.wouldCreateCycle(origin, importedFile);
30560
+ return this.cycleAnalyzer.wouldCreateCycle(origin, imported);
29459
30561
  }
29460
- _recordSyntheticImport(expr, origin) {
29461
- const imported = this._expressionToImportedFile(expr, origin);
30562
+ _recordSyntheticImport(importedFile, expr, origin) {
30563
+ const imported = this._resolveImportedFile(importedFile, expr, origin);
29462
30564
  if (imported === null) {
29463
30565
  return;
29464
30566
  }
@@ -29603,6 +30705,9 @@ Either add the @Injectable() decorator to '${provider.node.name
29603
30705
  },
29604
30706
  };
29605
30707
  }
30708
+ symbol() {
30709
+ return null;
30710
+ }
29606
30711
  register(node) {
29607
30712
  this.injectableRegistry.registerInjectable(node);
29608
30713
  }
@@ -29823,458 +30928,23 @@ Either add the @Injectable() decorator to '${provider.node.name
29823
30928
  * found in the LICENSE file at https://angular.io/license
29824
30929
  */
29825
30930
  /**
29826
- * Compiles @NgModule annotations to ngModuleDef fields.
29827
- *
29828
- * TODO(alxhub): handle injector side of things as well.
30931
+ * Represents an Angular pipe.
29829
30932
  */
29830
- class NgModuleDecoratorHandler {
29831
- constructor(reflector, evaluator, metaReader, metaRegistry, scopeRegistry, referencesRegistry, isCore, routeAnalyzer, refEmitter, factoryTracker, defaultImportRecorder, annotateForClosureCompiler, injectableRegistry, localeId) {
29832
- this.reflector = reflector;
29833
- this.evaluator = evaluator;
29834
- this.metaReader = metaReader;
29835
- this.metaRegistry = metaRegistry;
29836
- this.scopeRegistry = scopeRegistry;
29837
- this.referencesRegistry = referencesRegistry;
29838
- this.isCore = isCore;
29839
- this.routeAnalyzer = routeAnalyzer;
29840
- this.refEmitter = refEmitter;
29841
- this.factoryTracker = factoryTracker;
29842
- this.defaultImportRecorder = defaultImportRecorder;
29843
- this.annotateForClosureCompiler = annotateForClosureCompiler;
29844
- this.injectableRegistry = injectableRegistry;
29845
- this.localeId = localeId;
29846
- this.precedence = HandlerPrecedence.PRIMARY;
29847
- this.name = NgModuleDecoratorHandler.name;
29848
- }
29849
- detect(node, decorators) {
29850
- if (!decorators) {
29851
- return undefined;
29852
- }
29853
- const decorator = findAngularDecorator(decorators, 'NgModule', this.isCore);
29854
- if (decorator !== undefined) {
29855
- return {
29856
- trigger: decorator.node,
29857
- decorator: decorator,
29858
- metadata: decorator,
29859
- };
29860
- }
29861
- else {
29862
- return undefined;
29863
- }
29864
- }
29865
- analyze(node, decorator) {
29866
- const name = node.name.text;
29867
- if (decorator.args === null || decorator.args.length > 1) {
29868
- throw new FatalDiagnosticError(ErrorCode.DECORATOR_ARITY_WRONG, Decorator.nodeForError(decorator), `Incorrect number of arguments to @NgModule decorator`);
29869
- }
29870
- // @NgModule can be invoked without arguments. In case it is, pretend as if a blank object
29871
- // literal was specified. This simplifies the code below.
29872
- const meta = decorator.args.length === 1 ? unwrapExpression(decorator.args[0]) :
29873
- ts$1.createObjectLiteral([]);
29874
- if (!ts$1.isObjectLiteralExpression(meta)) {
29875
- throw new FatalDiagnosticError(ErrorCode.DECORATOR_ARG_NOT_LITERAL, meta, '@NgModule argument must be an object literal');
29876
- }
29877
- const ngModule = reflectObjectLiteral(meta);
29878
- if (ngModule.has('jit')) {
29879
- // The only allowed value is true, so there's no need to expand further.
29880
- return {};
29881
- }
29882
- const moduleResolvers = combineResolvers([
29883
- ref => this._extractModuleFromModuleWithProvidersFn(ref.node),
29884
- forwardRefResolver,
29885
- ]);
29886
- const diagnostics = [];
29887
- // Extract the module declarations, imports, and exports.
29888
- let declarationRefs = [];
29889
- let rawDeclarations = null;
29890
- if (ngModule.has('declarations')) {
29891
- rawDeclarations = ngModule.get('declarations');
29892
- const declarationMeta = this.evaluator.evaluate(rawDeclarations, forwardRefResolver);
29893
- declarationRefs =
29894
- this.resolveTypeList(rawDeclarations, declarationMeta, name, 'declarations');
29895
- // Look through the declarations to make sure they're all a part of the current compilation.
29896
- for (const ref of declarationRefs) {
29897
- if (ref.node.getSourceFile().isDeclarationFile) {
29898
- const errorNode = ref.getOriginForDiagnostics(rawDeclarations);
29899
- diagnostics.push(makeDiagnostic(ErrorCode.NGMODULE_INVALID_DECLARATION, errorNode, `Cannot declare '${ref.node.name
29900
- .text}' in an NgModule as it's not a part of the current compilation.`, [makeRelatedInformation(ref.node.name, `'${ref.node.name.text}' is declared here.`)]));
29901
- }
29902
- }
29903
- }
29904
- if (diagnostics.length > 0) {
29905
- return { diagnostics };
29906
- }
29907
- let importRefs = [];
29908
- let rawImports = null;
29909
- if (ngModule.has('imports')) {
29910
- rawImports = ngModule.get('imports');
29911
- const importsMeta = this.evaluator.evaluate(rawImports, moduleResolvers);
29912
- importRefs = this.resolveTypeList(rawImports, importsMeta, name, 'imports');
29913
- }
29914
- let exportRefs = [];
29915
- let rawExports = null;
29916
- if (ngModule.has('exports')) {
29917
- rawExports = ngModule.get('exports');
29918
- const exportsMeta = this.evaluator.evaluate(rawExports, moduleResolvers);
29919
- exportRefs = this.resolveTypeList(rawExports, exportsMeta, name, 'exports');
29920
- this.referencesRegistry.add(node, ...exportRefs);
29921
- }
29922
- let bootstrapRefs = [];
29923
- if (ngModule.has('bootstrap')) {
29924
- const expr = ngModule.get('bootstrap');
29925
- const bootstrapMeta = this.evaluator.evaluate(expr, forwardRefResolver);
29926
- bootstrapRefs = this.resolveTypeList(expr, bootstrapMeta, name, 'bootstrap');
29927
- }
29928
- const schemas = [];
29929
- if (ngModule.has('schemas')) {
29930
- const rawExpr = ngModule.get('schemas');
29931
- const result = this.evaluator.evaluate(rawExpr);
29932
- if (!Array.isArray(result)) {
29933
- throw createValueHasWrongTypeError(rawExpr, result, `NgModule.schemas must be an array`);
29934
- }
29935
- for (const schemaRef of result) {
29936
- if (!(schemaRef instanceof Reference$1)) {
29937
- throw createValueHasWrongTypeError(rawExpr, result, 'NgModule.schemas must be an array of schemas');
29938
- }
29939
- const id = schemaRef.getIdentityIn(schemaRef.node.getSourceFile());
29940
- if (id === null || schemaRef.ownedByModuleGuess !== '@angular/core') {
29941
- throw createValueHasWrongTypeError(rawExpr, result, 'NgModule.schemas must be an array of schemas');
29942
- }
29943
- // Since `id` is the `ts.Identifer` within the schema ref's declaration file, it's safe to
29944
- // use `id.text` here to figure out which schema is in use. Even if the actual reference was
29945
- // renamed when the user imported it, these names will match.
29946
- switch (id.text) {
29947
- case 'CUSTOM_ELEMENTS_SCHEMA':
29948
- schemas.push(CUSTOM_ELEMENTS_SCHEMA);
29949
- break;
29950
- case 'NO_ERRORS_SCHEMA':
29951
- schemas.push(NO_ERRORS_SCHEMA);
29952
- break;
29953
- default:
29954
- throw createValueHasWrongTypeError(rawExpr, schemaRef, `'${schemaRef.debugName}' is not a valid NgModule schema`);
29955
- }
29956
- }
29957
- }
29958
- const id = ngModule.has('id') ? new WrappedNodeExpr(ngModule.get('id')) : null;
29959
- const valueContext = node.getSourceFile();
29960
- let typeContext = valueContext;
29961
- const typeNode = this.reflector.getDtsDeclaration(node);
29962
- if (typeNode !== null) {
29963
- typeContext = typeNode.getSourceFile();
29964
- }
29965
- const bootstrap = bootstrapRefs.map(bootstrap => this._toR3Reference(bootstrap, valueContext, typeContext));
29966
- const declarations = declarationRefs.map(decl => this._toR3Reference(decl, valueContext, typeContext));
29967
- const imports = importRefs.map(imp => this._toR3Reference(imp, valueContext, typeContext));
29968
- const exports = exportRefs.map(exp => this._toR3Reference(exp, valueContext, typeContext));
29969
- const isForwardReference = (ref) => isExpressionForwardReference(ref.value, node.name, valueContext);
29970
- const containsForwardDecls = bootstrap.some(isForwardReference) ||
29971
- declarations.some(isForwardReference) || imports.some(isForwardReference) ||
29972
- exports.some(isForwardReference);
29973
- const type = wrapTypeReference(this.reflector, node);
29974
- const internalType = new WrappedNodeExpr(this.reflector.getInternalNameOfClass(node));
29975
- const adjacentType = new WrappedNodeExpr(this.reflector.getAdjacentNameOfClass(node));
29976
- const ngModuleDef = {
29977
- type,
29978
- internalType,
29979
- adjacentType,
29980
- bootstrap,
29981
- declarations,
29982
- exports,
29983
- imports,
29984
- containsForwardDecls,
29985
- id,
29986
- emitInline: false,
29987
- // TODO: to be implemented as a part of FW-1004.
29988
- schemas: [],
29989
- };
29990
- const rawProviders = ngModule.has('providers') ? ngModule.get('providers') : null;
29991
- const wrapperProviders = rawProviders !== null ?
29992
- new WrappedNodeExpr(this.annotateForClosureCompiler ? wrapFunctionExpressionsInParens(rawProviders) :
29993
- rawProviders) :
29994
- null;
29995
- // At this point, only add the module's imports as the injectors' imports. Any exported modules
29996
- // are added during `resolve`, as we need scope information to be able to filter out directives
29997
- // and pipes from the module exports.
29998
- const injectorImports = [];
29999
- if (ngModule.has('imports')) {
30000
- injectorImports.push(new WrappedNodeExpr(ngModule.get('imports')));
30001
- }
30002
- if (this.routeAnalyzer !== null) {
30003
- this.routeAnalyzer.add(node.getSourceFile(), name, rawImports, rawExports, rawProviders);
30004
- }
30005
- const ngInjectorDef = {
30006
- name,
30007
- type,
30008
- internalType,
30009
- deps: getValidConstructorDependencies(node, this.reflector, this.defaultImportRecorder, this.isCore),
30010
- providers: wrapperProviders,
30011
- imports: injectorImports,
30012
- };
30013
- return {
30014
- analysis: {
30015
- id,
30016
- schemas: schemas,
30017
- mod: ngModuleDef,
30018
- inj: ngInjectorDef,
30019
- declarations: declarationRefs,
30020
- rawDeclarations,
30021
- imports: importRefs,
30022
- exports: exportRefs,
30023
- providers: rawProviders,
30024
- providersRequiringFactory: rawProviders ?
30025
- resolveProvidersRequiringFactory(rawProviders, this.reflector, this.evaluator) :
30026
- null,
30027
- metadataStmt: generateSetClassMetadataCall(node, this.reflector, this.defaultImportRecorder, this.isCore, this.annotateForClosureCompiler),
30028
- factorySymbolName: node.name.text,
30029
- },
30030
- };
30031
- }
30032
- register(node, analysis) {
30033
- // Register this module's information with the LocalModuleScopeRegistry. This ensures that
30034
- // during the compile() phase, the module's metadata is available for selector scope
30035
- // computation.
30036
- this.metaRegistry.registerNgModuleMetadata({
30037
- ref: new Reference$1(node),
30038
- schemas: analysis.schemas,
30039
- declarations: analysis.declarations,
30040
- imports: analysis.imports,
30041
- exports: analysis.exports,
30042
- rawDeclarations: analysis.rawDeclarations,
30043
- });
30044
- if (this.factoryTracker !== null) {
30045
- this.factoryTracker.track(node.getSourceFile(), {
30046
- name: analysis.factorySymbolName,
30047
- hasId: analysis.id !== null,
30048
- });
30049
- }
30050
- this.injectableRegistry.registerInjectable(node);
30051
- }
30052
- resolve(node, analysis) {
30053
- const scope = this.scopeRegistry.getScopeOfModule(node);
30054
- const diagnostics = [];
30055
- const scopeDiagnostics = this.scopeRegistry.getDiagnosticsOfModule(node);
30056
- if (scopeDiagnostics !== null) {
30057
- diagnostics.push(...scopeDiagnostics);
30058
- }
30059
- if (analysis.providersRequiringFactory !== null) {
30060
- const providerDiagnostics = getProviderDiagnostics(analysis.providersRequiringFactory, analysis.providers, this.injectableRegistry);
30061
- diagnostics.push(...providerDiagnostics);
30062
- }
30063
- const data = {
30064
- injectorImports: [],
30065
- };
30066
- if (scope !== null && !scope.compilation.isPoisoned) {
30067
- // Using the scope information, extend the injector's imports using the modules that are
30068
- // specified as module exports.
30069
- const context = getSourceFile(node);
30070
- for (const exportRef of analysis.exports) {
30071
- if (isNgModule(exportRef.node, scope.compilation)) {
30072
- data.injectorImports.push(this.refEmitter.emit(exportRef, context));
30073
- }
30074
- }
30075
- for (const decl of analysis.declarations) {
30076
- const metadata = this.metaReader.getDirectiveMetadata(decl);
30077
- if (metadata !== null && metadata.selector === null) {
30078
- throw new FatalDiagnosticError(ErrorCode.DIRECTIVE_MISSING_SELECTOR, decl.node, `Directive ${decl.node.name.text} has no selector, please add it!`);
30079
- }
30080
- }
30081
- }
30082
- if (diagnostics.length > 0) {
30083
- return { diagnostics };
30084
- }
30085
- if (scope === null || scope.compilation.isPoisoned || scope.exported.isPoisoned ||
30086
- scope.reexports === null) {
30087
- return { data };
30088
- }
30089
- else {
30090
- return {
30091
- data,
30092
- reexports: scope.reexports,
30093
- };
30094
- }
30095
- }
30096
- compileFull(node, analysis, resolution) {
30097
- // Merge the injector imports (which are 'exports' that were later found to be NgModules)
30098
- // computed during resolution with the ones from analysis.
30099
- const ngInjectorDef = compileInjector(Object.assign(Object.assign({}, analysis.inj), { imports: [...analysis.inj.imports, ...resolution.injectorImports] }));
30100
- const ngModuleDef = compileNgModule(analysis.mod);
30101
- const ngModuleStatements = ngModuleDef.additionalStatements;
30102
- if (analysis.metadataStmt !== null) {
30103
- ngModuleStatements.push(analysis.metadataStmt);
30104
- }
30105
- const context = getSourceFile(node);
30106
- for (const decl of analysis.declarations) {
30107
- const remoteScope = this.scopeRegistry.getRemoteScope(decl.node);
30108
- if (remoteScope !== null) {
30109
- const directives = remoteScope.directives.map(directive => this.refEmitter.emit(directive, context));
30110
- const pipes = remoteScope.pipes.map(pipe => this.refEmitter.emit(pipe, context));
30111
- const directiveArray = new LiteralArrayExpr(directives);
30112
- const pipesArray = new LiteralArrayExpr(pipes);
30113
- const declExpr = this.refEmitter.emit(decl, context);
30114
- const setComponentScope = new ExternalExpr(Identifiers$1.setComponentScope);
30115
- const callExpr = new InvokeFunctionExpr(setComponentScope, [declExpr, directiveArray, pipesArray]);
30116
- ngModuleStatements.push(callExpr.toStmt());
30117
- }
30118
- }
30119
- const res = [
30120
- {
30121
- name: 'ɵmod',
30122
- initializer: ngModuleDef.expression,
30123
- statements: ngModuleStatements,
30124
- type: ngModuleDef.type,
30125
- },
30126
- {
30127
- name: 'ɵinj',
30128
- initializer: ngInjectorDef.expression,
30129
- statements: ngInjectorDef.statements,
30130
- type: ngInjectorDef.type,
30131
- }
30132
- ];
30133
- if (this.localeId) {
30134
- res.push({
30135
- name: 'ɵloc',
30136
- initializer: new LiteralExpr(this.localeId),
30137
- statements: [],
30138
- type: STRING_TYPE
30139
- });
30140
- }
30141
- return res;
30142
- }
30143
- _toR3Reference(valueRef, valueContext, typeContext) {
30144
- if (valueRef.hasOwningModuleGuess) {
30145
- return toR3Reference(valueRef, valueRef, valueContext, valueContext, this.refEmitter);
30146
- }
30147
- else {
30148
- let typeRef = valueRef;
30149
- let typeNode = this.reflector.getDtsDeclaration(typeRef.node);
30150
- if (typeNode !== null && isNamedClassDeclaration(typeNode)) {
30151
- typeRef = new Reference$1(typeNode);
30152
- }
30153
- return toR3Reference(valueRef, typeRef, valueContext, typeContext, this.refEmitter);
30154
- }
30155
- }
30156
- /**
30157
- * Given a `FunctionDeclaration`, `MethodDeclaration` or `FunctionExpression`, check if it is
30158
- * typed as a `ModuleWithProviders` and return an expression referencing the module if available.
30159
- */
30160
- _extractModuleFromModuleWithProvidersFn(node) {
30161
- const type = node.type || null;
30162
- return type &&
30163
- (this._reflectModuleFromTypeParam(type, node) || this._reflectModuleFromLiteralType(type));
30164
- }
30165
- /**
30166
- * Retrieve an `NgModule` identifier (T) from the specified `type`, if it is of the form:
30167
- * `ModuleWithProviders<T>`
30168
- * @param type The type to reflect on.
30169
- * @returns the identifier of the NgModule type if found, or null otherwise.
30170
- */
30171
- _reflectModuleFromTypeParam(type, node) {
30172
- // Examine the type of the function to see if it's a ModuleWithProviders reference.
30173
- if (!ts$1.isTypeReferenceNode(type)) {
30174
- return null;
30175
- }
30176
- const typeName = type &&
30177
- (ts$1.isIdentifier(type.typeName) && type.typeName ||
30178
- ts$1.isQualifiedName(type.typeName) && type.typeName.right) ||
30179
- null;
30180
- if (typeName === null) {
30181
- return null;
30182
- }
30183
- // Look at the type itself to see where it comes from.
30184
- const id = this.reflector.getImportOfIdentifier(typeName);
30185
- // If it's not named ModuleWithProviders, bail.
30186
- if (id === null || id.name !== 'ModuleWithProviders') {
30187
- return null;
30188
- }
30189
- // If it's not from @angular/core, bail.
30190
- if (!this.isCore && id.from !== '@angular/core') {
30191
- return null;
30192
- }
30193
- // If there's no type parameter specified, bail.
30194
- if (type.typeArguments === undefined || type.typeArguments.length !== 1) {
30195
- const parent = ts$1.isMethodDeclaration(node) && ts$1.isClassDeclaration(node.parent) ? node.parent : null;
30196
- const symbolName = (parent && parent.name ? parent.name.getText() + '.' : '') +
30197
- (node.name ? node.name.getText() : 'anonymous');
30198
- throw new FatalDiagnosticError(ErrorCode.NGMODULE_MODULE_WITH_PROVIDERS_MISSING_GENERIC, type, `${symbolName} returns a ModuleWithProviders type without a generic type argument. ` +
30199
- `Please add a generic type argument to the ModuleWithProviders type. If this ` +
30200
- `occurrence is in library code you don't control, please contact the library authors.`);
30201
- }
30202
- const arg = type.typeArguments[0];
30203
- return typeNodeToValueExpr(arg);
30933
+ class PipeSymbol extends SemanticSymbol {
30934
+ constructor(decl, name) {
30935
+ super(decl);
30936
+ this.name = name;
30204
30937
  }
30205
- /**
30206
- * Retrieve an `NgModule` identifier (T) from the specified `type`, if it is of the form:
30207
- * `A|B|{ngModule: T}|C`.
30208
- * @param type The type to reflect on.
30209
- * @returns the identifier of the NgModule type if found, or null otherwise.
30210
- */
30211
- _reflectModuleFromLiteralType(type) {
30212
- if (!ts$1.isIntersectionTypeNode(type)) {
30213
- return null;
30214
- }
30215
- for (const t of type.types) {
30216
- if (ts$1.isTypeLiteralNode(t)) {
30217
- for (const m of t.members) {
30218
- const ngModuleType = ts$1.isPropertySignature(m) && ts$1.isIdentifier(m.name) &&
30219
- m.name.text === 'ngModule' && m.type ||
30220
- null;
30221
- const ngModuleExpression = ngModuleType && typeNodeToValueExpr(ngModuleType);
30222
- if (ngModuleExpression) {
30223
- return ngModuleExpression;
30224
- }
30225
- }
30226
- }
30938
+ isPublicApiAffected(previousSymbol) {
30939
+ if (!(previousSymbol instanceof PipeSymbol)) {
30940
+ return true;
30227
30941
  }
30228
- return null;
30942
+ return this.name !== previousSymbol.name;
30229
30943
  }
30230
- // Verify that a "Declaration" reference is a `ClassDeclaration` reference.
30231
- isClassDeclarationReference(ref) {
30232
- return this.reflector.isClass(ref.node);
30233
- }
30234
- /**
30235
- * Compute a list of `Reference`s from a resolved metadata value.
30236
- */
30237
- resolveTypeList(expr, resolvedList, className, arrayName) {
30238
- const refList = [];
30239
- if (!Array.isArray(resolvedList)) {
30240
- throw createValueHasWrongTypeError(expr, resolvedList, `Expected array when reading the NgModule.${arrayName} of ${className}`);
30241
- }
30242
- resolvedList.forEach((entry, idx) => {
30243
- // Unwrap ModuleWithProviders for modules that are locally declared (and thus static
30244
- // resolution was able to descend into the function and return an object literal, a Map).
30245
- if (entry instanceof Map && entry.has('ngModule')) {
30246
- entry = entry.get('ngModule');
30247
- }
30248
- if (Array.isArray(entry)) {
30249
- // Recurse into nested arrays.
30250
- refList.push(...this.resolveTypeList(expr, entry, className, arrayName));
30251
- }
30252
- else if (entry instanceof Reference$1) {
30253
- if (!this.isClassDeclarationReference(entry)) {
30254
- throw createValueHasWrongTypeError(entry.node, entry, `Value at position ${idx} in the NgModule.${arrayName} of ${className} is not a class`);
30255
- }
30256
- refList.push(entry);
30257
- }
30258
- else {
30259
- // TODO(alxhub): Produce a better diagnostic here - the array index may be an inner array.
30260
- throw createValueHasWrongTypeError(expr, entry, `Value at position ${idx} in the NgModule.${arrayName} of ${className} is not a reference`);
30261
- }
30262
- });
30263
- return refList;
30944
+ isTypeCheckApiAffected(previousSymbol) {
30945
+ return this.isPublicApiAffected(previousSymbol);
30264
30946
  }
30265
30947
  }
30266
- function isNgModule(node, compilation) {
30267
- return !compilation.directives.some(directive => directive.ref.node === node) &&
30268
- !compilation.pipes.some(pipe => pipe.ref.node === node);
30269
- }
30270
-
30271
- /**
30272
- * @license
30273
- * Copyright Google LLC All Rights Reserved.
30274
- *
30275
- * Use of this source code is governed by an MIT-style license that can be
30276
- * found in the LICENSE file at https://angular.io/license
30277
- */
30278
30948
  class PipeDecoratorHandler {
30279
30949
  constructor(reflector, evaluator, metaRegistry, scopeRegistry, defaultImportRecorder, injectableRegistry, isCore) {
30280
30950
  this.reflector = reflector;
@@ -30350,6 +31020,9 @@ Either add the @Injectable() decorator to '${provider.node.name
30350
31020
  },
30351
31021
  };
30352
31022
  }
31023
+ symbol(node, analysis) {
31024
+ return new PipeSymbol(node, analysis.meta.name);
31025
+ }
30353
31026
  register(node, analysis) {
30354
31027
  const ref = new Reference$1(node);
30355
31028
  this.metaRegistry.registerPipeMetadata({ ref, name: analysis.meta.pipeName });
@@ -30479,8 +31152,8 @@ Either add the @Injectable() decorator to '${provider.node.name
30479
31152
  * dependencies within the same program are tracked; imports into packages on NPM are not.
30480
31153
  */
30481
31154
  class ImportGraph {
30482
- constructor(resolver) {
30483
- this.resolver = resolver;
31155
+ constructor(checker) {
31156
+ this.checker = checker;
30484
31157
  this.map = new Map();
30485
31158
  }
30486
31159
  /**
@@ -30557,24 +31230,28 @@ Either add the @Injectable() decorator to '${provider.node.name
30557
31230
  }
30558
31231
  scanImports(sf) {
30559
31232
  const imports = new Set();
30560
- // Look through the source file for import statements.
30561
- sf.statements.forEach(stmt => {
30562
- if ((ts$1.isImportDeclaration(stmt) || ts$1.isExportDeclaration(stmt)) &&
30563
- stmt.moduleSpecifier !== undefined && ts$1.isStringLiteral(stmt.moduleSpecifier)) {
30564
- // Resolve the module to a file, and check whether that file is in the ts.Program.
30565
- const moduleName = stmt.moduleSpecifier.text;
30566
- const moduleFile = this.resolver.resolveModule(moduleName, sf.fileName);
30567
- if (moduleFile !== null && isLocalFile(moduleFile)) {
30568
- // Record this local import.
30569
- imports.add(moduleFile);
30570
- }
31233
+ // Look through the source file for import and export statements.
31234
+ for (const stmt of sf.statements) {
31235
+ if ((!ts$1.isImportDeclaration(stmt) && !ts$1.isExportDeclaration(stmt)) ||
31236
+ stmt.moduleSpecifier === undefined) {
31237
+ continue;
30571
31238
  }
30572
- });
31239
+ const symbol = this.checker.getSymbolAtLocation(stmt.moduleSpecifier);
31240
+ if (symbol === undefined || symbol.valueDeclaration === undefined) {
31241
+ // No symbol could be found to skip over this import/export.
31242
+ continue;
31243
+ }
31244
+ const moduleFile = symbol.valueDeclaration;
31245
+ if (ts$1.isSourceFile(moduleFile) && isLocalFile(moduleFile)) {
31246
+ // Record this local import.
31247
+ imports.add(moduleFile);
31248
+ }
31249
+ }
30573
31250
  return imports;
30574
31251
  }
30575
31252
  }
30576
31253
  function isLocalFile(sf) {
30577
- return !sf.fileName.endsWith('.d.ts');
31254
+ return !sf.isDeclarationFile;
30578
31255
  }
30579
31256
  /**
30580
31257
  * A helper class to track which SourceFiles are being processed when searching for a path in
@@ -30824,21 +31501,6 @@ Either add the @Injectable() decorator to '${provider.node.name
30824
31501
  addResourceDependency(from, resource) {
30825
31502
  this.nodeFor(from).usesResources.add(resource);
30826
31503
  }
30827
- addTransitiveDependency(from, on) {
30828
- const nodeFrom = this.nodeFor(from);
30829
- nodeFrom.dependsOn.add(on.fileName);
30830
- const nodeOn = this.nodeFor(on);
30831
- for (const dep of nodeOn.dependsOn) {
30832
- nodeFrom.dependsOn.add(dep);
30833
- }
30834
- }
30835
- addTransitiveResources(from, resourcesOf) {
30836
- const nodeFrom = this.nodeFor(from);
30837
- const nodeOn = this.nodeFor(resourcesOf);
30838
- for (const dep of nodeOn.usesResources) {
30839
- nodeFrom.usesResources.add(dep);
30840
- }
30841
- }
30842
31504
  recordDependencyAnalysisFailure(file) {
30843
31505
  this.nodeFor(file).failedAnalysis = true;
30844
31506
  }
@@ -30846,9 +31508,6 @@ Either add the @Injectable() decorator to '${provider.node.name
30846
31508
  const node = this.nodes.get(from);
30847
31509
  return node ? [...node.usesResources] : [];
30848
31510
  }
30849
- isStale(sf, changedTsPaths, changedResources) {
30850
- return isLogicallyChanged(sf, this.nodeFor(sf), changedTsPaths, EMPTY_SET, changedResources);
30851
- }
30852
31511
  /**
30853
31512
  * Update the current dependency graph from a previous one, incorporating a set of physical
30854
31513
  * changes.
@@ -30926,7 +31585,6 @@ Either add the @Injectable() decorator to '${provider.node.name
30926
31585
  }
30927
31586
  return false;
30928
31587
  }
30929
- const EMPTY_SET = new Set();
30930
31588
 
30931
31589
  /**
30932
31590
  * @license
@@ -30939,8 +31597,7 @@ Either add the @Injectable() decorator to '${provider.node.name
30939
31597
  * Drives an incremental build, by tracking changes and determining which files need to be emitted.
30940
31598
  */
30941
31599
  class IncrementalDriver {
30942
- constructor(state, allTsFiles, depGraph, logicalChanges) {
30943
- this.allTsFiles = allTsFiles;
31600
+ constructor(state, depGraph, logicalChanges) {
30944
31601
  this.depGraph = depGraph;
30945
31602
  this.logicalChanges = logicalChanges;
30946
31603
  this.state = state;
@@ -30961,14 +31618,20 @@ Either add the @Injectable() decorator to '${provider.node.name
30961
31618
  state = oldDriver.state;
30962
31619
  }
30963
31620
  else {
31621
+ let priorGraph = null;
31622
+ if (oldDriver.state.lastGood !== null) {
31623
+ priorGraph = oldDriver.state.lastGood.semanticDepGraph;
31624
+ }
30964
31625
  // The previous build was successfully analyzed. `pendingEmit` is the only state carried
30965
31626
  // forward into this build.
30966
31627
  state = {
30967
31628
  kind: BuildStateKind.Pending,
30968
31629
  pendingEmit: oldDriver.state.pendingEmit,
31630
+ pendingTypeCheckEmit: oldDriver.state.pendingTypeCheckEmit,
30969
31631
  changedResourcePaths: new Set(),
30970
31632
  changedTsPaths: new Set(),
30971
31633
  lastGood: oldDriver.state.lastGood,
31634
+ semanticDepGraphUpdater: new SemanticDepGraphUpdater(priorGraph),
30972
31635
  };
30973
31636
  }
30974
31637
  // Merge the freshly modified resource files with any prior ones.
@@ -31011,6 +31674,7 @@ Either add the @Injectable() decorator to '${provider.node.name
31011
31674
  // The next step is to remove any deleted files from the state.
31012
31675
  for (const filePath of deletedTsPaths) {
31013
31676
  state.pendingEmit.delete(filePath);
31677
+ state.pendingTypeCheckEmit.delete(filePath);
31014
31678
  // Even if the file doesn't exist in the current compilation, it still might have been changed
31015
31679
  // in a previous one, so delete it from the set of changed TS files, just in case.
31016
31680
  state.changedTsPaths.delete(filePath);
@@ -31037,10 +31701,11 @@ Either add the @Injectable() decorator to '${provider.node.name
31037
31701
  // re-emitted.
31038
31702
  for (const change of logicalChanges) {
31039
31703
  state.pendingEmit.add(change);
31704
+ state.pendingTypeCheckEmit.add(change);
31040
31705
  }
31041
31706
  }
31042
31707
  // `state` now reflects the initial pending state of the current compilation.
31043
- return new IncrementalDriver(state, new Set(tsOnlyFiles(newProgram)), depGraph, logicalChanges);
31708
+ return new IncrementalDriver(state, depGraph, logicalChanges);
31044
31709
  }
31045
31710
  static fresh(program) {
31046
31711
  // Initialize the set of files which need to be emitted to the set of all TS files in the
@@ -31049,33 +31714,44 @@ Either add the @Injectable() decorator to '${provider.node.name
31049
31714
  const state = {
31050
31715
  kind: BuildStateKind.Pending,
31051
31716
  pendingEmit: new Set(tsFiles.map(sf => sf.fileName)),
31717
+ pendingTypeCheckEmit: new Set(tsFiles.map(sf => sf.fileName)),
31052
31718
  changedResourcePaths: new Set(),
31053
31719
  changedTsPaths: new Set(),
31054
31720
  lastGood: null,
31721
+ semanticDepGraphUpdater: new SemanticDepGraphUpdater(/* priorGraph */ null),
31055
31722
  };
31056
- return new IncrementalDriver(state, new Set(tsFiles), new FileDependencyGraph(), /* logicalChanges */ null);
31723
+ return new IncrementalDriver(state, new FileDependencyGraph(), /* logicalChanges */ null);
31724
+ }
31725
+ getSemanticDepGraphUpdater() {
31726
+ if (this.state.kind !== BuildStateKind.Pending) {
31727
+ throw new Error('Semantic dependency updater is only available when pending analysis');
31728
+ }
31729
+ return this.state.semanticDepGraphUpdater;
31057
31730
  }
31058
31731
  recordSuccessfulAnalysis(traitCompiler) {
31059
31732
  if (this.state.kind !== BuildStateKind.Pending) {
31060
31733
  // Changes have already been incorporated.
31061
31734
  return;
31062
31735
  }
31736
+ const { needsEmit, needsTypeCheckEmit, newGraph } = this.state.semanticDepGraphUpdater.finalize();
31063
31737
  const pendingEmit = this.state.pendingEmit;
31064
- const state = this.state;
31065
- for (const sf of this.allTsFiles) {
31066
- if (this.depGraph.isStale(sf, state.changedTsPaths, state.changedResourcePaths)) {
31067
- // Something has changed which requires this file be re-emitted.
31068
- pendingEmit.add(sf.fileName);
31069
- }
31738
+ for (const path of needsEmit) {
31739
+ pendingEmit.add(path);
31740
+ }
31741
+ const pendingTypeCheckEmit = this.state.pendingTypeCheckEmit;
31742
+ for (const path of needsTypeCheckEmit) {
31743
+ pendingTypeCheckEmit.add(path);
31070
31744
  }
31071
31745
  // Update the state to an `AnalyzedBuildState`.
31072
31746
  this.state = {
31073
31747
  kind: BuildStateKind.Analyzed,
31074
31748
  pendingEmit,
31749
+ pendingTypeCheckEmit,
31075
31750
  // Since this compilation was successfully analyzed, update the "last good" artifacts to the
31076
31751
  // ones from the current compilation.
31077
31752
  lastGood: {
31078
31753
  depGraph: this.depGraph,
31754
+ semanticDepGraph: newGraph,
31079
31755
  traitCompiler: traitCompiler,
31080
31756
  typeCheckingResults: null,
31081
31757
  },
@@ -31087,6 +31763,11 @@ Either add the @Injectable() decorator to '${provider.node.name
31087
31763
  return;
31088
31764
  }
31089
31765
  this.state.lastGood.typeCheckingResults = results;
31766
+ // Delete the files for which type-check code was generated from the set of pending type-check
31767
+ // files.
31768
+ for (const fileName of results.keys()) {
31769
+ this.state.pendingTypeCheckEmit.delete(fileName);
31770
+ }
31090
31771
  }
31091
31772
  recordSuccessfulEmit(sf) {
31092
31773
  this.state.pendingEmit.delete(sf.fileName);
@@ -31113,7 +31794,7 @@ Either add the @Injectable() decorator to '${provider.node.name
31113
31794
  this.state.priorTypeCheckingResults === null || this.logicalChanges === null) {
31114
31795
  return null;
31115
31796
  }
31116
- if (this.logicalChanges.has(sf.fileName)) {
31797
+ if (this.logicalChanges.has(sf.fileName) || this.state.pendingTypeCheckEmit.has(sf.fileName)) {
31117
31798
  return null;
31118
31799
  }
31119
31800
  const fileName = absoluteFromSourceFile(sf);
@@ -31651,7 +32332,7 @@ Either add the @Injectable() decorator to '${provider.node.name
31651
32332
  return;
31652
32333
  }
31653
32334
  const ngModuleExpr = this.emitter.emit(ngModule, decl.getSourceFile(), ImportFlags.ForceNewImport);
31654
- const ngModuleType = new ExpressionType(ngModuleExpr);
32335
+ const ngModuleType = new ExpressionType(ngModuleExpr.expression);
31655
32336
  const mwpNgType = new ExpressionType(new ExternalExpr(Identifiers$1.ModuleWithProviders), [ /* modifiers */], [ngModuleType]);
31656
32337
  dts.addTypeReplacement(decl, mwpNgType);
31657
32338
  }
@@ -32487,19 +33168,6 @@ Either add the @Injectable() decorator to '${provider.node.name
32487
33168
  return null;
32488
33169
  }
32489
33170
  }
32490
- /**
32491
- * Returns a collection of the compilation scope for each registered declaration.
32492
- */
32493
- getCompilationScopes() {
32494
- const scopes = [];
32495
- this.declarationToModule.forEach((declData, declaration) => {
32496
- const scope = this.getScopeOfModule(declData.ngModule);
32497
- if (scope !== null) {
32498
- scopes.push(Object.assign({ declaration, ngModule: declData.ngModule }, scope.compilation));
32499
- }
32500
- });
32501
- return scopes;
32502
- }
32503
33171
  registerDeclarationOfModule(ngModule, decl, rawDeclarations) {
32504
33172
  const declData = {
32505
33173
  ngModule,
@@ -32795,7 +33463,7 @@ Either add the @Injectable() decorator to '${provider.node.name
32795
33463
  });
32796
33464
  }
32797
33465
  else {
32798
- const expr = this.refEmitter.emit(exportRef.cloneWithNoIdentifiers(), sourceFile);
33466
+ const expr = this.refEmitter.emit(exportRef.cloneWithNoIdentifiers(), sourceFile).expression;
32799
33467
  if (!(expr instanceof ExternalExpr) || expr.value.moduleName === null ||
32800
33468
  expr.value.name === null) {
32801
33469
  throw new Error('Expected ExternalExpr');
@@ -34550,7 +35218,7 @@ Either add the @Injectable() decorator to '${provider.node.name
34550
35218
  // pass.
34551
35219
  const ngExpr = this.refEmitter.emit(ref, this.contextFile, ImportFlags.NoAliasing);
34552
35220
  // Use `translateExpression` to convert the `Expression` into a `ts.Expression`.
34553
- return translateExpression(ngExpr, this.importManager);
35221
+ return translateExpression(ngExpr.expression, this.importManager);
34554
35222
  }
34555
35223
  /**
34556
35224
  * Generate a `ts.TypeNode` that references the given node as a type.
@@ -34561,7 +35229,7 @@ Either add the @Injectable() decorator to '${provider.node.name
34561
35229
  const ngExpr = this.refEmitter.emit(ref, this.contextFile, ImportFlags.NoAliasing | ImportFlags.AllowTypeImports);
34562
35230
  // Create an `ExpressionType` from the `Expression` and translate it via `translateType`.
34563
35231
  // TODO(alxhub): support references to types with generic arguments in a clean way.
34564
- return translateType(new ExpressionType(ngExpr), this.importManager);
35232
+ return translateType(new ExpressionType(ngExpr.expression), this.importManager);
34565
35233
  }
34566
35234
  emitTypeParameters(declaration) {
34567
35235
  const emitter = new TypeParameterEmitter(declaration.typeParameters, this.reflector);
@@ -38604,7 +39272,7 @@ Either add the @Injectable() decorator to '${provider.node.name
38604
39272
  this.moduleResolver =
38605
39273
  new ModuleResolver(tsProgram, this.options, this.adapter, moduleResolutionCache);
38606
39274
  this.resourceManager = new AdapterResourceLoader(adapter, this.options);
38607
- this.cycleAnalyzer = new CycleAnalyzer(new ImportGraph(this.moduleResolver));
39275
+ this.cycleAnalyzer = new CycleAnalyzer(new ImportGraph(tsProgram.getTypeChecker()));
38608
39276
  this.incrementalStrategy.setIncrementalDriver(this.incrementalDriver, tsProgram);
38609
39277
  this.ignoreForDiagnostics =
38610
39278
  new Set(tsProgram.getSourceFiles().filter(sf => this.adapter.isShim(sf)));
@@ -38887,7 +39555,6 @@ Either add the @Injectable() decorator to '${provider.node.name
38887
39555
  }
38888
39556
  resolveCompilation(traitCompiler) {
38889
39557
  traitCompiler.resolve();
38890
- this.recordNgModuleScopeDependencies();
38891
39558
  // At this point, analysis is complete and the compiler can now calculate which files need to
38892
39559
  // be emitted, so do that.
38893
39560
  this.incrementalDriver.recordSuccessfulAnalysis(traitCompiler);
@@ -39042,67 +39709,6 @@ Either add the @Injectable() decorator to '${provider.node.name
39042
39709
  }
39043
39710
  return this.nonTemplateDiagnostics;
39044
39711
  }
39045
- /**
39046
- * Reifies the inter-dependencies of NgModules and the components within their compilation scopes
39047
- * into the `IncrementalDriver`'s dependency graph.
39048
- */
39049
- recordNgModuleScopeDependencies() {
39050
- const recordSpan = this.perfRecorder.start('recordDependencies');
39051
- const depGraph = this.incrementalDriver.depGraph;
39052
- for (const scope of this.compilation.scopeRegistry.getCompilationScopes()) {
39053
- const file = scope.declaration.getSourceFile();
39054
- const ngModuleFile = scope.ngModule.getSourceFile();
39055
- // A change to any dependency of the declaration causes the declaration to be invalidated,
39056
- // which requires the NgModule to be invalidated as well.
39057
- depGraph.addTransitiveDependency(ngModuleFile, file);
39058
- // A change to the NgModule file should cause the declaration itself to be invalidated.
39059
- depGraph.addDependency(file, ngModuleFile);
39060
- const meta = this.compilation.metaReader.getDirectiveMetadata(new Reference$1(scope.declaration));
39061
- if (meta !== null && meta.isComponent) {
39062
- // If a component's template changes, it might have affected the import graph, and thus the
39063
- // remote scoping feature which is activated in the event of potential import cycles. Thus,
39064
- // the module depends not only on the transitive dependencies of the component, but on its
39065
- // resources as well.
39066
- depGraph.addTransitiveResources(ngModuleFile, file);
39067
- // A change to any directive/pipe in the compilation scope should cause the component to be
39068
- // invalidated.
39069
- for (const directive of scope.directives) {
39070
- // When a directive in scope is updated, the component needs to be recompiled as e.g. a
39071
- // selector may have changed.
39072
- depGraph.addTransitiveDependency(file, directive.ref.node.getSourceFile());
39073
- }
39074
- for (const pipe of scope.pipes) {
39075
- // When a pipe in scope is updated, the component needs to be recompiled as e.g. the
39076
- // pipe's name may have changed.
39077
- depGraph.addTransitiveDependency(file, pipe.ref.node.getSourceFile());
39078
- }
39079
- // Components depend on the entire export scope. In addition to transitive dependencies on
39080
- // all directives/pipes in the export scope, they also depend on every NgModule in the
39081
- // scope, as changes to a module may add new directives/pipes to the scope.
39082
- for (const depModule of scope.ngModules) {
39083
- // There is a correctness issue here. To be correct, this should be a transitive
39084
- // dependency on the depModule file, since the depModule's exports might change via one of
39085
- // its dependencies, even if depModule's file itself doesn't change. However, doing this
39086
- // would also trigger recompilation if a non-exported component or directive changed,
39087
- // which causes performance issues for rebuilds.
39088
- //
39089
- // Given the rebuild issue is an edge case, currently we err on the side of performance
39090
- // instead of correctness. A correct and performant design would distinguish between
39091
- // changes to the depModule which affect its export scope and changes which do not, and
39092
- // only add a dependency for the former. This concept is currently in development.
39093
- //
39094
- // TODO(alxhub): fix correctness issue by understanding the semantics of the dependency.
39095
- depGraph.addDependency(file, depModule.getSourceFile());
39096
- }
39097
- }
39098
- else {
39099
- // Directives (not components) and pipes only depend on the NgModule which directly declares
39100
- // them.
39101
- depGraph.addDependency(file, ngModuleFile);
39102
- }
39103
- }
39104
- this.perfRecorder.stop(recordSpan);
39105
- }
39106
39712
  scanForMwp(sf) {
39107
39713
  this.compilation.mwpScanner.scan(sf, {
39108
39714
  addTypeReplacement: (node, type) => {
@@ -39175,6 +39781,7 @@ Either add the @Injectable() decorator to '${provider.node.name
39175
39781
  const depScopeReader = new MetadataDtsModuleScopeResolver(dtsReader, aliasingHost);
39176
39782
  const scopeRegistry = new LocalModuleScopeRegistry(localMetaReader, depScopeReader, refEmitter, aliasingHost);
39177
39783
  const scopeReader = scopeRegistry;
39784
+ const semanticDepGraphUpdater = this.incrementalDriver.getSemanticDepGraphUpdater();
39178
39785
  const metaRegistry = new CompoundMetadataRegistry([localMetaRegistry, scopeRegistry]);
39179
39786
  const injectableRegistry = new InjectableClassRegistry(reflector);
39180
39787
  const metaReader = new CompoundMetadataReader([localMetaReader, dtsReader]);
@@ -39206,11 +39813,11 @@ Either add the @Injectable() decorator to '${provider.node.name
39206
39813
  1 /* Error */;
39207
39814
  // Set up the IvyCompilation, which manages state for the Ivy transformer.
39208
39815
  const handlers = [
39209
- 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, this.closureCompilerEnabled),
39816
+ new ComponentDecoratorHandler(reflector, evaluator, metaRegistry, metaReader, scopeReader, scopeRegistry, typeCheckScopeRegistry, resourceRegistry, isCore, this.resourceManager, this.adapter.rootDirs, this.options.preserveWhitespaces || false, this.options.i18nUseExternalIds !== false, this.options.enableI18nLegacyMessageIdFormat !== false, this.usePoisonedData, this.options.i18nNormalizeLineEndingsInICUs, this.moduleResolver, this.cycleAnalyzer, cycleHandlingStrategy, refEmitter, defaultImportTracker, this.incrementalDriver.depGraph, injectableRegistry, semanticDepGraphUpdater, this.closureCompilerEnabled),
39210
39817
  // TODO(alxhub): understand why the cast here is necessary (something to do with `null`
39211
39818
  // not being assignable to `unknown` when wrapped in `Readonly`).
39212
39819
  // clang-format off
39213
- new DirectiveDecoratorHandler(reflector, evaluator, metaRegistry, scopeRegistry, metaReader, defaultImportTracker, injectableRegistry, isCore, this.closureCompilerEnabled, compileUndecoratedClassesWithAngularFeatures),
39820
+ new DirectiveDecoratorHandler(reflector, evaluator, metaRegistry, scopeRegistry, metaReader, defaultImportTracker, injectableRegistry, isCore, semanticDepGraphUpdater, this.closureCompilerEnabled, compileUndecoratedClassesWithAngularFeatures),
39214
39821
  // clang-format on
39215
39822
  // Pipe handler must be before injectable handler in list so pipe factories are printed
39216
39823
  // before injectable factories (so injectable factories can delegate to them)
@@ -39218,7 +39825,7 @@ Either add the @Injectable() decorator to '${provider.node.name
39218
39825
  new InjectableDecoratorHandler(reflector, defaultImportTracker, isCore, this.options.strictInjectionParameters || false, injectableRegistry),
39219
39826
  new NgModuleDecoratorHandler(reflector, evaluator, metaReader, metaRegistry, scopeRegistry, referencesRegistry, isCore, routeAnalyzer, refEmitter, this.adapter.factoryTracker, defaultImportTracker, this.closureCompilerEnabled, injectableRegistry, this.options.i18nInLocale),
39220
39827
  ];
39221
- const traitCompiler = new TraitCompiler(handlers, reflector, this.perfRecorder, this.incrementalDriver, this.options.compileNonExportedClasses !== false, compilationMode, dtsTransforms);
39828
+ const traitCompiler = new TraitCompiler(handlers, reflector, this.perfRecorder, this.incrementalDriver, this.options.compileNonExportedClasses !== false, compilationMode, dtsTransforms, semanticDepGraphUpdater);
39222
39829
  const templateTypeChecker = new TemplateTypeCheckerImpl(this.tsProgram, this.typeCheckingProgramStrategy, traitCompiler, this.getTypeCheckingConfig(), refEmitter, reflector, this.adapter, this.incrementalDriver, scopeRegistry, typeCheckScopeRegistry);
39223
39830
  return {
39224
39831
  isCore,
@@ -40215,7 +40822,7 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
40215
40822
  * input shares the same name as a DOM attribute, the `Map` will reflect the directive input
40216
40823
  * completion, not the DOM completion for that name.
40217
40824
  */
40218
- function buildAttributeCompletionTable(component, element, checker) {
40825
+ function buildAttributeCompletionTable(component, element, checker, includeDomSchemaAttributes) {
40219
40826
  const table = new Map();
40220
40827
  // Use the `ElementSymbol` or `TemplateSymbol` to iterate over directives present on the node, and
40221
40828
  // their inputs/outputs. These have the highest priority of completion results.
@@ -40351,7 +40958,7 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
40351
40958
  }
40352
40959
  }
40353
40960
  // Finally, add any DOM attributes not already covered by inputs.
40354
- if (element instanceof Element) {
40961
+ if (element instanceof Element && includeDomSchemaAttributes) {
40355
40962
  for (const { attribute, property } of checker.getPotentialDomBindings(element.name)) {
40356
40963
  const isAlsoProperty = attribute === property;
40357
40964
  if (!table.has(attribute)) {
@@ -40900,12 +41507,13 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
40900
41507
  * @param N type of the template node in question, narrowed accordingly.
40901
41508
  */
40902
41509
  class CompletionBuilder {
40903
- constructor(tsLS, compiler, component, node, targetDetails) {
41510
+ constructor(tsLS, compiler, component, node, targetDetails, inlineTemplate) {
40904
41511
  this.tsLS = tsLS;
40905
41512
  this.compiler = compiler;
40906
41513
  this.component = component;
40907
41514
  this.node = node;
40908
41515
  this.targetDetails = targetDetails;
41516
+ this.inlineTemplate = inlineTemplate;
40909
41517
  this.typeChecker = this.compiler.getNextProgram().getTypeChecker();
40910
41518
  this.templateTypeChecker = this.compiler.getTemplateTypeChecker();
40911
41519
  this.nodeParent = this.targetDetails.parent;
@@ -41172,8 +41780,13 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
41172
41780
  length = 0;
41173
41781
  }
41174
41782
  const replacementSpan = { start, length };
41175
- const entries = Array.from(templateTypeChecker.getPotentialElementTags(this.component))
41176
- .map(([tag, directive]) => ({
41783
+ let potentialTags = Array.from(templateTypeChecker.getPotentialElementTags(this.component));
41784
+ if (!this.inlineTemplate) {
41785
+ // If we are in an external template, don't provide non-Angular tags (directive === null)
41786
+ // because we expect other extensions (i.e. Emmet) to provide those for HTML files.
41787
+ potentialTags = potentialTags.filter(([_, directive]) => directive !== null);
41788
+ }
41789
+ const entries = potentialTags.map(([tag, directive]) => ({
41177
41790
  kind: tagCompletionKind(directive),
41178
41791
  name: tag,
41179
41792
  sortText: tag,
@@ -41244,7 +41857,7 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
41244
41857
  this.node.keySpan !== undefined) {
41245
41858
  replacementSpan = makeReplacementSpanFromParseSourceSpan(this.node.keySpan);
41246
41859
  }
41247
- const attrTable = buildAttributeCompletionTable(this.component, element, this.compiler.getTemplateTypeChecker());
41860
+ const attrTable = buildAttributeCompletionTable(this.component, element, this.compiler.getTemplateTypeChecker(), this.inlineTemplate);
41248
41861
  let entries = [];
41249
41862
  for (const completion of attrTable.values()) {
41250
41863
  // First, filter out completions that don't make sense for the current node. For example, if
@@ -41306,7 +41919,7 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
41306
41919
  // Nothing to do without an element to process.
41307
41920
  return undefined;
41308
41921
  }
41309
- const attrTable = buildAttributeCompletionTable(this.component, element, this.compiler.getTemplateTypeChecker());
41922
+ const attrTable = buildAttributeCompletionTable(this.component, element, this.compiler.getTemplateTypeChecker(), this.inlineTemplate);
41310
41923
  if (!attrTable.has(name)) {
41311
41924
  return undefined;
41312
41925
  }
@@ -41363,7 +41976,7 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
41363
41976
  // Nothing to do without an element to process.
41364
41977
  return undefined;
41365
41978
  }
41366
- const attrTable = buildAttributeCompletionTable(this.component, element, this.compiler.getTemplateTypeChecker());
41979
+ const attrTable = buildAttributeCompletionTable(this.component, element, this.compiler.getTemplateTypeChecker(), this.inlineTemplate);
41367
41980
  if (!attrTable.has(name)) {
41368
41981
  return undefined;
41369
41982
  }
@@ -42423,7 +43036,7 @@ https://v9.angular.io/guide/template-typecheck#template-type-checking`,
42423
43036
  const node = positionDetails.context.kind === TargetNodeKind.TwoWayBindingContext ?
42424
43037
  positionDetails.context.nodes[0] :
42425
43038
  positionDetails.context.node;
42426
- return new CompletionBuilder(this.tsLS, compiler, templateInfo.component, node, positionDetails);
43039
+ return new CompletionBuilder(this.tsLS, compiler, templateInfo.component, node, positionDetails, isTypeScriptFile(fileName));
42427
43040
  }
42428
43041
  getCompletionsAtPosition(fileName, position, options) {
42429
43042
  return this.withCompiler((compiler) => {