@angular/core 20.0.0-next.7 → 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-D6xf1HH-.d.ts → discovery.d-BCmtv8-u.d.ts} +21 -7
  4. package/event_dispatcher.d-DlbccpYq.d.ts +1 -1
  5. package/fesm2022/attribute-BWp59EjE.mjs +1 -1
  6. package/fesm2022/core.mjs +60 -159
  7. package/fesm2022/core.mjs.map +1 -1
  8. package/fesm2022/{debug_node-B3CixwNH.mjs → debug_node-sP8Ihhla.mjs} +191 -56
  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-DtpS_sTw.mjs → resource-aJC_NNLX.mjs} +29 -76
  14. package/fesm2022/resource-aJC_NNLX.mjs.map +1 -0
  15. package/fesm2022/{root_effect_scheduler-BK3l7wIO.mjs → root_effect_scheduler-C95A9imp.mjs} +25 -10
  16. package/fesm2022/root_effect_scheduler-C95A9imp.mjs.map +1 -0
  17. package/fesm2022/rxjs-interop.mjs +12 -7
  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 +40 -21
  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 +190 -127
  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 +4 -4
  33. package/schematics/bundles/{apply_import_manager-DczRKpTm.js → apply_import_manager-CxaTw_Wy.js} +3 -3
  34. package/schematics/bundles/{change_tracker-CWLh-wes.js → change_tracker-CkPYZ_km.js} +3 -3
  35. package/schematics/bundles/{checker-_f5wM7PH.js → checker-Crz1jSZM.js} +389 -122
  36. package/schematics/bundles/cleanup-unused-imports.js +6 -6
  37. package/schematics/bundles/{compiler-BaCbbux6.js → compiler-B4MK7BP9.js} +193 -88
  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--W6S49uu.js → index-BbZ6cSR1.js} +6 -5
  43. package/schematics/bundles/{index-rsJ8I_hu.js → index-Bk_3geTg.js} +463 -327
  44. package/schematics/bundles/inject-flags.js +6 -6
  45. package/schematics/bundles/inject-migration.js +14 -11
  46. package/schematics/bundles/leading_space-D9nQ8UQC.js +1 -1
  47. package/schematics/bundles/{migrate_ts_type_references-C4D_SzJk.js → migrate_ts_type_references-D6T3FlkH.js} +7 -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-Ce0O2u-M.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 +12 -9
  64. package/weak_ref.d-eGOEP9S1.d.ts +1 -1
  65. package/fesm2022/debug_node-B3CixwNH.mjs.map +0 -1
  66. package/fesm2022/resource-DtpS_sTw.mjs.map +0 -1
  67. package/fesm2022/root_effect_scheduler-BK3l7wIO.mjs.map +0 -1
@@ -1,12 +1,12 @@
1
1
  'use strict';
