@angular/core 20.0.0-next.8 → 20.0.0-next.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/{api.d-KjtSQajV.d.ts → api.d-B58KU5QT.d.ts} +25 -40
  2. package/chrome_dev_tools_performance.d-qv7drdAl.d.ts +1 -1
  3. package/{discovery.d-vJaEafe4.d.ts → discovery.d-BCmtv8-u.d.ts} +19 -5
  4. package/event_dispatcher.d-DlbccpYq.d.ts +1 -1
  5. package/fesm2022/attribute-BWp59EjE.mjs +1 -1
  6. package/fesm2022/core.mjs +59 -158
  7. package/fesm2022/core.mjs.map +1 -1
  8. package/fesm2022/{debug_node-DEfPCixm.mjs → debug_node-sP8Ihhla.mjs} +126 -42
  9. package/fesm2022/debug_node-sP8Ihhla.mjs.map +1 -0
  10. package/fesm2022/primitives/di.mjs +1 -1
  11. package/fesm2022/primitives/event-dispatch.mjs +1 -1
  12. package/fesm2022/primitives/signals.mjs +4 -4
  13. package/fesm2022/{resource-DhKtse7l.mjs → resource-aJC_NNLX.mjs} +29 -76
  14. package/fesm2022/resource-aJC_NNLX.mjs.map +1 -0
  15. package/fesm2022/{root_effect_scheduler-BZMWiScf.mjs → root_effect_scheduler-C95A9imp.mjs} +10 -7
  16. package/fesm2022/root_effect_scheduler-C95A9imp.mjs.map +1 -0
  17. package/fesm2022/rxjs-interop.mjs +11 -6
  18. package/fesm2022/rxjs-interop.mjs.map +1 -1
  19. package/fesm2022/{signal-B6pMq7KS.mjs → signal-CVVPheSN.mjs} +13 -3
  20. package/fesm2022/{signal-B6pMq7KS.mjs.map → signal-CVVPheSN.mjs.map} +1 -1
  21. package/fesm2022/testing.mjs +34 -26
  22. package/fesm2022/testing.mjs.map +1 -1
  23. package/fesm2022/{untracked-Bz5WMeU1.mjs → untracked-BLZYODu2.mjs} +3 -3
  24. package/fesm2022/{untracked-Bz5WMeU1.mjs.map → untracked-BLZYODu2.mjs.map} +1 -1
  25. package/fesm2022/weak_ref-BaIq-pgY.mjs +1 -1
  26. package/graph.d-BcIOep_B.d.ts +1 -1
  27. package/index.d.ts +135 -114
  28. package/package.json +2 -2
  29. package/primitives/di/index.d.ts +1 -1
  30. package/primitives/event-dispatch/index.d.ts +1 -1
  31. package/primitives/signals/index.d.ts +3 -3
  32. package/rxjs-interop/index.d.ts +3 -3
  33. package/schematics/bundles/{apply_import_manager-CaG-_cEq.js → apply_import_manager-CxaTw_Wy.js} +3 -3
  34. package/schematics/bundles/{change_tracker-ISzWfEHN.js → change_tracker-CkPYZ_km.js} +3 -3
  35. package/schematics/bundles/{checker-DV96LHWz.js → checker-Crz1jSZM.js} +386 -122
  36. package/schematics/bundles/cleanup-unused-imports.js +6 -6
  37. package/schematics/bundles/{compiler-BEZ6sUQS.js → compiler-B4MK7BP9.js} +49 -54
  38. package/schematics/bundles/compiler_host-CAfDJO3W.js +1 -1
  39. package/schematics/bundles/control-flow-migration.js +2 -2
  40. package/schematics/bundles/document-core.js +6 -6
  41. package/schematics/bundles/imports-CIX-JgAN.js +1 -1
  42. package/schematics/bundles/{index-6wv04ZFQ.js → index-BbZ6cSR1.js} +6 -5
  43. package/schematics/bundles/{index-B1R5GL-k.js → index-Bk_3geTg.js} +421 -317
  44. package/schematics/bundles/inject-flags.js +6 -6
  45. package/schematics/bundles/inject-migration.js +4 -4
  46. package/schematics/bundles/leading_space-D9nQ8UQC.js +1 -1
  47. package/schematics/bundles/{migrate_ts_type_references-Dh9TZgTr.js → migrate_ts_type_references-D6T3FlkH.js} +6 -6
  48. package/schematics/bundles/ng_decorators-DznZ5jMl.js +1 -1
  49. package/schematics/bundles/nodes-B16H9JUd.js +1 -1
  50. package/schematics/bundles/output-migration.js +7 -7
  51. package/schematics/bundles/{project_paths-B-Nevd-p.js → project_paths-CQ4-VKlW.js} +4 -4
  52. package/schematics/bundles/project_tsconfig_paths-CDVxT6Ov.js +1 -1
  53. package/schematics/bundles/property_name-BBwFuqMe.js +1 -1
  54. package/schematics/bundles/route-lazy-loading.js +4 -4
  55. package/schematics/bundles/self-closing-tags-migration.js +5 -5
  56. package/schematics/bundles/signal-input-migration.js +8 -8
  57. package/schematics/bundles/signal-queries-migration.js +8 -8
  58. package/schematics/bundles/signals.js +8 -8
  59. package/schematics/bundles/standalone-migration.js +5 -5
  60. package/schematics/bundles/symbol-VPWguRxr.js +1 -1
  61. package/schematics/bundles/test-bed-get.js +5 -5
  62. package/{signal.d-E0e5nW1p.d.ts → signal.d-D6VJ67xi.d.ts} +8 -2
  63. package/testing/index.d.ts +9 -12
  64. package/weak_ref.d-eGOEP9S1.d.ts +1 -1
  65. package/fesm2022/debug_node-DEfPCixm.mjs.map +0 -1
  66. package/fesm2022/resource-DhKtse7l.mjs.map +0 -1
  67. package/fesm2022/root_effect_scheduler-BZMWiScf.mjs.map +0 -1
@@ -1,12 +1,12 @@
1
1
  'use strict';
2
2
  /**
3
- * @license Angular v20.0.0-next.8
3
+ * @license Angular v20.0.0-next.9
4
4
  * (c) 2010-2025 Google LLC. https://angular.io/
5
5
  * License: MIT
6
6
  */
7
7
  'use strict';
8
8
 
9
- var compiler = require('./compiler-BEZ6sUQS.js');
9
+ var compiler = require('./compiler-B4MK7BP9.js');
10
10
  var ts = require('typescript');
11
11
  require('os');
12
12
  var fs$1 = require('fs');
@@ -162,6 +162,15 @@ exports.ErrorCode = void 0;
162
162
  * Raised when a `standalone: false` component is declared but `strictStandalone` is set.
163
163
  */
