@angular/core 20.0.6 → 20.1.0-next.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/{api.d.d.ts → api.d-Dwpmmn5j.d.ts} +2 -2
  2. package/{chrome_dev_tools_performance.d.d.ts → chrome_dev_tools_performance.d-Dk_7kdX9.d.ts} +7 -3
  3. package/{discovery.d.d.ts → discovery.d-AiW64LSq.d.ts} +5 -6
  4. package/{event_dispatcher.d.d.ts → event_dispatcher.d-BReQpZfC.d.ts} +1 -1
  5. package/fesm2022/{attribute.mjs → attribute-BWp59EjE.mjs} +2 -2
  6. package/fesm2022/attribute-BWp59EjE.mjs.map +1 -0
  7. package/fesm2022/core.mjs +22 -28
  8. package/fesm2022/core.mjs.map +1 -1
  9. package/fesm2022/{debug_node.mjs → debug_node-CGQXW8qF.mjs} +1479 -1466
  10. package/fesm2022/debug_node-CGQXW8qF.mjs.map +1 -0
  11. package/fesm2022/primitives/di.mjs +1 -1
  12. package/fesm2022/primitives/di.mjs.map +1 -1
  13. package/fesm2022/primitives/event-dispatch.mjs +2 -2
  14. package/fesm2022/primitives/event-dispatch.mjs.map +1 -1
  15. package/fesm2022/primitives/signals.mjs +5 -5
  16. package/fesm2022/primitives/signals.mjs.map +1 -1
  17. package/fesm2022/{resource.mjs → resource-CIODajJI.mjs} +8 -12
  18. package/fesm2022/resource-CIODajJI.mjs.map +1 -0
  19. package/fesm2022/{root_effect_scheduler.mjs → root_effect_scheduler-BvK6bnZD.mjs} +20 -27
  20. package/fesm2022/root_effect_scheduler-BvK6bnZD.mjs.map +1 -0
  21. package/fesm2022/rxjs-interop.mjs +5 -5
  22. package/fesm2022/rxjs-interop.mjs.map +1 -1
  23. package/fesm2022/{signal.mjs → signal-nCiHhWf6.mjs} +2 -2
  24. package/fesm2022/signal-nCiHhWf6.mjs.map +1 -0
  25. package/fesm2022/testing.mjs +72 -171
  26. package/fesm2022/testing.mjs.map +1 -1
  27. package/fesm2022/{untracked.mjs → untracked-DmD_2MlC.mjs} +3 -3
  28. package/fesm2022/untracked-DmD_2MlC.mjs.map +1 -0
  29. package/fesm2022/{weak_ref.mjs → weak_ref-BaIq-pgY.mjs} +2 -2
  30. package/fesm2022/weak_ref-BaIq-pgY.mjs.map +1 -0
  31. package/{graph.d.d.ts → graph.d-BcIOep_B.d.ts} +1 -1
  32. package/index.d.ts +23 -16
  33. package/package.json +2 -2
  34. package/primitives/di/index.d.ts +1 -1
  35. package/primitives/event-dispatch/index.d.ts +3 -3
  36. package/primitives/signals/index.d.ts +6 -6
  37. package/rxjs-interop/index.d.ts +5 -5
  38. package/schematics/bundles/{apply_import_manager-mlmcgZ0v.cjs → apply_import_manager-BsIRDO9W.cjs} +3 -3
  39. package/schematics/bundles/{checker-a0VNmSrQ.cjs → checker-CY7a8ko8.cjs} +771 -642
  40. package/schematics/bundles/cleanup-unused-imports.cjs +21 -35
  41. package/schematics/bundles/{compiler_host-CwrMDc6k.cjs → compiler_host-DNYQkH4l.cjs} +2 -2
  42. package/schematics/bundles/control-flow-migration.cjs +3 -3
  43. package/schematics/bundles/document-core.cjs +5 -5
  44. package/schematics/bundles/imports-CIX-JgAN.cjs +1 -1
  45. package/schematics/bundles/{index-DAP9ZmeX.cjs → index-BJ3PYYwQ.cjs} +17 -16
  46. package/schematics/bundles/{index-jMQgXbRg.cjs → index-BUgQDm-J.cjs} +1018 -589
  47. package/schematics/bundles/inject-flags.cjs +5 -5
  48. package/schematics/bundles/inject-migration.cjs +4 -9
  49. package/schematics/bundles/leading_space-D9nQ8UQC.cjs +1 -1
  50. package/schematics/bundles/{migrate_ts_type_references-CX0snBqz.cjs → migrate_ts_type_references-MBd4NBjn.cjs} +26 -77
  51. package/schematics/bundles/ng_decorators-B5HCqr20.cjs +1 -1
  52. package/schematics/bundles/nodes-B16H9JUd.cjs +1 -1
  53. package/schematics/bundles/output-migration.cjs +6 -6
  54. package/schematics/bundles/{project_paths-C4WM31v5.cjs → project_paths-C5Df24y1.cjs} +3 -3
  55. package/schematics/bundles/project_tsconfig_paths-CDVxT6Ov.cjs +1 -1
  56. package/schematics/bundles/property_name-BBwFuqMe.cjs +1 -1
  57. package/schematics/bundles/route-lazy-loading.cjs +3 -3
  58. package/schematics/bundles/self-closing-tags-migration.cjs +7 -19
  59. package/schematics/bundles/signal-input-migration.cjs +28 -12
  60. package/schematics/bundles/signal-queries-migration.cjs +7 -7
  61. package/schematics/bundles/signals.cjs +7 -7
  62. package/schematics/bundles/standalone-migration.cjs +4 -4
  63. package/schematics/bundles/symbol-VPWguRxr.cjs +1 -1
  64. package/schematics/bundles/test-bed-get.cjs +4 -4
  65. package/{signal.d.d.ts → signal.d-BcmOdASA.d.ts} +2 -2
  66. package/testing/index.d.ts +6 -73
  67. package/{weak_ref.d.d.ts → weak_ref.d-eGOEP9S1.d.ts} +1 -1
  68. package/fesm2022/attribute.mjs.map +0 -1
  69. package/fesm2022/debug_node.mjs.map +0 -1
  70. package/fesm2022/resource.mjs.map +0 -1
  71. package/fesm2022/root_effect_scheduler.mjs.map +0 -1
  72. package/fesm2022/signal.mjs.map +0 -1
  73. package/fesm2022/untracked.mjs.map +0 -1
  74. package/fesm2022/weak_ref.mjs.map +0 -1
@@ -1,12 +1,12 @@
1
1
  'use strict';
2
2
  /**
3
- * @license Angular v20.0.6
3
+ * @license Angular v20.1.0-next.1
4
4
  * (c) 2010-2025 Google LLC. https://angular.io/
5
5
  * License: MIT
6
6
  */
7
7
  'use strict';
8
8
 
9
- var checker = require('./checker-a0VNmSrQ.cjs');
9
+ var checker = require('./checker-CY7a8ko8.cjs');
10
10
  var ts = require('typescript');
11
11
  var p = require('path');
12
12
  require('os');