2
2
  /**
3
- * @license Angular v20.0.0-next.7
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-BaCbbux6.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-_f5wM7PH.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.
@@ -6074,6 +6093,7 @@ const BINARY_OPERATORS$1 = new Map([
6074
6093
  [compiler.BinaryOperator.Plus, '+'],
6075
6094
  [compiler.BinaryOperator.NullishCoalesce, '??'],
6076
6095
  [compiler.BinaryOperator.Exponentiation, '**'],
6096
+ [compiler.BinaryOperator.In, 'in'],
6077
6097
  ]);
6078
6098
  class ExpressionTranslatorVisitor {
6079
6099
  factory;
@@ -6798,6 +6818,7 @@ const BINARY_OPERATORS = {
6798
6818
  '||': ts.SyntaxKind.BarBarToken,
6799
6819
  '+': ts.SyntaxKind.PlusToken,
6800
6820
  '??': ts.SyntaxKind.QuestionQuestionToken,
6821
+ 'in': ts.SyntaxKind.InKeyword,
6801
6822
  };
6802
6823
  const VAR_TYPES = {
6803
6824
  'const': ts.NodeFlags.Const,
@@ -10787,11 +10808,11 @@ class RegistryDomSchemaChecker {
10787
10808
  constructor(resolver) {
10788
10809
  this.resolver = resolver;
10789
10810
  }
10790
- checkElement(id, element, schemas, hostIsStandalone) {
10811
+ checkElement(id, tagName, sourceSpanForDiagnostics, schemas, hostIsStandalone) {
10791
10812
  // HTML elements inside an SVG `foreignObject` are declared in the `xhtml` namespace.
10792
10813
  // We need to strip it before handing it over to the registry because all HTML tag names
10793
10814
  // in the registry are without a namespace.
10794
- const name = element.name.replace(REMOVE_XHTML_REGEX, '');
10815
+ const name = tagName.replace(REMOVE_XHTML_REGEX, '');
10795
10816
  if (!REGISTRY$1.hasElement(name, schemas)) {
10796
10817
  const mapping = this.resolver.getTemplateSourceMapping(id);
10797
10818
  const schemas = `'${hostIsStandalone ? '@Component' : '@NgModule'}.schemas'`;
@@ -10805,27 +10826,27 @@ class RegistryDomSchemaChecker {
10805
10826
  else {
10806
10827
  errorMsg += `2. To allow any element add 'NO_ERRORS_SCHEMA' to the ${schemas} of this component.`;
10807
10828
  }
10808
- 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);
10809
10830
  this._diagnostics.push(diag);
10810
10831
  }
10811
10832
  }
10812
- checkTemplateElementProperty(id, element, name, span, schemas, hostIsStandalone) {
10813
- if (!REGISTRY$1.hasProperty(element.name, name, schemas)) {
10833
+ checkTemplateElementProperty(id, tagName, name, span, schemas, hostIsStandalone) {
10834
+ if (!REGISTRY$1.hasProperty(tagName, name, schemas)) {
10814
10835
  const mapping = this.resolver.getTemplateSourceMapping(id);
10815
10836
  const decorator = hostIsStandalone ? '@Component' : '@NgModule';
10816
10837
  const schemas = `'${decorator}.schemas'`;
10817
- let errorMsg = `Can't bind to '${name}' since it isn't a known property of '${element.name}'.`;
10818
- 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-')) {
10819
10840
  errorMsg +=
10820
10841
  `\n1. If '${name}' is an Angular directive, then add 'CommonModule' to the '${decorator}.imports' of this component.` +
10821
10842
  `\n2. To allow any property add 'NO_ERRORS_SCHEMA' to the ${schemas} of this component.`;
10822
10843
  }
10823
- else if (element.name.indexOf('-') > -1) {
10844
+ else if (tagName.indexOf('-') > -1) {
10824
10845
  errorMsg +=
10825
- `\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
10826
10847
  ? "included in the '@Component.imports' of this component"
10827
10848
  : 'part of this module'}.` +
10828
- `\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.` +
10829
10850
  `\n3. To allow any property add 'NO_ERRORS_SCHEMA' to the ${schemas} of this component.`;
10830
10851
  }
10831
10852
  const diag = makeTemplateDiagnostic(id, mapping, span, ts.DiagnosticCategory.Error, ngErrorCode(exports.ErrorCode.SCHEMA_INVALID_ATTRIBUTE), errorMsg);
@@ -11613,12 +11634,16 @@ function isInHostBindingTcb(node) {
11613
11634
  return false;
11614
11635
  }
11615
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
11616
11639
  for (const stmt of file.statements) {
11617
11640
  if (ts.isFunctionDeclaration(stmt) && getTypeCheckId(stmt, file, isDiagnosticRequest) === id) {
11618
11641
  return stmt;
11619
11642
  }
11620
11643
  }
11621
- 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);
11622
11647
  }
11623
11648
  /**
11624
11649
  * Traverses up the AST starting from the given node to extract the source location from comments
@@ -11688,6 +11713,15 @@ function checkIfGenericTypeBoundsCanBeEmitted(node, reflector, env) {
11688
11713
  const emitter = new TypeParameterEmitter(node.typeParameters, reflector);
11689
11714
  return emitter.canEmit((ref) => env.canReferenceType(ref));
11690
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
+ }
11691
11725
 
11692
11726
  function generateTypeCtorDeclarationFn(env, meta, nodeTypeRef, typeParams) {
11693
11727
  const rawTypeArgs = typeParams !== undefined ? generateGenericArgs(typeParams) : undefined;
@@ -12244,6 +12278,20 @@ class OutOfBandDiagnosticRecorderImpl {
12244
12278
  const errorMsg = `Cannot declare @let called '${decl.name}' as there is another symbol in the template with the same name.`;
12245
12279
  this._diagnostics.push(makeTemplateDiagnostic(id, mapping, decl.sourceSpan, ts.DiagnosticCategory.Error, ngErrorCode(exports.ErrorCode.CONFLICTING_LET_DECLARATION), errorMsg));
12246
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
+ }
12247
12295
  }
12248
12296
  function makeInlineDiagnostic(id, code, node, messageText, relatedInformation) {
12249
12297
  return {
@@ -12403,6 +12451,7 @@ const BINARY_OPS = new Map([
12403
12451
  ['&', ts.SyntaxKind.AmpersandToken],
12404
12452
  ['|', ts.SyntaxKind.BarToken],
12405
12453
  ['??', ts.SyntaxKind.QuestionQuestionToken],
12454
+ ['in', ts.SyntaxKind.InKeyword],
12406
12455
  ]);
12407
12456
  /**
12408
12457
  * Convert an `AST` to TypeScript code directly, without going through an intermediate `Expression`
@@ -13153,62 +13202,13 @@ class TcbTemplateBodyOp extends TcbOp {
13153
13202
  // on the template can trigger extra guard expressions that serve to narrow types within the
13154
13203
  // `if`. `guard` is calculated by starting with `true` and adding other conditions as needed.
13155
13204
  // Collect these into `guards` by processing the directives.
13156
- const directiveGuards = [];
13157
- const directives = this.tcb.boundTarget.getDirectivesOfNode(this.template);
13158
- if (directives !== null) {
13159
- for (const dir of directives) {
13160
- const dirInstId = this.scope.resolve(this.template, dir);
13161
- const dirId = this.tcb.env.reference(dir.ref);
13162
- // There are two kinds of guards. Template guards (ngTemplateGuards) allow type narrowing of
13163
- // the expression passed to an @Input of the directive. Scan the directive to see if it has
13164
- // any template guards, and generate them if needed.
13165
- dir.ngTemplateGuards.forEach((guard) => {
13166
- // For each template guard function on the directive, look for a binding to that input.
13167
- const boundInput = this.template.inputs.find((i) => i.name === guard.inputName) ||
13168
- this.template.templateAttrs.find((i) => i instanceof compiler.BoundAttribute && i.name === guard.inputName);
13169
- if (boundInput !== undefined) {
13170
- // If there is such a binding, generate an expression for it.
13171
- const expr = tcbExpression(boundInput.value, this.tcb, this.scope);
13172
- // The expression has already been checked in the type constructor invocation, so
13173
- // it should be ignored when used within a template guard.
13174
- markIgnoreDiagnostics(expr);
13175
- if (guard.type === 'binding') {
13176
- // Use the binding expression itself as guard.
13177
- directiveGuards.push(expr);
13178
- }
13179
- else {
13180
- // Call the guard function on the directive with the directive instance and that
13181
- // expression.
13182
- const guardInvoke = tsCallMethod(dirId, `ngTemplateGuard_${guard.inputName}`, [
13183
- dirInstId,
13184
- expr,
13185
- ]);
13186
- addParseSpanInfo(guardInvoke, boundInput.value.sourceSpan);
13187
- directiveGuards.push(guardInvoke);
13188
- }
13189
- }
13190
- });
13191
- // The second kind of guard is a template context guard. This guard narrows the template
13192
- // rendering context variable `ctx`.
13193
- if (dir.hasNgTemplateContextGuard) {
13194
- if (this.tcb.env.config.applyTemplateContextGuards) {
13195
- const ctx = this.scope.resolve(this.template);
13196
- const guardInvoke = tsCallMethod(dirId, 'ngTemplateContextGuard', [dirInstId, ctx]);
13197
- addParseSpanInfo(guardInvoke, this.template.sourceSpan);
13198
- directiveGuards.push(guardInvoke);
13199
- }
13200
- else if (this.template.variables.length > 0 &&
13201
- this.tcb.env.config.suggestionsForSuboptimalTypeInference) {
13202
- // The compiler could have inferred a better type for the variables in this template,
13203
- // but was prevented from doing so by the type-checking configuration. Issue a warning
13204
- // diagnostic.
13205
- this.tcb.oobRecorder.suboptimalTypeInference(this.tcb.id, this.template.variables);
13206
- }
13207
- }
13208
- }
13209
- }
13210
13205
  // By default the guard is simply `true`.
13211
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
+ }
13212
13212
  // If there are any guards from directives, use them instead.
13213
13213
  if (directiveGuards.length > 0) {
13214
13214
  // Pop the first value and use it as the initializer to reduce(). This way, a single guard
@@ -13240,6 +13240,67 @@ class TcbTemplateBodyOp extends TcbOp {
13240
13240
  this.scope.addStatement(tmplBlock);
13241
13241
  return null;
13242
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
+ }
13243
13304
  }
13244
13305
  /**
13245
13306
  * A `TcbOp` which renders an Angular expression (e.g. `{{foo() && bar.baz}}`).
@@ -13504,7 +13565,7 @@ class TcbDirectiveCtorOp extends TcbOp {
13504
13565
  return id;
13505
13566
  }
13506
13567
  circularFallback() {
13507
- return new TcbDirectiveCtorCircularFallbackOp(this.tcb, this.scope, this.node, this.dir);
13568
+ return new TcbDirectiveCtorCircularFallbackOp(this.tcb, this.scope, this.dir);
13508
13569
  }
13509
13570
  }
13510
13571
  /**
@@ -13666,13 +13727,11 @@ class TcbDirectiveInputsOp extends TcbOp {
13666
13727
  class TcbDirectiveCtorCircularFallbackOp extends TcbOp {
13667
13728
  tcb;
13668
13729
  scope;
13669
- node;
13670
13730
  dir;
13671
- constructor(tcb, scope, node, dir) {
13731
+ constructor(tcb, scope, dir) {
13672
13732
  super();
13673
13733
  this.tcb = tcb;
13674
13734
  this.scope = scope;
13675
- this.node = node;
13676
13735
  this.dir = dir;
13677
13736
  }
13678
13737
  get optional() {
@@ -13714,10 +13773,10 @@ class TcbDomSchemaCheckerOp extends TcbOp {
13714
13773
  }
13715
13774
  execute() {
13716
13775
  const element = this.element;
13717
- const isTemplateElement = element instanceof compiler.Element;
13776
+ const isTemplateElement = element instanceof compiler.Element || element instanceof compiler.Component;
13718
13777
  const bindings = isTemplateElement ? element.inputs : element.bindings;
13719
13778
  if (this.checkElement && isTemplateElement) {
13720
- 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);
13721
13780
  }
13722
13781
  // TODO(alxhub): this could be more efficient.
13723
13782
  for (const binding of bindings) {
@@ -13730,7 +13789,7 @@ class TcbDomSchemaCheckerOp extends TcbOp {
13730
13789
  // A direct binding to a property.
13731
13790
  const propertyName = ATTR_TO_PROP.get(binding.name) ?? binding.name;
13732
13791
  if (isTemplateElement) {
13733
- 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);
13734
13793
  }
13735
13794
  else {
13736
13795
  this.tcb.domSchemaChecker.checkHostElementProperty(this.tcb.id, element, propertyName, binding.keySpan, this.tcb.schemas);
@@ -13739,6 +13798,9 @@ class TcbDomSchemaCheckerOp extends TcbOp {
13739
13798
  }
13740
13799
  return null;
13741
13800
  }
13801
+ getTagName(node) {
13802
+ return node instanceof compiler.Element ? node.name : getComponentTagName(node);
13803
+ }
13742
13804
  }
13743
13805
  /**
13744
13806
  * A `TcbOp` that finds and flags control flow nodes that interfere with content projection.
@@ -13873,6 +13935,30 @@ class TcbHostElementOp extends TcbOp {
13873
13935
  return id;
13874
13936
  }
13875
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
+ }
13876
13962
  /**
13877
13963
  * Mapping between attributes names that don't correspond to their element property names.
13878
13964
  * Note: this mapping has to be kept in sync with the equally named mapping in the runtime.
@@ -14478,6 +14564,10 @@ class Scope {
14478
14564
  * A map of `TmplAstHostElement`s to the index of their `TcbHostElementOp` in the `opQueue`
14479
14565
  */