164
164
  ErrorCode[ErrorCode["NON_STANDALONE_NOT_ALLOWED"] = 2023] = "NON_STANDALONE_NOT_ALLOWED";
165
+ /**
166
+ * Raised when a named template dependency isn't defined in the component's source file.
167
+ */
168
+ ErrorCode[ErrorCode["MISSING_NAMED_TEMPLATE_DEPENDENCY"] = 2024] = "MISSING_NAMED_TEMPLATE_DEPENDENCY";
169
+ /**
170
+ * Raised if an incorrect type is used for a named template dependency (e.g. directive
171
+ * class used as a component).
172
+ */
173
+ ErrorCode[ErrorCode["INCORRECT_NAMED_TEMPLATE_DEPENDENCY_TYPE"] = 2025] = "INCORRECT_NAMED_TEMPLATE_DEPENDENCY_TYPE";
165
174
  ErrorCode[ErrorCode["SYMBOL_NOT_EXPORTED"] = 3001] = "SYMBOL_NOT_EXPORTED";
166
175
  /**
167
176
  * Raised when a relationship between directives and/or pipes would cause a cyclic import to be
@@ -341,6 +350,11 @@ exports.ErrorCode = void 0;
341
350
  ErrorCode[ErrorCode["LET_USED_BEFORE_DEFINITION"] = 8016] = "LET_USED_BEFORE_DEFINITION";
342
351
  /** A `@let` declaration conflicts with another symbol in the same scope. */
343
352
  ErrorCode[ErrorCode["CONFLICTING_LET_DECLARATION"] = 8017] = "CONFLICTING_LET_DECLARATION";
353
+ /**
354
+ * A binding inside selectorless directive syntax did
355
+ * not match any inputs/outputs of the directive.
356
+ */
357
+ ErrorCode[ErrorCode["UNCLAIMED_DIRECTIVE_BINDING"] = 8018] = "UNCLAIMED_DIRECTIVE_BINDING";
344
358
  /**
345
359
  * A two way binding in a template has an incorrect syntax,
346
360
  * parentheses outside brackets. For example:
@@ -495,6 +509,10 @@ exports.ErrorCode = void 0;
495
509
  * ```
496
510
  */
497
511
  ErrorCode[ErrorCode["UNINVOKED_TRACK_FUNCTION"] = 8115] = "UNINVOKED_TRACK_FUNCTION";