@@ -891,7 +891,7 @@ const MINIMUM_PARTIAL_LINKER_DEFER_SUPPORT_VERSION = '18.0.0';
891
891
  function compileDeclareClassMetadata(metadata) {
892
892
  const definitionMap = new checker.DefinitionMap();
893
893
  definitionMap.set('minVersion', checker.literal(MINIMUM_PARTIAL_LINKER_VERSION$5));
894
- definitionMap.set('version', checker.literal('20.0.6'));
894
+ definitionMap.set('version', checker.literal('20.1.0-next.1'));
895
895
  definitionMap.set('ngImport', checker.importExpr(checker.Identifiers.core));
896
896
  definitionMap.set('type', metadata.type);
897
897
  definitionMap.set('decorators', metadata.decorators);
@@ -909,7 +909,7 @@ function compileComponentDeclareClassMetadata(metadata, dependencies) {
909
909
  callbackReturnDefinitionMap.set('ctorParameters', metadata.ctorParameters ?? checker.literal(null));
910
910
  callbackReturnDefinitionMap.set('propDecorators', metadata.propDecorators ?? checker.literal(null));
911
911
  definitionMap.set('minVersion', checker.literal(MINIMUM_PARTIAL_LINKER_DEFER_SUPPORT_VERSION));
912
- definitionMap.set('version', checker.literal('20.0.6'));
912
+ definitionMap.set('version', checker.literal('20.1.0-next.1'));
913
913
  definitionMap.set('ngImport', checker.importExpr(checker.Identifiers.core));
914
914
  definitionMap.set('type', metadata.type);
915
915
  definitionMap.set('resolveDeferredDeps', compileComponentMetadataAsyncResolver(dependencies));
@@ -1004,7 +1004,7 @@ function createDirectiveDefinitionMap(meta) {
1004
1004
  const definitionMap = new checker.DefinitionMap();
1005
1005
  const minVersion = getMinimumVersionForPartialOutput(meta);
1006
1006
  definitionMap.set('minVersion', checker.literal(minVersion));
1007
- definitionMap.set('version', checker.literal('20.0.6'));
1007
+ definitionMap.set('version', checker.literal('20.1.0-next.1'));
1008
1008
  // e.g. `type: MyDirective`
1009
1009
  definitionMap.set('type', meta.type.value);
1010
1010
  if (meta.isStandalone !== undefined) {
@@ -1420,7 +1420,7 @@ const MINIMUM_PARTIAL_LINKER_VERSION$4 = '12.0.0';
1420
1420
  function compileDeclareFactoryFunction(meta) {
1421
1421
  const definitionMap = new checker.DefinitionMap();
1422
1422
  definitionMap.set('minVersion', checker.literal(MINIMUM_PARTIAL_LINKER_VERSION$4));
1423
- definitionMap.set('version', checker.literal('20.0.6'));
1423
+ definitionMap.set('version', checker.literal('20.1.0-next.1'));
1424
1424
  definitionMap.set('ngImport', checker.importExpr(checker.Identifiers.core));
1425
1425
  definitionMap.set('type', meta.type.value);
1426
1426
  definitionMap.set('deps', compileDependencies(meta.deps));
@@ -1455,7 +1455,7 @@ function compileDeclareInjectableFromMetadata(meta) {
1455
1455
  function createInjectableDefinitionMap(meta) {
1456
1456
  const definitionMap = new checker.DefinitionMap();
1457
1457
  definitionMap.set('minVersion', checker.literal(MINIMUM_PARTIAL_LINKER_VERSION$3));
1458
- definitionMap.set('version', checker.literal('20.0.6'));
1458
+ definitionMap.set('version', checker.literal('20.1.0-next.1'));
1459
1459
  definitionMap.set('ngImport', checker.importExpr(checker.Identifiers.core));
1460
1460
  definitionMap.set('type', meta.type.value);
1461
1461
  // Only generate providedIn property if it has a non-null value
@@ -1506,7 +1506,7 @@ function compileDeclareInjectorFromMetadata(meta) {
1506
1506
  function createInjectorDefinitionMap(meta) {
1507
1507
  const definitionMap = new checker.DefinitionMap();
1508
1508
  definitionMap.set('minVersion', checker.literal(MINIMUM_PARTIAL_LINKER_VERSION$2));
1509
- definitionMap.set('version', checker.literal('20.0.6'));
1509
+ definitionMap.set('version', checker.literal('20.1.0-next.1'));
1510
1510
  definitionMap.set('ngImport', checker.importExpr(checker.Identifiers.core));
1511
1511
  definitionMap.set('type', meta.type.value);
1512
1512
  definitionMap.set('providers', meta.providers);
@@ -1539,7 +1539,7 @@ function createNgModuleDefinitionMap(meta) {
1539
1539
  throw new Error('Invalid path! Local compilation mode should not get into the partial compilation path');
1540
1540
  }
1541
1541
  definitionMap.set('minVersion', checker.literal(MINIMUM_PARTIAL_LINKER_VERSION$1));
1542
- definitionMap.set('version', checker.literal('20.0.6'));
1542
+ definitionMap.set('version', checker.literal('20.1.0-next.1'));
1543
1543
  definitionMap.set('ngImport', checker.importExpr(checker.Identifiers.core));
1544
1544
  definitionMap.set('type', meta.type.value);
1545
1545
  // We only generate the keys in the metadata if the arrays contain values.
@@ -1590,7 +1590,7 @@ function compileDeclarePipeFromMetadata(meta) {
1590
1590
  function createPipeDefinitionMap(meta) {
1591
1591
  const definitionMap = new checker.DefinitionMap();
1592
1592
  definitionMap.set('minVersion', checker.literal(MINIMUM_PARTIAL_LINKER_VERSION));
1593
- definitionMap.set('version', checker.literal('20.0.6'));
1593
+ definitionMap.set('version', checker.literal('20.1.0-next.1'));
1594
1594
  definitionMap.set('ngImport', checker.importExpr(checker.Identifiers.core));
1595
1595
  // e.g. `type: MyPipe`
1596
1596
  definitionMap.set('type', meta.type.value);
@@ -1598,7 +1598,7 @@ function createPipeDefinitionMap(meta) {
1598
1598
  definitionMap.set('isStandalone', checker.literal(meta.isStandalone));
1599
1599
  }
1600
1600
  // e.g. `name: "myPipe"`
1601
- definitionMap.set('name', checker.literal(meta.pipeName));
1601
+ definitionMap.set('name', checker.literal(meta.pipeName ?? meta.name));
1602
1602
  if (meta.pure === false) {
1603
1603
  // e.g. `pure: false`
1604
1604
  definitionMap.set('pure', checker.literal(meta.pure));
@@ -2546,6 +2546,10 @@ class DtsMetadataReader {
2546
2546
  // used to increase the accuracy of a diagnostic.
2547
2547
  preserveWhitespaces: false,
2548
2548
  isExplicitlyDeferred: false,
2549
+ // We don't need to know if imported components from .d.ts
2550
+ // files are selectorless for type-checking purposes.
2551
+ selectorlessEnabled: false,
2552
+ localReferencedSymbols: null,
2549
2553
  };
2550
2554
  }
2551
2555
  /**
@@ -2567,11 +2571,12 @@ class DtsMetadataReader {
2567
2571
  return null;
2568
2572
  }
2569
2573
  const type = def.type.typeArguments[1];
2570
- if (!ts.isLiteralTypeNode(type) || !ts.isStringLiteral(type.literal)) {
2574
+ if (!ts.isLiteralTypeNode(type) ||
2575
+ (!ts.isStringLiteral(type.literal) && type.literal.kind !== ts.SyntaxKind.NullKeyword)) {
2571
2576
  // The type metadata was the wrong type.
2572
2577
  return null;
2573
2578
  }
2574
- const name = type.literal.text;
2579
+ const name = ts.isStringLiteral(type.literal) ? type.literal.text : null;
2575
2580
  const isStandalone = def.type.typeArguments.length > 2 && (checker.readBooleanType(def.type.typeArguments[2]) ?? false);
2576
2581
  return {
2577
2582
  kind: checker.MetaKind.Pipe,
@@ -3187,6 +3192,7 @@ class TraitCompiler {
3187
3192
  dtsTransforms;
3188
3193
  semanticDepGraphUpdater;
3189
3194
  sourceFileTypeIdentifier;
3195
+ emitDeclarationOnly;
3190
3196
  /**
3191
3197
  * Maps class declarations to their `ClassRecord`, which tracks the Ivy traits being applied to
3192
3198
  * those classes.
@@ -3204,7 +3210,7 @@ class TraitCompiler {
3204
3210
  filesWithoutTraits = new Set();
3205
3211
  reexportMap = new Map();
3206
3212
  handlersByName = new Map();
3207
- constructor(handlers, reflector, perf, incrementalBuild, compileNonExportedClasses, compilationMode, dtsTransforms, semanticDepGraphUpdater, sourceFileTypeIdentifier) {
3213
+ constructor(handlers, reflector, perf, incrementalBuild, compileNonExportedClasses, compilationMode, dtsTransforms, semanticDepGraphUpdater, sourceFileTypeIdentifier, emitDeclarationOnly) {
3208
3214
  this.handlers = handlers;
3209
3215
  this.reflector = reflector;
3210
3216
  this.perf = perf;
@@ -3214,6 +3220,7 @@ class TraitCompiler {
3214
3220
  this.dtsTransforms = dtsTransforms;
3215
3221
  this.semanticDepGraphUpdater = semanticDepGraphUpdater;
3216
3222
  this.sourceFileTypeIdentifier = sourceFileTypeIdentifier;
3223
+ this.emitDeclarationOnly = emitDeclarationOnly;
3217
3224
  for (const handler of handlers) {
3218
3225
  this.handlersByName.set(handler.name, handler);
3219
3226
  }
@@ -3422,13 +3429,16 @@ class TraitCompiler {
3422
3429
  record.metaDiagnostics === null) {
3423
3430
  // Custom decorators found in local compilation mode! In this mode we don't support custom
3424
3431
  // decorators yet. But will eventually do (b/320536434). For now a temporary error is thrown.
3432
+ const compilationModeName = this.emitDeclarationOnly
3433
+ ? 'experimental declaration-only emission'
3434
+ : 'local compilation';
3425
3435
  record.metaDiagnostics = [...nonNgDecoratorsInLocalMode].map((decorator) => ({
3426
3436
  category: ts.DiagnosticCategory.Error,
3427
3437
  code: Number('-99' + checker.ErrorCode.DECORATOR_UNEXPECTED),
3428
3438
  file: checker.getSourceFile(clazz),
3429
3439
  start: decorator.node.getStart(),
3430
3440
  length: decorator.node.getWidth(),
3431
- messageText: 'In local compilation mode, Angular does not support custom decorators. Ensure all class decorators are from Angular.',
3441
+ messageText: `In ${compilationModeName} mode, Angular does not support custom decorators. Ensure all class decorators are from Angular.`,
3432
3442
  }));
3433
3443
  record.traits = foundTraits = [];
3434
3444
  }
@@ -3848,9 +3858,6 @@ class DtsTransformer {
3848
3858
  if (ts.isClassDeclaration(node)) {
3849
3859
  return this.transformClassDeclaration(node, transforms, imports);
3850
3860
  }
3851
- else if (ts.isFunctionDeclaration(node)) {
3852
- return this.transformFunctionDeclaration(node, transforms, imports);
3853
- }
3854
3861
  else {
3855
3862
  // Otherwise return node as is.
3856
3863
  return ts.visitEachChild(node, visitor, this.ctx);
@@ -3862,52 +3869,13 @@ class DtsTransformer {
3862
3869
  return imports.transformTsFile(this.ctx, sf);
3863
3870
  }
3864
3871
  transformClassDeclaration(clazz, transforms, imports) {
3865
- let elements = clazz.members;
3866
- let elementsChanged = false;
3867
- for (const transform of transforms) {
3868
- if (transform.transformClassElement !== undefined) {
3869
- for (let i = 0; i < elements.length; i++) {
3870
- const res = transform.transformClassElement(elements[i], imports);
3871
- if (res !== elements[i]) {
3872
- if (!elementsChanged) {
3873
- elements = [...elements];
3874
- elementsChanged = true;
3875
- }
3876
- elements[i] = res;
3877
- }
3878
- }
3879
- }
3880
- }
3881
3872
  let newClazz = clazz;
3882
3873
  for (const transform of transforms) {
3883
3874
  if (transform.transformClass !== undefined) {
3884
- // If no DtsTransform has changed the class yet, then the (possibly mutated) elements have
3885
- // not yet been incorporated. Otherwise, `newClazz.members` holds the latest class members.
3886
- const inputMembers = clazz === newClazz ? elements : newClazz.members;
3887
- newClazz = transform.transformClass(newClazz, inputMembers, this.reflector, this.refEmitter, imports);
3888
- }
3889
- }
3890
- // If some elements have been transformed but the class itself has not been transformed, create
3891
- // an updated class declaration with the updated elements.
3892
- if (elementsChanged && clazz === newClazz) {
3893
- newClazz = ts.factory.updateClassDeclaration(
3894
- /* node */ clazz,
3895
- /* modifiers */ clazz.modifiers,
3896
- /* name */ clazz.name,
3897
- /* typeParameters */ clazz.typeParameters,
3898
- /* heritageClauses */ clazz.heritageClauses,
3899
- /* members */ elements);
3900
- }
3901
- return newClazz;
3902
- }
3903
- transformFunctionDeclaration(declaration, transforms, imports) {
3904
- let newDecl = declaration;
3905
- for (const transform of transforms) {
3906
- if (transform.transformFunctionDeclaration !== undefined) {
3907
- newDecl = transform.transformFunctionDeclaration(newDecl, imports);
3875
+ newClazz = transform.transformClass(newClazz, newClazz.members, this.reflector, this.refEmitter, imports);
3908
3876
  }
3909
3877
  }
3910
- return newDecl;
3878
+ return newClazz;
3911
3879
  }
3912
3880
  }
3913
3881
  class IvyDeclarationDtsTransform {
@@ -4036,11 +4004,11 @@ class Visitor {
4036
4004
 
4037
4005
  const NO_DECORATORS = new Set();
4038
4006
  const CLOSURE_FILE_OVERVIEW_REGEXP = /\s+@fileoverview\s+/i;
4039
- function ivyTransformFactory(compilation, reflector, importRewriter, defaultImportTracker, localCompilationExtraImportsTracker, perf, isCore, isClosureCompilerEnabled) {
4007
+ function ivyTransformFactory(compilation, reflector, importRewriter, defaultImportTracker, localCompilationExtraImportsTracker, perf, isCore, isClosureCompilerEnabled, emitDeclarationOnly) {
4040
4008
  const recordWrappedNode = createRecorderFn(defaultImportTracker);
4041
4009
  return (context) => {
4042
4010
  return (file) => {
4043
- return perf.inPhase(checker.PerfPhase.Compile, () => transformIvySourceFile(compilation, context, reflector, importRewriter, localCompilationExtraImportsTracker, file, isCore, isClosureCompilerEnabled, recordWrappedNode));
4011
+ return perf.inPhase(checker.PerfPhase.Compile, () => transformIvySourceFile(compilation, context, reflector, importRewriter, localCompilationExtraImportsTracker, file, isCore, isClosureCompilerEnabled, emitDeclarationOnly, recordWrappedNode));
4044
4012
  };
4045
4013
  };
4046
4014
  }
@@ -4250,7 +4218,7 @@ class IvyTransformationVisitor extends Visitor {
4250
4218
  /**
4251
4219
  * A transformer which operates on ts.SourceFiles and applies changes from an `IvyCompilation`.
4252
4220
  */
4253
- function transformIvySourceFile(compilation, context, reflector, importRewriter, localCompilationExtraImportsTracker, file, isCore, isClosureCompilerEnabled, recordWrappedNode) {
4221
+ function transformIvySourceFile(compilation, context, reflector, importRewriter, localCompilationExtraImportsTracker, file, isCore, isClosureCompilerEnabled, emitDeclarationOnly, recordWrappedNode) {
4254
4222
  const constantPool = new checker.ConstantPool(isClosureCompilerEnabled);
4255
4223
  const importManager = new checker.ImportManager({
4256
4224
  ...checker.presetImportManagerForceNamespaceImports,
@@ -4268,6 +4236,10 @@ function transformIvySourceFile(compilation, context, reflector, importRewriter,
4268
4236
  // Step 1. Go though all classes in AST, perform compilation and collect the results.
4269
4237
  const compilationVisitor = new IvyCompilationVisitor(compilation, constantPool);
4270
4238
  visit(file, compilationVisitor, context);
4239
+ // If we are emitting declarations only, we can skip the script transforms.
4240
+ if (emitDeclarationOnly) {
4241
+ return file;
4242
+ }
4271
4243
  // Step 2. Scan through the AST again and perform transformations based on Ivy compilation
4272
4244
  // results obtained at Step 1.
4273
4245
  const transformationVisitor = new IvyTransformationVisitor(compilation, compilationVisitor.classCompilationMap, reflector, importManager, recordWrappedNode, isClosureCompilerEnabled, isCore, compilationVisitor.deferrableImports);
@@ -4380,6 +4352,369 @@ function nodeArrayFromDecoratorsArray(decorators) {
4380
4352
  return array;
4381
4353
  }
4382
4354
 
4355
+ function insertDebugNameIntoCallExpression(callExpression, debugName) {
4356
+ const signalExpressionHasNoArguments = callExpression.arguments.length === 0;
4357
+ const signalExpressionIsRequired = isRequiredSignalFunction(callExpression.expression);
4358
+ let configPosition = signalExpressionIsRequired ? 0 : 1;
4359
+ // If the call expression has no arguments, we pretend that the config object is at position 0.
4360
+ // We do this so that we can insert a spread element at the start of the args list in a way where
4361
+ // undefined can be the first argument but still get tree-shaken out in production builds.
4362
+ if (signalExpressionHasNoArguments) {
4363
+ configPosition = 0;
4364
+ }
4365
+ const nodeArgs = Array.from(callExpression.arguments);
4366
+ let existingArgument = nodeArgs[configPosition];
4367
+ if (existingArgument === undefined) {
4368
+ existingArgument = ts.factory.createObjectLiteralExpression([]);
4369
+ }
4370
+ // Do nothing if an identifier is used as the config object
4371
+ // Ex -
4372
+ // const defaultObject = { equals: () => false };
4373
+ // signal(123, defaultObject)
4374
+ if (ts.isIdentifier(existingArgument)) {
4375
+ return callExpression;
4376
+ }
4377
+ if (!ts.isObjectLiteralExpression(existingArgument)) {
4378
+ return callExpression;
4379
+ }
4380
+ // insert debugName into the existing config object
4381
+ const properties = Array.from(existingArgument.properties);
4382
+ const debugNameExists = properties.some((prop) => ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name) && prop.name.text === 'debugName');
4383
+ if (debugNameExists) {
4384
+ return callExpression;
4385
+ }
4386
+ // We prepend instead of appending so that we don't overwrite an existing debugName Property
4387
+ // `{ foo: 'bar' }` -> `{ debugName: 'myDebugName', foo: 'bar' }`
4388
+ properties.unshift(ts.factory.createPropertyAssignment('debugName', ts.factory.createStringLiteral(debugName)));
4389
+ const transformedConfigProperties = ts.factory.createObjectLiteralExpression(properties);
4390
+ const ngDevModeIdentifier = ts.factory.createIdentifier('ngDevMode');
4391
+ let devModeCase;
4392
+ // if the signal expression has no arguments and the config object is not required,
4393
+ // we need to add an undefined identifier to the start of the args list so that we can spread the
4394
+ // config object in the right place.
4395
+ if (signalExpressionHasNoArguments && !signalExpressionIsRequired) {
4396
+ devModeCase = ts.factory.createArrayLiteralExpression([
4397
+ ts.factory.createIdentifier('undefined'),
4398
+ transformedConfigProperties,
4399
+ ]);
4400
+ }
4401
+ else {
4402
+ devModeCase = ts.factory.createArrayLiteralExpression([
4403
+ transformedConfigProperties,
4404
+ ...nodeArgs.slice(configPosition + 1),
4405
+ ]);
4406
+ }
4407
+ const nonDevModeCase = signalExpressionIsRequired
4408
+ ? ts.factory.createArrayLiteralExpression(nodeArgs)
4409
+ : ts.factory.createArrayLiteralExpression(nodeArgs.slice(configPosition));
4410
+ const spreadElementContainingUpdatedOptions = ts.factory.createSpreadElement(ts.factory.createParenthesizedExpression(ts.factory.createConditionalExpression(ngDevModeIdentifier,
4411
+ /* question token */ undefined, devModeCase,
4412
+ /* colon token */ undefined, nonDevModeCase)));
4413
+ let transformedSignalArgs;
4414
+ if (signalExpressionIsRequired || signalExpressionHasNoArguments) {
4415
+ // 1. If the call expression is a required signal function, there is no args other than the config object.
4416
+ // So we just use the spread element as the only argument.
4417
+ // or
4418
+ // 2. If the call expression has no arguments (ex. input(), model(), etc), we already added the undefined
4419
+ // identifier in the spread element above. So we use that spread Element as is.
4420
+ transformedSignalArgs = ts.factory.createNodeArray([spreadElementContainingUpdatedOptions]);
4421
+ }
4422
+ else {
4423
+ // 3. Signal expression is not required and has arguments.
4424
+ // Here we leave the first argument as is and spread the rest.
4425
+ transformedSignalArgs = ts.factory.createNodeArray([
4426
+ nodeArgs[0],
4427
+ spreadElementContainingUpdatedOptions,
4428
+ ]);
4429
+ }
4430
+ return ts.factory.updateCallExpression(callExpression, callExpression.expression, callExpression.typeArguments, transformedSignalArgs);
4431
+ }
4432
+ /**
4433
+ *
4434
+ * Determines if the node is a variable declaration with a call expression initializer.
4435
+ * Ex:
4436
+ * ```ts
4437
+ * const mySignal = signal(123);
4438
+ * ```
4439
+ */
4440
+ function isVariableDeclarationCase(node) {
4441
+ if (!ts.isVariableDeclaration(node)) {
4442
+ return false;
4443
+ }
4444
+ if (!node.initializer || !ts.isCallExpression(node.initializer)) {
4445
+ return false;
4446
+ }
4447
+ let expression = node.initializer.expression;
4448
+ if (ts.isPropertyAccessExpression(expression)) {
4449
+ expression = expression.expression;
4450
+ }
4451
+ return ts.isIdentifier(expression) && isSignalFunction(expression);
4452
+ }
4453
+ /**
4454
+ *
4455
+ * Determines if the node is a property assignment with a call expression initializer.
4456
+ *
4457
+ * Ex:
4458
+ * ```ts
4459
+ * class MyClass {
4460
+ * mySignal: Signal<number>;
4461
+ * constructor() {
4462
+ * this.mySignal = signal(123);
4463
+ * }
4464
+ * }
4465
+ * ```
4466
+ */
4467
+ function isPropertyAssignmentCase(node) {
4468
+ if (!ts.isExpressionStatement(node)) {
4469
+ return false;
4470
+ }
4471
+ if (!ts.isBinaryExpression(node.expression)) {
4472
+ return false;
4473
+ }
4474
+ const binaryExpression = node.expression;
4475
+ if (binaryExpression.operatorToken.kind !== ts.SyntaxKind.EqualsToken) {
4476
+ return false;
4477
+ }
4478
+ if (!ts.isCallExpression(binaryExpression.right)) {
4479
+ return false;
4480
+ }
4481
+ if (!ts.isPropertyAccessExpression(binaryExpression.left)) {
4482
+ return false;
4483
+ }
4484
+ let expression = binaryExpression.right.expression;
4485
+ if (ts.isPropertyAccessExpression(expression)) {
4486
+ expression = expression.expression;
4487
+ }
4488
+ return ts.isIdentifier(expression) && isSignalFunction(expression);
4489
+ }
4490
+ /**
4491
+ *
4492
+ * Determines if the node is a property declaration with a call expression initializer.
4493
+ *
4494
+ * Ex:
4495
+ * ```ts
4496
+ * class MyClass {
4497
+ * mySignal: Signal<number> = signal(123);
4498
+ * }
4499
+ * ```
4500
+ */
4501
+ function isPropertyDeclarationCase(node) {
4502
+ if (!ts.isPropertyDeclaration(node)) {
4503
+ return false;
4504
+ }
4505
+ if (!(node.initializer && ts.isCallExpression(node.initializer))) {
4506
+ return false;
4507
+ }
4508
+ let expression = node.initializer.expression;
4509
+ if (ts.isPropertyAccessExpression(expression)) {
4510
+ expression = expression.expression;
4511
+ }
4512
+ return ts.isIdentifier(expression) && isSignalFunction(expression);
4513
+ }
4514
+ /**
4515
+ *
4516
+ * Determines if a node is an expression that references an @angular/core imported symbol.
4517
+ * Ex:
4518
+ * ```ts
4519
+ * import { signal } from '@angular/core';
4520
+ * const mySignal = signal(123); // expressionIsUsingAngularImportedSymbol === true
4521
+ * ```
4522
+ */
4523
+ function expressionIsUsingAngularCoreImportedSymbol(program, expression) {
4524
+ const symbol = program.getTypeChecker().getSymbolAtLocation(expression);
4525
+ if (symbol === undefined) {
4526
+ return false;
4527
+ }
4528
+ const declarations = symbol.declarations;
4529
+ if (declarations === undefined || declarations.length === 0) {
4530
+ return false;
4531
+ }
4532
+ // climb up the tree from the import specifier to the import declaration
4533
+ const importSpecifier = declarations[0];
4534
+ if (!ts.isImportSpecifier(importSpecifier)) {
4535
+ return false;
4536
+ }
4537
+ const namedImports = importSpecifier.parent;
4538
+ if (!ts.isNamedImports(namedImports)) {
4539
+ return false;
4540
+ }
4541
+ const importsClause = namedImports.parent;
4542
+ if (!ts.isImportClause(importsClause)) {
4543
+ return false;
4544
+ }
4545
+ const importDeclaration = importsClause.parent;
4546
+ if (!ts.isImportDeclaration(importDeclaration) ||
4547
+ !ts.isStringLiteral(importDeclaration.moduleSpecifier)) {
4548
+ return false;
4549
+ }
4550
+ const specifier = importDeclaration.moduleSpecifier.text;
4551
+ return (specifier !== undefined &&
4552
+ (specifier === '@angular/core' || specifier.startsWith('@angular/core/')));
4553
+ }
4554
+ const signalFunctions = new Set([
4555
+ 'signal',
4556
+ 'computed',
4557
+ 'input',
4558
+ 'model',
4559
+ 'viewChild',
4560
+ 'viewChildren',
4561
+ 'contentChild',
4562
+ 'contentChildren',
4563
+ 'effect',
4564
+ ]);
4565
+ function isSignalFunction(expression) {
4566
+ const text = expression.text;
4567
+ return signalFunctions.has(text);
4568
+ }
4569
+ function isRequiredSignalFunction(expression) {
4570
+ // Check for a property access expression that uses the 'required' property
4571
+ if (ts.isPropertyAccessExpression(expression) &&
4572
+ ts.isIdentifier(expression.name) &&
4573
+ ts.isIdentifier(expression.expression)) {
4574
+ const accessName = expression.name.text;
4575
+ if (accessName === 'required') {
4576
+ return true;
4577
+ }
4578
+ }
4579
+ return false;
4580
+ }
4581
+ function transformVariableDeclaration(program, node) {
4582
+ if (!node.initializer || !ts.isCallExpression(node.initializer))
4583
+ return node;
4584
+ const expression = node.initializer.expression;
4585
+ if (ts.isPropertyAccessExpression(expression)) {
4586
+ if (!expressionIsUsingAngularCoreImportedSymbol(program, expression.expression)) {
4587
+ return node;
4588
+ }
4589
+ }
4590
+ else if (!expressionIsUsingAngularCoreImportedSymbol(program, expression)) {
4591
+ return node;
4592
+ }
4593
+ try {
4594
+ // may throw if the node does not have a source file. Ignore this case for now
4595
+ const nodeText = node.name.getText();
4596
+ return ts.factory.updateVariableDeclaration(node, node.name, node.exclamationToken, node.type, insertDebugNameIntoCallExpression(node.initializer, nodeText));
4597
+ }
4598
+ catch {
4599
+ return node;
4600
+ }
4601
+ }
4602
+ function transformPropertyAssignment(program, node) {
4603
+ const expression = node.expression.right.expression;
4604
+ if (ts.isPropertyAccessExpression(expression)) {
4605
+ if (!expressionIsUsingAngularCoreImportedSymbol(program, expression.expression)) {
4606
+ return node;
4607
+ }
4608
+ }
4609
+ else if (!expressionIsUsingAngularCoreImportedSymbol(program, expression)) {
4610
+ return node;
4611
+ }
4612
+ return ts.factory.updateExpressionStatement(node, ts.factory.createBinaryExpression(node.expression.left, node.expression.operatorToken, insertDebugNameIntoCallExpression(node.expression.right, node.expression.left.name.text)));
4613
+ }
4614
+ function transformPropertyDeclaration(program, node) {
4615
+ if (!node.initializer || !ts.isCallExpression(node.initializer))
4616
+ return node;
4617
+ const expression = node.initializer.expression;
4618
+ if (ts.isPropertyAccessExpression(expression)) {
4619
+ if (!expressionIsUsingAngularCoreImportedSymbol(program, expression.expression)) {
4620
+ return node;
4621
+ }
4622
+ }
4623
+ else if (!expressionIsUsingAngularCoreImportedSymbol(program, expression)) {
4624
+ return node;
4625
+ }
4626
+ try {
4627
+ // may throw if the node does not have a source file. Ignore this case for now.
4628
+ const nodeText = node.name.getText();
4629
+ return ts.factory.updatePropertyDeclaration(node, node.modifiers, node.name, node.questionToken, node.type, insertDebugNameIntoCallExpression(node.initializer, nodeText));
4630
+ }
4631
+ catch {
4632
+ return node;
4633
+ }
4634
+ }
4635
+ /**
4636
+ *
4637
+ * This transformer adds a debugName property to the config object of signal functions like
4638
+ * signal, computed, effect, etc.
4639
+ *
4640
+ * The debugName property is added conditionally based on the value of ngDevMode. This is done
4641
+ * to avoid adding the debugName property in production builds.
4642
+ *
4643
+ * Ex:
4644
+ * ```ts
4645
+ * import {signal} from '@angular/core';
4646
+ * const mySignal = signal('Hello World');
4647
+ * ```
4648
+ *
4649
+ * is transformed to:
4650
+ * ```ts
4651
+ * import {signal} from '@angular/core';
4652
+ * const mySignal = signal('Hello World', ...(ngDevMode ? [{ debugName: "mySignal" }] : []));
4653
+ * ```
4654
+ *
4655
+ * The transformer supports the following cases:
4656
+ *
4657
+ * # Variable declaration
4658
+ * ```ts
4659
+ * const mySignal = signal('Hello World');
4660
+ * ```
4661
+ *
4662
+ * becomes
4663
+ * ```
4664
+ * const mySignal = signal('Hello World', ...(ngDevMode ? [{ debugName: "mySignal" }] : []));
4665
+ * ```
4666
+ *
4667
+ * # Property assignment
4668
+ * ```ts
4669
+ * class MyClass {
4670
+ * mySignal: Signal<string>;
4671
+ * constructor() {
4672
+ * this.mySignal = signal('Hello World');
4673
+ * }
4674
+ * }
4675
+ * ```
4676
+ * becomes
4677
+ * ```ts
4678
+ * class MyClass {
4679
+ * mySignal: Signal<string>;
4680
+ * constructor() {
4681
+ * this.mySignal = signal(...(ngDevMode ? ['Hello World', { debugName: "mySignal" }] : ['Hello World']));
4682
+ * }
4683
+ * }
4684
+ * ```
4685
+ *
4686
+ * # Property declaration
4687
+ * ```ts
4688
+ * class MyClass {
4689
+ * mySignal = signal('Hello World');
4690
+ * }
4691
+ * ```
4692
+ * becomes
4693
+ * ```ts
4694
+ * class MyClass {
4695
+ * mySignal = signal(...(ngDevMode ? ['Hello World', { debugName: "mySignal" }] : ['Hello World']));
4696
+ * }
4697
+ * ```
4698
+ *
4699
+ */
4700
+ function signalMetadataTransform(program) {
4701
+ return (context) => (rootNode) => {
4702
+ const visit = (node) => {
4703
+ if (isVariableDeclarationCase(node)) {
4704
+ return transformVariableDeclaration(program, node);
4705
+ }
4706
+ if (isPropertyAssignmentCase(node)) {
4707
+ return transformPropertyAssignment(program, node);
4708
+ }
4709
+ if (isPropertyDeclarationCase(node)) {
4710
+ return transformPropertyDeclaration(program, node);
4711
+ }
4712
+ return ts.visitEachChild(node, visit, context);
4713
+ };
4714
+ return ts.visitNode(rootNode, visit);
4715
+ };
4716
+ }
4717
+
4383
4718
  function resolveEnumValue(evaluator, metadata, field, enumSymbolName, isCore) {
4384
4719
  let resolved = null;
4385
4720
  if (metadata.has(field)) {
@@ -5880,6 +6215,111 @@ function reexportCollision(module, refA, refB) {
5880
6215
  ]);
5881
6216
  }
5882
6217
 
6218
+ /**
6219
+ * Computes the scope for a selectorless component by looking at imports within the same
6220
+ * file and resolving them to metadata.
6221
+ */
6222
+ class SelectorlessComponentScopeReader {
6223
+ metaReader;
6224
+ reflector;
6225
+ cache = new Map();
6226
+ constructor(metaReader, reflector) {
6227
+ this.metaReader = metaReader;
6228
+ this.reflector = reflector;
6229
+ }
6230
+ getScopeForComponent(node) {
6231
+ if (this.cache.has(node)) {
6232
+ return this.cache.get(node);
6233
+ }
6234
+ const clazzRef = new checker.Reference(node);
6235
+ const meta = this.metaReader.getDirectiveMetadata(clazzRef);
6236
+ if (meta === null || !meta.isComponent || !meta.isStandalone || !meta.selectorlessEnabled) {
6237
+ this.cache.set(node, null);
6238
+ return null;
6239
+ }
6240
+ const eligibleIdentifiers = this.getAvailableIdentifiers(node);
6241
+ const dependencies = new Map();
6242
+ const dependencyIdentifiers = [];
6243
+ let isPoisoned = meta.isPoisoned;
6244
+ for (const [name, identifier] of eligibleIdentifiers) {
6245
+ if (dependencies.has(name)) {
6246
+ continue;
6247
+ }
6248
+ const dep = this.getMetaFromIdentifier(meta, name, identifier);
6249
+ if (dep !== null) {
6250
+ dependencies.set(name, dep);
6251
+ dependencyIdentifiers.push(identifier);
6252
+ if (dep.kind === checker.MetaKind.Directive && dep.isPoisoned) {
6253
+ isPoisoned = true;
6254
+ }
6255
+ }
6256
+ }
6257
+ const scope = {
6258
+ kind: checker.ComponentScopeKind.Selectorless,
6259
+ component: node,
6260
+ dependencies,
6261
+ dependencyIdentifiers,
6262
+ isPoisoned,
6263
+ schemas: meta.schemas ?? [],
6264
+ };
6265
+ this.cache.set(node, scope);
6266
+ return scope;
6267
+ }
6268
+ getRemoteScope() {
6269
+ return null;
6270
+ }
6271
+ /** Determines which identifiers a class has access to. */
6272
+ getAvailableIdentifiers(node) {
6273
+ const result = new Map();
6274
+ let current = ts.getOriginalNode(node).parent;
6275
+ while (current) {
6276
+ // Note: doesn't account for some cases like function parameters,
6277
+ // but we likely don't want to support those anyways.
6278
+ if (!ts.isSourceFile(current) && !ts.isBlock(current)) {
6279
+ current = current.parent;
6280
+ continue;
6281
+ }
6282
+ for (const stmt of current.statements) {
6283
+ if (this.reflector.isClass(stmt)) {
6284
+ result.set(stmt.name.text, stmt.name);
6285
+ continue;
6286
+ }
6287
+ if (ts.isImportDeclaration(stmt) &&
6288
+ stmt.importClause !== undefined &&
6289
+ !stmt.importClause.isTypeOnly) {
6290
+ const clause = stmt.importClause;
6291
+ if (clause.namedBindings !== undefined && ts.isNamedImports(clause.namedBindings)) {
6292
+ for (const element of clause.namedBindings.elements) {
6293
+ if (!element.isTypeOnly) {
6294
+ result.set(element.name.text, element.name);
6295
+ }
6296
+ }
6297
+ }
6298
+ if (clause.name !== undefined) {
6299
+ result.set(clause.name.text, clause.name);
6300
+ }
6301
+ continue;
6302
+ }
6303
+ }
6304
+ current = current.parent;
6305
+ }
6306
+ return result;
6307
+ }
6308
+ getMetaFromIdentifier(meta, localName, node) {
6309
+ // Consult the set of used names in the template so we don't hit the type checker for every
6310
+ // import in the file. Most likely a subset of imports in the file will be used in the template.
6311
+ if (meta.localReferencedSymbols === null || !meta.localReferencedSymbols.has(localName)) {
6312
+ return null;
6313
+ }
6314
+ const declaration = this.reflector.getDeclarationOfIdentifier(node);
6315
+ if (declaration === null || !this.reflector.isClass(declaration.node)) {
6316
+ return null;
6317
+ }
6318
+ const ref = new checker.Reference(declaration.node);
6319
+ return this.metaReader.getDirectiveMetadata(ref) ?? this.metaReader.getPipeMetadata(ref);
6320
+ }
6321
+ }
6322
+
5883
6323
  /**
5884
6324
  * Computes scope information to be used in template type checking.
5885
6325
  */
@@ -5907,13 +6347,12 @@ class TypeCheckScopeRegistry {
5907
6347
  * an empty type-check scope is returned.
5908
6348
  */
5909
6349
  getTypeCheckScope(node) {
5910
- const matcher = new checker.SelectorMatcher();
5911
6350
  const directives = [];
5912
6351
  const pipes = new Map();
5913
6352
  const scope = this.scopeReader.getScopeForComponent(node);
5914
6353
  if (scope === null) {
5915
6354
  return {
5916
- matcher,
6355
+ matcher: null,
5917
6356
  directives,
5918
6357
  pipes,
5919
6358
  schemas: [],
@@ -5921,36 +6360,40 @@ class TypeCheckScopeRegistry {
5921
6360
  };
5922
6361
  }
5923
6362
  const isNgModuleScope = scope.kind === checker.ComponentScopeKind.NgModule;
6363
+ const isSelectorlessScope = scope.kind === checker.ComponentScopeKind.Selectorless;
5924
6364
  const cacheKey = isNgModuleScope ? scope.ngModule : scope.component;
5925
- const dependencies = isNgModuleScope ? scope.compilation.dependencies : scope.dependencies;
5926
6365
  if (this.scopeCache.has(cacheKey)) {
5927
6366
  return this.scopeCache.get(cacheKey);
5928
6367
  }
5929
- let allDependencies = dependencies;
5930
- if (!isNgModuleScope &&
5931
- Array.isArray(scope.deferredDependencies) &&
5932
- scope.deferredDependencies.length > 0) {
5933
- allDependencies = [...allDependencies, ...scope.deferredDependencies];
5934
- }
5935
- for (const meta of allDependencies) {
5936
- if (meta.kind === checker.MetaKind.Directive && meta.selector !== null) {
5937
- const extMeta = this.getTypeCheckDirectiveMetadata(meta.ref);
5938
- if (extMeta === null) {
5939
- continue;
6368
+ let matcher;
6369
+ if (isSelectorlessScope) {
6370
+ matcher = this.getSelectorlessMatcher(scope);
6371
+ for (const [name, dep] of scope.dependencies) {
6372
+ if (dep.kind === checker.MetaKind.Directive) {
6373
+ directives.push(dep);
6374
+ }
6375
+ else {
6376
+ // Pipes should be available under the imported name in selectorless.
6377
+ pipes.set(name, dep);
5940
6378
  }
5941
- // Carry over the `isExplicitlyDeferred` flag from the dependency info.
5942
- const directiveMeta = this.applyExplicitlyDeferredFlag(extMeta, meta.isExplicitlyDeferred);
5943
- matcher.addSelectables(checker.CssSelector.parse(meta.selector), [
5944
- ...this.hostDirectivesResolver.resolve(directiveMeta),
5945
- directiveMeta,
5946
- ]);
5947
- directives.push(directiveMeta);
5948
6379
  }
5949
- else if (meta.kind === checker.MetaKind.Pipe) {
5950
- if (!ts.isClassDeclaration(meta.ref.node)) {
5951
- throw new Error(`Unexpected non-class declaration ${ts.SyntaxKind[meta.ref.node.kind]} for pipe ${meta.ref.debugName}`);
6380
+ }
6381
+ else {
6382
+ const dependencies = isNgModuleScope ? scope.compilation.dependencies : scope.dependencies;
6383
+ let allDependencies = dependencies;
6384
+ if (!isNgModuleScope &&
6385
+ Array.isArray(scope.deferredDependencies) &&
6386
+ scope.deferredDependencies.length > 0) {
6387
+ allDependencies = [...allDependencies, ...scope.deferredDependencies];
6388
+ }
6389
+ matcher = this.getSelectorMatcher(allDependencies);
6390
+ for (const dep of allDependencies) {
6391
+ if (dep.kind === checker.MetaKind.Directive) {
6392
+ directives.push(dep);
6393
+ }
6394
+ else if (dep.kind === checker.MetaKind.Pipe && dep.name !== null) {
6395
+ pipes.set(dep.name, dep);
5952
6396
  }
5953
- pipes.set(meta.name, meta);
5954
6397
  }
5955
6398
  }
5956
6399
  const typeCheckScope = {
@@ -5980,6 +6423,34 @@ class TypeCheckScopeRegistry {
5980
6423
  applyExplicitlyDeferredFlag(meta, isExplicitlyDeferred) {
5981
6424
  return isExplicitlyDeferred === true ? { ...meta, isExplicitlyDeferred } : meta;
5982
6425
  }
6426
+ getSelectorMatcher(allDependencies) {
6427
+ const matcher = new checker.SelectorMatcher();
6428
+ for (const meta of allDependencies) {
6429
+ if (meta.kind === checker.MetaKind.Directive && meta.selector !== null) {
6430
+ const extMeta = this.getTypeCheckDirectiveMetadata(meta.ref);
6431
+ if (extMeta === null) {
6432
+ continue;
6433
+ }
6434
+ // Carry over the `isExplicitlyDeferred` flag from the dependency info.
6435
+ const directiveMeta = this.applyExplicitlyDeferredFlag(extMeta, meta.isExplicitlyDeferred);
6436
+ matcher.addSelectables(checker.CssSelector.parse(meta.selector), [
6437
+ ...this.hostDirectivesResolver.resolve(directiveMeta),
6438
+ directiveMeta,
6439
+ ]);
6440
+ }
6441
+ }
6442
+ return matcher;
6443
+ }
6444
+ getSelectorlessMatcher(scope) {
6445
+ const registry = new Map();
6446
+ for (const [name, dep] of scope.dependencies) {
6447
+ const extMeta = dep.kind === checker.MetaKind.Directive ? this.getTypeCheckDirectiveMetadata(dep.ref) : null;
6448
+ if (extMeta !== null) {
6449
+ registry.set(name, [extMeta, ...this.hostDirectivesResolver.resolve(extMeta)]);
6450
+ }
6451
+ }
6452
+ return new checker.SelectorlessMatcher(registry);
6453
+ }
5983
6454
  }
5984
6455
 
5985
6456
  /**
@@ -6599,7 +7070,8 @@ class DirectiveDecoratorHandler {
6599
7070
  implicitStandaloneValue;
6600
7071
  usePoisonedData;
6601
7072
  typeCheckHostBindings;
6602
- constructor(reflector, evaluator, metaRegistry, scopeRegistry, metaReader, injectableRegistry, refEmitter, referencesRegistry, isCore, strictCtorDeps, semanticDepGraphUpdater, annotateForClosureCompiler, perf, importTracker, includeClassMetadata, typeCheckScopeRegistry, compilationMode, jitDeclarationRegistry, resourceRegistry, strictStandalone, implicitStandaloneValue, usePoisonedData, typeCheckHostBindings) {
7073
+ emitDeclarationOnly;
7074
+ constructor(reflector, evaluator, metaRegistry, scopeRegistry, metaReader, injectableRegistry, refEmitter, referencesRegistry, isCore, strictCtorDeps, semanticDepGraphUpdater, annotateForClosureCompiler, perf, importTracker, includeClassMetadata, typeCheckScopeRegistry, compilationMode, jitDeclarationRegistry, resourceRegistry, strictStandalone, implicitStandaloneValue, usePoisonedData, typeCheckHostBindings, emitDeclarationOnly) {
6603
7075
  this.reflector = reflector;
6604
7076
  this.evaluator = evaluator;
6605
7077
  this.metaRegistry = metaRegistry;
@@ -6623,6 +7095,7 @@ class DirectiveDecoratorHandler {
6623
7095
  this.implicitStandaloneValue = implicitStandaloneValue;
6624
7096
  this.usePoisonedData = usePoisonedData;
6625
7097
  this.typeCheckHostBindings = typeCheckHostBindings;
7098
+ this.emitDeclarationOnly = emitDeclarationOnly;
6626
7099
  }
6627
7100
  precedence = checker.HandlerPrecedence.PRIMARY;
6628
7101
  name = 'DirectiveDecoratorHandler';
@@ -6656,7 +7129,7 @@ class DirectiveDecoratorHandler {
6656
7129
  }
6657
7130
  this.perf.eventCount(checker.PerfEvent.AnalyzeDirective);
6658
7131
  const directiveResult = checker.extractDirectiveMetadata(node, decorator, this.reflector, this.importTracker, this.evaluator, this.refEmitter, this.referencesRegistry, this.isCore, this.annotateForClosureCompiler, this.compilationMode,
6659
- /* defaultSelector */ null, this.strictStandalone, this.implicitStandaloneValue);
7132
+ /* defaultSelector */ null, this.strictStandalone, this.implicitStandaloneValue, this.emitDeclarationOnly);
6660
7133
  // `extractDirectiveMetadata` returns `jitForced = true` when the `@Directive` has
6661
7134
  // set `jit: true`. In this case, compilation of the decorator is skipped. Returning
6662
7135
  // an empty object signifies that no analysis was produced.
@@ -6734,6 +7207,8 @@ class DirectiveDecoratorHandler {
6734
7207
  // Instead, we statically analyze their imports to make a direct determination.
6735
7208
  assumedToExportProviders: false,
6736
7209
  isExplicitlyDeferred: false,
7210
+ selectorlessEnabled: false,
7211
+ localReferencedSymbols: null,
6737
7212
  });
6738
7213
  this.resourceRegistry.registerResources(analysis.resources, node);
6739
7214
  this.injectableRegistry.registerInjectable(node, {
@@ -7063,7 +7538,8 @@ class NgModuleDecoratorHandler {
7063
7538
  compilationMode;
7064
7539
  localCompilationExtraImportsTracker;
7065
7540
  jitDeclarationRegistry;
7066
- constructor(reflector, evaluator, metaReader, metaRegistry, scopeRegistry, referencesRegistry, exportedProviderStatusResolver, semanticDepGraphUpdater, isCore, refEmitter, annotateForClosureCompiler, onlyPublishPublicTypings, injectableRegistry, perf, includeClassMetadata, includeSelectorScope, compilationMode, localCompilationExtraImportsTracker, jitDeclarationRegistry) {
7541
+ emitDeclarationOnly;
7542
+ constructor(reflector, evaluator, metaReader, metaRegistry, scopeRegistry, referencesRegistry, exportedProviderStatusResolver, semanticDepGraphUpdater, isCore, refEmitter, annotateForClosureCompiler, onlyPublishPublicTypings, injectableRegistry, perf, includeClassMetadata, includeSelectorScope, compilationMode, localCompilationExtraImportsTracker, jitDeclarationRegistry, emitDeclarationOnly) {
7067
7543
  this.reflector = reflector;
7068
7544
  this.evaluator = evaluator;
7069
7545
  this.metaReader = metaReader;
@@ -7083,6 +7559,7 @@ class NgModuleDecoratorHandler {
7083
7559
  this.compilationMode = compilationMode;
7084
7560
  this.localCompilationExtraImportsTracker = localCompilationExtraImportsTracker;
7085
7561
  this.jitDeclarationRegistry = jitDeclarationRegistry;
7562
+ this.emitDeclarationOnly = emitDeclarationOnly;
7086
7563
  }
7087
7564
  precedence = checker.HandlerPrecedence.PRIMARY;
7088
7565
  name = 'NgModuleDecoratorHandler';
@@ -7127,13 +7604,14 @@ class NgModuleDecoratorHandler {
7127
7604
  createModuleWithProvidersResolver(this.reflector, this.isCore),
7128
7605
  forwardRefResolver,
7129
7606
  ]);
7607
+ const allowUnresolvedReferences = this.compilationMode === checker.CompilationMode.LOCAL && !this.emitDeclarationOnly;
7130
7608
  const diagnostics = [];
7131
7609
  // Resolving declarations
7132
7610
  let declarationRefs = [];
7133
7611
  const rawDeclarations = ngModule.get('declarations') ?? null;
7134
7612
  if (rawDeclarations !== null) {
7135
7613
  const declarationMeta = this.evaluator.evaluate(rawDeclarations, forwardRefResolver);
7136
- declarationRefs = this.resolveTypeList(rawDeclarations, declarationMeta, name, 'declarations', 0, this.compilationMode === checker.CompilationMode.LOCAL).references;
7614
+ declarationRefs = this.resolveTypeList(rawDeclarations, declarationMeta, name, 'declarations', 0, allowUnresolvedReferences).references;
7137
7615
  // Look through the declarations to make sure they're all a part of the current compilation.
7138
7616
  for (const ref of declarationRefs) {
7139
7617
  if (ref.node.getSourceFile().isDeclarationFile) {
@@ -7150,7 +7628,7 @@ class NgModuleDecoratorHandler {
7150
7628
  let rawImports = ngModule.get('imports') ?? null;
7151
7629
  if (rawImports !== null) {
7152
7630
  const importsMeta = this.evaluator.evaluate(rawImports, moduleResolvers);
7153
- const result = this.resolveTypeList(rawImports, importsMeta, name, 'imports', 0, this.compilationMode === checker.CompilationMode.LOCAL);
7631
+ const result = this.resolveTypeList(rawImports, importsMeta, name, 'imports', 0, allowUnresolvedReferences);
7154
7632
  if (this.compilationMode === checker.CompilationMode.LOCAL &&
7155
7633
  this.localCompilationExtraImportsTracker !== null) {
7156
7634
  // For generating extra imports in local mode, the NgModule imports that are from external
@@ -7173,13 +7651,13 @@ class NgModuleDecoratorHandler {
7173
7651
  const rawExports = ngModule.get('exports') ?? null;
7174
7652
  if (rawExports !== null) {
7175
7653
  const exportsMeta = this.evaluator.evaluate(rawExports, moduleResolvers);
7176
- exportRefs = this.resolveTypeList(rawExports, exportsMeta, name, 'exports', 0, this.compilationMode === checker.CompilationMode.LOCAL).references;
7654
+ exportRefs = this.resolveTypeList(rawExports, exportsMeta, name, 'exports', 0, allowUnresolvedReferences).references;
7177
7655
  this.referencesRegistry.add(node, ...exportRefs);
7178
7656
  }
7179
7657
  // Resolving bootstrap
7180
7658
  let bootstrapRefs = [];
7181
7659
  const rawBootstrap = ngModule.get('bootstrap') ?? null;
7182
- if (this.compilationMode !== checker.CompilationMode.LOCAL && rawBootstrap !== null) {
7660
+ if (!allowUnresolvedReferences && rawBootstrap !== null) {
7183
7661
  const bootstrapMeta = this.evaluator.evaluate(rawBootstrap, forwardRefResolver);
7184
7662
  bootstrapRefs = this.resolveTypeList(rawBootstrap, bootstrapMeta, name, 'bootstrap', 0,
7185
7663
  /* allowUnresolvedReferences */ false).references;
@@ -7245,7 +7723,7 @@ class NgModuleDecoratorHandler {
7245
7723
  exports.some(isForwardReference);
7246
7724
  const type = checker.wrapTypeReference(this.reflector, node);
7247
7725
  let ngModuleMetadata;
7248
- if (this.compilationMode === checker.CompilationMode.LOCAL) {
7726
+ if (allowUnresolvedReferences) {
7249
7727
  ngModuleMetadata = {
7250
7728
  kind: checker.R3NgModuleMetadataKind.Local,
7251
7729
  type,
@@ -7295,7 +7773,7 @@ class NgModuleDecoratorHandler {
7295
7773
  : rawProviders);
7296
7774
  }
7297
7775
  const topLevelImports = [];
7298
- if (this.compilationMode !== checker.CompilationMode.LOCAL && ngModule.has('imports')) {
7776
+ if (!allowUnresolvedReferences && ngModule.has('imports')) {
7299
7777
  const rawImports = checker.unwrapExpression(ngModule.get('imports'));
7300
7778
  let topLevelExpressions = [];
7301
7779
  if (ts.isArrayLiteralExpression(rawImports)) {
@@ -7332,7 +7810,7 @@ class NgModuleDecoratorHandler {
7332
7810
  providers: wrappedProviders,
7333
7811
  imports: [],
7334
7812
  };
7335
- if (this.compilationMode === checker.CompilationMode.LOCAL) {
7813
+ if (allowUnresolvedReferences) {
7336
7814
  // Adding NgModule's raw imports/exports to the injector's imports field in local compilation
7337
7815
  // mode.
7338
7816
  for (const exp of [rawImports, rawExports]) {
@@ -7719,6 +8197,12 @@ class NgModuleDecoratorHandler {
7719
8197
  dynamicValueSet.add(entry);
7720
8198
  continue;
7721
8199
  }
8200
+ else if (this.emitDeclarationOnly &&
8201
+ entry instanceof checker.DynamicValue &&
8202
+ entry.isFromUnknownIdentifier()) {
8203
+ throw checker.createValueHasWrongTypeError(entry.node, entry, `Value at position ${absoluteIndex} in the NgModule.${arrayName} of ${className} is an external reference. ` +
8204
+ 'External references in @NgModule declarations are not supported in experimental declaration-only emission mode');
8205
+ }
7722
8206
  else {
7723
8207
  // TODO(alxhub): Produce a better diagnostic here - the array index may be an inner array.
7724
8208
  throw checker.createValueHasWrongTypeError(expr, entry, `Value at position ${absoluteIndex} in the NgModule.${arrayName} of ${className} is not a reference`);
@@ -8847,6 +9331,52 @@ class HmrModuleImportRewriter {
8847
9331
  }
8848
9332
  }
8849
9333
 
9334
+ /*!
9335
+ * @license
9336
+ * Copyright Google LLC All Rights Reserved.
9337
+ *
9338
+ * Use of this source code is governed by an MIT-style license that can be
9339
+ * found in the LICENSE file at https://angular.dev/license
9340
+ */
9341
+ /**
9342
+ * Analyzes a component's template to determine if it's using selectorless syntax
9343
+ * and to extract the names of the selectorless symbols that are referenced.
9344
+ */
9345
+ function analyzeTemplateForSelectorless(template) {
9346
+ const analyzer = new SelectorlessDirectivesAnalyzer();
9347
+ checker.visitAll(analyzer, template);
9348
+ const isSelectorless = analyzer.symbols !== null && analyzer.symbols.size > 0;
9349
+ const localReferencedSymbols = analyzer.symbols;
9350
+ // The template is considered selectorless only if there
9351
+ // are direct references to directives or pipes.
9352
+ return { isSelectorless, localReferencedSymbols };
9353
+ }
9354
+ /**
9355
+ * Visitor that traverses all the template nodes and
9356
+ * expressions to look for selectorless references.
9357
+ */
9358
+ class SelectorlessDirectivesAnalyzer extends checker.CombinedRecursiveAstVisitor {
9359
+ symbols = null;
9360
+ visit(node) {
9361
+ if (node instanceof checker.BindingPipe && node.type === checker.BindingPipeType.ReferencedDirectly) {
9362
+ this.trackSymbol(node.name);
9363
+ }
9364
+ super.visit(node);
9365
+ }
9366
+ visitComponent(component) {
9367
+ this.trackSymbol(component.componentName);
9368
+ super.visitComponent(component);
9369
+ }
9370
+ visitDirective(directive) {
9371
+ this.trackSymbol(directive.name);
9372
+ super.visitDirective(directive);
9373
+ }
9374
+ trackSymbol(name) {
9375
+ this.symbols ??= new Set();
9376
+ this.symbols.add(name);
9377
+ }
9378
+ }
9379
+
8850
9380
  const EMPTY_ARRAY = [];
8851
9381
  const isUsedDirective = (decl) => decl.kind === checker.R3TemplateDependencyKind.Directive;
8852
9382
  const isUsedPipe = (decl) => decl.kind === checker.R3TemplateDependencyKind.Pipe;
@@ -8899,7 +9429,8 @@ class ComponentDecoratorHandler {
8899
9429
  implicitStandaloneValue;
8900
9430
  typeCheckHostBindings;
8901
9431
  enableSelectorless;
8902
- constructor(reflector, evaluator, metaRegistry, metaReader, scopeReader, compilerHost, scopeRegistry, typeCheckScopeRegistry, resourceRegistry, isCore, strictCtorDeps, resourceLoader, rootDirs, defaultPreserveWhitespaces, i18nUseExternalIds, enableI18nLegacyMessageIdFormat, usePoisonedData, i18nNormalizeLineEndingsInICUs, moduleResolver, cycleAnalyzer, cycleHandlingStrategy, refEmitter, referencesRegistry, depTracker, injectableRegistry, semanticDepGraphUpdater, annotateForClosureCompiler, perf, hostDirectivesResolver, importTracker, includeClassMetadata, compilationMode, deferredSymbolTracker, forbidOrphanRendering, enableBlockSyntax, enableLetSyntax, externalRuntimeStyles, localCompilationExtraImportsTracker, jitDeclarationRegistry, i18nPreserveSignificantWhitespace, strictStandalone, enableHmr, implicitStandaloneValue, typeCheckHostBindings, enableSelectorless) {
9432
+ emitDeclarationOnly;
9433
+ constructor(reflector, evaluator, metaRegistry, metaReader, scopeReader, compilerHost, scopeRegistry, typeCheckScopeRegistry, resourceRegistry, isCore, strictCtorDeps, resourceLoader, rootDirs, defaultPreserveWhitespaces, i18nUseExternalIds, enableI18nLegacyMessageIdFormat, usePoisonedData, i18nNormalizeLineEndingsInICUs, moduleResolver, cycleAnalyzer, cycleHandlingStrategy, refEmitter, referencesRegistry, depTracker, injectableRegistry, semanticDepGraphUpdater, annotateForClosureCompiler, perf, hostDirectivesResolver, importTracker, includeClassMetadata, compilationMode, deferredSymbolTracker, forbidOrphanRendering, enableBlockSyntax, enableLetSyntax, externalRuntimeStyles, localCompilationExtraImportsTracker, jitDeclarationRegistry, i18nPreserveSignificantWhitespace, strictStandalone, enableHmr, implicitStandaloneValue, typeCheckHostBindings, enableSelectorless, emitDeclarationOnly) {
8903
9434
  this.reflector = reflector;
8904
9435
  this.evaluator = evaluator;
8905
9436
  this.metaRegistry = metaRegistry;
@@ -8945,6 +9476,7 @@ class ComponentDecoratorHandler {
8945
9476
  this.implicitStandaloneValue = implicitStandaloneValue;
8946
9477
  this.typeCheckHostBindings = typeCheckHostBindings;
8947
9478
  this.enableSelectorless = enableSelectorless;
9479
+ this.emitDeclarationOnly = emitDeclarationOnly;
8948
9480
  this.extractTemplateOptions = {
8949
9481
  enableI18nLegacyMessageIdFormat: this.enableI18nLegacyMessageIdFormat,
8950
9482
  i18nNormalizeLineEndingsInICUs: this.i18nNormalizeLineEndingsInICUs,
@@ -9082,7 +9614,7 @@ class ComponentDecoratorHandler {
9082
9614
  let isPoisoned = false;
9083
9615
  // @Component inherits @Directive, so begin by extracting the @Directive metadata and building
9084
9616
  // on it.
9085
- const directiveResult = checker.extractDirectiveMetadata(node, decorator, this.reflector, this.importTracker, this.evaluator, this.refEmitter, this.referencesRegistry, this.isCore, this.annotateForClosureCompiler, this.compilationMode, this.elementSchemaRegistry.getDefaultComponentElementName(), this.strictStandalone, this.implicitStandaloneValue);
9617
+ const directiveResult = checker.extractDirectiveMetadata(node, decorator, this.reflector, this.importTracker, this.evaluator, this.refEmitter, this.referencesRegistry, this.isCore, this.annotateForClosureCompiler, this.compilationMode, this.elementSchemaRegistry.getDefaultComponentElementName(), this.strictStandalone, this.implicitStandaloneValue, this.emitDeclarationOnly);
9086
9618
  // `extractDirectiveMetadata` returns `jitForced = true` when the `@Component` has
9087
9619
  // set `jit: true`. In this case, compilation of the decorator is skipped. Returning
9088
9620
  // an empty object signifies that no analysis was produced.
@@ -9262,6 +9794,25 @@ class ComponentDecoratorHandler {
9262
9794
  node: template.sourceMapping.node,
9263
9795
  };
9264
9796
  const relativeTemplatePath = getProjectRelativePath(templateResource.path ?? ts.getOriginalNode(node).getSourceFile().fileName, this.rootDirs, this.compilerHost);
9797
+ let selectorlessEnabled = false;
9798
+ let localReferencedSymbols = null;
9799
+ if (this.enableSelectorless) {
9800
+ const templateAnalysis = analyzeTemplateForSelectorless(template.nodes);
9801
+ selectorlessEnabled = templateAnalysis.isSelectorless;
9802
+ localReferencedSymbols = templateAnalysis.localReferencedSymbols;
9803
+ }
9804
+ if (selectorlessEnabled) {
9805
+ if (!metadata.isStandalone) {
9806
+ isPoisoned = true;
9807
+ diagnostics ??= [];
9808
+ diagnostics.push(checker.makeDiagnostic(checker.ErrorCode.COMPONENT_NOT_STANDALONE, component.get('standalone') || node.name, `Cannot use selectorless with a component that is not standalone`));
9809
+ }
9810
+ else if (rawImports || rawDeferredImports) {
9811
+ isPoisoned = true;
9812
+ diagnostics ??= [];
9813
+ diagnostics.push(checker.makeDiagnostic(checker.ErrorCode.UNSUPPORTED_SELECTORLESS_COMPONENT_FIELD, (rawImports || rawDeferredImports), `Cannot use the "${rawImports === null ? 'deferredImports' : 'imports'}" field in a selectorless component`));
9814
+ }
9815
+ }
9265
9816
  // Figure out the set of styles. The ordering here is important: external resources (styleUrls)
9266
9817
  // precede inline styles, and styles defined in the template override styles defined in the
9267
9818
  // component.
@@ -9379,6 +9930,8 @@ class ComponentDecoratorHandler {
9379
9930
  outputs,
9380
9931
  hostDirectives,
9381
9932
  rawHostDirectives,
9933
+ selectorlessEnabled,
9934
+ localReferencedSymbols,
9382
9935
  meta: {
9383
9936
  ...metadata,
9384
9937
  template,
@@ -9464,6 +10017,8 @@ class ComponentDecoratorHandler {
9464
10017
  ngContentSelectors: analysis.template.ngContentSelectors,
9465
10018
  preserveWhitespaces: analysis.template.preserveWhitespaces ?? false,
9466
10019
  isExplicitlyDeferred: false,
10020
+ selectorlessEnabled: analysis.selectorlessEnabled,
10021
+ localReferencedSymbols: analysis.localReferencedSymbols,
9467
10022
  });
9468
10023
  this.resourceRegistry.registerResources(analysis.resources, node);
9469
10024
  this.injectableRegistry.registerInjectable(node, {
@@ -9476,25 +10031,19 @@ class ComponentDecoratorHandler {
9476
10031
  }
9477
10032
  const scope = this.scopeReader.getScopeForComponent(node);
9478
10033
  const selector = analysis.meta.selector;
9479
- const matcher = new checker.SelectorMatcher();
10034
+ let matcher = null;
9480
10035
  if (scope !== null) {
9481
- let { dependencies, isPoisoned } = scope.kind === checker.ComponentScopeKind.NgModule ? scope.compilation : scope;
10036
+ const isPoisoned = scope.kind === checker.ComponentScopeKind.NgModule
10037
+ ? scope.compilation.isPoisoned
10038
+ : scope.isPoisoned;
9482
10039
  if ((isPoisoned || (scope.kind === checker.ComponentScopeKind.NgModule && scope.exported.isPoisoned)) &&
9483
10040
  !this.usePoisonedData) {
9484
10041
  // Don't bother indexing components which had erroneous scopes, unless specifically
9485
10042
  // requested.
9486
- return null;
9487
- }
9488
- for (const dep of dependencies) {
9489
- if (dep.kind === checker.MetaKind.Directive && dep.selector !== null) {
9490
- matcher.addSelectables(checker.CssSelector.parse(dep.selector), [
9491
- ...this.hostDirectivesResolver.resolve(dep),
9492
- dep,
9493
- ]);
9494
- }
10043
+ return null;
9495
10044
  }
10045
+ matcher = createMatcherFromScope(scope, this.hostDirectivesResolver);
9496
10046
  }
9497
- // TODO(crisbeto): implement for selectorless.
9498
10047
  const binder = new checker.R3TargetBinder(matcher);
9499
10048
  const boundTemplate = binder.bind({ template: analysis.template.diagNodes });
9500
10049
  context.addComponent({
@@ -9517,7 +10066,6 @@ class ComponentDecoratorHandler {
9517
10066
  // Don't type-check components that had errors in their scopes, unless requested.
9518
10067
  return;
9519
10068
  }
9520
- // TODO(crisbeto): implement for selectorless.
9521
10069
  const binder = new checker.R3TargetBinder(scope.matcher);
9522
10070
  const templateContext = {
9523
10071
  nodes: meta.template.diagNodes,
@@ -9608,8 +10156,8 @@ class ComponentDecoratorHandler {
9608
10156
  data.deferPerBlockDependencies = this.locateDeferBlocksWithoutScope(metadata.template);
9609
10157
  }
9610
10158
  else {
9611
- const { eagerlyUsed, deferBlocks, allDependencies, wholeTemplateUsed } = this.resolveComponentDependencies(node, context, analysis, scope, metadata, diagnostics);
9612
- const declarations = this.componentDependenciesToDeclarations(node, context, allDependencies, wholeTemplateUsed);
10159
+ const { eagerlyUsed, deferBlocks, allDependencies, wholeTemplateUsed, pipes } = this.resolveComponentDependencies(node, context, analysis, scope, metadata, diagnostics);
10160
+ const declarations = this.componentDependenciesToDeclarations(node, context, allDependencies, wholeTemplateUsed, pipes);
9613
10161
  if (this.semanticDepGraphUpdater !== null) {
9614
10162
  const getSemanticReference = (decl) => this.semanticDepGraphUpdater.getSemanticReference(decl.ref.node, decl.type);
9615
10163
  symbol.usedDirectives = Array.from(declarations.values())
@@ -9621,7 +10169,7 @@ class ComponentDecoratorHandler {
9621
10169
  }
9622
10170
  // Process information related to defer blocks
9623
10171
  if (this.compilationMode !== checker.CompilationMode.LOCAL) {
9624
- this.resolveDeferBlocks(node, deferBlocks, declarations, data, analysis, eagerlyUsed);
10172
+ this.resolveDeferBlocks(node, scope, deferBlocks, declarations, data, analysis, eagerlyUsed);
9625
10173
  }
9626
10174
  this.handleDependencyCycles(node, context, scope, data, analysis, metadata, declarations, eagerlyUsed, symbol);
9627
10175
  }
@@ -9691,7 +10239,7 @@ class ComponentDecoratorHandler {
9691
10239
  if (perComponentDeferredDeps !== null) {
9692
10240
  removeDeferrableTypesFromComponentDecorator(analysis, perComponentDeferredDeps);
9693
10241
  }
9694
- const def = checker.compileComponentFromMetadata(meta, pool, checker.makeBindingParser());
10242
+ const def = checker.compileComponentFromMetadata(meta, pool, this.getNewBindingParser());
9695
10243
  const inputTransformFields = compileInputTransformFields(analysis.inputs);
9696
10244
  const classMetadata = analysis.classMetadata !== null
9697
10245
  ? compileComponentClassMetadata(analysis.classMetadata, perComponentDeferredDeps).toStmt()
@@ -9759,7 +10307,7 @@ class ComponentDecoratorHandler {
9759
10307
  removeDeferrableTypesFromComponentDecorator(analysis, deferrableTypes);
9760
10308
  }
9761
10309
  const fac = compileNgFactoryDefField(checker.toFactoryMetadata(meta, checker.FactoryTarget.Component));
9762
- const def = checker.compileComponentFromMetadata(meta, pool, checker.makeBindingParser());
10310
+ const def = checker.compileComponentFromMetadata(meta, pool, this.getNewBindingParser());
9763
10311
  const inputTransformFields = compileInputTransformFields(analysis.inputs);
9764
10312
  const classMetadata = analysis.classMetadata !== null
9765
10313
  ? compileComponentClassMetadata(analysis.classMetadata, deferrableTypes).toStmt()
@@ -9789,7 +10337,7 @@ class ComponentDecoratorHandler {
9789
10337
  defer,
9790
10338
  };
9791
10339
  const fac = compileNgFactoryDefField(checker.toFactoryMetadata(meta, checker.FactoryTarget.Component));
9792
- const def = checker.compileComponentFromMetadata(meta, pool, checker.makeBindingParser());
10340
+ const def = checker.compileComponentFromMetadata(meta, pool, this.getNewBindingParser());
9793
10341
  const classMetadata = analysis.classMetadata !== null
9794
10342
  ? compileComponentClassMetadata(analysis.classMetadata, null).toStmt()
9795
10343
  : null;
@@ -9829,10 +10377,30 @@ class ComponentDecoratorHandler {
9829
10377
  // is an alternative implementation of template matching which is used for template
9830
10378
  // type-checking and will eventually replace matching in the TemplateDefinitionBuilder.
9831
10379
  const isModuleScope = scope.kind === checker.ComponentScopeKind.NgModule;
9832
- // Dependencies coming from the regular `imports` field.
9833
- const dependencies = isModuleScope ? scope.compilation.dependencies : scope.dependencies;
10380
+ const isSelectorlessScope = scope.kind === checker.ComponentScopeKind.Selectorless;
10381
+ const pipes = new Map();
9834
10382
  // Dependencies from the `@Component.deferredImports` field.
9835
- const explicitlyDeferredDependencies = getExplicitlyDeferredDeps(scope);
10383
+ const explicitlyDeferredDependencies = scope.kind === checker.ComponentScopeKind.Standalone ? scope.deferredDependencies : null;
10384
+ const dependencies = [];
10385
+ if (isSelectorlessScope) {
10386
+ for (const [localName, dep] of scope.dependencies) {
10387
+ // In selectorless the pipes are referred to by their local name.
10388
+ if (dep.kind === checker.MetaKind.Pipe) {
10389
+ pipes.set(localName, dep);
10390
+ }
10391
+ dependencies.push(dep);
10392
+ }
10393
+ }
10394
+ else {
10395
+ const scopeDeps = isModuleScope ? scope.compilation.dependencies : scope.dependencies;
10396
+ for (const dep of scopeDeps) {
10397
+ // Outside of selectorless the pipes are referred to by their defined name.
10398
+ if (dep.kind === checker.MetaKind.Pipe && dep.name !== null) {
10399
+ pipes.set(dep.name, dep);
10400
+ }
10401
+ dependencies.push(dep);
10402
+ }
10403
+ }
9836
10404
  // Mark the component is an NgModule-based component with its NgModule in a different file
9837
10405
  // then mark this file for extra import generation
9838
10406
  if (isModuleScope && context.fileName !== checker.getSourceFile(scope.ngModule).fileName) {
@@ -9840,8 +10408,10 @@ class ComponentDecoratorHandler {
9840
10408
  }
9841
10409
  // Make sure that `@Component.imports` and `@Component.deferredImports` do not have
9842
10410
  // the same dependencies.
9843
- if (metadata.isStandalone &&
10411
+ if (!isSelectorlessScope &&
10412
+ metadata.isStandalone &&
9844
10413
  analysis.rawDeferredImports !== null &&
10414
+ explicitlyDeferredDependencies !== null &&
9845
10415
  explicitlyDeferredDependencies.length > 0) {
9846
10416
  const diagnostic = validateNoImportOverlap(dependencies, explicitlyDeferredDependencies, analysis.rawDeferredImports);
9847
10417
  if (diagnostic !== null) {
@@ -9849,7 +10419,7 @@ class ComponentDecoratorHandler {
9849
10419
  }
9850
10420
  }
9851
10421
  // Set up the R3TargetBinder.
9852
- const binder = createTargetBinder(dependencies);
10422
+ const binder = new checker.R3TargetBinder(createMatcherFromScope(scope, this.hostDirectivesResolver));
9853
10423
  let allDependencies = dependencies;
9854
10424
  let deferBlockBinder = binder;
9855
10425
  // If there are any explicitly deferred dependencies (via `@Component.deferredImports`),
@@ -9858,13 +10428,19 @@ class ComponentDecoratorHandler {
9858
10428
  // and need to be referenced later when determining what dependencies need to be in a
9859
10429
  // defer function / instruction call. Otherwise they end up treated as a standard
9860
10430
  // import, which is wrong.
9861
- if (explicitlyDeferredDependencies.length > 0) {
10431
+ if (explicitlyDeferredDependencies !== null && explicitlyDeferredDependencies.length > 0) {
9862
10432
  allDependencies = [...explicitlyDeferredDependencies, ...dependencies];
9863
- deferBlockBinder = createTargetBinder(allDependencies);
10433
+ const deferBlockMatcher = new checker.SelectorMatcher();
10434
+ for (const dep of allDependencies) {
10435
+ if (dep.kind === checker.MetaKind.Pipe && dep.name !== null) {
10436
+ pipes.set(dep.name, dep);
10437
+ }
10438
+ else if (dep.kind === checker.MetaKind.Directive && dep.selector !== null) {
10439
+ deferBlockMatcher.addSelectables(checker.CssSelector.parse(dep.selector), [dep]);
10440
+ }
10441
+ }
10442
+ deferBlockBinder = new checker.R3TargetBinder(deferBlockMatcher);
9864
10443
  }
9865
- // Set up the pipes map that is later used to determine which dependencies are used in
9866
- // the template.
9867
- const pipes = extractPipes(allDependencies);
9868
10444
  // Next, the component template AST is bound using the R3TargetBinder. This produces a
9869
10445
  // BoundTarget, which is similar to a ts.TypeChecker.
9870
10446
  const bound = binder.bind({ template: metadata.template.nodes });
@@ -9916,13 +10492,13 @@ class ComponentDecoratorHandler {
9916
10492
  wholeTemplateUsed.add(pipes.get(name).ref.node);
9917
10493
  }
9918
10494
  }
9919
- return { allDependencies, eagerlyUsed, wholeTemplateUsed, deferBlocks };
10495
+ return { allDependencies, eagerlyUsed, wholeTemplateUsed, deferBlocks, pipes };
9920
10496
  }
9921
10497
  /**
9922
10498
  * Converts component dependencies into declarations by
9923
10499
  * resolving their metadata and deduplicating them.
9924
10500
  */
9925
- componentDependenciesToDeclarations(node, context, allDependencies, wholeTemplateUsed) {
10501
+ componentDependenciesToDeclarations(node, context, allDependencies, wholeTemplateUsed, pipes) {
9926
10502
  const declarations = new Map();
9927
10503
  // Transform the dependencies list, filtering out unused dependencies.
9928
10504
  for (const dep of allDependencies) {
@@ -9949,20 +10525,6 @@ class ComponentDecoratorHandler {
9949
10525
  isComponent: dep.isComponent,
9950
10526
  });
9951
10527
  break;
9952
- case checker.MetaKind.Pipe:
9953
- if (!wholeTemplateUsed.has(dep.ref.node)) {
9954
- continue;
9955
- }
9956
- const pipeType = this.refEmitter.emit(dep.ref, context);
9957
- checker.assertSuccessfulReferenceEmit(pipeType, node.name, 'pipe');
9958
- declarations.set(dep.ref.node, {
9959
- kind: checker.R3TemplateDependencyKind.Pipe,
9960
- type: pipeType.expression,
9961
- name: dep.name,
9962
- ref: dep.ref,
9963
- importedFile: pipeType.importedFile,
9964
- });
9965
- break;
9966
10528
  case checker.MetaKind.NgModule:
9967
10529
  const ngModuleType = this.refEmitter.emit(dep.ref, context);
9968
10530
  checker.assertSuccessfulReferenceEmit(ngModuleType, node.name, 'NgModule');
@@ -9974,6 +10536,21 @@ class ComponentDecoratorHandler {
9974
10536
  break;
9975
10537
  }
9976
10538
  }
10539
+ for (const [localName, dep] of pipes) {
10540
+ if (!wholeTemplateUsed.has(dep.ref.node)) {
10541
+ continue;
10542
+ }
10543
+ const pipeType = this.refEmitter.emit(dep.ref, context);
10544
+ checker.assertSuccessfulReferenceEmit(pipeType, node.name, 'pipe');
10545
+ declarations.set(dep.ref.node, {
10546
+ kind: checker.R3TemplateDependencyKind.Pipe,
10547
+ type: pipeType.expression,
10548
+ // Use the local name for pipes to account for selectorless.
10549
+ name: localName,
10550
+ ref: dep.ref,
10551
+ importedFile: pipeType.importedFile,
10552
+ });
10553
+ }
9977
10554
  return declarations;
9978
10555
  }
9979
10556
  /** Handles any cycles in the dependencies of a component. */
@@ -10211,7 +10788,7 @@ class ComponentDecoratorHandler {
10211
10788
  * Resolves information about defer blocks dependencies to make it
10212
10789
  * available for the final `compile` step.
10213
10790
  */
10214
- resolveDeferBlocks(componentClassDecl, deferBlocks, deferrableDecls, resolutionData, analysisData, eagerlyUsedDecls) {
10791
+ resolveDeferBlocks(componentClassDecl, scope, deferBlocks, deferrableDecls, resolutionData, analysisData, eagerlyUsedDecls) {
10215
10792
  // Collect all deferred decls from all defer blocks from the entire template
10216
10793
  // to intersect with the information from the `imports` field of a particular
10217
10794
  // Component.
@@ -10252,15 +10829,27 @@ class ComponentDecoratorHandler {
10252
10829
  allDeferredDecls.add(decl.ref.node);
10253
10830
  }
10254
10831
  }
10255
- // For standalone components with the `imports` and `deferredImports` fields -
10256
- // inspect the list of referenced symbols and mark the ones used in defer blocks
10257
- // as potential candidates for defer loading.
10258
10832
  if (analysisData.meta.isStandalone) {
10259
- if (analysisData.rawImports !== null) {
10260
- this.registerDeferrableCandidates(componentClassDecl, analysisData.rawImports, false /* isDeferredImport */, allDeferredDecls, eagerlyUsedDecls, resolutionData);
10833
+ // For standalone components with the `imports` and `deferredImports` fields -
10834
+ // inspect the list of referenced symbols and mark the ones used in defer blocks
10835
+ // as potential candidates for defer loading.
10836
+ if (analysisData.rawImports !== null &&
10837
+ ts.isArrayLiteralExpression(analysisData.rawImports)) {
10838
+ for (const element of analysisData.rawImports.elements) {
10839
+ this.registerDeferrableCandidate(componentClassDecl, element, false /* isDeferredImport */, allDeferredDecls, eagerlyUsedDecls, resolutionData);
10840
+ }
10261
10841
  }
10262
- if (analysisData.rawDeferredImports !== null) {
10263
- this.registerDeferrableCandidates(componentClassDecl, analysisData.rawDeferredImports, true /* isDeferredImport */, allDeferredDecls, eagerlyUsedDecls, resolutionData);
10842
+ if (analysisData.rawDeferredImports !== null &&
10843
+ ts.isArrayLiteralExpression(analysisData.rawDeferredImports)) {
10844
+ for (const element of analysisData.rawDeferredImports.elements) {
10845
+ this.registerDeferrableCandidate(componentClassDecl, element, false /* isDeferredImport */, allDeferredDecls, eagerlyUsedDecls, resolutionData);
10846
+ }
10847
+ }
10848
+ // Selectorless references dependencies directly so we register through the identifiers.
10849
+ if (scope.kind === checker.ComponentScopeKind.Selectorless) {
10850
+ for (const identifier of scope.dependencyIdentifiers) {
10851
+ this.registerDeferrableCandidate(componentClassDecl, identifier, false /* isDeferredImport */, allDeferredDecls, eagerlyUsedDecls, resolutionData);
10852
+ }
10264
10853
  }
10265
10854
  }
10266
10855
  }
@@ -10269,58 +10858,53 @@ class ComponentDecoratorHandler {
10269
10858
  * `@Component.deferredImports`) and registers imported types as deferrable
10270
10859
  * candidates.
10271
10860
  */
10272
- registerDeferrableCandidates(componentClassDecl, importsExpr, isDeferredImport, allDeferredDecls, eagerlyUsedDecls, resolutionData) {
10273
- if (!ts.isArrayLiteralExpression(importsExpr)) {
10861
+ registerDeferrableCandidate(componentClassDecl, element, isDeferredImport, allDeferredDecls, eagerlyUsedDecls, resolutionData) {
10862
+ const node = checker.tryUnwrapForwardRef(element, this.reflector) || element;
10863
+ if (!ts.isIdentifier(node)) {
10864
+ // Can't defer-load non-literal references.
10274
10865
  return;
10275
10866
  }
10276
- for (const element of importsExpr.elements) {
10277
- const node = checker.tryUnwrapForwardRef(element, this.reflector) || element;
10278
- if (!ts.isIdentifier(node)) {
10279
- // Can't defer-load non-literal references.
10280
- continue;
10281
- }
10282
- const imp = this.reflector.getImportOfIdentifier(node);
10283
- if (imp === null) {
10284
- // Can't defer-load symbols which aren't imported.
10285
- continue;
10286
- }
10287
- const decl = this.reflector.getDeclarationOfIdentifier(node);
10288
- if (decl === null) {
10289
- // Can't defer-load symbols which don't exist.
10290
- continue;
10291
- }
10292
- if (!checker.isNamedClassDeclaration(decl.node)) {
10293
- // Can't defer-load symbols which aren't classes.
10294
- continue;
10295
- }
10296
- // Are we even trying to defer-load this symbol?
10297
- if (!allDeferredDecls.has(decl.node)) {
10298
- continue;
10299
- }
10300
- if (eagerlyUsedDecls.has(decl.node)) {
10301
- // Can't defer-load symbols that are eagerly referenced as a dependency
10302
- // in a template outside of a defer block.
10303
- continue;
10304
- }
10305
- // Is it a standalone directive/component?
10306
- const dirMeta = this.metaReader.getDirectiveMetadata(new checker.Reference(decl.node));
10307
- if (dirMeta !== null && !dirMeta.isStandalone) {
10308
- continue;
10309
- }
10310
- // Is it a standalone pipe?
10311
- const pipeMeta = this.metaReader.getPipeMetadata(new checker.Reference(decl.node));
10312
- if (pipeMeta !== null && !pipeMeta.isStandalone) {
10313
- continue;
10314
- }
10315
- if (dirMeta === null && pipeMeta === null) {
10316
- // This is not a directive or a pipe.
10317
- continue;
10318
- }
10319
- // Keep track of how this class made it into the current source file
10320
- // (which ts.ImportDeclaration was used for this symbol).
10321
- resolutionData.deferrableDeclToImportDecl.set(decl.node, imp.node);
10322
- this.deferredSymbolTracker.markAsDeferrableCandidate(node, imp.node, componentClassDecl, isDeferredImport);
10867
+ const imp = this.reflector.getImportOfIdentifier(node);
10868
+ if (imp === null) {
10869
+ // Can't defer-load symbols which aren't imported.
10870
+ return;
10871
+ }
10872
+ const decl = this.reflector.getDeclarationOfIdentifier(node);
10873
+ if (decl === null) {
10874
+ // Can't defer-load symbols which don't exist.
10875
+ return;
10876
+ }
10877
+ if (!checker.isNamedClassDeclaration(decl.node)) {
10878
+ // Can't defer-load symbols which aren't classes.
10879
+ return;
10880
+ }
10881
+ // Are we even trying to defer-load this symbol?
10882
+ if (!allDeferredDecls.has(decl.node)) {
10883
+ return;
10884
+ }
10885
+ if (eagerlyUsedDecls.has(decl.node)) {
10886
+ // Can't defer-load symbols that are eagerly referenced as a dependency
10887
+ // in a template outside of a defer block.
10888
+ return;
10889
+ }
10890
+ // Is it a standalone directive/component?
10891
+ const dirMeta = this.metaReader.getDirectiveMetadata(new checker.Reference(decl.node));
10892
+ if (dirMeta !== null && !dirMeta.isStandalone) {
10893
+ return;
10894
+ }
10895
+ // Is it a standalone pipe?
10896
+ const pipeMeta = this.metaReader.getPipeMetadata(new checker.Reference(decl.node));
10897
+ if (pipeMeta !== null && !pipeMeta.isStandalone) {
10898
+ return;
10899
+ }
10900
+ if (dirMeta === null && pipeMeta === null) {
10901
+ // This is not a directive or a pipe.
10902
+ return;
10323
10903
  }
10904
+ // Keep track of how this class made it into the current source file
10905
+ // (which ts.ImportDeclaration was used for this symbol).
10906
+ resolutionData.deferrableDeclToImportDecl.set(decl.node, imp.node);
10907
+ this.deferredSymbolTracker.markAsDeferrableCandidate(node, imp.node, componentClassDecl, isDeferredImport);
10324
10908
  }
10325
10909
  compileDeferBlocks(resolution) {
10326
10910
  const { deferBlockDepsEmitMode: mode, deferPerBlockDependencies: perBlockDeps, deferPerComponentDependencies: perComponentDeps, } = resolution;
@@ -10347,36 +10931,31 @@ class ComponentDecoratorHandler {
10347
10931
  }
10348
10932
  throw new Error(`Invalid deferBlockDepsEmitMode. Cannot compile deferred block metadata.`);
10349
10933
  }
10934
+ /** Creates a new binding parser. */
10935
+ getNewBindingParser() {
10936
+ return checker.makeBindingParser(undefined, this.enableSelectorless);
10937
+ }
10350
10938
  }
10351
- /**
10352
- * Creates an instance of a target binder based on provided dependencies.
10353
- */
10354
- function createTargetBinder(dependencies) {
10939
+ function createMatcherFromScope(scope, hostDirectivesResolver) {
10940
+ if (scope.kind === checker.ComponentScopeKind.Selectorless) {
10941
+ const registry = new Map();
10942
+ for (const [name, dep] of scope.dependencies) {
10943
+ if (dep.kind === checker.MetaKind.Directive) {
10944
+ registry.set(name, [dep, ...hostDirectivesResolver.resolve(dep)]);
10945
+ }
10946
+ }
10947
+ return new checker.SelectorlessMatcher(registry);
10948
+ }
10355
10949
  const matcher = new checker.SelectorMatcher();
10950
+ const dependencies = scope.kind === checker.ComponentScopeKind.NgModule
10951
+ ? scope.compilation.dependencies
10952
+ : scope.dependencies;
10356
10953
  for (const dep of dependencies) {
10357
10954
  if (dep.kind === checker.MetaKind.Directive && dep.selector !== null) {
10358
10955
  matcher.addSelectables(checker.CssSelector.parse(dep.selector), [dep]);
10359
10956
  }
10360
10957
  }
10361
- // TODO(crisbeto): implement for selectorless.
10362
- return new checker.R3TargetBinder(matcher);
10363
- }
10364
- /**
10365
- * Returns the list of dependencies from `@Component.deferredImports` if provided.
10366
- */
10367
- function getExplicitlyDeferredDeps(scope) {
10368
- return scope.kind === checker.ComponentScopeKind.NgModule
10369
- ? []
10370
- : scope.deferredDependencies;
10371
- }
10372
- function extractPipes(dependencies) {
10373
- const pipes = new Map();
10374
- for (const dep of dependencies) {
10375
- if (dep.kind === checker.MetaKind.Pipe) {
10376
- pipes.set(dep.name, dep);
10377
- }
10378
- }
10379
- return pipes;
10958
+ return matcher;
10380
10959
  }
10381
10960
  /**
10382
10961
  * Drop references to existing imports for deferrable symbols that should be present
@@ -10816,41 +11395,49 @@ class PipeDecoratorHandler {
10816
11395
  if (decorator.args === null) {
10817
11396
  throw new checker.FatalDiagnosticError(checker.ErrorCode.DECORATOR_NOT_CALLED, decorator.node, `@Pipe must be called`);
10818
11397
  }
10819
- if (decorator.args.length !== 1) {
10820
- throw new checker.FatalDiagnosticError(checker.ErrorCode.DECORATOR_ARITY_WRONG, decorator.node, '@Pipe must have exactly one argument');
10821
- }
10822
- const meta = checker.unwrapExpression(decorator.args[0]);
10823
- if (!ts.isObjectLiteralExpression(meta)) {
10824
- throw new checker.FatalDiagnosticError(checker.ErrorCode.DECORATOR_ARG_NOT_LITERAL, meta, '@Pipe must have a literal argument');
10825
- }
10826
- const pipe = checker.reflectObjectLiteral(meta);
10827
- if (!pipe.has('name')) {
10828
- throw new checker.FatalDiagnosticError(checker.ErrorCode.PIPE_MISSING_NAME, meta, `@Pipe decorator is missing name field`);
10829
- }
10830
- const pipeNameExpr = pipe.get('name');
10831
- const pipeName = this.evaluator.evaluate(pipeNameExpr);
10832
- if (typeof pipeName !== 'string') {
10833
- throw checker.createValueHasWrongTypeError(pipeNameExpr, pipeName, `@Pipe.name must be a string`);
10834
- }
11398
+ const meta = decorator.args.length === 0 ||
11399
+ // TODO(crisbeto): temporary for testing until we've changed
11400
+ // the pipe public API not to require a name.
11401
+ (ts.isNonNullExpression(decorator.args[0]) &&
11402
+ decorator.args[0].expression.kind === ts.SyntaxKind.NullKeyword)
11403
+ ? null
11404
+ : checker.unwrapExpression(decorator.args[0]);
11405
+ let pipeName = null;
11406
+ let pipeNameExpr = null;
10835
11407
  let pure = true;
10836
- if (pipe.has('pure')) {
10837
- const expr = pipe.get('pure');
10838
- const pureValue = this.evaluator.evaluate(expr);
10839
- if (typeof pureValue !== 'boolean') {
10840
- throw checker.createValueHasWrongTypeError(expr, pureValue, `@Pipe.pure must be a boolean`);
10841
- }
10842
- pure = pureValue;
10843
- }
10844
11408
  let isStandalone = this.implicitStandaloneValue;
10845
- if (pipe.has('standalone')) {
10846
- const expr = pipe.get('standalone');
10847
- const resolved = this.evaluator.evaluate(expr);
10848
- if (typeof resolved !== 'boolean') {
10849
- throw checker.createValueHasWrongTypeError(expr, resolved, `standalone flag must be a boolean`);
11409
+ if (meta !== null) {
11410
+ if (!ts.isObjectLiteralExpression(meta)) {
11411
+ throw new checker.FatalDiagnosticError(checker.ErrorCode.DECORATOR_ARG_NOT_LITERAL, meta, '@Pipe must have a literal argument');
11412
+ }
11413
+ const pipe = checker.reflectObjectLiteral(meta);
11414
+ if (!pipe.has('name')) {
11415
+ throw new checker.FatalDiagnosticError(checker.ErrorCode.PIPE_MISSING_NAME, meta, `@Pipe decorator is missing name field`);
11416
+ }
11417
+ pipeNameExpr = pipe.get('name');
11418
+ const evaluatedName = this.evaluator.evaluate(pipeNameExpr);
11419
+ if (typeof evaluatedName !== 'string') {
11420
+ throw checker.createValueHasWrongTypeError(pipeNameExpr, evaluatedName, `@Pipe.name must be a string`);
11421
+ }
11422
+ pipeName = evaluatedName;
11423
+ if (pipe.has('pure')) {
11424
+ const expr = pipe.get('pure');
11425
+ const pureValue = this.evaluator.evaluate(expr);
11426
+ if (typeof pureValue !== 'boolean') {
11427
+ throw checker.createValueHasWrongTypeError(expr, pureValue, `@Pipe.pure must be a boolean`);
11428
+ }
11429
+ pure = pureValue;
10850
11430
  }
10851
- isStandalone = resolved;
10852
- if (!isStandalone && this.strictStandalone) {
10853
- throw new checker.FatalDiagnosticError(checker.ErrorCode.NON_STANDALONE_NOT_ALLOWED, expr, `Only standalone pipes are allowed when 'strictStandalone' is enabled.`);
11431
+ if (pipe.has('standalone')) {
11432
+ const expr = pipe.get('standalone');
11433
+ const resolved = this.evaluator.evaluate(expr);
11434
+ if (typeof resolved !== 'boolean') {
11435
+ throw checker.createValueHasWrongTypeError(expr, resolved, `standalone flag must be a boolean`);
11436
+ }
11437
+ isStandalone = resolved;
11438
+ if (!isStandalone && this.strictStandalone) {
11439
+ throw new checker.FatalDiagnosticError(checker.ErrorCode.NON_STANDALONE_NOT_ALLOWED, expr, `Only standalone pipes are allowed when 'strictStandalone' is enabled.`);
11440
+ }
10854
11441
  }
10855
11442
  }
10856
11443
  return {
@@ -10873,7 +11460,7 @@ class PipeDecoratorHandler {
10873
11460
  };
10874
11461
  }
10875
11462
  symbol(node, analysis) {
10876
- return new PipeSymbol(node, analysis.meta.pipeName);
11463
+ return new PipeSymbol(node, analysis.meta.pipeName ?? analysis.meta.name);
10877
11464
  }
10878
11465
  register(node, analysis) {
10879
11466
  const ref = new checker.Reference(node);
@@ -10935,7 +11522,7 @@ class PipeDecoratorHandler {
10935
11522
  * @description
10936
11523
  * Entry point for all public APIs of the compiler-cli package.
10937
11524
  */
10938
- new checker.Version('20.0.6');
11525
+ new checker.Version('20.1.0-next.1');
10939
11526
 
10940
11527
  /**
10941
11528
  * Whether a given decorator should be treated as an Angular decorator.
@@ -14000,6 +14587,8 @@ var IdentifierKind;
14000
14587
  IdentifierKind[IdentifierKind["Reference"] = 5] = "Reference";
14001
14588
  IdentifierKind[IdentifierKind["Variable"] = 6] = "Variable";
14002
14589
  IdentifierKind[IdentifierKind["LetDeclaration"] = 7] = "LetDeclaration";
14590
+ IdentifierKind[IdentifierKind["Component"] = 8] = "Component";
14591
+ IdentifierKind[IdentifierKind["Directive"] = 9] = "Directive";
14003
14592
  })(IdentifierKind || (IdentifierKind = {}));
14004
14593
  /**
14005
14594
  * Describes the absolute byte offsets of a text anchor in a source code.
@@ -14029,109 +14618,20 @@ class IndexingContext {
14029
14618
  }
14030
14619
  }
14031
14620
 
14032
- /**
14033
- * Visits the AST of an Angular template syntax expression, finding interesting
14034
- * entities (variable references, etc.). Creates an array of Entities found in
14035
- * the expression, with the location of the Entities being relative to the
14036
- * expression.
14037
- *
14038
- * Visiting `text {{prop}}` will return
14039
- * `[TopLevelIdentifier {name: 'prop', span: {start: 7, end: 11}}]`.
14040
- */
14041
- class ExpressionVisitor extends checker.RecursiveAstVisitor {
14042
- expressionStr;
14043
- absoluteOffset;
14044
- boundTemplate;
14045
- targetToIdentifier;
14046
- identifiers = [];
14047
- errors = [];
14048
- constructor(expressionStr, absoluteOffset, boundTemplate, targetToIdentifier) {
14049
- super();
14050
- this.expressionStr = expressionStr;
14051
- this.absoluteOffset = absoluteOffset;
14052
- this.boundTemplate = boundTemplate;
14053
- this.targetToIdentifier = targetToIdentifier;
14054
- }
14055
- /**
14056
- * Returns identifiers discovered in an expression.
14057
- *
14058
- * @param ast expression AST to visit
14059
- * @param source expression AST source code
14060
- * @param absoluteOffset absolute byte offset from start of the file to the start of the AST
14061
- * source code.
14062
- * @param boundTemplate bound target of the entire template, which can be used to query for the
14063
- * entities expressions target.
14064
- * @param targetToIdentifier closure converting a template target node to its identifier.
14065
- */
14066
- static getIdentifiers(ast, source, absoluteOffset, boundTemplate, targetToIdentifier) {
14067
- const visitor = new ExpressionVisitor(source, absoluteOffset, boundTemplate, targetToIdentifier);
14068
- visitor.visit(ast);
14069
- return { identifiers: visitor.identifiers, errors: visitor.errors };
14070
- }
14071
- visit(ast) {
14072
- ast.visit(this);
14073
- }
14074
- visitPropertyRead(ast, context) {
14075
- this.visitIdentifier(ast, IdentifierKind.Property);
14076
- super.visitPropertyRead(ast, context);
14077
- }
14078
- visitPropertyWrite(ast, context) {
14079
- this.visitIdentifier(ast, IdentifierKind.Property);
14080
- super.visitPropertyWrite(ast, context);
14081
- }
14082
- /**
14083
- * Visits an identifier, adding it to the identifier store if it is useful for indexing.
14084
- *
14085
- * @param ast expression AST the identifier is in
14086
- * @param kind identifier kind
14087
- */
14088
- visitIdentifier(ast, kind) {
14089
- // The definition of a non-top-level property such as `bar` in `{{foo.bar}}` is currently
14090
- // impossible to determine by an indexer and unsupported by the indexing module.
14091
- // The indexing module also does not currently support references to identifiers declared in the
14092
- // template itself, which have a non-null expression target.
14093
- if (!(ast.receiver instanceof checker.ImplicitReceiver)) {
14094
- return;
14095
- }
14096
- // The source span of the requested AST starts at a location that is offset from the expression.
14097
- let identifierStart = ast.sourceSpan.start - this.absoluteOffset;
14098
- if (ast instanceof checker.PropertyRead || ast instanceof checker.PropertyWrite) {
14099
- // For `PropertyRead` and `PropertyWrite`, the identifier starts at the `nameSpan`, not
14100
- // necessarily the `sourceSpan`.
14101
- identifierStart = ast.nameSpan.start - this.absoluteOffset;
14102
- }
14103
- if (!this.expressionStr.substring(identifierStart).startsWith(ast.name)) {
14104
- this.errors.push(new Error(`Impossible state: "${ast.name}" not found in "${this.expressionStr}" at location ${identifierStart}`));
14105
- return;
14106
- }
14107
- // Join the relative position of the expression within a node with the absolute position
14108
- // of the node to get the absolute position of the expression in the source code.
14109
- const absoluteStart = this.absoluteOffset + identifierStart;
14110
- const span = new AbsoluteSourceSpan(absoluteStart, absoluteStart + ast.name.length);
14111
- const targetAst = this.boundTemplate.getExpressionTarget(ast);
14112
- const target = targetAst ? this.targetToIdentifier(targetAst) : null;
14113
- const identifier = {
14114
- name: ast.name,
14115
- span,
14116
- kind,
14117
- target,
14118
- };
14119
- this.identifiers.push(identifier);
14120
- }
14121
- }
14122
14621
  /**
14123
14622
  * Visits the AST of a parsed Angular template. Discovers and stores
14124
14623
  * identifiers of interest, deferring to an `ExpressionVisitor` as needed.
14125
14624
  */
14126
- let TemplateVisitor$1 = class TemplateVisitor extends checker.RecursiveVisitor {
14625
+ let TemplateVisitor$1 = class TemplateVisitor extends checker.CombinedRecursiveAstVisitor {
14127
14626
  boundTemplate;
14128
14627
  // Identifiers of interest found in the template.
14129
14628
  identifiers = new Set();
14130
14629
  errors = [];
14630
+ currentAstWithSource = null;
14131
14631
  // Map of targets in a template to their identifiers.
14132
14632
  targetIdentifierCache = new Map();
14133
14633
  // Map of elements and templates to their identifiers.
14134
- elementAndTemplateIdentifierCache = new Map();
14634
+ directiveHostIdentifierCache = new Map();
14135
14635
  /**
14136
14636
  * Creates a template visitor for a bound template target. The bound target can be used when
14137
14637
  * deferred to the expression visitor to get information about the target of an expression.
@@ -14142,139 +14642,78 @@ let TemplateVisitor$1 = class TemplateVisitor extends checker.RecursiveVisitor {
14142
14642
  super();
14143
14643
  this.boundTemplate = boundTemplate;
14144
14644
  }
14145
- /**
14146
- * Visits a node in the template.
14147
- *
14148
- * @param node node to visit
14149
- */
14150
- visit(node) {
14151
- node.visit(this);
14152
- }
14153
- visitAll(nodes) {
14154
- nodes.forEach((node) => this.visit(node));
14155
- }
14156
14645
  /**
14157
14646
  * Add an identifier for an HTML element and visit its children recursively.
14158
14647
  *
14159
14648
  * @param element
14160
14649
  */
14161
14650
  visitElement(element) {
14162
- const elementIdentifier = this.elementOrTemplateToIdentifier(element);
14651
+ const elementIdentifier = this.directiveHostToIdentifier(element);
14163
14652
  if (elementIdentifier !== null) {
14164
14653
  this.identifiers.add(elementIdentifier);
14165
14654
  }
14166
- this.visitAll(element.references);
14167
- this.visitAll(element.inputs);
14168
- this.visitAll(element.attributes);
14169
- this.visitAll(element.directives);
14170
- this.visitAll(element.children);
14171
- this.visitAll(element.outputs);
14655
+ super.visitElement(element);
14172
14656
  }
14173
14657
  visitTemplate(template) {
14174
- const templateIdentifier = this.elementOrTemplateToIdentifier(template);
14658
+ const templateIdentifier = this.directiveHostToIdentifier(template);
14175
14659
  if (templateIdentifier !== null) {
14176
14660
  this.identifiers.add(templateIdentifier);
14177
14661
  }
14178
- this.visitAll(template.directives);
14179
- this.visitAll(template.variables);
14180
- this.visitAll(template.attributes);
14181
- this.visitAll(template.templateAttrs);
14182
- this.visitAll(template.children);
14183
- this.visitAll(template.references);
14184
- }
14185
- visitBoundAttribute(attribute) {
14186
- // If the bound attribute has no value, it cannot have any identifiers in the value expression.
14187
- if (attribute.valueSpan === undefined) {
14188
- return;
14189
- }
14190
- const { identifiers, errors } = ExpressionVisitor.getIdentifiers(attribute.value, attribute.valueSpan.toString(), attribute.valueSpan.start.offset, this.boundTemplate, this.targetToIdentifier.bind(this));
14191
- identifiers.forEach((id) => this.identifiers.add(id));
14192
- this.errors.push(...errors);
14193
- }
14194
- visitBoundEvent(attribute) {
14195
- this.visitExpression(attribute.handler);
14196
- }
14197
- visitBoundText(text) {
14198
- this.visitExpression(text.value);
14662
+ super.visitTemplate(template);
14199
14663
  }
14200
14664
  visitReference(reference) {
14201
14665
  const referenceIdentifier = this.targetToIdentifier(reference);
14202
- if (referenceIdentifier === null) {
14203
- return;
14666
+ if (referenceIdentifier !== null) {
14667
+ this.identifiers.add(referenceIdentifier);
14204
14668
  }
14205
- this.identifiers.add(referenceIdentifier);
14669
+ super.visitReference(reference);
14206
14670
  }
14207
14671
  visitVariable(variable) {
14208
14672
  const variableIdentifier = this.targetToIdentifier(variable);
14209
- if (variableIdentifier === null) {
14210
- return;
14211
- }
14212
- this.identifiers.add(variableIdentifier);
14213
- }
14214
- visitDeferredBlock(deferred) {
14215
- deferred.visitAll(this);
14216
- }
14217
- visitDeferredBlockPlaceholder(block) {
14218
- this.visitAll(block.children);
14219
- }
14220
- visitDeferredBlockError(block) {
14221
- this.visitAll(block.children);
14222
- }
14223
- visitDeferredBlockLoading(block) {
14224
- this.visitAll(block.children);
14225
- }
14226
- visitDeferredTrigger(trigger) {
14227
- if (trigger instanceof checker.BoundDeferredTrigger) {
14228
- this.visitExpression(trigger.value);
14673
+ if (variableIdentifier !== null) {
14674
+ this.identifiers.add(variableIdentifier);
14229
14675
  }
14230
- }
14231
- visitSwitchBlock(block) {
14232
- this.visitExpression(block.expression);
14233
- this.visitAll(block.cases);
14234
- }
14235
- visitSwitchBlockCase(block) {
14236
- block.expression && this.visitExpression(block.expression);
14237
- this.visitAll(block.children);
14238
- }
14239
- visitForLoopBlock(block) {
14240
- block.item.visit(this);
14241
- this.visitAll(block.contextVariables);
14242
- this.visitExpression(block.expression);
14243
- this.visitAll(block.children);
14244
- block.empty?.visit(this);
14245
- }
14246
- visitForLoopBlockEmpty(block) {
14247
- this.visitAll(block.children);
14248
- }
14249
- visitIfBlock(block) {
14250
- this.visitAll(block.branches);
14251
- }
14252
- visitIfBlockBranch(block) {
14253
- block.expression && this.visitExpression(block.expression);
14254
- block.expressionAlias?.visit(this);
14255
- this.visitAll(block.children);
14676
+ super.visitVariable(variable);
14256
14677
  }
14257
14678
  visitLetDeclaration(decl) {
14258
14679
  const identifier = this.targetToIdentifier(decl);
14259
14680
  if (identifier !== null) {
14260
14681
  this.identifiers.add(identifier);
14261
14682
  }
14262
- this.visitExpression(decl.value);
14683
+ super.visitLetDeclaration(decl);
14263
14684
  }
14264
14685
  visitComponent(component) {
14265
- throw new Error('TODO');
14686
+ const identifier = this.directiveHostToIdentifier(component);
14687
+ if (identifier !== null) {
14688
+ this.identifiers.add(identifier);
14689
+ }
14690
+ super.visitComponent(component);
14266
14691
  }
14267
14692
  visitDirective(directive) {
14268
- throw new Error('TODO');
14693
+ const identifier = this.directiveHostToIdentifier(directive);
14694
+ if (identifier !== null) {
14695
+ this.identifiers.add(identifier);
14696
+ }
14697
+ super.visitDirective(directive);
14698
+ }
14699
+ visitPropertyRead(ast) {
14700
+ this.visitIdentifier(ast, IdentifierKind.Property);
14701
+ super.visitPropertyRead(ast, null);
14702
+ }
14703
+ visitBoundAttribute(attribute) {
14704
+ const previous = this.currentAstWithSource;
14705
+ this.currentAstWithSource = {
14706
+ source: attribute.valueSpan?.toString() || null,
14707
+ absoluteOffset: attribute.valueSpan ? attribute.valueSpan.start.offset : -1,
14708
+ };
14709
+ this.visit(attribute.value instanceof checker.ASTWithSource ? attribute.value.ast : attribute.value);
14710
+ this.currentAstWithSource = previous;
14269
14711
  }
14270
14712
  /** Creates an identifier for a template element or template node. */
14271
- elementOrTemplateToIdentifier(node) {
14272
- if (node instanceof checker.Component || node instanceof checker.Directive) {
14273
- throw new Error('TODO');
14274
- }
14713
+ directiveHostToIdentifier(node) {
14275
14714
  // If this node has already been seen, return the cached result.
14276
- if (this.elementAndTemplateIdentifierCache.has(node)) {
14277
- return this.elementAndTemplateIdentifierCache.get(node);
14715
+ if (this.directiveHostIdentifierCache.has(node)) {
14716
+ return this.directiveHostIdentifierCache.get(node);
14278
14717
  }
14279
14718
  let name;
14280
14719
  let kind;
@@ -14282,14 +14721,23 @@ let TemplateVisitor$1 = class TemplateVisitor extends checker.RecursiveVisitor {
14282
14721
  name = node.tagName ?? 'ng-template';
14283
14722
  kind = IdentifierKind.Template;
14284
14723
  }
14285
- else {
14724
+ else if (node instanceof checker.Element) {
14286
14725
  name = node.name;
14287
14726
  kind = IdentifierKind.Element;
14288
14727
  }
14728
+ else if (node instanceof checker.Component) {
14729
+ name = node.fullName;
14730
+ kind = IdentifierKind.Component;
14731
+ }
14732
+ else {
14733
+ name = node.name;
14734
+ kind = IdentifierKind.Directive;
14735
+ }
14289
14736
  // Namespaced elements have a particular format for `node.name` that needs to be handled.
14290
14737
  // For example, an `<svg>` element has a `node.name` of `':svg:svg'`.
14291
14738
  // TODO(alxhub): properly handle namespaced elements
14292
- if (name.startsWith(':')) {
14739
+ if ((node instanceof checker.Template || node instanceof checker.Element) &&
14740
+ name.startsWith(':')) {
14293
14741
  name = name.split(':').pop();
14294
14742
  }
14295
14743
  const sourceSpan = node.startSourceSpan;
@@ -14324,7 +14772,7 @@ let TemplateVisitor$1 = class TemplateVisitor extends checker.RecursiveVisitor {
14324
14772
  })),
14325
14773
  // cast b/c pre-TypeScript 3.5 unions aren't well discriminated
14326
14774
  };
14327
- this.elementAndTemplateIdentifierCache.set(node, identifier);
14775
+ this.directiveHostIdentifierCache.set(node, identifier);
14328
14776
  return identifier;
14329
14777
  }
14330
14778
  /** Creates an identifier for a template reference or template variable target. */
@@ -14353,10 +14801,10 @@ let TemplateVisitor$1 = class TemplateVisitor extends checker.RecursiveVisitor {
14353
14801
  refTarget instanceof checker.Template ||
14354
14802
  refTarget instanceof checker.Component ||
14355
14803
  refTarget instanceof checker.Directive) {
14356
- node = this.elementOrTemplateToIdentifier(refTarget);
14804
+ node = this.directiveHostToIdentifier(refTarget);
14357
14805
  }
14358
14806
  else {
14359
- node = this.elementOrTemplateToIdentifier(refTarget.node);
14807
+ node = this.directiveHostToIdentifier(refTarget.node);
14360
14808
  directive = refTarget.directive.ref.node;
14361
14809
  }
14362
14810
  if (node === null) {
@@ -14406,16 +14854,60 @@ let TemplateVisitor$1 = class TemplateVisitor extends checker.RecursiveVisitor {
14406
14854
  *
14407
14855
  * @param node node whose expression to visit
14408
14856
  */
14409
- visitExpression(ast) {
14410
- // Only include ASTs that have information about their source and absolute source spans.
14411
- if (ast instanceof checker.ASTWithSource && ast.source !== null) {
14412
- // Make target to identifier mapping closure stateful to this visitor instance.
14413
- const targetToIdentifier = this.targetToIdentifier.bind(this);
14414
- const absoluteOffset = ast.sourceSpan.start;
14415
- const { identifiers, errors } = ExpressionVisitor.getIdentifiers(ast, ast.source, absoluteOffset, this.boundTemplate, targetToIdentifier);
14416
- identifiers.forEach((id) => this.identifiers.add(id));
14417
- this.errors.push(...errors);
14857
+ visit(node) {
14858
+ if (node instanceof checker.ASTWithSource) {
14859
+ const previous = this.currentAstWithSource;
14860
+ this.currentAstWithSource = { source: node.source, absoluteOffset: node.sourceSpan.start };
14861
+ super.visit(node.ast);
14862
+ this.currentAstWithSource = previous;
14863
+ }
14864
+ else {
14865
+ super.visit(node);
14866
+ }
14867
+ }
14868
+ /**
14869
+ * Visits an identifier, adding it to the identifier store if it is useful for indexing.
14870
+ *
14871
+ * @param ast expression AST the identifier is in
14872
+ * @param kind identifier kind
14873
+ */
14874
+ visitIdentifier(ast, kind) {
14875
+ // Only handle identifiers in expressions that have a source location.
14876
+ if (this.currentAstWithSource === null || this.currentAstWithSource.source === null) {
14877
+ return;
14878
+ }
14879
+ // The definition of a non-top-level property such as `bar` in `{{foo.bar}}` is currently
14880
+ // impossible to determine by an indexer and unsupported by the indexing module.
14881
+ // The indexing module also does not currently support references to identifiers declared in the
14882
+ // template itself, which have a non-null expression target.
14883
+ if (!(ast.receiver instanceof checker.ImplicitReceiver)) {
14884
+ return;
14418
14885
  }
14886
+ const { absoluteOffset, source: expressionStr } = this.currentAstWithSource;
14887
+ // The source span of the requested AST starts at a location that is offset from the expression.
14888
+ let identifierStart = ast.sourceSpan.start - absoluteOffset;
14889
+ if (ast instanceof checker.PropertyRead) {
14890
+ // For `PropertyRead` and the identifier starts at the `nameSpan`,
14891
+ // not necessarily the `sourceSpan`.
14892
+ identifierStart = ast.nameSpan.start - absoluteOffset;
14893
+ }
14894
+ if (!expressionStr.substring(identifierStart).startsWith(ast.name)) {
14895
+ this.errors.push(new Error(`Impossible state: "${ast.name}" not found in "${expressionStr}" at location ${identifierStart}`));
14896
+ return;
14897
+ }
14898
+ // Join the relative position of the expression within a node with the absolute position
14899
+ // of the node to get the absolute position of the expression in the source code.
14900
+ const absoluteStart = absoluteOffset + identifierStart;
14901
+ const span = new AbsoluteSourceSpan(absoluteStart, absoluteStart + ast.name.length);
14902
+ const targetAst = this.boundTemplate.getExpressionTarget(ast);
14903
+ const target = targetAst ? this.targetToIdentifier(targetAst) : null;
14904
+ const identifier = {
14905
+ name: ast.name,
14906
+ span,
14907
+ kind,
14908
+ target,
14909
+ };
14910
+ this.identifiers.add(identifier);
14419
14911
  }
14420
14912
  };
14421
14913
  /**
@@ -14427,7 +14919,7 @@ let TemplateVisitor$1 = class TemplateVisitor extends checker.RecursiveVisitor {
14427
14919
  function getTemplateIdentifiers(boundTemplate) {
14428
14920
  const visitor = new TemplateVisitor$1(boundTemplate);
14429
14921
  if (boundTemplate.target.template !== undefined) {
14430
- visitor.visitAll(boundTemplate.target.template);
14922
+ checker.visitAll(visitor, boundTemplate.target.template);
14431
14923
  }
14432
14924
  return { identifiers: visitor.identifiers, errors: visitor.errors };
14433
14925
  }
@@ -14839,7 +15331,10 @@ class StandaloneComponentScopeReader {
14839
15331
  if (!this.cache.has(clazz)) {
14840
15332
  const clazzRef = new checker.Reference(clazz);
14841
15333
  const clazzMeta = this.metaReader.getDirectiveMetadata(clazzRef);
14842
- if (clazzMeta === null || !clazzMeta.isComponent || !clazzMeta.isStandalone) {
15334
+ if (clazzMeta === null ||
15335
+ !clazzMeta.isComponent ||
15336
+ !clazzMeta.isStandalone ||
15337
+ clazzMeta.selectorlessEnabled) {
14843
15338
  this.cache.set(clazz, null);
14844
15339
  return null;
14845
15340
  }
@@ -14994,7 +15489,7 @@ class TemplateCheckWithVisitor {
14994
15489
  /**
14995
15490
  * Visits all nodes in a template (TmplAstNode and AST) and calls `visitNode` for each one.
14996
15491
  */
14997
- class TemplateVisitor extends checker.RecursiveAstVisitor {
15492
+ class TemplateVisitor extends checker.CombinedRecursiveAstVisitor {
14998
15493
  ctx;
14999
15494
  component;
15000
15495
  check;
@@ -15005,133 +15500,33 @@ class TemplateVisitor extends checker.RecursiveAstVisitor {
15005
15500
  this.component = component;
15006
15501
  this.check = check;
15007
15502
  }
15008
- visit(node, context) {
15503
+ visit(node) {
15009
15504
  this.diagnostics.push(...this.check.visitNode(this.ctx, this.component, node));
15010
- node.visit(this);
15011
- }
15012
- visitAllNodes(nodes) {
15013
- for (const node of nodes) {
15014
- this.visit(node);
15015
- }
15016
- }
15017
- visitAst(ast) {
15018
- if (ast instanceof checker.ASTWithSource) {
15019
- ast = ast.ast;
15020
- }
15021
- this.visit(ast);
15022
- }
15023
- visitElement(element) {
15024
- this.visitAllNodes(element.attributes);
15025
- this.visitAllNodes(element.inputs);
15026
- this.visitAllNodes(element.outputs);
15027
- this.visitAllNodes(element.directives);
15028
- this.visitAllNodes(element.references);
15029
- this.visitAllNodes(element.children);
15505
+ super.visit(node);
15030
15506
  }
15031
15507
  visitTemplate(template) {
15032
15508
  const isInlineTemplate = template.tagName === 'ng-template';
15033
- this.visitAllNodes(template.attributes);
15509
+ this.visitAllTemplateNodes(template.attributes);
15034
15510
  if (isInlineTemplate) {
15035
15511
  // Only visit input/outputs if this isn't an inline template node generated for a structural
15036
15512
  // directive (like `<div *ngIf></div>`). These nodes would be visited when the underlying
15037
15513
  // element of an inline template node is processed.
15038
- this.visitAllNodes(template.inputs);
15039
- this.visitAllNodes(template.outputs);
15514
+ this.visitAllTemplateNodes(template.inputs);
15515
+ this.visitAllTemplateNodes(template.outputs);
15040
15516
  }
15041
- this.visitAllNodes(template.directives);
15517
+ this.visitAllTemplateNodes(template.directives);
15042
15518
  // TODO(crisbeto): remove this condition when deleting `canVisitStructuralAttributes`.
15043
15519
  if (this.check.canVisitStructuralAttributes || isInlineTemplate) {
15044
15520
  // `templateAttrs` aren't transferred over to the inner element so we always have to visit them.
15045
- this.visitAllNodes(template.templateAttrs);
15046
- }
15047
- this.visitAllNodes(template.variables);
15048
- this.visitAllNodes(template.references);
15049
- this.visitAllNodes(template.children);
15050
- }
15051
- visitContent(content) {
15052
- this.visitAllNodes(content.children);
15053
- }
15054
- visitVariable(variable) { }
15055
- visitReference(reference) { }
15056
- visitTextAttribute(attribute) { }
15057
- visitUnknownBlock(block) { }
15058
- visitBoundAttribute(attribute) {
15059
- this.visitAst(attribute.value);
15060
- }
15061
- visitBoundEvent(attribute) {
15062
- this.visitAst(attribute.handler);
15063
- }
15064
- visitText(text) { }
15065
- visitBoundText(text) {
15066
- this.visitAst(text.value);
15067
- }
15068
- visitIcu(icu) {
15069
- Object.keys(icu.vars).forEach((key) => this.visit(icu.vars[key]));
15070
- Object.keys(icu.placeholders).forEach((key) => this.visit(icu.placeholders[key]));
15071
- }
15072
- visitDeferredBlock(deferred) {
15073
- deferred.visitAll(this);
15074
- }
15075
- visitDeferredTrigger(trigger) {
15076
- if (trigger instanceof checker.BoundDeferredTrigger) {
15077
- this.visitAst(trigger.value);
15521
+ this.visitAllTemplateNodes(template.templateAttrs);
15078
15522
  }
15079
- }
15080
- visitDeferredBlockPlaceholder(block) {
15081
- this.visitAllNodes(block.children);
15082
- }
15083
- visitDeferredBlockError(block) {
15084
- this.visitAllNodes(block.children);
15085
- }
15086
- visitDeferredBlockLoading(block) {
15087
- this.visitAllNodes(block.children);
15088
- }
15089
- visitSwitchBlock(block) {
15090
- this.visitAst(block.expression);
15091
- this.visitAllNodes(block.cases);
15092
- }
15093
- visitSwitchBlockCase(block) {
15094
- block.expression && this.visitAst(block.expression);
15095
- this.visitAllNodes(block.children);
15096
- }
15097
- visitForLoopBlock(block) {
15098
- block.item.visit(this);
15099
- this.visitAllNodes(block.contextVariables);
15100
- this.visitAst(block.expression);
15101
- this.visitAllNodes(block.children);
15102
- block.empty?.visit(this);
15103
- }
15104
- visitForLoopBlockEmpty(block) {
15105
- this.visitAllNodes(block.children);
15106
- }
15107
- visitIfBlock(block) {
15108
- this.visitAllNodes(block.branches);
15109
- }
15110
- visitIfBlockBranch(block) {
15111
- block.expression && this.visitAst(block.expression);
15112
- block.expressionAlias?.visit(this);
15113
- this.visitAllNodes(block.children);
15114
- }
15115
- visitLetDeclaration(decl) {
15116
- this.visitAst(decl.value);
15117
- }
15118
- visitComponent(component) {
15119
- this.visitAllNodes(component.attributes);
15120
- this.visitAllNodes(component.inputs);
15121
- this.visitAllNodes(component.outputs);
15122
- this.visitAllNodes(component.directives);
15123
- this.visitAllNodes(component.references);
15124
- this.visitAllNodes(component.children);
15125
- }
15126
- visitDirective(directive) {
15127
- this.visitAllNodes(directive.attributes);
15128
- this.visitAllNodes(directive.inputs);
15129
- this.visitAllNodes(directive.outputs);
15130
- this.visitAllNodes(directive.references);
15523
+ this.visitAllTemplateNodes(template.variables);
15524
+ this.visitAllTemplateNodes(template.references);
15525
+ this.visitAllTemplateNodes(template.children);
15131
15526
  }
15132
15527
  getDiagnostics(template) {
15133
15528
  this.diagnostics = [];
15134
- this.visitAllNodes(template);
15529
+ this.visitAllTemplateNodes(template);
15135
15530
  return this.diagnostics;
15136
15531
  }
15137
15532
  }
@@ -15152,6 +15547,7 @@ class InterpolatedSignalCheck extends TemplateCheckWithVisitor {
15152
15547
  // interpolations like `{{ mySignal }}`
15153
15548
  if (node instanceof checker.Interpolation) {
15154
15549
  return node.expressions
15550
+ .map((item) => (item instanceof checker.PrefixNot ? item.expression : item))
15155
15551
  .filter((item) => item instanceof checker.PropertyRead)
15156
15552
  .flatMap((item) => buildDiagnosticForSignal(ctx, item, component));
15157
15553
  }
@@ -15164,6 +15560,7 @@ class InterpolatedSignalCheck extends TemplateCheckWithVisitor {
15164
15560
  return [];
15165
15561
  }
15166
15562
  // otherwise, we check if the node is
15563
+ const nodeAst = isPropertyReadNodeAst(node);
15167
15564
  if (
15168
15565
  // a bound property like `[prop]="mySignal"`
15169
15566
  (node.type === checker.BindingType.Property ||
@@ -15175,14 +15572,25 @@ class InterpolatedSignalCheck extends TemplateCheckWithVisitor {
15175
15572
  node.type === checker.BindingType.Attribute ||
15176
15573
  // or an animation binding like `[@myAnimation]="mySignal"`
15177
15574
  node.type === checker.BindingType.Animation) &&
15178
- node.value instanceof checker.ASTWithSource &&
15179
- node.value.ast instanceof checker.PropertyRead) {
15180
- return buildDiagnosticForSignal(ctx, node.value.ast, component);
15575
+ nodeAst) {
15576
+ return buildDiagnosticForSignal(ctx, nodeAst, component);
15181
15577
  }
15182
15578
  }
15183
15579
  return [];
15184
15580
  }
15185
15581
  }
15582
+ function isPropertyReadNodeAst(node) {
15583
+ if (node.value instanceof checker.ASTWithSource === false) {
15584
+ return undefined;
15585
+ }
15586
+ if (node.value.ast instanceof checker.PrefixNot && node.value.ast.expression instanceof checker.PropertyRead) {
15587
+ return node.value.ast.expression;
15588
+ }
15589
+ if (node.value.ast instanceof checker.PropertyRead) {
15590
+ return node.value.ast;
15591
+ }
15592
+ return undefined;
15593
+ }
15186
15594
  function isFunctionInstanceProperty(name) {
15187
15595
  return FUNCTION_INSTANCE_PROPERTIES.has(name);
15188
15596
  }
@@ -15932,9 +16340,13 @@ class ExpressionsSemanticsVisitor extends checker.RecursiveAstVisitor {
15932
16340
  this.component = component;
15933
16341
  this.diagnostics = diagnostics;
15934
16342
  }
15935
- visitPropertyWrite(ast, context) {
15936
- super.visitPropertyWrite(ast, context);
15937
- this.checkForIllegalWriteInEventBinding(ast, context);
16343
+ visitBinary(ast, context) {
16344
+ if (ast.operation === '=' && ast.left instanceof checker.PropertyRead) {
16345
+ this.checkForIllegalWriteInEventBinding(ast.left, context);
16346
+ }
16347
+ else {
16348
+ super.visitBinary(ast, context);
16349
+ }
15938
16350
  }
15939
16351
  visitPropertyRead(ast, context) {
15940
16352
  super.visitPropertyRead(ast, context);
@@ -16146,6 +16558,7 @@ class UnusedStandaloneImportsRule {
16146
16558
  const pipeMeta = this.templateTypeChecker.getPipeMetadata(currentNode);
16147
16559
  if (pipeMeta !== null &&
16148
16560
  pipeMeta.isStandalone &&
16561
+ pipeMeta.name !== null &&
16149
16562
  !usedPipes.has(pipeMeta.name) &&
16150
16563
  !this.isPotentialSharedReference(current, rawImports)) {
16151
16564
  unused ??= [];
@@ -18974,7 +19387,7 @@ var semver = /*@__PURE__*/getDefaultExportFromCjs(semverExports);
18974
19387
  * @param minVersion Minimum required version for the feature.
18975
19388
  */
18976
19389
  function coreVersionSupportsFeature(coreVersion, minVersion) {
18977
- // A version of `20.0.6` usually means that core is at head so it supports
19390
+ // A version of `20.1.0-next.1` usually means that core is at head so it supports
18978
19391
  // all features. Use string interpolation prevent the placeholder from being replaced
18979
19392
  // with the current version during build time.
18980
19393
  if (coreVersion === `0.0.0-${'PLACEHOLDER'}`) {
@@ -19090,6 +19503,7 @@ class NgCompiler {
19090
19503
  enableHmr;
19091
19504
  implicitStandaloneValue;
19092
19505
  enableSelectorless;
19506
+ emitDeclarationOnly;
19093
19507
  /**
19094
19508
  * `NgCompiler` can be reused for multiple compilations (for resource-only changes), and each
19095
19509
  * new compilation uses a fresh `PerfRecorder`. Thus, classes created with a lifespan of the
@@ -19135,6 +19549,8 @@ class NgCompiler {
19135
19549
  this.enableBlockSyntax = options['_enableBlockSyntax'] ?? true;
19136
19550
  this.enableLetSyntax = options['_enableLetSyntax'] ?? true;
19137
19551
  this.enableSelectorless = options['_enableSelectorless'] ?? false;
19552
+ this.emitDeclarationOnly =
19553
+ !!options.emitDeclarationOnly && !!options._experimentalAllowEmitDeclarationOnly;
19138
19554
  // Standalone by default is enabled since v19. We need to toggle it here,
19139
19555
  // because the language service extension may be running with the latest
19140
19556
  // version of the compiler against an older version of Angular.
@@ -19421,7 +19837,7 @@ class NgCompiler {
19421
19837
  }
19422
19838
  const defaultImportTracker = new checker.DefaultImportTracker();
19423
19839
  const before = [
19424
- ivyTransformFactory(compilation.traitCompiler, compilation.reflector, importRewriter, defaultImportTracker, compilation.localCompilationExtraImportsTracker, this.delegatingPerfRecorder, compilation.isCore, this.closureCompilerEnabled),
19840
+ ivyTransformFactory(compilation.traitCompiler, compilation.reflector, importRewriter, defaultImportTracker, compilation.localCompilationExtraImportsTracker, this.delegatingPerfRecorder, compilation.isCore, this.closureCompilerEnabled, this.emitDeclarationOnly),
19425
19841
  aliasTransformFactory(compilation.traitCompiler.exportStatements),
19426
19842
  defaultImportTracker.importPreservingTransformer(),
19427
19843
  ];
@@ -19447,11 +19863,19 @@ class NgCompiler {
19447
19863
  };
19448
19864
  });
19449
19865
  }
19866
+ // Typescript transformer to add debugName metadata to signal functions.
19867
+ before.push(signalMetadataTransform(this.inputProgram));
19450
19868
  const afterDeclarations = [];
19451
19869
  // In local compilation mode we don't make use of .d.ts files for Angular compilation, so their
19452
19870
  // transformation can be ditched.
19453
- if (this.options.compilationMode !== 'experimental-local' &&
19871
+ if ((this.options.compilationMode !== 'experimental-local' || this.emitDeclarationOnly) &&
19454
19872
  compilation.dtsTransforms !== null) {
19873
+ // If we are emitting declarations only, the script transformations are skipped by the TS
19874
+ // compiler, so we have to add them to the afterDeclarations transforms to run their analysis
19875
+ // because the declaration transform depends on their metadata output.
19876
+ if (this.emitDeclarationOnly) {
19877
+ afterDeclarations.push(...before);
19878
+ }
19455
19879
  afterDeclarations.push(declarationTransformFactory(compilation.dtsTransforms, compilation.reflector, compilation.refEmitter, importRewriter));
19456
19880
  }
19457
19881
  // Only add aliasing re-exports to the .d.ts output if the `AliasingHost` requests it.
@@ -19789,6 +20213,9 @@ class NgCompiler {
19789
20213
  break;
19790
20214
  }
19791
20215
  }
20216
+ if (this.emitDeclarationOnly) {
20217
+ compilationMode = checker.CompilationMode.LOCAL;
20218
+ }
19792
20219
  const checker$1 = this.inputProgram.getTypeChecker();
19793
20220
  const reflector = new checker.TypeScriptReflectionHost(checker$1, compilationMode === checker.CompilationMode.LOCAL);
19794
20221
  // Construct the ReferenceEmitter.
@@ -19856,8 +20283,10 @@ class NgCompiler {
19856
20283
  const ngModuleIndex = new NgModuleIndexImpl(metaReader, localMetaReader);
19857
20284
  const ngModuleScopeRegistry = new LocalModuleScopeRegistry(localMetaReader, metaReader, depScopeReader, refEmitter, aliasingHost);
19858
20285
  const standaloneScopeReader = new StandaloneComponentScopeReader(metaReader, ngModuleScopeRegistry, depScopeReader);
20286
+ const selectorlessScopeReader = new SelectorlessComponentScopeReader(metaReader, reflector);
19859
20287
  const scopeReader = new CompoundComponentScopeReader([
19860
20288
  ngModuleScopeRegistry,
20289
+ selectorlessScopeReader,
19861
20290
  standaloneScopeReader,
19862
20291
  ]);
19863
20292
  const semanticDepGraphUpdater = this.incrementalCompilation.semanticDepGraphUpdater;
@@ -19915,17 +20344,17 @@ class NgCompiler {
19915
20344
  const jitDeclarationRegistry = new JitDeclarationRegistry();
19916
20345
  // Set up the IvyCompilation, which manages state for the Ivy transformer.
19917
20346
  const handlers = [
19918
- new ComponentDecoratorHandler(reflector, evaluator, metaRegistry, metaReader, scopeReader, this.adapter, ngModuleScopeRegistry, typeCheckScopeRegistry, resourceRegistry, isCore, strictCtorDeps, this.resourceManager, this.adapter.rootDirs, this.options.preserveWhitespaces || false, this.options.i18nUseExternalIds !== false, this.options.enableI18nLegacyMessageIdFormat !== false, this.usePoisonedData, this.options.i18nNormalizeLineEndingsInICUs === true, this.moduleResolver, this.cycleAnalyzer, cycleHandlingStrategy, refEmitter, referencesRegistry, this.incrementalCompilation.depGraph, injectableRegistry, semanticDepGraphUpdater, this.closureCompilerEnabled, this.delegatingPerfRecorder, hostDirectivesResolver, importTracker, supportTestBed, compilationMode, deferredSymbolsTracker, !!this.options.forbidOrphanComponents, this.enableBlockSyntax, this.enableLetSyntax, externalRuntimeStyles, localCompilationExtraImportsTracker, jitDeclarationRegistry, this.options.i18nPreserveWhitespaceForLegacyExtraction ?? true, !!this.options.strictStandalone, this.enableHmr, this.implicitStandaloneValue, typeCheckHostBindings, this.enableSelectorless),
20347
+ new ComponentDecoratorHandler(reflector, evaluator, metaRegistry, metaReader, scopeReader, this.adapter, ngModuleScopeRegistry, typeCheckScopeRegistry, resourceRegistry, isCore, strictCtorDeps, this.resourceManager, this.adapter.rootDirs, this.options.preserveWhitespaces || false, this.options.i18nUseExternalIds !== false, this.options.enableI18nLegacyMessageIdFormat !== false, this.usePoisonedData, this.options.i18nNormalizeLineEndingsInICUs === true, this.moduleResolver, this.cycleAnalyzer, cycleHandlingStrategy, refEmitter, referencesRegistry, this.incrementalCompilation.depGraph, injectableRegistry, semanticDepGraphUpdater, this.closureCompilerEnabled, this.delegatingPerfRecorder, hostDirectivesResolver, importTracker, supportTestBed, compilationMode, deferredSymbolsTracker, !!this.options.forbidOrphanComponents, this.enableBlockSyntax, this.enableLetSyntax, externalRuntimeStyles, localCompilationExtraImportsTracker, jitDeclarationRegistry, this.options.i18nPreserveWhitespaceForLegacyExtraction ?? true, !!this.options.strictStandalone, this.enableHmr, this.implicitStandaloneValue, typeCheckHostBindings, this.enableSelectorless, this.emitDeclarationOnly),
19919
20348
  // TODO(alxhub): understand why the cast here is necessary (something to do with `null`
19920
20349
  // not being assignable to `unknown` when wrapped in `Readonly`).
19921
- new DirectiveDecoratorHandler(reflector, evaluator, metaRegistry, ngModuleScopeRegistry, metaReader, injectableRegistry, refEmitter, referencesRegistry, isCore, strictCtorDeps, semanticDepGraphUpdater, this.closureCompilerEnabled, this.delegatingPerfRecorder, importTracker, supportTestBed, typeCheckScopeRegistry, compilationMode, jitDeclarationRegistry, resourceRegistry, !!this.options.strictStandalone, this.implicitStandaloneValue, this.usePoisonedData, typeCheckHostBindings),
20350
+ new DirectiveDecoratorHandler(reflector, evaluator, metaRegistry, ngModuleScopeRegistry, metaReader, injectableRegistry, refEmitter, referencesRegistry, isCore, strictCtorDeps, semanticDepGraphUpdater, this.closureCompilerEnabled, this.delegatingPerfRecorder, importTracker, supportTestBed, typeCheckScopeRegistry, compilationMode, jitDeclarationRegistry, resourceRegistry, !!this.options.strictStandalone, this.implicitStandaloneValue, this.usePoisonedData, typeCheckHostBindings, this.emitDeclarationOnly),
19922
20351
  // Pipe handler must be before injectable handler in list so pipe factories are printed
19923
20352
  // before injectable factories (so injectable factories can delegate to them)
19924
20353
  new PipeDecoratorHandler(reflector, evaluator, metaRegistry, ngModuleScopeRegistry, injectableRegistry, isCore, this.delegatingPerfRecorder, supportTestBed, compilationMode, !!this.options.generateExtraImportsInLocalMode, !!this.options.strictStandalone, this.implicitStandaloneValue),
19925
20354
  new InjectableDecoratorHandler(reflector, evaluator, isCore, strictCtorDeps, injectableRegistry, this.delegatingPerfRecorder, supportTestBed, compilationMode),
19926
- new NgModuleDecoratorHandler(reflector, evaluator, metaReader, metaRegistry, ngModuleScopeRegistry, referencesRegistry, exportedProviderStatusResolver, semanticDepGraphUpdater, isCore, refEmitter, this.closureCompilerEnabled, this.options.onlyPublishPublicTypingsForNgModules ?? false, injectableRegistry, this.delegatingPerfRecorder, supportTestBed, supportJitMode, compilationMode, localCompilationExtraImportsTracker, jitDeclarationRegistry),
20355
+ new NgModuleDecoratorHandler(reflector, evaluator, metaReader, metaRegistry, ngModuleScopeRegistry, referencesRegistry, exportedProviderStatusResolver, semanticDepGraphUpdater, isCore, refEmitter, this.closureCompilerEnabled, this.options.onlyPublishPublicTypingsForNgModules ?? false, injectableRegistry, this.delegatingPerfRecorder, supportTestBed, supportJitMode, compilationMode, localCompilationExtraImportsTracker, jitDeclarationRegistry, this.emitDeclarationOnly),
19927
20356
  ];
19928
- const traitCompiler = new TraitCompiler(handlers, reflector, this.delegatingPerfRecorder, this.incrementalCompilation, this.options.compileNonExportedClasses !== false, compilationMode, dtsTransforms, semanticDepGraphUpdater, this.adapter);
20357
+ const traitCompiler = new TraitCompiler(handlers, reflector, this.delegatingPerfRecorder, this.incrementalCompilation, this.options.compileNonExportedClasses !== false, compilationMode, dtsTransforms, semanticDepGraphUpdater, this.adapter, this.emitDeclarationOnly);
19929
20358
  // Template type-checking may use the `ProgramDriver` to produce new `ts.Program`(s). If this
19930
20359
  // happens, they need to be tracked by the `NgCompiler`.
19931
20360
  const notifyingDriver = new NotifyingProgramDriverWrapper(this.programDriver, (program) => {