14480
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();
14481
14571
  /**
14482
14572
  * A map of maps which tracks the index of `TcbDirectiveCtorOp`s in the `opQueue` for each
14483
14573
  * directive on a `TmplAstElement` or `TmplAstTemplate` node.
@@ -14730,22 +14820,23 @@ class Scope {
14730
14820
  // Execute the `TcbTemplateContextOp` for the template.
14731
14821
  return this.resolveOp(this.templateCtxOpMap.get(ref));
14732
14822
  }
14733
- 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) &&
14734
14827
  directive !== undefined &&
14735
14828
  this.directiveOpMap.has(ref)) {
14736
14829
  // Resolving a directive on an element or sub-template.
14737
14830
  const dirMap = this.directiveOpMap.get(ref);
14738
- if (dirMap.has(directive)) {
14739
- return this.resolveOp(dirMap.get(directive));
14740
- }
14741
- else {
14742
- return null;
14743
- }
14831
+ return dirMap.has(directive) ? this.resolveOp(dirMap.get(directive)) : null;
14744
14832
  }
14745
14833
  else if (ref instanceof compiler.Element && this.elementOpMap.has(ref)) {
14746
14834
  // Resolving the DOM node of an element in this template.
14747
14835
  return this.resolveOp(this.elementOpMap.get(ref));
14748
14836
  }
14837
+ else if (ref instanceof compiler.Component && this.componentNodeOpMap.has(ref)) {
14838
+ return this.resolveOp(this.componentNodeOpMap.get(ref));
14839
+ }
14749
14840
  else if (ref instanceof compiler.HostElement && this.hostElementOpMap.has(ref)) {
14750
14841
  return this.resolveOp(this.hostElementOpMap.get(ref));
14751
14842
  }
@@ -14794,15 +14885,17 @@ class Scope {
14794
14885
  if (this.tcb.env.config.controlFlowPreventingContentProjection !== 'suppress') {
14795
14886
  this.appendContentProjectionCheckOp(node);
14796
14887
  }
14797
- this.appendDirectivesAndInputsOfNode(node);
14798
- this.appendOutputsOfNode(node);
14888
+ this.appendDirectivesAndInputsOfElementLikeNode(node);
14889
+ this.appendOutputsOfElementLikeNode(node);
14890
+ this.appendSelectorlessDirectives(node);
14799
14891
  this.appendChildren(node);
14800
14892
  this.checkAndAppendReferencesOfNode(node);
14801
14893
  }
14802
14894
  else if (node instanceof compiler.Template) {
14803
14895
  // Template children are rendered in a child scope.
14804
- this.appendDirectivesAndInputsOfNode(node);
14805
- this.appendOutputsOfNode(node);
14896
+ this.appendDirectivesAndInputsOfElementLikeNode(node);
14897
+ this.appendOutputsOfElementLikeNode(node);
14898
+ this.appendSelectorlessDirectives(node);
14806
14899
  const ctxIndex = this.opQueue.push(new TcbTemplateContextOp(this.tcb, this)) - 1;
14807
14900
  this.templateCtxOpMap.set(node, ctxIndex);
14808
14901
  if (this.tcb.env.config.checkTemplateBodies) {
@@ -14813,6 +14906,9 @@ class Scope {
14813
14906
  }
14814
14907
  this.checkAndAppendReferencesOfNode(node);
14815
14908
  }
14909
+ else if (node instanceof compiler.Component) {
14910
+ this.appendComponentNode(node);
14911
+ }
14816
14912
  else if (node instanceof compiler.DeferredBlock) {
14817
14913
  this.appendDeferredBlock(node);
14818
14914
  }
@@ -14875,9 +14971,11 @@ class Scope {
14875
14971
  this.referenceOpMap.set(ref, ctxIndex);
14876
14972
  }
14877
14973
  }
14878
- appendDirectivesAndInputsOfNode(node) {
14974
+ appendDirectivesAndInputsOfElementLikeNode(node) {
14879
14975
  // Collect all the inputs on the element.
14880
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.
14881
14979
  const directives = this.tcb.boundTarget.getDirectivesOfNode(node);
14882
14980
  if (directives === null || directives.length === 0) {
14883
14981
  // If there are no directives, then all inputs are unclaimed inputs, so queue an operation
@@ -14887,42 +14985,18 @@ class Scope {
14887
14985
  }
14888
14986
  return;
14889
14987
  }
14890
- else {
14891
- if (node instanceof compiler.Element) {
14892
- const isDeferred = this.tcb.boundTarget.isDeferred(node);
14893
- if (!isDeferred && directives.some((dirMeta) => dirMeta.isExplicitlyDeferred)) {
14894
- // This node has directives/components that were defer-loaded (included into
14895
- // `@Component.deferredImports`), but the node itself was used outside of a
14896
- // `@defer` block, which is the error.
14897
- this.tcb.oobRecorder.deferredComponentUsedEagerly(this.tcb.id, node);
14898
- }
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);
14899
14995
  }
14900
14996
  }
14901
14997
  const dirMap = new Map();
14902
14998
  for (const dir of directives) {
14903
- let directiveOp;
14904
- const host = this.tcb.env.reflector;
14905
- const dirRef = dir.ref;
14906
- if (!dir.isGeneric) {
14907
- // The most common case is that when a directive is not generic, we use the normal
14908
- // `TcbNonDirectiveTypeOp`.
14909
- directiveOp = new TcbNonGenericDirectiveTypeOp(this.tcb, this, node, dir);
14910
- }
14911
- else if (!requiresInlineTypeCtor(dirRef.node, host, this.tcb.env) ||
14912
- this.tcb.env.config.useInlineTypeConstructors) {
14913
- // For generic directives, we use a type constructor to infer types. If a directive requires
14914
- // an inline type constructor, then inlining must be available to use the
14915
- // `TcbDirectiveCtorOp`. If not we, we fallback to using `any` – see below.
14916
- directiveOp = new TcbDirectiveCtorOp(this.tcb, this, node, dir);
14917
- }
14918
- else {
14919
- // If inlining is not available, then we give up on inferring the generic params, and use
14920
- // `any` type for the directive's generic parameters.
14921
- directiveOp = new TcbGenericDirectiveTypeWithAnyParamsOp(this.tcb, this, node, dir);
14922
- }
14923
- const dirIndex = this.opQueue.push(directiveOp) - 1;
14924
- dirMap.set(dir, dirIndex);
14925
- this.opQueue.push(new TcbDirectiveInputsOp(this.tcb, this, node, dir));
14999
+ this.appendDirectiveInputs(dir, node, dirMap);
14926
15000
  }
14927
15001
  this.directiveOpMap.set(node, dirMap);
14928
15002
  // After expanding the directives, we might need to queue an operation to check any unclaimed
@@ -14943,9 +15017,12 @@ class Scope {
14943
15017
  this.opQueue.push(new TcbDomSchemaCheckerOp(this.tcb, node, checkElement, claimedInputs));
14944
15018
  }
14945
15019
  }
14946
- appendOutputsOfNode(node) {
15020
+ appendOutputsOfElementLikeNode(node) {
14947
15021
  // Collect all the outputs on the element.
14948
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.
14949
15026
  const directives = this.tcb.boundTarget.getDirectivesOfNode(node);
14950
15027
  if (directives === null || directives.length === 0) {
14951
15028
  // If there are no directives, then all outputs are unclaimed outputs, so queue an operation
@@ -14971,6 +15048,107 @@ class Scope {
14971
15048
  this.opQueue.push(new TcbUnclaimedOutputsOp(this.tcb, this, node, node.outputs, node.inputs, claimedOutputs));
14972
15049
  }
14973
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
+ }
14974
15152
  appendDeepSchemaChecks(nodes) {
14975
15153
  for (const node of nodes) {
14976
15154
  if (!(node instanceof compiler.Element || node instanceof compiler.Template)) {
@@ -14978,7 +15156,14 @@ class Scope {
14978
15156
  }
14979
15157
  if (node instanceof compiler.Element) {
14980
15158
  const claimedInputs = new Set();
14981
- 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
+ }
14982
15167
  let hasDirectives;
14983
15168
  if (directives === null || directives.length === 0) {
14984
15169
  hasDirectives = false;
@@ -15017,6 +15202,32 @@ class Scope {
15017
15202
  }
15018
15203
  }
15019
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
+ }
15020
15231
  appendDeferredBlock(block) {
15021
15232
  this.appendDeferredTriggers(block, block.triggers);
15022
15233
  this.appendDeferredTriggers(block, block.prefetchTriggers);
@@ -15495,6 +15706,12 @@ class TcbForLoopTrackTranslator extends TcbExpressionTranslator {
15495
15706
  return super.resolve(ast);
15496
15707
  }
15497
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
+ }
15498
15715
 
15499
15716
  /**
15500
15717
  * An `Environment` representing the single type-checking file into which most (if not all) Type
@@ -16266,7 +16483,26 @@ class SymbolBuilder {
16266
16483
  if (directives === null) {
16267
16484
  return null;
16268
16485
  }
16269
- 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;
16270
16506
  }
16271
16507
  getDirectiveModule(declaration) {
16272
16508
  const scope = this.componentScopeReader.getScopeForComponent(declaration);
@@ -16765,6 +17001,37 @@ function unwrapSignalInputWriteTAccessor(expr) {
16765
17001
  typeExpr: expr,
16766
17002
  };
16767
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
+ }
16768
17035
 
16769
17036
  const REGISTRY = new compiler.DomElementSchemaRegistry();
16770
17037
  /**