512
+ /**
513
+ * A structural directive is used in a template, but the directive is not imported.
514
+ */
515
+ ErrorCode[ErrorCode["MISSING_STRUCTURAL_DIRECTIVE"] = 8116] = "MISSING_STRUCTURAL_DIRECTIVE";
498
516
  /**
499
517
  * The template type-checking engine would need to generate an inline type check block for a
500
518
  * component, but the current type-checking environment doesn't support it.
@@ -644,6 +662,7 @@ exports.ExtendedTemplateDiagnosticName = void 0;
644
662
  ExtendedTemplateDiagnosticName["NULLISH_COALESCING_NOT_NULLABLE"] = "nullishCoalescingNotNullable";
645
663
  ExtendedTemplateDiagnosticName["OPTIONAL_CHAIN_NOT_NULLABLE"] = "optionalChainNotNullable";
646
664
  ExtendedTemplateDiagnosticName["MISSING_CONTROL_FLOW_DIRECTIVE"] = "missingControlFlowDirective";
665
+ ExtendedTemplateDiagnosticName["MISSING_STRUCTURAL_DIRECTIVE"] = "missingStructuralDirective";
647
666
  ExtendedTemplateDiagnosticName["TEXT_ATTRIBUTE_NOT_BINDING"] = "textAttributeNotBinding";
648
667
  ExtendedTemplateDiagnosticName["UNINVOKED_FUNCTION_IN_EVENT_BINDING"] = "uninvokedFunctionInEventBinding";
649
668
  ExtendedTemplateDiagnosticName["MISSING_NGFOROF_LET"] = "missingNgForOfLet";
@@ -981,7 +1000,7 @@ class NodeJSPathManipulation {
981
1000
  // G3-ESM-MARKER: G3 uses CommonJS, but externally everything in ESM.
982
1001
  // CommonJS/ESM interop for determining the current file name and containing dir.
983
1002
  const isCommonJS = typeof __filename !== 'undefined';
984
- const currentFileUrl = isCommonJS ? null : (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('checker-DV96LHWz.js', document.baseURI).href));
1003
+ const currentFileUrl = isCommonJS ? null : (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('checker-Crz1jSZM.js', document.baseURI).href));
985
1004
  const currentFileName = isCommonJS ? __filename : url.fileURLToPath(currentFileUrl);
986
1005
  /**
987
1006
  * A wrapper around the Node.js file-system that supports readonly operations and path manipulation.
@@ -10789,11 +10808,11 @@ class RegistryDomSchemaChecker {
10789
10808
  constructor(resolver) {
10790
10809
  this.resolver = resolver;
10791
10810
  }
10792
- checkElement(id, element, schemas, hostIsStandalone) {
10811
+ checkElement(id, tagName, sourceSpanForDiagnostics, schemas, hostIsStandalone) {
10793
10812
  // HTML elements inside an SVG `foreignObject` are declared in the `xhtml` namespace.
10794
10813
  // We need to strip it before handing it over to the registry because all HTML tag names
10795
10814
  // in the registry are without a namespace.
10796
- const name = element.name.replace(REMOVE_XHTML_REGEX, '');
10815
+ const name = tagName.replace(REMOVE_XHTML_REGEX, '');
10797
10816
  if (!REGISTRY$1.hasElement(name, schemas)) {
10798
10817
  const mapping = this.resolver.getTemplateSourceMapping(id);
10799
10818
  const schemas = `'${hostIsStandalone ? '@Component' : '@NgModule'}.schemas'`;
@@ -10807,27 +10826,27 @@ class RegistryDomSchemaChecker {
10807
10826
  else {
10808
10827
  errorMsg += `2. To allow any element add 'NO_ERRORS_SCHEMA' to the ${schemas} of this component.`;
10809
10828
  }
10810
- const diag = makeTemplateDiagnostic(id, mapping, element.startSourceSpan, ts.DiagnosticCategory.Error, ngErrorCode(exports.ErrorCode.SCHEMA_INVALID_ELEMENT), errorMsg);
10829
+ const diag = makeTemplateDiagnostic(id, mapping, sourceSpanForDiagnostics, ts.DiagnosticCategory.Error, ngErrorCode(exports.ErrorCode.SCHEMA_INVALID_ELEMENT), errorMsg);
10811
10830
  this._diagnostics.push(diag);
10812
10831
  }
10813
10832
  }
10814
- checkTemplateElementProperty(id, element, name, span, schemas, hostIsStandalone) {
10815
- if (!REGISTRY$1.hasProperty(element.name, name, schemas)) {
10833
+ checkTemplateElementProperty(id, tagName, name, span, schemas, hostIsStandalone) {
10834
+ if (!REGISTRY$1.hasProperty(tagName, name, schemas)) {
10816
10835
  const mapping = this.resolver.getTemplateSourceMapping(id);
10817
10836
  const decorator = hostIsStandalone ? '@Component' : '@NgModule';
10818
10837
  const schemas = `'${decorator}.schemas'`;
10819
- let errorMsg = `Can't bind to '${name}' since it isn't a known property of '${element.name}'.`;
10820
- if (element.name.startsWith('ng-')) {
10838
+ let errorMsg = `Can't bind to '${name}' since it isn't a known property of '${tagName}'.`;
10839
+ if (tagName.startsWith('ng-')) {
10821
10840
  errorMsg +=
10822
10841
  `\n1. If '${name}' is an Angular directive, then add 'CommonModule' to the '${decorator}.imports' of this component.` +
10823
10842
  `\n2. To allow any property add 'NO_ERRORS_SCHEMA' to the ${schemas} of this component.`;
10824
10843
  }
10825
- else if (element.name.indexOf('-') > -1) {
10844
+ else if (tagName.indexOf('-') > -1) {
10826
10845
  errorMsg +=
10827
- `\n1. If '${element.name}' is an Angular component and it has '${name}' input, then verify that it is ${hostIsStandalone
10846
+ `\n1. If '${tagName}' is an Angular component and it has '${name}' input, then verify that it is ${hostIsStandalone
10828
10847
  ? "included in the '@Component.imports' of this component"
10829
10848
  : 'part of this module'}.` +
10830
- `\n2. If '${element.name}' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the ${schemas} of this component to suppress this message.` +
10849
+ `\n2. If '${tagName}' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the ${schemas} of this component to suppress this message.` +
10831
10850
  `\n3. To allow any property add 'NO_ERRORS_SCHEMA' to the ${schemas} of this component.`;
10832
10851
  }
10833
10852
  const diag = makeTemplateDiagnostic(id, mapping, span, ts.DiagnosticCategory.Error, ngErrorCode(exports.ErrorCode.SCHEMA_INVALID_ATTRIBUTE), errorMsg);
@@ -11615,12 +11634,16 @@ function isInHostBindingTcb(node) {
11615
11634
  return false;
11616
11635
  }
11617
11636
  function findTypeCheckBlock(file, id, isDiagnosticRequest) {
11637
+ // This prioritised-level statements using a breadth-first search
11638
+ // This is usually sufficient to find the TCB we're looking for
11618
11639
  for (const stmt of file.statements) {
11619
11640
  if (ts.isFunctionDeclaration(stmt) && getTypeCheckId(stmt, file, isDiagnosticRequest) === id) {
11620
11641
  return stmt;
11621
11642
  }
11622
11643
  }
11623
- return null;
11644
+ // In case the TCB we're looking for is nested (which is not common)
11645
+ // eg: when a directive is declared inside a function, as it can happen in test files
11646
+ return findNodeInFile(file, (node) => ts.isFunctionDeclaration(node) && getTypeCheckId(node, file, isDiagnosticRequest) === id);
11624
11647
  }
11625
11648
  /**
11626
11649
  * Traverses up the AST starting from the given node to extract the source location from comments
@@ -11690,6 +11713,15 @@ function checkIfGenericTypeBoundsCanBeEmitted(node, reflector, env) {
11690
11713
  const emitter = new TypeParameterEmitter(node.typeParameters, reflector);
11691
11714
  return emitter.canEmit((ref) => env.canReferenceType(ref));
11692
11715
  }
11716
+ function findNodeInFile(file, predicate) {
11717
+ const visit = (node) => {
11718
+ if (predicate(node)) {
11719
+ return node;
11720
+ }
11721
+ return ts.forEachChild(node, visit) ?? null;
11722
+ };
11723
+ return ts.forEachChild(file, visit) ?? null;
11724
+ }
11693
11725
 
11694
11726
  function generateTypeCtorDeclarationFn(env, meta, nodeTypeRef, typeParams) {
11695
11727
  const rawTypeArgs = typeParams !== undefined ? generateGenericArgs(typeParams) : undefined;
@@ -12246,6 +12278,20 @@ class OutOfBandDiagnosticRecorderImpl {
12246
12278
  const errorMsg = `Cannot declare @let called '${decl.name}' as there is another symbol in the template with the same name.`;
12247
12279
  this._diagnostics.push(makeTemplateDiagnostic(id, mapping, decl.sourceSpan, ts.DiagnosticCategory.Error, ngErrorCode(exports.ErrorCode.CONFLICTING_LET_DECLARATION), errorMsg));
12248
12280
  }
12281
+ missingNamedTemplateDependency(id, node) {
12282
+ this._diagnostics.push(makeTemplateDiagnostic(id, this.resolver.getTemplateSourceMapping(id), node.startSourceSpan, ts.DiagnosticCategory.Error, ngErrorCode(exports.ErrorCode.MISSING_NAMED_TEMPLATE_DEPENDENCY),
12283
+ // Wording is meant to mimic the wording TS uses in their diagnostic for missing symbols.
12284
+ `Cannot find name "${node instanceof compiler.Directive ? node.name : node.componentName}".`));
12285
+ }
12286
+ incorrectTemplateDependencyType(id, node) {
12287
+ this._diagnostics.push(makeTemplateDiagnostic(id, this.resolver.getTemplateSourceMapping(id), node.startSourceSpan, ts.DiagnosticCategory.Error, ngErrorCode(exports.ErrorCode.INCORRECT_NAMED_TEMPLATE_DEPENDENCY_TYPE), `Incorrect reference type. Type must be an ${node instanceof compiler.Component ? '@Component' : '@Directive'}.`));
12288
+ }
12289
+ unclaimedDirectiveBinding(id, directive, node) {
12290
+ const errorMsg = `Directive ${directive.name} does not have an ` +
12291
+ `${node instanceof compiler.BoundEvent ? 'output' : 'input'} named "${node.name}". ` +
12292
+ `Bindings to directives must target existing inputs or outputs.`;
12293
+ this._diagnostics.push(makeTemplateDiagnostic(id, this.resolver.getTemplateSourceMapping(id), node.keySpan || node.sourceSpan, ts.DiagnosticCategory.Error, ngErrorCode(exports.ErrorCode.UNCLAIMED_DIRECTIVE_BINDING), errorMsg));
12294
+ }
12249
12295
  }
12250
12296
  function makeInlineDiagnostic(id, code, node, messageText, relatedInformation) {
12251
12297
  return {
@@ -13156,62 +13202,13 @@ class TcbTemplateBodyOp extends TcbOp {
13156
13202
  // on the template can trigger extra guard expressions that serve to narrow types within the
13157
13203
  // `if`. `guard` is calculated by starting with `true` and adding other conditions as needed.
13158
13204
  // Collect these into `guards` by processing the directives.
13159
- const directiveGuards = [];
13160
- const directives = this.tcb.boundTarget.getDirectivesOfNode(this.template);
13161
- if (directives !== null) {
13162
- for (const dir of directives) {
13163
- const dirInstId = this.scope.resolve(this.template, dir);
13164
- const dirId = this.tcb.env.reference(dir.ref);
13165
- // There are two kinds of guards. Template guards (ngTemplateGuards) allow type narrowing of
13166
- // the expression passed to an @Input of the directive. Scan the directive to see if it has
13167
- // any template guards, and generate them if needed.
13168
- dir.ngTemplateGuards.forEach((guard) => {
13169
- // For each template guard function on the directive, look for a binding to that input.
13170
- const boundInput = this.template.inputs.find((i) => i.name === guard.inputName) ||
13171
- this.template.templateAttrs.find((i) => i instanceof compiler.BoundAttribute && i.name === guard.inputName);
13172
- if (boundInput !== undefined) {
13173
- // If there is such a binding, generate an expression for it.
13174
- const expr = tcbExpression(boundInput.value, this.tcb, this.scope);
13175
- // The expression has already been checked in the type constructor invocation, so
13176
- // it should be ignored when used within a template guard.
13177
- markIgnoreDiagnostics(expr);
13178
- if (guard.type === 'binding') {
13179
- // Use the binding expression itself as guard.
13180
- directiveGuards.push(expr);
13181
- }
13182
- else {
13183
- // Call the guard function on the directive with the directive instance and that
13184
- // expression.
13185
- const guardInvoke = tsCallMethod(dirId, `ngTemplateGuard_${guard.inputName}`, [
13186
- dirInstId,
13187
- expr,
13188
- ]);
13189
- addParseSpanInfo(guardInvoke, boundInput.value.sourceSpan);
13190
- directiveGuards.push(guardInvoke);
13191
- }
13192
- }
13193
- });
13194
- // The second kind of guard is a template context guard. This guard narrows the template
13195
- // rendering context variable `ctx`.
13196
- if (dir.hasNgTemplateContextGuard) {
13197
- if (this.tcb.env.config.applyTemplateContextGuards) {
13198
- const ctx = this.scope.resolve(this.template);
13199
- const guardInvoke = tsCallMethod(dirId, 'ngTemplateContextGuard', [dirInstId, ctx]);
13200
- addParseSpanInfo(guardInvoke, this.template.sourceSpan);
13201
- directiveGuards.push(guardInvoke);
13202
- }
13203
- else if (this.template.variables.length > 0 &&
13204
- this.tcb.env.config.suggestionsForSuboptimalTypeInference) {
13205
- // The compiler could have inferred a better type for the variables in this template,
13206
- // but was prevented from doing so by the type-checking configuration. Issue a warning
13207
- // diagnostic.
13208
- this.tcb.oobRecorder.suboptimalTypeInference(this.tcb.id, this.template.variables);
13209
- }
13210
- }
13211
- }
13212
- }
13213
13205
  // By default the guard is simply `true`.
13214
13206
  let guard = null;
13207
+ const directiveGuards = [];
13208
+ this.addDirectiveGuards(directiveGuards, this.template, this.tcb.boundTarget.getDirectivesOfNode(this.template));
13209
+ for (const directive of this.template.directives) {
13210
+ this.addDirectiveGuards(directiveGuards, directive, this.tcb.boundTarget.getDirectivesOfNode(directive));
13211
+ }
13215
13212
  // If there are any guards from directives, use them instead.
13216
13213
  if (directiveGuards.length > 0) {
13217
13214
  // Pop the first value and use it as the initializer to reduce(). This way, a single guard
@@ -13243,6 +13240,67 @@ class TcbTemplateBodyOp extends TcbOp {
13243
13240
  this.scope.addStatement(tmplBlock);
13244
13241
  return null;
13245
13242
  }
13243
+ addDirectiveGuards(guards, hostNode, directives) {
13244
+ if (directives === null || directives.length === 0) {
13245
+ return;
13246
+ }
13247
+ const isTemplate = hostNode instanceof compiler.Template;
13248
+ for (const dir of directives) {
13249
+ const dirInstId = this.scope.resolve(hostNode, dir);
13250
+ const dirId = this.tcb.env.reference(dir.ref);
13251
+ // There are two kinds of guards. Template guards (ngTemplateGuards) allow type narrowing of
13252
+ // the expression passed to an @Input of the directive. Scan the directive to see if it has
13253
+ // any template guards, and generate them if needed.
13254
+ dir.ngTemplateGuards.forEach((guard) => {
13255
+ // For each template guard function on the directive, look for a binding to that input.
13256
+ const boundInput = hostNode.inputs.find((i) => i.name === guard.inputName) ||
13257
+ (isTemplate
13258
+ ? hostNode.templateAttrs.find((input) => {
13259
+ return input instanceof compiler.BoundAttribute && input.name === guard.inputName;
13260
+ })
13261
+ : undefined);
13262
+ if (boundInput !== undefined) {
13263
+ // If there is such a binding, generate an expression for it.
13264
+ const expr = tcbExpression(boundInput.value, this.tcb, this.scope);
13265
+ // The expression has already been checked in the type constructor invocation, so
13266
+ // it should be ignored when used within a template guard.
13267
+ markIgnoreDiagnostics(expr);
13268
+ if (guard.type === 'binding') {
13269
+ // Use the binding expression itself as guard.
13270
+ guards.push(expr);
13271
+ }
13272
+ else {
13273
+ // Call the guard function on the directive with the directive instance and that
13274
+ // expression.
13275
+ const guardInvoke = tsCallMethod(dirId, `ngTemplateGuard_${guard.inputName}`, [
13276
+ dirInstId,
13277
+ expr,
13278
+ ]);
13279
+ addParseSpanInfo(guardInvoke, boundInput.value.sourceSpan);
13280
+ guards.push(guardInvoke);
13281
+ }
13282
+ }
13283
+ });
13284
+ // The second kind of guard is a template context guard. This guard narrows the template
13285
+ // rendering context variable `ctx`.
13286
+ if (dir.hasNgTemplateContextGuard) {
13287
+ if (this.tcb.env.config.applyTemplateContextGuards) {
13288
+ const ctx = this.scope.resolve(hostNode);
13289
+ const guardInvoke = tsCallMethod(dirId, 'ngTemplateContextGuard', [dirInstId, ctx]);
13290
+ addParseSpanInfo(guardInvoke, hostNode.sourceSpan);
13291
+ guards.push(guardInvoke);
13292
+ }
13293
+ else if (isTemplate &&
13294
+ hostNode.variables.length > 0 &&
13295
+ this.tcb.env.config.suggestionsForSuboptimalTypeInference) {
13296
+ // The compiler could have inferred a better type for the variables in this template,
13297
+ // but was prevented from doing so by the type-checking configuration. Issue a warning
13298
+ // diagnostic.
13299
+ this.tcb.oobRecorder.suboptimalTypeInference(this.tcb.id, hostNode.variables);
13300
+ }
13301
+ }
13302
+ }
13303
+ }
13246
13304
  }
13247
13305
  /**
13248
13306
  * A `TcbOp` which renders an Angular expression (e.g. `{{foo() && bar.baz}}`).
@@ -13507,7 +13565,7 @@ class TcbDirectiveCtorOp extends TcbOp {
13507
13565
  return id;
13508
13566
  }
13509
13567
  circularFallback() {
13510
- return new TcbDirectiveCtorCircularFallbackOp(this.tcb, this.scope, this.node, this.dir);
13568
+ return new TcbDirectiveCtorCircularFallbackOp(this.tcb, this.scope, this.dir);
13511
13569
  }
13512
13570
  }
13513
13571
  /**
@@ -13669,13 +13727,11 @@ class TcbDirectiveInputsOp extends TcbOp {
13669
13727
  class TcbDirectiveCtorCircularFallbackOp extends TcbOp {
13670
13728
  tcb;
13671
13729
  scope;
13672
- node;
13673
13730
  dir;
13674
- constructor(tcb, scope, node, dir) {
13731
+ constructor(tcb, scope, dir) {
13675
13732
  super();
13676
13733
  this.tcb = tcb;
13677
13734
  this.scope = scope;
13678
- this.node = node;
13679
13735
  this.dir = dir;
13680
13736
  }
13681
13737
  get optional() {
@@ -13717,10 +13773,10 @@ class TcbDomSchemaCheckerOp extends TcbOp {
13717
13773
  }
13718
13774
  execute() {
13719
13775
  const element = this.element;
13720
- const isTemplateElement = element instanceof compiler.Element;
13776
+ const isTemplateElement = element instanceof compiler.Element || element instanceof compiler.Component;
13721
13777
  const bindings = isTemplateElement ? element.inputs : element.bindings;
13722
13778
  if (this.checkElement && isTemplateElement) {
13723
- this.tcb.domSchemaChecker.checkElement(this.tcb.id, element, this.tcb.schemas, this.tcb.hostIsStandalone);
13779
+ this.tcb.domSchemaChecker.checkElement(this.tcb.id, this.getTagName(element), element.startSourceSpan, this.tcb.schemas, this.tcb.hostIsStandalone);
13724
13780
  }
13725
13781
  // TODO(alxhub): this could be more efficient.
13726
13782
  for (const binding of bindings) {
@@ -13733,7 +13789,7 @@ class TcbDomSchemaCheckerOp extends TcbOp {
13733
13789
  // A direct binding to a property.
13734
13790
  const propertyName = ATTR_TO_PROP.get(binding.name) ?? binding.name;
13735
13791
  if (isTemplateElement) {
13736
- this.tcb.domSchemaChecker.checkTemplateElementProperty(this.tcb.id, element, propertyName, binding.sourceSpan, this.tcb.schemas, this.tcb.hostIsStandalone);
13792
+ this.tcb.domSchemaChecker.checkTemplateElementProperty(this.tcb.id, this.getTagName(element), propertyName, binding.sourceSpan, this.tcb.schemas, this.tcb.hostIsStandalone);
13737
13793
  }
13738
13794
  else {
13739
13795
  this.tcb.domSchemaChecker.checkHostElementProperty(this.tcb.id, element, propertyName, binding.keySpan, this.tcb.schemas);
@@ -13742,6 +13798,9 @@ class TcbDomSchemaCheckerOp extends TcbOp {
13742
13798
  }
13743
13799
  return null;
13744
13800
  }
13801
+ getTagName(node) {
13802
+ return node instanceof compiler.Element ? node.name : getComponentTagName(node);
13803
+ }
13745
13804
  }
13746
13805
  /**
13747
13806
  * A `TcbOp` that finds and flags control flow nodes that interfere with content projection.
@@ -13876,6 +13935,30 @@ class TcbHostElementOp extends TcbOp {
13876
13935
  return id;
13877
13936
  }
13878
13937
  }
13938
+ /**
13939
+ * A `TcbOp` which creates an expression for a native DOM element from a `TmplAstComponent`.
13940
+ *
13941
+ * Executing this operation returns a reference to the element variable.
13942
+ */
13943
+ class TcbComponentNodeOp extends TcbOp {
13944
+ tcb;
13945
+ scope;
13946
+ component;
13947
+ optional = true;
13948
+ constructor(tcb, scope, component) {
13949
+ super();
13950
+ this.tcb = tcb;
13951
+ this.scope = scope;
13952
+ this.component = component;
13953
+ }
13954
+ execute() {
13955
+ const id = this.tcb.allocateId();
13956
+ const initializer = tsCreateElement(getComponentTagName(this.component));
13957
+ addParseSpanInfo(initializer, this.component.startSourceSpan || this.component.sourceSpan);
13958
+ this.scope.addStatement(tsCreateVariable(id, initializer));
13959
+ return id;
13960
+ }
13961
+ }
13879
13962
  /**
13880
13963
  * Mapping between attributes names that don't correspond to their element property names.
13881
13964
  * Note: this mapping has to be kept in sync with the equally named mapping in the runtime.
@@ -14481,6 +14564,10 @@ class Scope {
14481
14564
  * A map of `TmplAstHostElement`s to the index of their `TcbHostElementOp` in the `opQueue`
14482
14565
  */
14483
14566
  hostElementOpMap = new Map();
14567
+ /**
14568
+ * A map of `TmplAstComponent`s to the index of their `TcbComponentNodeOp` in the `opQueue`
14569
+ */
14570
+ componentNodeOpMap = new Map();
14484
14571
  /**
14485
14572
  * A map of maps which tracks the index of `TcbDirectiveCtorOp`s in the `opQueue` for each
14486
14573
  * directive on a `TmplAstElement` or `TmplAstTemplate` node.
@@ -14733,22 +14820,23 @@ class Scope {
14733
14820
  // Execute the `TcbTemplateContextOp` for the template.
14734
14821
  return this.resolveOp(this.templateCtxOpMap.get(ref));
14735
14822
  }
14736
- else if ((ref instanceof compiler.Element || ref instanceof compiler.Template) &&
14823
+ else if ((ref instanceof compiler.Element ||
14824
+ ref instanceof compiler.Template ||
14825
+ ref instanceof compiler.Component ||
14826
+ ref instanceof compiler.Directive) &&
14737
14827
  directive !== undefined &&
14738
14828
  this.directiveOpMap.has(ref)) {
14739
14829
  // Resolving a directive on an element or sub-template.
14740
14830
  const dirMap = this.directiveOpMap.get(ref);
14741
- if (dirMap.has(directive)) {
14742
- return this.resolveOp(dirMap.get(directive));
14743
- }
14744
- else {
14745
- return null;
14746
- }
14831
+ return dirMap.has(directive) ? this.resolveOp(dirMap.get(directive)) : null;
14747
14832
  }
14748
14833
  else if (ref instanceof compiler.Element && this.elementOpMap.has(ref)) {
14749
14834
  // Resolving the DOM node of an element in this template.
14750
14835
  return this.resolveOp(this.elementOpMap.get(ref));
14751
14836
  }
14837
+ else if (ref instanceof compiler.Component && this.componentNodeOpMap.has(ref)) {
14838
+ return this.resolveOp(this.componentNodeOpMap.get(ref));
14839
+ }
14752
14840
  else if (ref instanceof compiler.HostElement && this.hostElementOpMap.has(ref)) {
14753
14841
  return this.resolveOp(this.hostElementOpMap.get(ref));
14754
14842
  }
@@ -14797,15 +14885,17 @@ class Scope {
14797
14885
  if (this.tcb.env.config.controlFlowPreventingContentProjection !== 'suppress') {
14798
14886
  this.appendContentProjectionCheckOp(node);
14799
14887
  }
14800
- this.appendDirectivesAndInputsOfNode(node);
14801
- this.appendOutputsOfNode(node);
14888
+ this.appendDirectivesAndInputsOfElementLikeNode(node);
14889
+ this.appendOutputsOfElementLikeNode(node);
14890
+ this.appendSelectorlessDirectives(node);
14802
14891
  this.appendChildren(node);
14803
14892
  this.checkAndAppendReferencesOfNode(node);
14804
14893
  }
14805
14894
  else if (node instanceof compiler.Template) {
14806
14895
  // Template children are rendered in a child scope.
14807
- this.appendDirectivesAndInputsOfNode(node);
14808
- this.appendOutputsOfNode(node);
14896
+ this.appendDirectivesAndInputsOfElementLikeNode(node);
14897
+ this.appendOutputsOfElementLikeNode(node);
14898
+ this.appendSelectorlessDirectives(node);
14809
14899
  const ctxIndex = this.opQueue.push(new TcbTemplateContextOp(this.tcb, this)) - 1;
14810
14900
  this.templateCtxOpMap.set(node, ctxIndex);
14811
14901
  if (this.tcb.env.config.checkTemplateBodies) {
@@ -14816,6 +14906,9 @@ class Scope {
14816
14906
  }
14817
14907
  this.checkAndAppendReferencesOfNode(node);
14818
14908
  }
14909
+ else if (node instanceof compiler.Component) {
14910
+ this.appendComponentNode(node);
14911
+ }
14819
14912
  else if (node instanceof compiler.DeferredBlock) {
14820
14913
  this.appendDeferredBlock(node);
14821
14914
  }
@@ -14878,9 +14971,11 @@ class Scope {
14878
14971
  this.referenceOpMap.set(ref, ctxIndex);
14879
14972
  }
14880
14973
  }
14881
- appendDirectivesAndInputsOfNode(node) {
14974
+ appendDirectivesAndInputsOfElementLikeNode(node) {
14882
14975
  // Collect all the inputs on the element.
14883
14976
  const claimedInputs = new Set();
14977
+ // Don't resolve directives when selectorless is enabled and treat all the inputs on the element
14978
+ // as unclaimed. In selectorless the inputs are defined either in component or directive nodes.
14884
14979
  const directives = this.tcb.boundTarget.getDirectivesOfNode(node);
14885
14980
  if (directives === null || directives.length === 0) {
14886
14981
  // If there are no directives, then all inputs are unclaimed inputs, so queue an operation
@@ -14890,42 +14985,18 @@ class Scope {
14890
14985
  }
14891
14986
  return;
14892
14987
  }
14893
- else {
14894
- if (node instanceof compiler.Element) {
14895
- const isDeferred = this.tcb.boundTarget.isDeferred(node);
14896
- if (!isDeferred && directives.some((dirMeta) => dirMeta.isExplicitlyDeferred)) {
14897
- // This node has directives/components that were defer-loaded (included into
14898
- // `@Component.deferredImports`), but the node itself was used outside of a
14899
- // `@defer` block, which is the error.
14900
- this.tcb.oobRecorder.deferredComponentUsedEagerly(this.tcb.id, node);
14901
- }
14988
+ if (node instanceof compiler.Element) {
14989
+ const isDeferred = this.tcb.boundTarget.isDeferred(node);
14990
+ if (!isDeferred && directives.some((dirMeta) => dirMeta.isExplicitlyDeferred)) {
14991
+ // This node has directives/components that were defer-loaded (included into
14992
+ // `@Component.deferredImports`), but the node itself was used outside of a
14993
+ // `@defer` block, which is the error.
14994
+ this.tcb.oobRecorder.deferredComponentUsedEagerly(this.tcb.id, node);
14902
14995
  }
14903
14996
  }
14904
14997
  const dirMap = new Map();
14905
14998
  for (const dir of directives) {
14906
- let directiveOp;
14907
- const host = this.tcb.env.reflector;
14908
- const dirRef = dir.ref;
14909
- if (!dir.isGeneric) {
14910
- // The most common case is that when a directive is not generic, we use the normal
14911
- // `TcbNonDirectiveTypeOp`.
14912
- directiveOp = new TcbNonGenericDirectiveTypeOp(this.tcb, this, node, dir);
14913
- }
14914
- else if (!requiresInlineTypeCtor(dirRef.node, host, this.tcb.env) ||
14915
- this.tcb.env.config.useInlineTypeConstructors) {
14916
- // For generic directives, we use a type constructor to infer types. If a directive requires
14917
- // an inline type constructor, then inlining must be available to use the
14918
- // `TcbDirectiveCtorOp`. If not we, we fallback to using `any` – see below.
14919
- directiveOp = new TcbDirectiveCtorOp(this.tcb, this, node, dir);
14920
- }
14921
- else {
14922
- // If inlining is not available, then we give up on inferring the generic params, and use
14923
- // `any` type for the directive's generic parameters.
14924
- directiveOp = new TcbGenericDirectiveTypeWithAnyParamsOp(this.tcb, this, node, dir);
14925
- }
14926
- const dirIndex = this.opQueue.push(directiveOp) - 1;
14927
- dirMap.set(dir, dirIndex);
14928
- this.opQueue.push(new TcbDirectiveInputsOp(this.tcb, this, node, dir));
14999
+ this.appendDirectiveInputs(dir, node, dirMap);
14929
15000
  }
14930
15001
  this.directiveOpMap.set(node, dirMap);
14931
15002
  // After expanding the directives, we might need to queue an operation to check any unclaimed
@@ -14946,9 +15017,12 @@ class Scope {
14946
15017
  this.opQueue.push(new TcbDomSchemaCheckerOp(this.tcb, node, checkElement, claimedInputs));
14947
15018
  }
14948
15019
  }
14949
- appendOutputsOfNode(node) {
15020
+ appendOutputsOfElementLikeNode(node) {
14950
15021
  // Collect all the outputs on the element.
14951
15022
  const claimedOutputs = new Set();
15023
+ // Don't resolve directives when selectorless is enabled and treat all the outputs on the
15024
+ // element as unclaimed. In selectorless the outputs are defined either in component or
15025
+ // directive nodes.
14952
15026
  const directives = this.tcb.boundTarget.getDirectivesOfNode(node);
14953
15027
  if (directives === null || directives.length === 0) {
14954
15028
  // If there are no directives, then all outputs are unclaimed outputs, so queue an operation
@@ -14974,6 +15048,107 @@ class Scope {
14974
15048
  this.opQueue.push(new TcbUnclaimedOutputsOp(this.tcb, this, node, node.outputs, node.inputs, claimedOutputs));
14975
15049
  }
14976
15050
  }
15051
+ appendInputsOfSelectorlessNode(node) {
15052
+ // Only resolve the directives that were brought in by this specific directive.
15053
+ const directives = this.tcb.boundTarget.getDirectivesOfNode(node);
15054
+ const claimedInputs = new Set();
15055
+ if (directives !== null && directives.length > 0) {
15056
+ const dirMap = new Map();
15057
+ for (const dir of directives) {
15058
+ this.appendDirectiveInputs(dir, node, dirMap);
15059
+ for (const propertyName of dir.inputs.propertyNames) {
15060
+ claimedInputs.add(propertyName);
15061
+ }
15062
+ }
15063
+ this.directiveOpMap.set(node, dirMap);
15064
+ }
15065
+ // In selectorless all directive inputs have to be claimed.
15066
+ if (node instanceof compiler.Directive) {
15067
+ for (const input of node.inputs) {
15068
+ if (!claimedInputs.has(input.name)) {
15069
+ this.tcb.oobRecorder.unclaimedDirectiveBinding(this.tcb.id, node, input);
15070
+ }
15071
+ }
15072
+ for (const attr of node.attributes) {
15073
+ if (!claimedInputs.has(attr.name)) {
15074
+ this.tcb.oobRecorder.unclaimedDirectiveBinding(this.tcb.id, node, attr);
15075
+ }
15076
+ }
15077
+ }
15078
+ else {
15079
+ const checkElement = node.tagName !== null;
15080
+ this.opQueue.push(new TcbUnclaimedInputsOp(this.tcb, this, node.inputs, node, claimedInputs), new TcbDomSchemaCheckerOp(this.tcb, node, checkElement, claimedInputs));
15081
+ }
15082
+ }
15083
+ appendOutputsOfSelectorlessNode(node) {
15084
+ // Only resolve the directives that were brought in by this specific directive.
15085
+ const directives = this.tcb.boundTarget.getDirectivesOfNode(node);
15086
+ const claimedOutputs = new Set();
15087
+ if (directives !== null && directives.length > 0) {
15088
+ for (const dir of directives) {
15089
+ this.opQueue.push(new TcbDirectiveOutputsOp(this.tcb, this, node, dir));
15090
+ for (const outputProperty of dir.outputs.propertyNames) {
15091
+ claimedOutputs.add(outputProperty);
15092
+ }
15093
+ }
15094
+ }
15095
+ // In selectorless all directive outputs have to be claimed.
15096
+ if (node instanceof compiler.Directive) {
15097
+ for (const output of node.outputs) {
15098
+ if (!claimedOutputs.has(output.name)) {
15099
+ this.tcb.oobRecorder.unclaimedDirectiveBinding(this.tcb.id, node, output);
15100
+ }
15101
+ }
15102
+ }
15103
+ else {
15104
+ this.opQueue.push(new TcbUnclaimedOutputsOp(this.tcb, this, node, node.outputs, node.inputs, claimedOutputs));
15105
+ }
15106
+ }
15107
+ appendDirectiveInputs(dir, node, dirMap) {
15108
+ let directiveOp;
15109
+ const host = this.tcb.env.reflector;
15110
+ const dirRef = dir.ref;
15111
+ if (!dir.isGeneric) {
15112
+ // The most common case is that when a directive is not generic, we use the normal
15113
+ // `TcbNonDirectiveTypeOp`.
15114
+ directiveOp = new TcbNonGenericDirectiveTypeOp(this.tcb, this, node, dir);
15115
+ }
15116
+ else if (!requiresInlineTypeCtor(dirRef.node, host, this.tcb.env) ||
15117
+ this.tcb.env.config.useInlineTypeConstructors) {
15118
+ // For generic directives, we use a type constructor to infer types. If a directive requires
15119
+ // an inline type constructor, then inlining must be available to use the
15120
+ // `TcbDirectiveCtorOp`. If not we, we fallback to using `any` – see below.
15121
+ directiveOp = new TcbDirectiveCtorOp(this.tcb, this, node, dir);
15122
+ }
15123
+ else {
15124
+ // If inlining is not available, then we give up on inferring the generic params, and use
15125
+ // `any` type for the directive's generic parameters.
15126
+ directiveOp = new TcbGenericDirectiveTypeWithAnyParamsOp(this.tcb, this, node, dir);
15127
+ }
15128
+ const dirIndex = this.opQueue.push(directiveOp) - 1;
15129
+ dirMap.set(dir, dirIndex);
15130
+ this.opQueue.push(new TcbDirectiveInputsOp(this.tcb, this, node, dir));
15131
+ }
15132
+ appendSelectorlessDirectives(node) {
15133
+ for (const directive of node.directives) {
15134
+ // Check that the directive exists.
15135
+ if (!this.tcb.boundTarget.referencedDirectiveExists(directive.name)) {
15136
+ this.tcb.oobRecorder.missingNamedTemplateDependency(this.tcb.id, directive);
15137
+ continue;
15138
+ }
15139
+ // Check that the class is a directive class.
15140
+ const directives = this.tcb.boundTarget.getDirectivesOfNode(directive);
15141
+ if (directives === null ||
15142
+ directives.length === 0 ||
15143
+ directives.some((dir) => dir.isComponent)) {
15144
+ this.tcb.oobRecorder.incorrectTemplateDependencyType(this.tcb.id, directive);
15145
+ continue;
15146
+ }
15147
+ this.appendInputsOfSelectorlessNode(directive);
15148
+ this.appendOutputsOfSelectorlessNode(directive);
15149
+ this.checkAndAppendReferencesOfNode(directive);
15150
+ }
15151
+ }
14977
15152
  appendDeepSchemaChecks(nodes) {
14978
15153
  for (const node of nodes) {
14979
15154
  if (!(node instanceof compiler.Element || node instanceof compiler.Template)) {
@@ -14981,7 +15156,14 @@ class Scope {
14981
15156
  }
14982
15157
  if (node instanceof compiler.Element) {
14983
15158
  const claimedInputs = new Set();
14984
- const directives = this.tcb.boundTarget.getDirectivesOfNode(node);
15159
+ let directives = this.tcb.boundTarget.getDirectivesOfNode(node);
15160
+ for (const dirNode of node.directives) {
15161
+ const directiveResults = this.tcb.boundTarget.getDirectivesOfNode(dirNode);
15162
+ if (directiveResults !== null && directiveResults.length > 0) {
15163
+ directives ??= [];
15164
+ directives.push(...directiveResults);
15165
+ }
15166
+ }
14985
15167
  let hasDirectives;
14986
15168
  if (directives === null || directives.length === 0) {
14987
15169
  hasDirectives = false;
@@ -15020,6 +15202,32 @@ class Scope {
15020
15202
  }
15021
15203
  }
15022
15204
  }
15205
+ appendComponentNode(node) {
15206
+ // TODO(crisbeto): should we still append the children if the component is invalid?
15207
+ // Check that the referenced class exists.
15208
+ if (!this.tcb.boundTarget.referencedDirectiveExists(node.componentName)) {
15209
+ this.tcb.oobRecorder.missingNamedTemplateDependency(this.tcb.id, node);
15210
+ return;
15211
+ }
15212
+ // Check that the class is a component.
15213
+ const directives = this.tcb.boundTarget.getDirectivesOfNode(node);
15214
+ if (directives === null ||
15215
+ directives.length === 0 ||
15216
+ directives.every((dir) => !dir.isComponent)) {
15217
+ this.tcb.oobRecorder.incorrectTemplateDependencyType(this.tcb.id, node);
15218
+ return;
15219
+ }
15220
+ const opIndex = this.opQueue.push(new TcbComponentNodeOp(this.tcb, this, node)) - 1;
15221
+ this.componentNodeOpMap.set(node, opIndex);
15222
+ if (this.tcb.env.config.controlFlowPreventingContentProjection !== 'suppress') {
15223
+ this.appendContentProjectionCheckOp(node);
15224
+ }
15225
+ this.appendInputsOfSelectorlessNode(node);
15226
+ this.appendOutputsOfSelectorlessNode(node);
15227
+ this.appendSelectorlessDirectives(node);
15228
+ this.appendChildren(node);
15229
+ this.checkAndAppendReferencesOfNode(node);
15230
+ }
15023
15231
  appendDeferredBlock(block) {
15024
15232
  this.appendDeferredTriggers(block, block.triggers);
15025
15233
  this.appendDeferredTriggers(block, block.prefetchTriggers);
@@ -15498,6 +15706,12 @@ class TcbForLoopTrackTranslator extends TcbExpressionTranslator {
15498
15706
  return super.resolve(ast);
15499
15707
  }
15500
15708
  }
15709
+ // TODO(crisbeto): the logic for determining the fallback tag name of a Component node is
15710
+ // still being designed. For now fall back to `ng-component`, but this will have to be
15711
+ // revisited once the design is finalized.
15712
+ function getComponentTagName(node) {
15713
+ return node.tagName || 'ng-component';
15714
+ }
15501
15715
 
15502
15716
  /**
15503
15717
  * An `Environment` representing the single type-checking file into which most (if not all) Type
@@ -16269,7 +16483,26 @@ class SymbolBuilder {
16269
16483
  if (directives === null) {
16270
16484
  return null;
16271
16485
  }
16272
- return directives.find((m) => m.ref.node === directiveDeclaration) ?? null;
16486
+ const directive = directives.find((m) => m.ref.node === directiveDeclaration);
16487
+ if (directive) {
16488
+ return directive;
16489
+ }
16490
+ const originalFile = directiveDeclaration.getSourceFile()[NgOriginalFile];
16491
+ if (originalFile !== undefined) {
16492
+ // This is a preliminary check ahead of a more expensive search
16493
+ const hasPotentialCandidate = directives.find((m) => m.ref.node.name.text === directiveDeclaration.name?.text);
16494
+ if (hasPotentialCandidate) {
16495
+ // In case the TCB has been inlined,
16496
+ // We will look for a matching class
16497
+ // If we find one, we look for it in the directives array
16498
+ const classWithSameName = findMatchingDirective(originalFile, directiveDeclaration);
16499
+ if (classWithSameName !== null) {
16500
+ return directives.find((m) => m.ref.node === classWithSameName) ?? null;
16501
+ }
16502
+ }
16503
+ }
16504
+ // Really nothing was found
16505
+ return null;
16273
16506
  }
16274
16507
  getDirectiveModule(declaration) {
16275
16508
  const scope = this.componentScopeReader.getScopeForComponent(declaration);
@@ -16768,6 +17001,37 @@ function unwrapSignalInputWriteTAccessor(expr) {
16768
17001
  typeExpr: expr,
16769
17002
  };
16770
17003
  }
17004
+ /**
17005
+ * Looks for a class declaration in the original source file that matches a given directive
17006
+ * from the type check source file.
17007
+ *
17008
+ * @param originalSourceFile The original source where the runtime code resides
17009
+ * @param directiveDeclarationInTypeCheckSourceFile The directive from the type check source file
17010
+ */
17011
+ function findMatchingDirective(originalSourceFile, directiveDeclarationInTypeCheckSourceFile) {
17012
+ const className = directiveDeclarationInTypeCheckSourceFile.name?.text ?? '';
17013
+ // We build an index of the class declarations with the same name
17014
+ // To then compare the indexes to confirm we found the right class declaration
17015
+ const ogClasses = collectClassesWithName(originalSourceFile, className);
17016
+ const typecheckClasses = collectClassesWithName(directiveDeclarationInTypeCheckSourceFile.getSourceFile(), className);
17017
+ return ogClasses[typecheckClasses.indexOf(directiveDeclarationInTypeCheckSourceFile)] ?? null;
17018
+ }
17019
+ /**
17020
+ * Builds a list of class declarations of a given name
17021
+ * Is used as a index based reference to compare class declarations
17022
+ * between the typecheck source file and the original source file
17023
+ */
17024
+ function collectClassesWithName(sourceFile, className) {
17025
+ const classes = [];
17026
+ function visit(node) {
17027
+ if (ts.isClassDeclaration(node) && node.name?.text === className) {
17028
+ classes.push(node);
17029
+ }
17030
+ ts.forEachChild(node, visit);
17031
+ }
17032
+ sourceFile.forEachChild(visit);
17033
+ return classes;
17034
+ }
16771
17035
 
16772
17036
  const REGISTRY = new compiler.DomElementSchemaRegistry();
16773
17037
  /**