@angular/core 20.0.0-next.5 → 20.0.0-next.7

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 (69) hide show
  1. package/{api.d-mxcXqDpA.d.ts → api.d-KjtSQajV.d.ts} +4 -4
  2. package/{ng_i18n_closure_mode.d-DLxSUiDr.d.ts → chrome_dev_tools_performance.d-qv7drdAl.d.ts} +20 -5
  3. package/{discovery.d-CyYpOJ7j.d.ts → discovery.d-D6xf1HH-.d.ts} +8 -79
  4. package/{event_dispatcher.d-PWnbqZDx.d.ts → event_dispatcher.d-DlbccpYq.d.ts} +3 -3
  5. package/fesm2022/{attribute-B17mgaqe.mjs → attribute-BWp59EjE.mjs} +3 -3
  6. package/fesm2022/{attribute-B17mgaqe.mjs.map → attribute-BWp59EjE.mjs.map} +1 -1
  7. package/fesm2022/core.mjs +22 -31
  8. package/fesm2022/core.mjs.map +1 -1
  9. package/fesm2022/{debug_node-xKpCIZm-.mjs → debug_node-B3CixwNH.mjs} +218 -141
  10. package/fesm2022/debug_node-B3CixwNH.mjs.map +1 -0
  11. package/fesm2022/primitives/di.mjs +1 -1
  12. package/fesm2022/primitives/event-dispatch.mjs +2 -2
  13. package/fesm2022/primitives/signals.mjs +5 -5
  14. package/fesm2022/{resource-BPpYEDic.mjs → resource-DtpS_sTw.mjs} +6 -8
  15. package/fesm2022/resource-DtpS_sTw.mjs.map +1 -0
  16. package/fesm2022/{root_effect_scheduler-D0_b1cf_.mjs → root_effect_scheduler-BK3l7wIO.mjs} +144 -68
  17. package/fesm2022/root_effect_scheduler-BK3l7wIO.mjs.map +1 -0
  18. package/fesm2022/rxjs-interop.mjs +7 -84
  19. package/fesm2022/rxjs-interop.mjs.map +1 -1
  20. package/fesm2022/{signal-DhRAAi7R.mjs → signal-B6pMq7KS.mjs} +3 -3
  21. package/fesm2022/{signal-DhRAAi7R.mjs.map → signal-B6pMq7KS.mjs.map} +1 -1
  22. package/fesm2022/testing.mjs +167 -103
  23. package/fesm2022/testing.mjs.map +1 -1
  24. package/fesm2022/{untracked-DaaW3JJm.mjs → untracked-Bz5WMeU1.mjs} +4 -4
  25. package/fesm2022/{untracked-DaaW3JJm.mjs.map → untracked-Bz5WMeU1.mjs.map} +1 -1
  26. package/fesm2022/{weak_ref-DrMdAIDh.mjs → weak_ref-BaIq-pgY.mjs} +3 -3
  27. package/fesm2022/{weak_ref-DrMdAIDh.mjs.map → weak_ref-BaIq-pgY.mjs.map} +1 -1
  28. package/{graph.d-StYigYp1.d.ts → graph.d-BcIOep_B.d.ts} +3 -3
  29. package/index.d.ts +34 -41
  30. package/package.json +2 -2
  31. package/primitives/di/index.d.ts +1 -1
  32. package/primitives/event-dispatch/index.d.ts +3 -3
  33. package/primitives/signals/index.d.ts +6 -6
  34. package/rxjs-interop/index.d.ts +8 -17
  35. package/schematics/bundles/{apply_import_manager-C-ysxahq.js → apply_import_manager-DczRKpTm.js} +6 -6
  36. package/schematics/bundles/{change_tracker-0Ktek5Xl.js → change_tracker-CWLh-wes.js} +3 -3
  37. package/schematics/bundles/{checker-DqUKCGda.js → checker-_f5wM7PH.js} +20 -3
  38. package/schematics/bundles/cleanup-unused-imports.js +25 -20
  39. package/schematics/bundles/{compiler-CuoiHqkc.js → compiler-BaCbbux6.js} +964 -298
  40. package/schematics/bundles/compiler_host-CAfDJO3W.js +1 -1
  41. package/schematics/bundles/control-flow-migration.js +2 -2
  42. package/schematics/bundles/document-core.js +12 -12
  43. package/schematics/bundles/imports-CIX-JgAN.js +1 -1
  44. package/schematics/bundles/{index-CwFQSYXZ.js → index--W6S49uu.js} +10 -10
  45. package/schematics/bundles/{index-WFXCe5Q0.js → index-rsJ8I_hu.js} +131 -64
  46. package/schematics/bundles/inject-flags.js +14 -14
  47. package/schematics/bundles/inject-migration.js +108 -19
  48. package/schematics/bundles/leading_space-D9nQ8UQC.js +1 -1
  49. package/schematics/bundles/{migrate_ts_type_references-BNuHufqZ.js → migrate_ts_type_references-C4D_SzJk.js} +21 -21
  50. package/schematics/bundles/ng_decorators-DznZ5jMl.js +1 -1
  51. package/schematics/bundles/nodes-B16H9JUd.js +1 -1
  52. package/schematics/bundles/output-migration.js +80 -22
  53. package/schematics/bundles/{run_in_devkit-CmHxABFr.js → project_paths-Ce0O2u-M.js} +254 -244
  54. package/schematics/bundles/project_tsconfig_paths-CDVxT6Ov.js +1 -1
  55. package/schematics/bundles/property_name-BBwFuqMe.js +1 -1
  56. package/schematics/bundles/route-lazy-loading.js +4 -4
  57. package/schematics/bundles/self-closing-tags-migration.js +20 -15
  58. package/schematics/bundles/signal-input-migration.js +26 -21
  59. package/schematics/bundles/signal-queries-migration.js +32 -27
  60. package/schematics/bundles/signals.js +8 -8
  61. package/schematics/bundles/standalone-migration.js +5 -5
  62. package/schematics/bundles/symbol-VPWguRxr.js +1 -1
  63. package/schematics/bundles/test-bed-get.js +13 -13
  64. package/{signal.d-BeaTIeOE.d.ts → signal.d-E0e5nW1p.d.ts} +4 -4
  65. package/testing/index.d.ts +9 -25
  66. package/{weak_ref.d-ttyj86RV.d.ts → weak_ref.d-eGOEP9S1.d.ts} +2 -2
  67. package/fesm2022/debug_node-xKpCIZm-.mjs.map +0 -1
  68. package/fesm2022/resource-BPpYEDic.mjs.map +0 -1
  69. package/fesm2022/root_effect_scheduler-D0_b1cf_.mjs.map +0 -1
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
  /**
3
- * @license Angular v20.0.0-next.5
3
+ * @license Angular v20.0.0-next.7
4
4
  * (c) 2010-2025 Google LLC. https://angular.io/
5
5
  * License: MIT
6
6
  */
@@ -4915,17 +4915,19 @@ let Element$1 = class Element {
4915
4915
  attributes;
4916
4916
  inputs;
4917
4917
  outputs;
4918
+ directives;
4918
4919
  children;
4919
4920
  references;
4920
4921
  sourceSpan;
4921
4922
  startSourceSpan;
4922
4923
  endSourceSpan;
4923
4924
  i18n;
4924
- constructor(name, attributes, inputs, outputs, children, references, sourceSpan, startSourceSpan, endSourceSpan, i18n) {
4925
+ constructor(name, attributes, inputs, outputs, directives, children, references, sourceSpan, startSourceSpan, endSourceSpan, i18n) {
4925
4926
  this.name = name;
4926
4927
  this.attributes = attributes;
4927
4928
  this.inputs = inputs;
4928
4929
  this.outputs = outputs;
4930
+ this.directives = directives;
4929
4931
  this.children = children;
4930
4932
  this.references = references;
4931
4933
  this.sourceSpan = sourceSpan;
@@ -5224,11 +5226,70 @@ let LetDeclaration$1 = class LetDeclaration {
5224
5226
  return visitor.visitLetDeclaration(this);
5225
5227
  }
5226
5228
  };
5229
+ let Component$1 = class Component {
5230
+ componentName;
5231
+ tagName;
5232
+ fullName;
5233
+ attributes;
5234
+ inputs;
5235
+ outputs;
5236
+ directives;
5237
+ children;
5238
+ references;
5239
+ sourceSpan;
5240
+ startSourceSpan;
5241
+ endSourceSpan;
5242
+ i18n;
5243
+ constructor(componentName, tagName, fullName, attributes, inputs, outputs, directives, children, references, sourceSpan, startSourceSpan, endSourceSpan, i18n) {
5244
+ this.componentName = componentName;
5245
+ this.tagName = tagName;
5246
+ this.fullName = fullName;
5247
+ this.attributes = attributes;
5248
+ this.inputs = inputs;
5249
+ this.outputs = outputs;
5250
+ this.directives = directives;
5251
+ this.children = children;
5252
+ this.references = references;
5253
+ this.sourceSpan = sourceSpan;
5254
+ this.startSourceSpan = startSourceSpan;
5255
+ this.endSourceSpan = endSourceSpan;
5256
+ this.i18n = i18n;
5257
+ }
5258
+ visit(visitor) {
5259
+ return visitor.visitComponent(this);
5260
+ }
5261
+ };
5262
+ let Directive$1 = class Directive {
5263
+ name;
5264
+ attributes;
5265
+ inputs;
5266
+ outputs;
5267
+ references;
5268
+ sourceSpan;
5269
+ startSourceSpan;
5270
+ endSourceSpan;
5271
+ i18n;
5272
+ constructor(name, attributes, inputs, outputs, references, sourceSpan, startSourceSpan, endSourceSpan, i18n) {
5273
+ this.name = name;
5274
+ this.attributes = attributes;
5275
+ this.inputs = inputs;
5276
+ this.outputs = outputs;
5277
+ this.references = references;
5278
+ this.sourceSpan = sourceSpan;
5279
+ this.startSourceSpan = startSourceSpan;
5280
+ this.endSourceSpan = endSourceSpan;
5281
+ this.i18n = i18n;
5282
+ }
5283
+ visit(visitor) {
5284
+ return visitor.visitDirective(this);
5285
+ }
5286
+ };
5227
5287
  class Template {
5228
5288
  tagName;
5229
5289
  attributes;
5230
5290
  inputs;
5231
5291
  outputs;
5292
+ directives;
5232
5293
  templateAttrs;
5233
5294
  children;
5234
5295
  references;
@@ -5242,11 +5303,12 @@ class Template {
5242
5303
  // `null` is a special case for when there is a structural directive on an `ng-template` so
5243
5304
  // the renderer can differentiate between the synthetic template and the one written in the
5244
5305
  // file.
5245
- tagName, attributes, inputs, outputs, templateAttrs, children, references, variables, sourceSpan, startSourceSpan, endSourceSpan, i18n) {
5306
+ tagName, attributes, inputs, outputs, directives, templateAttrs, children, references, variables, sourceSpan, startSourceSpan, endSourceSpan, i18n) {
5246
5307
  this.tagName = tagName;
5247
5308
  this.attributes = attributes;
5248
5309
  this.inputs = inputs;
5249
5310
  this.outputs = outputs;
5311
+ this.directives = directives;
5250
5312
  this.templateAttrs = templateAttrs;
5251
5313
  this.children = children;
5252
5314
  this.references = references;
@@ -5265,13 +5327,17 @@ class Content {
5265
5327
  attributes;
5266
5328
  children;
5267
5329
  sourceSpan;
5330
+ startSourceSpan;
5331
+ endSourceSpan;
5268
5332
  i18n;
5269
5333
  name = 'ng-content';
5270
- constructor(selector, attributes, children, sourceSpan, i18n) {
5334
+ constructor(selector, attributes, children, sourceSpan, startSourceSpan, endSourceSpan, i18n) {
5271
5335
  this.selector = selector;
5272
5336
  this.attributes = attributes;
5273
5337
  this.children = children;
5274
5338
  this.sourceSpan = sourceSpan;
5339
+ this.startSourceSpan = startSourceSpan;
5340
+ this.endSourceSpan = endSourceSpan;
5275
5341
  this.i18n = i18n;
5276
5342
  }
5277
5343
  visit(visitor) {
@@ -5354,6 +5420,7 @@ let RecursiveVisitor$1 = class RecursiveVisitor {
5354
5420
  visitAll$1(this, element.attributes);
5355
5421
  visitAll$1(this, element.inputs);
5356
5422
  visitAll$1(this, element.outputs);
5423
+ visitAll$1(this, element.directives);
5357
5424
  visitAll$1(this, element.children);
5358
5425
  visitAll$1(this, element.references);
5359
5426
  }
@@ -5361,6 +5428,7 @@ let RecursiveVisitor$1 = class RecursiveVisitor {
5361
5428
  visitAll$1(this, template.attributes);
5362
5429
  visitAll$1(this, template.inputs);
5363
5430
  visitAll$1(this, template.outputs);
5431
+ visitAll$1(this, template.directives);
5364
5432
  visitAll$1(this, template.children);
5365
5433
  visitAll$1(this, template.references);
5366
5434
  visitAll$1(this, template.variables);
@@ -5402,6 +5470,20 @@ let RecursiveVisitor$1 = class RecursiveVisitor {
5402
5470
  visitContent(content) {
5403
5471
  visitAll$1(this, content.children);
5404
5472
  }
5473
+ visitComponent(component) {
5474
+ visitAll$1(this, component.attributes);
5475
+ visitAll$1(this, component.inputs);
5476
+ visitAll$1(this, component.outputs);
5477
+ visitAll$1(this, component.directives);
5478
+ visitAll$1(this, component.children);
5479
+ visitAll$1(this, component.references);
5480
+ }
5481
+ visitDirective(directive) {
5482
+ visitAll$1(this, directive.attributes);
5483
+ visitAll$1(this, directive.inputs);
5484
+ visitAll$1(this, directive.outputs);
5485
+ visitAll$1(this, directive.references);
5486
+ }
5405
5487
  visitVariable(variable) { }
5406
5488
  visitReference(reference) { }
5407
5489
  visitTextAttribute(attribute) { }
@@ -6028,8 +6110,8 @@ const I18N_ICU_VAR_PREFIX = 'VAR_';
6028
6110
  function isI18nAttribute(name) {
6029
6111
  return name === I18N_ATTR || name.startsWith(I18N_ATTR_PREFIX);
6030
6112
  }
6031
- function hasI18nAttrs(element) {
6032
- return element.attrs.some((attr) => isI18nAttribute(attr.name));
6113
+ function hasI18nAttrs(node) {
6114
+ return node.attrs.some((attr) => isI18nAttribute(attr.name));
6033
6115
  }
6034
6116
  function icuFromI18nMessage(message) {
6035
6117
  return message.nodes[0];
@@ -13423,13 +13505,15 @@ class Attribute extends NodeWithI18n {
13423
13505
  class Element extends NodeWithI18n {
13424
13506
  name;
13425
13507
  attrs;
13508
+ directives;
13426
13509
  children;
13427
13510
  startSourceSpan;
13428
13511
  endSourceSpan;
13429
- constructor(name, attrs, children, sourceSpan, startSourceSpan, endSourceSpan = null, i18n) {
13512
+ constructor(name, attrs, directives, children, sourceSpan, startSourceSpan, endSourceSpan = null, i18n) {
13430
13513
  super(sourceSpan, i18n);
13431
13514
  this.name = name;
13432
13515
  this.attrs = attrs;
13516
+ this.directives = directives;
13433
13517
  this.children = children;
13434
13518
  this.startSourceSpan = startSourceSpan;
13435
13519
  this.endSourceSpan = endSourceSpan;
@@ -13469,6 +13553,47 @@ class Block extends NodeWithI18n {
13469
13553
  return visitor.visitBlock(this, context);
13470
13554
  }
13471
13555
  }
13556
+ class Component extends NodeWithI18n {
13557
+ componentName;
13558
+ tagName;
13559
+ fullName;
13560
+ attrs;
13561
+ directives;
13562
+ children;
13563
+ startSourceSpan;
13564
+ endSourceSpan;
13565
+ constructor(componentName, tagName, fullName, attrs, directives, children, sourceSpan, startSourceSpan, endSourceSpan = null, i18n) {
13566
+ super(sourceSpan, i18n);
13567
+ this.componentName = componentName;
13568
+ this.tagName = tagName;
13569
+ this.fullName = fullName;
13570
+ this.attrs = attrs;
13571
+ this.directives = directives;
13572
+ this.children = children;
13573
+ this.startSourceSpan = startSourceSpan;
13574
+ this.endSourceSpan = endSourceSpan;
13575
+ }
13576
+ visit(visitor, context) {
13577
+ return visitor.visitComponent(this, context);
13578
+ }
13579
+ }
13580
+ class Directive {
13581
+ name;
13582
+ attrs;
13583
+ sourceSpan;
13584
+ startSourceSpan;
13585
+ endSourceSpan;
13586
+ constructor(name, attrs, sourceSpan, startSourceSpan, endSourceSpan = null) {
13587
+ this.name = name;
13588
+ this.attrs = attrs;
13589
+ this.sourceSpan = sourceSpan;
13590
+ this.startSourceSpan = startSourceSpan;
13591
+ this.endSourceSpan = endSourceSpan;
13592
+ }
13593
+ visit(visitor, context) {
13594
+ return visitor.visitDirective(this, context);
13595
+ }
13596
+ }
13472
13597
  class BlockParameter {
13473
13598
  expression;
13474
13599
  sourceSpan;
@@ -13515,6 +13640,7 @@ class RecursiveVisitor {
13515
13640
  visitElement(ast, context) {
13516
13641
  this.visitChildren(context, (visit) => {
13517
13642
  visit(ast.attrs);
13643
+ visit(ast.directives);
13518
13644
  visit(ast.children);
13519
13645
  });
13520
13646
  }
@@ -13535,6 +13661,17 @@ class RecursiveVisitor {
13535
13661
  }
13536
13662
  visitBlockParameter(ast, context) { }
13537
13663
  visitLetDeclaration(decl, context) { }
13664
+ visitComponent(component, context) {
13665
+ this.visitChildren(context, (visit) => {
13666
+ visit(component.attrs);
13667
+ visit(component.children);
13668
+ });
13669
+ }
13670
+ visitDirective(directive, context) {
13671
+ this.visitChildren(context, (visit) => {
13672
+ visit(directive.attrs);
13673
+ });
13674
+ }
13538
13675
  visitChildren(context, cb) {
13539
13676
  let results = [];
13540
13677
  let t = this;
@@ -15738,11 +15875,13 @@ class _Tokenizer {
15738
15875
  _currentTokenStart = null;
15739
15876
  _currentTokenType = null;
15740
15877
  _expansionCaseStack = [];
15878
+ _openDirectiveCount = 0;
15741
15879
  _inInterpolation = false;
15742
15880
  _preserveLineEndings;
15743
15881
  _i18nNormalizeLineEndingsInICUs;
15744
15882
  _tokenizeBlocks;
15745
15883
  _tokenizeLet;
15884
+ _selectorlessEnabled;
15746
15885
  tokens = [];
15747
15886
  errors = [];
15748
15887
  nonNormalizedIcuExpressions = [];
@@ -15770,6 +15909,7 @@ class _Tokenizer {
15770
15909
  this._i18nNormalizeLineEndingsInICUs = options.i18nNormalizeLineEndingsInICUs || false;
15771
15910
  this._tokenizeBlocks = options.tokenizeBlocks ?? true;
15772
15911
  this._tokenizeLet = options.tokenizeLet ?? true;
15912
+ this._selectorlessEnabled = options.selectorlessEnabled ?? false;
15773
15913
  try {
15774
15914
  this._cursor.init();
15775
15915
  }
@@ -15838,7 +15978,7 @@ class _Tokenizer {
15838
15978
  this.handleError(e);
15839
15979
  }
15840
15980
  }
15841
- this._beginToken(33 /* TokenType.EOF */);
15981
+ this._beginToken(41 /* TokenType.EOF */);
15842
15982
  this._endToken([]);
15843
15983
  }
15844
15984
  _getBlockName() {
@@ -16237,7 +16377,7 @@ class _Tokenizer {
16237
16377
  this._cursor.advance();
16238
16378
  this._endToken([content]);
16239
16379
  }
16240
- _consumePrefixAndName() {
16380
+ _consumePrefixAndName(endPredicate) {
16241
16381
  const nameOrPrefixStart = this._cursor.clone();
16242
16382
  let prefix = '';
16243
16383
  while (this._cursor.peek() !== $COLON && !isPrefixEnd(this._cursor.peek())) {
@@ -16252,41 +16392,64 @@ class _Tokenizer {
16252
16392
  else {
16253
16393
  nameStart = nameOrPrefixStart;
16254
16394
  }
16255
- this._requireCharCodeUntilFn(isNameEnd, prefix === '' ? 0 : 1);
16395
+ this._requireCharCodeUntilFn(endPredicate, prefix === '' ? 0 : 1);
16256
16396
  const name = this._cursor.getChars(nameStart);
16257
16397
  return [prefix, name];
16258
16398
  }
16259
16399
  _consumeTagOpen(start) {
16260
16400
  let tagName;
16261
16401
  let prefix;
16262
- let openTagToken;
16402
+ let closingTagName;
16403
+ let openToken;
16263
16404
  try {
16264
- if (!isAsciiLetter(this._cursor.peek())) {
16265
- throw this._createError(_unexpectedCharacterErrorMsg(this._cursor.peek()), this._cursor.getSpan(start));
16266
- }
16267
- openTagToken = this._consumeTagOpenStart(start);
16268
- prefix = openTagToken.parts[0];
16269
- tagName = openTagToken.parts[1];
16270
- this._attemptCharCodeUntilFn(isNotWhitespace);
16271
- while (this._cursor.peek() !== $SLASH &&
16272
- this._cursor.peek() !== $GT &&
16273
- this._cursor.peek() !== $LT &&
16274
- this._cursor.peek() !== $EOF) {
16275
- this._consumeAttributeName();
16405
+ if (this._selectorlessEnabled && isSelectorlessNameStart(this._cursor.peek())) {
16406
+ openToken = this._consumeComponentOpenStart(start);
16407
+ [closingTagName, prefix, tagName] = openToken.parts;
16408
+ if (prefix) {
16409
+ closingTagName += `:${prefix}`;
16410
+ }
16411
+ if (tagName) {
16412
+ closingTagName += `:${tagName}`;
16413
+ }
16276
16414
  this._attemptCharCodeUntilFn(isNotWhitespace);
16277
- if (this._attemptCharCode($EQ)) {
16278
- this._attemptCharCodeUntilFn(isNotWhitespace);
16279
- this._consumeAttributeValue();
16415
+ }
16416
+ else {
16417
+ if (!isAsciiLetter(this._cursor.peek())) {
16418
+ throw this._createError(_unexpectedCharacterErrorMsg(this._cursor.peek()), this._cursor.getSpan(start));
16280
16419
  }
16420
+ openToken = this._consumeTagOpenStart(start);
16421
+ prefix = openToken.parts[0];
16422
+ tagName = closingTagName = openToken.parts[1];
16281
16423
  this._attemptCharCodeUntilFn(isNotWhitespace);
16282
16424
  }
16283
- this._consumeTagOpenEnd();
16425
+ while (!isAttributeTerminator(this._cursor.peek())) {
16426
+ if (this._selectorlessEnabled && this._cursor.peek() === $AT) {
16427
+ const start = this._cursor.clone();
16428
+ const nameStart = start.clone();
16429
+ nameStart.advance();
16430
+ if (isSelectorlessNameStart(nameStart.peek())) {
16431
+ this._consumeDirective(start, nameStart);
16432
+ }
16433
+ }
16434
+ else {
16435
+ this._consumeAttribute();
16436
+ }
16437
+ }
16438
+ if (openToken.type === 33 /* TokenType.COMPONENT_OPEN_START */) {
16439
+ this._consumeComponentOpenEnd();
16440
+ }
16441
+ else {
16442
+ this._consumeTagOpenEnd();
16443
+ }
16284
16444
  }
16285
16445
  catch (e) {
16286
16446
  if (e instanceof _ControlFlowError) {
16287
- if (openTagToken) {
16447
+ if (openToken) {
16288
16448
  // We errored before we could close the opening tag, so it is incomplete.
16289
- openTagToken.type = 4 /* TokenType.INCOMPLETE_TAG_OPEN */;
16449
+ openToken.type =
16450
+ openToken.type === 33 /* TokenType.COMPONENT_OPEN_START */
16451
+ ? 37 /* TokenType.INCOMPLETE_COMPONENT_OPEN */
16452
+ : 4 /* TokenType.INCOMPLETE_TAG_OPEN */;
16290
16453
  }
16291
16454
  else {
16292
16455
  // When the start tag is invalid, assume we want a "<" as text.
@@ -16300,13 +16463,13 @@ class _Tokenizer {
16300
16463
  }
16301
16464
  const contentTokenType = this._getTagDefinition(tagName).getContentType(prefix);
16302
16465
  if (contentTokenType === exports.TagContentType.RAW_TEXT) {
16303
- this._consumeRawTextWithTagClose(prefix, tagName, false);
16466
+ this._consumeRawTextWithTagClose(openToken, closingTagName, false);
16304
16467
  }
16305
16468
  else if (contentTokenType === exports.TagContentType.ESCAPABLE_RAW_TEXT) {
16306
- this._consumeRawTextWithTagClose(prefix, tagName, true);
16469
+ this._consumeRawTextWithTagClose(openToken, closingTagName, true);
16307
16470
  }
16308
16471
  }
16309
- _consumeRawTextWithTagClose(prefix, tagName, consumeEntities) {
16472
+ _consumeRawTextWithTagClose(openToken, tagName, consumeEntities) {
16310
16473
  this._consumeRawText(consumeEntities, () => {
16311
16474
  if (!this._attemptCharCode($LT))
16312
16475
  return false;
@@ -16318,23 +16481,79 @@ class _Tokenizer {
16318
16481
  this._attemptCharCodeUntilFn(isNotWhitespace);
16319
16482
  return this._attemptCharCode($GT);
16320
16483
  });
16321
- this._beginToken(3 /* TokenType.TAG_CLOSE */);
16484
+ this._beginToken(openToken.type === 33 /* TokenType.COMPONENT_OPEN_START */
16485
+ ? 36 /* TokenType.COMPONENT_CLOSE */
16486
+ : 3 /* TokenType.TAG_CLOSE */);
16322
16487
  this._requireCharCodeUntilFn((code) => code === $GT, 3);
16323
16488
  this._cursor.advance(); // Consume the `>`
16324
- this._endToken([prefix, tagName]);
16489
+ this._endToken(openToken.parts);
16325
16490
  }
16326
16491
  _consumeTagOpenStart(start) {
16327
16492
  this._beginToken(0 /* TokenType.TAG_OPEN_START */, start);
16328
- const parts = this._consumePrefixAndName();
16493
+ const parts = this._consumePrefixAndName(isNameEnd);
16494
+ return this._endToken(parts);
16495
+ }
16496
+ _consumeComponentOpenStart(start) {
16497
+ this._beginToken(33 /* TokenType.COMPONENT_OPEN_START */, start);
16498
+ const parts = this._consumeComponentName();
16329
16499
  return this._endToken(parts);
16330
16500
  }
16501
+ _consumeComponentName() {
16502
+ const nameStart = this._cursor.clone();
16503
+ while (isSelectorlessNameChar(this._cursor.peek())) {
16504
+ this._cursor.advance();
16505
+ }
16506
+ const name = this._cursor.getChars(nameStart);
16507
+ let prefix = '';
16508
+ let tagName = '';
16509
+ if (this._cursor.peek() === $COLON) {
16510
+ this._cursor.advance();
16511
+ [prefix, tagName] = this._consumePrefixAndName(isNameEnd);
16512
+ }
16513
+ return [name, prefix, tagName];
16514
+ }
16515
+ _consumeAttribute() {
16516
+ this._consumeAttributeName();
16517
+ this._attemptCharCodeUntilFn(isNotWhitespace);
16518
+ if (this._attemptCharCode($EQ)) {
16519
+ this._attemptCharCodeUntilFn(isNotWhitespace);
16520
+ this._consumeAttributeValue();
16521
+ }
16522
+ this._attemptCharCodeUntilFn(isNotWhitespace);
16523
+ }
16331
16524
  _consumeAttributeName() {
16332
16525
  const attrNameStart = this._cursor.peek();
16333
16526
  if (attrNameStart === $SQ || attrNameStart === $DQ) {
16334
16527
  throw this._createError(_unexpectedCharacterErrorMsg(attrNameStart), this._cursor.getSpan());
16335
16528
  }
16336
16529
  this._beginToken(14 /* TokenType.ATTR_NAME */);
16337
- const prefixAndName = this._consumePrefixAndName();
16530
+ let nameEndPredicate;
16531
+ if (this._openDirectiveCount > 0) {
16532
+ // If we're parsing attributes inside of directive syntax, we have to terminate the name
16533
+ // on the first non-matching closing paren. For example, if we have `@Dir(someAttr)`,
16534
+ // `@Dir` and `(` will have already been captured as `DIRECTIVE_NAME` and `DIRECTIVE_OPEN`
16535
+ // respectively, but the `)` will get captured as a part of the name for `someAttr`
16536
+ // because normally that would be an event binding.
16537
+ let openParens = 0;
16538
+ nameEndPredicate = (code) => {
16539
+ if (this._openDirectiveCount > 0) {
16540
+ if (code === $LPAREN) {
16541
+ openParens++;
16542
+ }
16543
+ else if (code === $RPAREN) {
16544
+ if (openParens === 0) {
16545
+ return true;
16546
+ }
16547
+ openParens--;
16548
+ }
16549
+ }
16550
+ return isNameEnd(code);
16551
+ };
16552
+ }
16553
+ else {
16554
+ nameEndPredicate = isNameEnd;
16555
+ }
16556
+ const prefixAndName = this._consumePrefixAndName(nameEndPredicate);
16338
16557
  this._endToken(prefixAndName);
16339
16558
  }
16340
16559
  _consumeAttributeValue() {
@@ -16365,10 +16584,32 @@ class _Tokenizer {
16365
16584
  this._requireCharCode($GT);
16366
16585
  this._endToken([]);
16367
16586
  }
16587
+ _consumeComponentOpenEnd() {
16588
+ const tokenType = this._attemptCharCode($SLASH)
16589
+ ? 35 /* TokenType.COMPONENT_OPEN_END_VOID */
16590
+ : 34 /* TokenType.COMPONENT_OPEN_END */;
16591
+ this._beginToken(tokenType);
16592
+ this._requireCharCode($GT);
16593
+ this._endToken([]);
16594
+ }
16368
16595
  _consumeTagClose(start) {
16596
+ if (this._selectorlessEnabled) {
16597
+ const clone = start.clone();
16598
+ while (clone.peek() !== $GT && !isSelectorlessNameStart(clone.peek())) {
16599
+ clone.advance();
16600
+ }
16601
+ if (isSelectorlessNameStart(clone.peek())) {
16602
+ this._beginToken(36 /* TokenType.COMPONENT_CLOSE */, start);
16603
+ const parts = this._consumeComponentName();
16604
+ this._attemptCharCodeUntilFn(isNotWhitespace);
16605
+ this._requireCharCode($GT);
16606
+ this._endToken(parts);
16607
+ return;
16608
+ }
16609
+ }
16369
16610
  this._beginToken(3 /* TokenType.TAG_CLOSE */, start);
16370
16611
  this._attemptCharCodeUntilFn(isNotWhitespace);
16371
- const prefixAndName = this._consumePrefixAndName();
16612
+ const prefixAndName = this._consumePrefixAndName(isNameEnd);
16372
16613
  this._attemptCharCodeUntilFn(isNotWhitespace);
16373
16614
  this._requireCharCode($GT);
16374
16615
  this._endToken(prefixAndName);
@@ -16524,6 +16765,50 @@ class _Tokenizer {
16524
16765
  parts.push(this._getProcessedChars(expressionStart, this._cursor));
16525
16766
  this._endToken(parts);
16526
16767
  }
16768
+ _consumeDirective(start, nameStart) {
16769
+ this._requireCharCode($AT);
16770
+ // Skip over the @ since it's not part of the name.
16771
+ this._cursor.advance();
16772
+ // Capture the rest of the name.
16773
+ while (isSelectorlessNameChar(this._cursor.peek())) {
16774
+ this._cursor.advance();
16775
+ }
16776
+ // Capture the opening token.
16777
+ this._beginToken(38 /* TokenType.DIRECTIVE_NAME */, start);
16778
+ const name = this._cursor.getChars(nameStart);
16779
+ this._endToken([name]);
16780
+ this._attemptCharCodeUntilFn(isNotWhitespace);
16781
+ // Optionally there might be attributes bound to the specific directive.
16782
+ // Stop parsing if there's no opening character for them.
16783
+ if (this._cursor.peek() !== $LPAREN) {
16784
+ return;
16785
+ }
16786
+ this._openDirectiveCount++;
16787
+ this._beginToken(39 /* TokenType.DIRECTIVE_OPEN */);
16788
+ this._cursor.advance();
16789
+ this._endToken([]);
16790
+ this._attemptCharCodeUntilFn(isNotWhitespace);
16791
+ // Capture all the attributes until we hit a closing paren.
16792
+ while (!isAttributeTerminator(this._cursor.peek()) && this._cursor.peek() !== $RPAREN) {
16793
+ this._consumeAttribute();
16794
+ }
16795
+ // Trim any trailing whitespace.
16796
+ this._attemptCharCodeUntilFn(isNotWhitespace);
16797
+ this._openDirectiveCount--;
16798
+ if (this._cursor.peek() !== $RPAREN) {
16799
+ // Stop parsing, instead of throwing, if we've hit the end of the tag.
16800
+ // This can be handled better later when turning the tokens into AST.
16801
+ if (this._cursor.peek() === $GT || this._cursor.peek() === $SLASH) {
16802
+ return;
16803
+ }
16804
+ throw this._createError(_unexpectedCharacterErrorMsg(this._cursor.peek()), this._cursor.getSpan(start));
16805
+ }
16806
+ // Capture the closing token.
16807
+ this._beginToken(40 /* TokenType.DIRECTIVE_CLOSE */);
16808
+ this._cursor.advance();
16809
+ this._endToken([]);
16810
+ this._attemptCharCodeUntilFn(isNotWhitespace);
16811
+ }
16527
16812
  _getProcessedChars(start, end) {
16528
16813
  return this._processCarriageReturns(end.getChars(start));
16529
16814
  }
@@ -16639,6 +16924,15 @@ function isBlockNameChar(code) {
16639
16924
  function isBlockParameterChar(code) {
16640
16925
  return code !== $SEMICOLON && isNotWhitespace(code);
16641
16926
  }
16927
+ function isSelectorlessNameStart(code) {
16928
+ return code === $_ || (code >= $A && code <= $Z);
16929
+ }
16930
+ function isSelectorlessNameChar(code) {
16931
+ return isAsciiLetter(code) || isDigit(code) || code === $_;
16932
+ }
16933
+ function isAttributeTerminator(code) {
16934
+ return code === $SLASH || code === $GT || code === $LT || code === $EOF;
16935
+ }
16642
16936
  function mergeTextTokens(srcTokens) {
16643
16937
  const dstTokens = [];
16644
16938
  let lastDstToken = undefined;
@@ -16935,26 +17229,26 @@ let Parser$1 = class Parser {
16935
17229
  };
16936
17230
  class _TreeBuilder {
16937
17231
  tokens;
16938
- getTagDefinition;
17232
+ tagDefinitionResolver;
16939
17233
  _index = -1;
16940
17234
  // `_peek` will be initialized by the call to `_advance()` in the constructor.
16941
17235
  _peek;
16942
17236
  _containerStack = [];
16943
17237
  rootNodes = [];
16944
17238
  errors = [];
16945
- constructor(tokens, getTagDefinition) {
17239
+ constructor(tokens, tagDefinitionResolver) {
16946
17240
  this.tokens = tokens;
16947
- this.getTagDefinition = getTagDefinition;
17241
+ this.tagDefinitionResolver = tagDefinitionResolver;
16948
17242
  this._advance();
16949
17243
  }
16950
17244
  build() {
16951
- while (this._peek.type !== 33 /* TokenType.EOF */) {
17245
+ while (this._peek.type !== 41 /* TokenType.EOF */) {
16952
17246
  if (this._peek.type === 0 /* TokenType.TAG_OPEN_START */ ||
16953
17247
  this._peek.type === 4 /* TokenType.INCOMPLETE_TAG_OPEN */) {
16954
- this._consumeStartTag(this._advance());
17248
+ this._consumeElementStartTag(this._advance());
16955
17249
  }
16956
17250
  else if (this._peek.type === 3 /* TokenType.TAG_CLOSE */) {
16957
- this._consumeEndTag(this._advance());
17251
+ this._consumeElementEndTag(this._advance());
16958
17252
  }
16959
17253
  else if (this._peek.type === 12 /* TokenType.CDATA_START */) {
16960
17254
  this._closeVoidElement();
@@ -16993,6 +17287,13 @@ class _TreeBuilder {
16993
17287
  this._closeVoidElement();
16994
17288
  this._consumeIncompleteLet(this._advance());
16995
17289
  }
17290
+ else if (this._peek.type === 33 /* TokenType.COMPONENT_OPEN_START */ ||
17291
+ this._peek.type === 37 /* TokenType.INCOMPLETE_COMPONENT_OPEN */) {
17292
+ this._consumeComponentStartTag(this._advance());
17293
+ }
17294
+ else if (this._peek.type === 36 /* TokenType.COMPONENT_CLOSE */) {
17295
+ this._consumeComponentEndTag(this._advance());
17296
+ }
16996
17297
  else {
16997
17298
  // Skip all other tokens...
16998
17299
  this._advance();
@@ -17066,9 +17367,9 @@ class _TreeBuilder {
17066
17367
  if (!exp)
17067
17368
  return null;
17068
17369
  const end = this._advance();
17069
- exp.push({ type: 33 /* TokenType.EOF */, parts: [], sourceSpan: end.sourceSpan });
17370
+ exp.push({ type: 41 /* TokenType.EOF */, parts: [], sourceSpan: end.sourceSpan });
17070
17371
  // parse everything in between { and }
17071
- const expansionCaseParser = new _TreeBuilder(exp, this.getTagDefinition);
17372
+ const expansionCaseParser = new _TreeBuilder(exp, this.tagDefinitionResolver);
17072
17373
  expansionCaseParser.build();
17073
17374
  if (expansionCaseParser.errors.length > 0) {
17074
17375
  this.errors = this.errors.concat(expansionCaseParser.errors);
@@ -17106,7 +17407,7 @@ class _TreeBuilder {
17106
17407
  return null;
17107
17408
  }
17108
17409
  }
17109
- if (this._peek.type === 33 /* TokenType.EOF */) {
17410
+ if (this._peek.type === 41 /* TokenType.EOF */) {
17110
17411
  this.errors.push(TreeError.create(null, start.sourceSpan, `Invalid ICU message. Missing '}'.`));
17111
17412
  return null;
17112
17413
  }
@@ -17121,7 +17422,7 @@ class _TreeBuilder {
17121
17422
  const parent = this._getContainer();
17122
17423
  if (parent != null &&
17123
17424
  parent.children.length === 0 &&
17124
- this.getTagDefinition(parent.name).ignoreFirstLf) {
17425
+ this._getTagDefinition(parent)?.ignoreFirstLf) {
17125
17426
  text = text.substring(1);
17126
17427
  tokens[0] = { type: token.type, sourceSpan: token.sourceSpan, parts: [text] };
17127
17428
  }
@@ -17152,25 +17453,23 @@ class _TreeBuilder {
17152
17453
  }
17153
17454
  _closeVoidElement() {
17154
17455
  const el = this._getContainer();
17155
- if (el instanceof Element && this.getTagDefinition(el.name).isVoid) {
17456
+ if (el !== null && this._getTagDefinition(el)?.isVoid) {
17156
17457
  this._containerStack.pop();
17157
17458
  }
17158
17459
  }
17159
- _consumeStartTag(startTagToken) {
17160
- const [prefix, name] = startTagToken.parts;
17460
+ _consumeElementStartTag(startTagToken) {
17161
17461
  const attrs = [];
17162
- while (this._peek.type === 14 /* TokenType.ATTR_NAME */) {
17163
- attrs.push(this._consumeAttr(this._advance()));
17164
- }
17165
- const fullName = this._getElementFullName(prefix, name, this._getClosestParentElement());
17462
+ const directives = [];
17463
+ this._consumeAttributesAndDirectives(attrs, directives);
17464
+ const fullName = this._getElementFullName(startTagToken, this._getClosestElementLikeParent());
17166
17465
  let selfClosing = false;
17167
17466
  // Note: There could have been a tokenizer error
17168
17467
  // so that we don't get a token for the end tag...
17169
17468
  if (this._peek.type === 2 /* TokenType.TAG_OPEN_END_VOID */) {
17170
17469
  this._advance();
17171
17470
  selfClosing = true;
17172
- const tagDef = this.getTagDefinition(fullName);
17173
- if (!(tagDef.canSelfClose || getNsPrefix(fullName) !== null || tagDef.isVoid)) {
17471
+ const tagDef = this._getTagDefinition(fullName);
17472
+ if (!(tagDef?.canSelfClose || getNsPrefix(fullName) !== null || tagDef?.isVoid)) {
17174
17473
  this.errors.push(TreeError.create(fullName, startTagToken.sourceSpan, `Only void, custom and foreign elements can be self closed "${startTagToken.parts[1]}"`));
17175
17474
  }
17176
17475
  }
@@ -17182,10 +17481,10 @@ class _TreeBuilder {
17182
17481
  const span = new ParseSourceSpan(startTagToken.sourceSpan.start, end, startTagToken.sourceSpan.fullStart);
17183
17482
  // Create a separate `startSpan` because `span` will be modified when there is an `end` span.
17184
17483
  const startSpan = new ParseSourceSpan(startTagToken.sourceSpan.start, end, startTagToken.sourceSpan.fullStart);
17185
- const el = new Element(fullName, attrs, [], span, startSpan, undefined);
17186
- const parentEl = this._getContainer();
17187
- this._pushContainer(el, parentEl instanceof Element &&
17188
- this.getTagDefinition(parentEl.name).isClosedByChild(el.name));
17484
+ const el = new Element(fullName, attrs, directives, [], span, startSpan, undefined);
17485
+ const parent = this._getContainer();
17486
+ const isClosedByChild = parent !== null && !!this._getTagDefinition(parent)?.isClosedByChild(el.name);
17487
+ this._pushContainer(el, isClosedByChild);
17189
17488
  if (selfClosing) {
17190
17489
  // Elements that are self-closed have their `endSourceSpan` set to the full span, as the
17191
17490
  // element start tag also represents the end tag.
@@ -17198,6 +17497,73 @@ class _TreeBuilder {
17198
17497
  this.errors.push(TreeError.create(fullName, span, `Opening tag "${fullName}" not terminated.`));
17199
17498
  }
17200
17499
  }
17500
+ _consumeComponentStartTag(startToken) {
17501
+ const componentName = startToken.parts[0];
17502
+ const attrs = [];
17503
+ const directives = [];
17504
+ this._consumeAttributesAndDirectives(attrs, directives);
17505
+ const closestElement = this._getClosestElementLikeParent();
17506
+ const tagName = this._getComponentTagName(startToken, closestElement);
17507
+ const fullName = this._getComponentFullName(startToken, closestElement);
17508
+ const selfClosing = this._peek.type === 35 /* TokenType.COMPONENT_OPEN_END_VOID */;
17509
+ this._advance();
17510
+ const end = this._peek.sourceSpan.fullStart;
17511
+ const span = new ParseSourceSpan(startToken.sourceSpan.start, end, startToken.sourceSpan.fullStart);
17512
+ const startSpan = new ParseSourceSpan(startToken.sourceSpan.start, end, startToken.sourceSpan.fullStart);
17513
+ const node = new Component(componentName, tagName, fullName, attrs, directives, [], span, startSpan, undefined);
17514
+ const parent = this._getContainer();
17515
+ const isClosedByChild = parent !== null &&
17516
+ node.tagName !== null &&
17517
+ !!this._getTagDefinition(parent)?.isClosedByChild(node.tagName);
17518
+ this._pushContainer(node, isClosedByChild);
17519
+ if (selfClosing) {
17520
+ this._popContainer(fullName, Component, span);
17521
+ }
17522
+ else if (startToken.type === 37 /* TokenType.INCOMPLETE_COMPONENT_OPEN */) {
17523
+ this._popContainer(fullName, Component, null);
17524
+ this.errors.push(TreeError.create(fullName, span, `Opening tag "${fullName}" not terminated.`));
17525
+ }
17526
+ }
17527
+ _consumeAttributesAndDirectives(attributesResult, directivesResult) {
17528
+ while (this._peek.type === 14 /* TokenType.ATTR_NAME */ ||
17529
+ this._peek.type === 38 /* TokenType.DIRECTIVE_NAME */) {
17530
+ if (this._peek.type === 38 /* TokenType.DIRECTIVE_NAME */) {
17531
+ directivesResult.push(this._consumeDirective(this._peek));
17532
+ }
17533
+ else {
17534
+ attributesResult.push(this._consumeAttr(this._advance()));
17535
+ }
17536
+ }
17537
+ }
17538
+ _consumeComponentEndTag(endToken) {
17539
+ const fullName = this._getComponentFullName(endToken, this._getClosestElementLikeParent());
17540
+ if (!this._popContainer(fullName, Component, endToken.sourceSpan)) {
17541
+ const container = this._containerStack[this._containerStack.length - 1];
17542
+ let suffix;
17543
+ if (container instanceof Component && container.componentName === endToken.parts[0]) {
17544
+ suffix = `, did you mean "${container.fullName}"?`;
17545
+ }
17546
+ else {
17547
+ suffix = '. It may happen when the tag has already been closed by another tag.';
17548
+ }
17549
+ const errMsg = `Unexpected closing tag "${fullName}"${suffix}`;
17550
+ this.errors.push(TreeError.create(fullName, endToken.sourceSpan, errMsg));
17551
+ }
17552
+ }
17553
+ _getTagDefinition(nodeOrName) {
17554
+ if (typeof nodeOrName === 'string') {
17555
+ return this.tagDefinitionResolver(nodeOrName);
17556
+ }
17557
+ else if (nodeOrName instanceof Element) {
17558
+ return this.tagDefinitionResolver(nodeOrName.name);
17559
+ }
17560
+ else if (nodeOrName instanceof Component && nodeOrName.tagName !== null) {
17561
+ return this.tagDefinitionResolver(nodeOrName.tagName);
17562
+ }
17563
+ else {
17564
+ return null;
17565
+ }
17566
+ }
17201
17567
  _pushContainer(node, isClosedByChild) {
17202
17568
  if (isClosedByChild) {
17203
17569
  this._containerStack.pop();
@@ -17205,9 +17571,9 @@ class _TreeBuilder {
17205
17571
  this._addToParent(node);
17206
17572
  this._containerStack.push(node);
17207
17573
  }
17208
- _consumeEndTag(endTagToken) {
17209
- const fullName = this._getElementFullName(endTagToken.parts[0], endTagToken.parts[1], this._getClosestParentElement());
17210
- if (this.getTagDefinition(fullName).isVoid) {
17574
+ _consumeElementEndTag(endTagToken) {
17575
+ const fullName = this._getElementFullName(endTagToken, this._getClosestElementLikeParent());
17576
+ if (this._getTagDefinition(fullName)?.isVoid) {
17211
17577
  this.errors.push(TreeError.create(fullName, endTagToken.sourceSpan, `Void elements do not have end tags "${endTagToken.parts[1]}"`));
17212
17578
  }
17213
17579
  else if (!this._popContainer(fullName, Element, endTagToken.sourceSpan)) {
@@ -17225,7 +17591,8 @@ class _TreeBuilder {
17225
17591
  let unexpectedCloseTagDetected = false;
17226
17592
  for (let stackIndex = this._containerStack.length - 1; stackIndex >= 0; stackIndex--) {
17227
17593
  const node = this._containerStack[stackIndex];
17228
- if ((node.name === expectedName || expectedName === null) && node instanceof expectedType) {
17594
+ const nodeName = node instanceof Component ? node.fullName : node.name;
17595
+ if ((nodeName === expectedName || expectedName === null) && node instanceof expectedType) {
17229
17596
  // Record the parse span with the element that is being closed. Any elements that are
17230
17597
  // removed from the element stack at this point are closed implicitly, so they won't get
17231
17598
  // an end source span (as there is no explicit closing element).
@@ -17235,8 +17602,7 @@ class _TreeBuilder {
17235
17602
  return !unexpectedCloseTagDetected;
17236
17603
  }
17237
17604
  // Blocks and most elements are not self closing.
17238
- if (node instanceof Block ||
17239
- (node instanceof Element && !this.getTagDefinition(node.name).closedByParent)) {
17605
+ if (node instanceof Block || !this._getTagDefinition(node)?.closedByParent) {
17240
17606
  // Note that we encountered an unexpected close tag but continue processing the element
17241
17607
  // stack so we can assign an `endSourceSpan` if there is a corresponding start tag for this
17242
17608
  // end tag in the stack.
@@ -17296,6 +17662,31 @@ class _TreeBuilder {
17296
17662
  new ParseSourceSpan(valueStartSpan.start, valueEnd, valueStartSpan.fullStart);
17297
17663
  return new Attribute(fullName, value, new ParseSourceSpan(attrName.sourceSpan.start, attrEnd, attrName.sourceSpan.fullStart), attrName.sourceSpan, valueSpan, valueTokens.length > 0 ? valueTokens : undefined, undefined);
17298
17664
  }
17665
+ _consumeDirective(nameToken) {
17666
+ const attributes = [];
17667
+ let startSourceSpanEnd = nameToken.sourceSpan.end;
17668
+ let endSourceSpan = null;
17669
+ this._advance();
17670
+ if (this._peek.type === 39 /* TokenType.DIRECTIVE_OPEN */) {
17671
+ // Capture the opening token in the start span.
17672
+ startSourceSpanEnd = this._peek.sourceSpan.end;
17673
+ this._advance();
17674
+ // Cast here is necessary, because TS doesn't know that `_advance` changed `_peek`.
17675
+ while (this._peek.type === 14 /* TokenType.ATTR_NAME */) {
17676
+ attributes.push(this._consumeAttr(this._advance()));
17677
+ }
17678
+ if (this._peek.type === 40 /* TokenType.DIRECTIVE_CLOSE */) {
17679
+ endSourceSpan = this._peek.sourceSpan;
17680
+ this._advance();
17681
+ }
17682
+ else {
17683
+ this.errors.push(TreeError.create(null, nameToken.sourceSpan, 'Unterminated directive definition'));
17684
+ }
17685
+ }
17686
+ const startSourceSpan = new ParseSourceSpan(nameToken.sourceSpan.start, startSourceSpanEnd, nameToken.sourceSpan.fullStart);
17687
+ const sourceSpan = new ParseSourceSpan(startSourceSpan.start, endSourceSpan === null ? nameToken.sourceSpan.end : endSourceSpan.end, startSourceSpan.fullStart);
17688
+ return new Directive(nameToken.parts[0], attributes, sourceSpan, startSourceSpan, endSourceSpan);
17689
+ }
17299
17690
  _consumeBlockOpen(token) {
17300
17691
  const parameters = [];
17301
17692
  while (this._peek.type === 27 /* TokenType.BLOCK_PARAMETER */) {
@@ -17386,10 +17777,11 @@ class _TreeBuilder {
17386
17777
  ? this._containerStack[this._containerStack.length - 1]
17387
17778
  : null;
17388
17779
  }
17389
- _getClosestParentElement() {
17780
+ _getClosestElementLikeParent() {
17390
17781
  for (let i = this._containerStack.length - 1; i > -1; i--) {
17391
- if (this._containerStack[i] instanceof Element) {
17392
- return this._containerStack[i];
17782
+ const current = this._containerStack[i];
17783
+ if (current instanceof Element || current instanceof Component) {
17784
+ return current;
17393
17785
  }
17394
17786
  }
17395
17787
  return null;
@@ -17403,18 +17795,57 @@ class _TreeBuilder {
17403
17795
  parent.children.push(node);
17404
17796
  }
17405
17797
  }
17406
- _getElementFullName(prefix, localName, parentElement) {
17407
- if (prefix === '') {
17408
- prefix = this.getTagDefinition(localName).implicitNamespacePrefix || '';
17409
- if (prefix === '' && parentElement != null) {
17410
- const parentTagName = splitNsName(parentElement.name)[1];
17411
- const parentTagDefinition = this.getTagDefinition(parentTagName);
17412
- if (!parentTagDefinition.preventNamespaceInheritance) {
17413
- prefix = getNsPrefix(parentElement.name);
17798
+ _getElementFullName(token, parent) {
17799
+ const prefix = this._getPrefix(token, parent);
17800
+ return mergeNsAndName(prefix, token.parts[1]);
17801
+ }
17802
+ _getComponentFullName(token, parent) {
17803
+ const componentName = token.parts[0];
17804
+ const tagName = this._getComponentTagName(token, parent);
17805
+ if (tagName === null) {
17806
+ return componentName;
17807
+ }
17808
+ return tagName.startsWith(':') ? componentName + tagName : `${componentName}:${tagName}`;
17809
+ }
17810
+ _getComponentTagName(token, parent) {
17811
+ const prefix = this._getPrefix(token, parent);
17812
+ const tagName = token.parts[2];
17813
+ if (!prefix && !tagName) {
17814
+ return null;
17815
+ }
17816
+ else if (!prefix && tagName) {
17817
+ return tagName;
17818
+ }
17819
+ else {
17820
+ // TODO(crisbeto): re-evaluate this fallback. Maybe base it off the class name?
17821
+ return mergeNsAndName(prefix, tagName || 'ng-component');
17822
+ }
17823
+ }
17824
+ _getPrefix(token, parent) {
17825
+ let prefix;
17826
+ let tagName;
17827
+ if (token.type === 33 /* TokenType.COMPONENT_OPEN_START */ ||
17828
+ token.type === 37 /* TokenType.INCOMPLETE_COMPONENT_OPEN */ ||
17829
+ token.type === 36 /* TokenType.COMPONENT_CLOSE */) {
17830
+ prefix = token.parts[1];
17831
+ tagName = token.parts[2];
17832
+ }
17833
+ else {
17834
+ prefix = token.parts[0];
17835
+ tagName = token.parts[1];
17836
+ }
17837
+ prefix = prefix || this._getTagDefinition(tagName)?.implicitNamespacePrefix || '';
17838
+ if (!prefix && parent) {
17839
+ const parentName = parent instanceof Element ? parent.name : parent.tagName;
17840
+ if (parentName !== null) {
17841
+ const parentTagName = splitNsName(parentName)[1];
17842
+ const parentTagDefinition = this._getTagDefinition(parentTagName);
17843
+ if (parentTagDefinition !== null && !parentTagDefinition.preventNamespaceInheritance) {
17844
+ prefix = getNsPrefix(parentName);
17414
17845
  }
17415
17846
  }
17416
17847
  }
17417
- return mergeNsAndName(prefix, localName);
17848
+ return prefix;
17418
17849
  }
17419
17850
  }
17420
17851
  function lastOnStack(stack, element) {
@@ -17493,11 +17924,11 @@ class WhitespaceVisitor {
17493
17924
  if (SKIP_WS_TRIM_TAGS.has(element.name) || hasPreserveWhitespacesAttr(element.attrs)) {
17494
17925
  // don't descent into elements where we need to preserve whitespaces
17495
17926
  // but still visit all attributes to eliminate one used as a market to preserve WS
17496
- const newElement = new Element(element.name, visitAllWithSiblings(this, element.attrs), element.children, element.sourceSpan, element.startSourceSpan, element.endSourceSpan, element.i18n);
17927
+ const newElement = new Element(element.name, visitAllWithSiblings(this, element.attrs), visitAllWithSiblings(this, element.directives), element.children, element.sourceSpan, element.startSourceSpan, element.endSourceSpan, element.i18n);
17497
17928
  this.originalNodeMap?.set(newElement, element);
17498
17929
  return newElement;
17499
17930
  }
17500
- const newElement = new Element(element.name, element.attrs, visitAllWithSiblings(this, element.children), element.sourceSpan, element.startSourceSpan, element.endSourceSpan, element.i18n);
17931
+ const newElement = new Element(element.name, element.attrs, element.directives, visitAllWithSiblings(this, element.children), element.sourceSpan, element.startSourceSpan, element.endSourceSpan, element.i18n);
17501
17932
  this.originalNodeMap?.set(newElement, element);
17502
17933
  return newElement;
17503
17934
  }
@@ -17571,6 +18002,22 @@ class WhitespaceVisitor {
17571
18002
  visitLetDeclaration(decl, context) {
17572
18003
  return decl;
17573
18004
  }
18005
+ visitComponent(node, context) {
18006
+ if ((node.tagName && SKIP_WS_TRIM_TAGS.has(node.tagName)) ||
18007
+ hasPreserveWhitespacesAttr(node.attrs)) {
18008
+ // don't descent into elements where we need to preserve whitespaces
18009
+ // but still visit all attributes to eliminate one used as a market to preserve WS
18010
+ const newElement = new Component(node.componentName, node.tagName, node.fullName, visitAllWithSiblings(this, node.attrs), visitAllWithSiblings(this, node.directives), node.children, node.sourceSpan, node.startSourceSpan, node.endSourceSpan, node.i18n);
18011
+ this.originalNodeMap?.set(newElement, node);
18012
+ return newElement;
18013
+ }
18014
+ const newElement = new Component(node.componentName, node.tagName, node.fullName, node.attrs, node.directives, visitAllWithSiblings(this, node.children), node.sourceSpan, node.startSourceSpan, node.endSourceSpan, node.i18n);
18015
+ this.originalNodeMap?.set(newElement, node);
18016
+ return newElement;
18017
+ }
18018
+ visitDirective(directive, context) {
18019
+ return directive;
18020
+ }
17574
18021
  visit(_node, context) {
17575
18022
  // `visitAllWithSiblings` provides context necessary for ICU messages to be handled correctly.
17576
18023
  // Prefer that over calling `html.visitAll` directly on this visitor.
@@ -19754,7 +20201,7 @@ const SCHEMA = [
19754
20201
  /* added manually to avoid breaking changes */
19755
20202
  ',*message,*mozfullscreenchange,*mozfullscreenerror,*mozpointerlockchange,*mozpointerlockerror,*webglcontextcreationerror,*webglcontextlost,*webglcontextrestored',
19756
20203
  '[HTMLElement]^[Element]|accessKey,autocapitalize,!autofocus,contentEditable,dir,!draggable,enterKeyHint,!hidden,!inert,innerText,inputMode,lang,nonce,*abort,*animationend,*animationiteration,*animationstart,*auxclick,*beforexrselect,*blur,*cancel,*canplay,*canplaythrough,*change,*click,*close,*contextmenu,*copy,*cuechange,*cut,*dblclick,*drag,*dragend,*dragenter,*dragleave,*dragover,*dragstart,*drop,*durationchange,*emptied,*ended,*error,*focus,*formdata,*gotpointercapture,*input,*invalid,*keydown,*keypress,*keyup,*load,*loadeddata,*loadedmetadata,*loadstart,*lostpointercapture,*mousedown,*mouseenter,*mouseleave,*mousemove,*mouseout,*mouseover,*mouseup,*mousewheel,*paste,*pause,*play,*playing,*pointercancel,*pointerdown,*pointerenter,*pointerleave,*pointermove,*pointerout,*pointerover,*pointerrawupdate,*pointerup,*progress,*ratechange,*reset,*resize,*scroll,*securitypolicyviolation,*seeked,*seeking,*select,*selectionchange,*selectstart,*slotchange,*stalled,*submit,*suspend,*timeupdate,*toggle,*transitioncancel,*transitionend,*transitionrun,*transitionstart,*volumechange,*waiting,*webkitanimationend,*webkitanimationiteration,*webkitanimationstart,*webkittransitionend,*wheel,outerText,!spellcheck,%style,#tabIndex,title,!translate,virtualKeyboardPolicy',
19757
- 'abbr,address,article,aside,b,bdi,bdo,cite,content,code,dd,dfn,dt,em,figcaption,figure,footer,header,hgroup,i,kbd,main,mark,nav,noscript,rb,rp,rt,rtc,ruby,s,samp,section,small,strong,sub,sup,u,var,wbr^[HTMLElement]|accessKey,autocapitalize,!autofocus,contentEditable,dir,!draggable,enterKeyHint,!hidden,innerText,inputMode,lang,nonce,*abort,*animationend,*animationiteration,*animationstart,*auxclick,*beforexrselect,*blur,*cancel,*canplay,*canplaythrough,*change,*click,*close,*contextmenu,*copy,*cuechange,*cut,*dblclick,*drag,*dragend,*dragenter,*dragleave,*dragover,*dragstart,*drop,*durationchange,*emptied,*ended,*error,*focus,*formdata,*gotpointercapture,*input,*invalid,*keydown,*keypress,*keyup,*load,*loadeddata,*loadedmetadata,*loadstart,*lostpointercapture,*mousedown,*mouseenter,*mouseleave,*mousemove,*mouseout,*mouseover,*mouseup,*mousewheel,*paste,*pause,*play,*playing,*pointercancel,*pointerdown,*pointerenter,*pointerleave,*pointermove,*pointerout,*pointerover,*pointerrawupdate,*pointerup,*progress,*ratechange,*reset,*resize,*scroll,*securitypolicyviolation,*seeked,*seeking,*select,*selectionchange,*selectstart,*slotchange,*stalled,*submit,*suspend,*timeupdate,*toggle,*transitioncancel,*transitionend,*transitionrun,*transitionstart,*volumechange,*waiting,*webkitanimationend,*webkitanimationiteration,*webkitanimationstart,*webkittransitionend,*wheel,outerText,!spellcheck,%style,#tabIndex,title,!translate,virtualKeyboardPolicy',
20204
+ 'abbr,address,article,aside,b,bdi,bdo,cite,content,code,dd,dfn,dt,em,figcaption,figure,footer,header,hgroup,i,kbd,main,mark,nav,noscript,rb,rp,rt,rtc,ruby,s,samp,search,section,small,strong,sub,sup,u,var,wbr^[HTMLElement]|accessKey,autocapitalize,!autofocus,contentEditable,dir,!draggable,enterKeyHint,!hidden,innerText,inputMode,lang,nonce,*abort,*animationend,*animationiteration,*animationstart,*auxclick,*beforexrselect,*blur,*cancel,*canplay,*canplaythrough,*change,*click,*close,*contextmenu,*copy,*cuechange,*cut,*dblclick,*drag,*dragend,*dragenter,*dragleave,*dragover,*dragstart,*drop,*durationchange,*emptied,*ended,*error,*focus,*formdata,*gotpointercapture,*input,*invalid,*keydown,*keypress,*keyup,*load,*loadeddata,*loadedmetadata,*loadstart,*lostpointercapture,*mousedown,*mouseenter,*mouseleave,*mousemove,*mouseout,*mouseover,*mouseup,*mousewheel,*paste,*pause,*play,*playing,*pointercancel,*pointerdown,*pointerenter,*pointerleave,*pointermove,*pointerout,*pointerover,*pointerrawupdate,*pointerup,*progress,*ratechange,*reset,*resize,*scroll,*securitypolicyviolation,*seeked,*seeking,*select,*selectionchange,*selectstart,*slotchange,*stalled,*submit,*suspend,*timeupdate,*toggle,*transitioncancel,*transitionend,*transitionrun,*transitionstart,*volumechange,*waiting,*webkitanimationend,*webkitanimationiteration,*webkitanimationstart,*webkittransitionend,*wheel,outerText,!spellcheck,%style,#tabIndex,title,!translate,virtualKeyboardPolicy',
19758
20205
  'media^[HTMLElement]|!autoplay,!controls,%controlsList,%crossOrigin,#currentTime,!defaultMuted,#defaultPlaybackRate,!disableRemotePlayback,!loop,!muted,*encrypted,*waitingforkey,#playbackRate,preload,!preservesPitch,src,%srcObject,#volume',
19759
20206
  ':svg:^[HTMLElement]|!autofocus,nonce,*abort,*animationend,*animationiteration,*animationstart,*auxclick,*beforexrselect,*blur,*cancel,*canplay,*canplaythrough,*change,*click,*close,*contextmenu,*copy,*cuechange,*cut,*dblclick,*drag,*dragend,*dragenter,*dragleave,*dragover,*dragstart,*drop,*durationchange,*emptied,*ended,*error,*focus,*formdata,*gotpointercapture,*input,*invalid,*keydown,*keypress,*keyup,*load,*loadeddata,*loadedmetadata,*loadstart,*lostpointercapture,*mousedown,*mouseenter,*mouseleave,*mousemove,*mouseout,*mouseover,*mouseup,*mousewheel,*paste,*pause,*play,*playing,*pointercancel,*pointerdown,*pointerenter,*pointerleave,*pointermove,*pointerout,*pointerover,*pointerrawupdate,*pointerup,*progress,*ratechange,*reset,*resize,*scroll,*securitypolicyviolation,*seeked,*seeking,*select,*selectionchange,*selectstart,*slotchange,*stalled,*submit,*suspend,*timeupdate,*toggle,*transitioncancel,*transitionend,*transitionrun,*transitionstart,*volumechange,*waiting,*webkitanimationend,*webkitanimationiteration,*webkitanimationstart,*webkittransitionend,*wheel,%style,#tabIndex',
19760
20207
  ':svg:graphics^:svg:|',
@@ -19821,6 +20268,7 @@ const SCHEMA = [
19821
20268
  'source^[HTMLElement]|#height,media,sizes,src,srcset,type,#width',
19822
20269
  'span^[HTMLElement]|',
19823
20270
  'style^[HTMLElement]|!disabled,media,type',
20271
+ 'search^[HTMLELement]|',
19824
20272
  'caption^[HTMLElement]|align',
19825
20273
  'th,td^[HTMLElement]|abbr,align,axis,bgColor,ch,chOff,#colSpan,headers,height,!noWrap,#rowSpan,scope,vAlign,width',
19826
20274
  'col,colgroup^[HTMLElement]|align,ch,chOff,#span,vAlign,width',
@@ -20471,28 +20919,13 @@ class _I18nVisitor {
20471
20919
  return new Message(i18nodes, context.placeholderToContent, context.placeholderToMessage, meaning, description, customId);
20472
20920
  }
20473
20921
  visitElement(el, context) {
20474
- const children = visitAll(this, el.children, context);
20475
- const attrs = {};
20476
- el.attrs.forEach((attr) => {
20477
- // Do not visit the attributes, translatable ones are top-level ASTs
20478
- attrs[attr.name] = attr.value;
20479
- });
20480
- const isVoid = getHtmlTagDefinition(el.name).isVoid;
20481
- const startPhName = context.placeholderRegistry.getStartTagPlaceholderName(el.name, attrs, isVoid);
20482
- context.placeholderToContent[startPhName] = {
20483
- text: el.startSourceSpan.toString(),
20484
- sourceSpan: el.startSourceSpan,
20485
- };
20486
- let closePhName = '';
20487
- if (!isVoid) {
20488
- closePhName = context.placeholderRegistry.getCloseTagPlaceholderName(el.name);
20489
- context.placeholderToContent[closePhName] = {
20490
- text: `</${el.name}>`,
20491
- sourceSpan: el.endSourceSpan ?? el.sourceSpan,
20492
- };
20493
- }
20494
- const node = new TagPlaceholder(el.name, attrs, startPhName, closePhName, children, isVoid, el.sourceSpan, el.startSourceSpan, el.endSourceSpan);
20495
- return context.visitNodeFn(el, node);
20922
+ return this._visitElementLike(el, context);
20923
+ }
20924
+ visitComponent(component, context) {
20925
+ return this._visitElementLike(component, context);
20926
+ }
20927
+ visitDirective(directive, context) {
20928
+ throw new Error('Unreachable code');
20496
20929
  }
20497
20930
  visitAttribute(attribute, context) {
20498
20931
  const node = attribute.valueTokens === undefined || attribute.valueTokens.length === 1
@@ -20566,7 +20999,42 @@ class _I18nVisitor {
20566
20999
  visitLetDeclaration(decl, context) {
20567
21000
  return null;
20568
21001
  }
20569
- /**
21002
+ _visitElementLike(node, context) {
21003
+ const children = visitAll(this, node.children, context);
21004
+ const attrs = {};
21005
+ const visitAttribute = (attr) => {
21006
+ // Do not visit the attributes, translatable ones are top-level ASTs
21007
+ attrs[attr.name] = attr.value;
21008
+ };
21009
+ let nodeName;
21010
+ let isVoid;
21011
+ if (node instanceof Element) {
21012
+ nodeName = node.name;
21013
+ isVoid = getHtmlTagDefinition(node.name).isVoid;
21014
+ }
21015
+ else {
21016
+ nodeName = node.fullName;
21017
+ isVoid = node.tagName ? getHtmlTagDefinition(node.tagName).isVoid : false;
21018
+ }
21019
+ node.attrs.forEach(visitAttribute);
21020
+ node.directives.forEach((dir) => dir.attrs.forEach(visitAttribute));
21021
+ const startPhName = context.placeholderRegistry.getStartTagPlaceholderName(nodeName, attrs, isVoid);
21022
+ context.placeholderToContent[startPhName] = {
21023
+ text: node.startSourceSpan.toString(),
21024
+ sourceSpan: node.startSourceSpan,
21025
+ };
21026
+ let closePhName = '';
21027
+ if (!isVoid) {
21028
+ closePhName = context.placeholderRegistry.getCloseTagPlaceholderName(nodeName);
21029
+ context.placeholderToContent[closePhName] = {
21030
+ text: `</${nodeName}>`,
21031
+ sourceSpan: node.endSourceSpan ?? node.sourceSpan,
21032
+ };
21033
+ }
21034
+ const i18nNode = new TagPlaceholder(nodeName, attrs, startPhName, closePhName, children, isVoid, node.sourceSpan, node.startSourceSpan, node.endSourceSpan);
21035
+ return context.visitNodeFn(node, i18nNode);
21036
+ }
21037
+ /**
20570
21038
  * Convert, text and interpolated tokens up into text and placeholder pieces.
20571
21039
  *
20572
21040
  * @param tokens The text and interpolated tokens.
@@ -20828,15 +21296,74 @@ class I18nMetaVisitor {
20828
21296
  return new ParseTreeResult(result, this._errors);
20829
21297
  }
20830
21298
  visitElement(element) {
21299
+ this._visitElementLike(element);
21300
+ return element;
21301
+ }
21302
+ visitComponent(component, context) {
21303
+ this._visitElementLike(component);
21304
+ return component;
21305
+ }
21306
+ visitExpansion(expansion, currentMessage) {
21307
+ let message;
21308
+ const meta = expansion.i18n;
21309
+ this.hasI18nMeta = true;
21310
+ if (meta instanceof IcuPlaceholder) {
21311
+ // set ICU placeholder name (e.g. "ICU_1"),
21312
+ // generated while processing root element contents,
21313
+ // so we can reference it when we output translation
21314
+ const name = meta.name;
21315
+ message = this._generateI18nMessage([expansion], meta);
21316
+ const icu = icuFromI18nMessage(message);
21317
+ icu.name = name;
21318
+ if (currentMessage !== null) {
21319
+ // Also update the placeholderToMessage map with this new message
21320
+ currentMessage.placeholderToMessage[name] = message;
21321
+ }
21322
+ }
21323
+ else {
21324
+ // ICU is a top level message, try to use metadata from container element if provided via
21325
+ // `context` argument. Note: context may not be available for standalone ICUs (without
21326
+ // wrapping element), so fallback to ICU metadata in this case.
21327
+ message = this._generateI18nMessage([expansion], currentMessage || meta);
21328
+ }
21329
+ expansion.i18n = message;
21330
+ return expansion;
21331
+ }
21332
+ visitText(text) {
21333
+ return text;
21334
+ }
21335
+ visitAttribute(attribute) {
21336
+ return attribute;
21337
+ }
21338
+ visitComment(comment) {
21339
+ return comment;
21340
+ }
21341
+ visitExpansionCase(expansionCase) {
21342
+ return expansionCase;
21343
+ }
21344
+ visitBlock(block, context) {
21345
+ visitAll(this, block.children, context);
21346
+ return block;
21347
+ }
21348
+ visitBlockParameter(parameter, context) {
21349
+ return parameter;
21350
+ }
21351
+ visitLetDeclaration(decl, context) {
21352
+ return decl;
21353
+ }
21354
+ visitDirective(directive, context) {
21355
+ return directive;
21356
+ }
21357
+ _visitElementLike(node) {
20831
21358
  let message = undefined;
20832
- if (hasI18nAttrs(element)) {
21359
+ if (hasI18nAttrs(node)) {
20833
21360
  this.hasI18nMeta = true;
20834
21361
  const attrs = [];
20835
21362
  const attrsMeta = {};
20836
- for (const attr of element.attrs) {
21363
+ for (const attr of node.attrs) {
20837
21364
  if (attr.name === I18N_ATTR) {
20838
21365
  // root 'i18n' node attribute
20839
- const i18n = element.i18n || attr.value;
21366
+ const i18n = node.i18n || attr.value;
20840
21367
  // Generate a new AST with whitespace trimmed, but also generate a map
20841
21368
  // to correlate each new node to its original so we can apply i18n
20842
21369
  // information to the original node based on the trimmed content.
@@ -20850,20 +21377,27 @@ class I18nMetaVisitor {
20850
21377
  // backwards compatibility.
20851
21378
  const originalNodeMap = new Map();
20852
21379
  const trimmedNodes = this.preserveSignificantWhitespace
20853
- ? element.children
20854
- : visitAllWithSiblings(new WhitespaceVisitor(false /* preserveSignificantWhitespace */, originalNodeMap), element.children);
21380
+ ? node.children
21381
+ : visitAllWithSiblings(new WhitespaceVisitor(false /* preserveSignificantWhitespace */, originalNodeMap), node.children);
20855
21382
  message = this._generateI18nMessage(trimmedNodes, i18n, setI18nRefs(originalNodeMap));
20856
21383
  if (message.nodes.length === 0) {
20857
21384
  // Ignore the message if it is empty.
20858
21385
  message = undefined;
20859
21386
  }
20860
21387
  // Store the message on the element
20861
- element.i18n = message;
21388
+ node.i18n = message;
20862
21389
  }
20863
21390
  else if (attr.name.startsWith(I18N_ATTR_PREFIX)) {
20864
21391
  // 'i18n-*' attributes
20865
21392
  const name = attr.name.slice(I18N_ATTR_PREFIX.length);
20866
- if (isTrustedTypesSink(element.name, name)) {
21393
+ let isTrustedType;
21394
+ if (node instanceof Component) {
21395
+ isTrustedType = node.tagName === null ? false : isTrustedTypesSink(node.tagName, name);
21396
+ }
21397
+ else {
21398
+ isTrustedType = isTrustedTypesSink(node.name, name);
21399
+ }
21400
+ if (isTrustedType) {
20867
21401
  this._reportError(attr, `Translating attribute '${name}' is disallowed for security reasons.`);
20868
21402
  }
20869
21403
  else {
@@ -20888,59 +21422,10 @@ class I18nMetaVisitor {
20888
21422
  if (!this.keepI18nAttrs) {
20889
21423
  // update element's attributes,
20890
21424
  // keeping only non-i18n related ones
20891
- element.attrs = attrs;
20892
- }
20893
- }
20894
- visitAll(this, element.children, message);
20895
- return element;
20896
- }
20897
- visitExpansion(expansion, currentMessage) {
20898
- let message;
20899
- const meta = expansion.i18n;
20900
- this.hasI18nMeta = true;
20901
- if (meta instanceof IcuPlaceholder) {
20902
- // set ICU placeholder name (e.g. "ICU_1"),
20903
- // generated while processing root element contents,
20904
- // so we can reference it when we output translation
20905
- const name = meta.name;
20906
- message = this._generateI18nMessage([expansion], meta);
20907
- const icu = icuFromI18nMessage(message);
20908
- icu.name = name;
20909
- if (currentMessage !== null) {
20910
- // Also update the placeholderToMessage map with this new message
20911
- currentMessage.placeholderToMessage[name] = message;
21425
+ node.attrs = attrs;
20912
21426
  }
20913
21427
  }
20914
- else {
20915
- // ICU is a top level message, try to use metadata from container element if provided via
20916
- // `context` argument. Note: context may not be available for standalone ICUs (without
20917
- // wrapping element), so fallback to ICU metadata in this case.
20918
- message = this._generateI18nMessage([expansion], currentMessage || meta);
20919
- }
20920
- expansion.i18n = message;
20921
- return expansion;
20922
- }
20923
- visitText(text) {
20924
- return text;
20925
- }
20926
- visitAttribute(attribute) {
20927
- return attribute;
20928
- }
20929
- visitComment(comment) {
20930
- return comment;
20931
- }
20932
- visitExpansionCase(expansionCase) {
20933
- return expansionCase;
20934
- }
20935
- visitBlock(block, context) {
20936
- visitAll(this, block.children, context);
20937
- return block;
20938
- }
20939
- visitBlockParameter(parameter, context) {
20940
- return parameter;
20941
- }
20942
- visitLetDeclaration(decl, context) {
20943
- return decl;
21428
+ visitAll(this, node.children, message);
20944
21429
  }
20945
21430
  /**
20946
21431
  * Parse the general form `meta` passed into extract the explicit metadata needed to create a
@@ -27357,15 +27842,22 @@ function isAnimationLabel(name) {
27357
27842
  return name[0] == '@';
27358
27843
  }
27359
27844
  function calcPossibleSecurityContexts(registry, selector, propName, isAttribute) {
27360
- const ctxs = [];
27361
- CssSelector.parse(selector).forEach((selector) => {
27362
- const elementNames = selector.element ? [selector.element] : registry.allKnownElementNames();
27363
- const notElementNames = new Set(selector.notSelectors
27364
- .filter((selector) => selector.isElementSelector())
27365
- .map((selector) => selector.element));
27366
- const possibleElementNames = elementNames.filter((elementName) => !notElementNames.has(elementName));
27367
- ctxs.push(...possibleElementNames.map((elementName) => registry.securityContext(elementName, propName, isAttribute)));
27368
- });
27845
+ let ctxs;
27846
+ const nameToContext = (elName) => registry.securityContext(elName, propName, isAttribute);
27847
+ if (selector === null) {
27848
+ ctxs = registry.allKnownElementNames().map(nameToContext);
27849
+ }
27850
+ else {
27851
+ ctxs = [];
27852
+ CssSelector.parse(selector).forEach((selector) => {
27853
+ const elementNames = selector.element ? [selector.element] : registry.allKnownElementNames();
27854
+ const notElementNames = new Set(selector.notSelectors
27855
+ .filter((selector) => selector.isElementSelector())
27856
+ .map((selector) => selector.element));
27857
+ const possibleElementNames = elementNames.filter((elName) => !notElementNames.has(elName));
27858
+ ctxs.push(...possibleElementNames.map(nameToContext));
27859
+ });
27860
+ }
27369
27861
  return ctxs.length === 0 ? [SecurityContext.NONE] : Array.from(new Set(ctxs)).sort();
27370
27862
  }
27371
27863
  /**
@@ -28450,6 +28942,17 @@ const BINDING_DELIMS = {
28450
28942
  EVENT: { start: '(', end: ')' },
28451
28943
  };
28452
28944
  const TEMPLATE_ATTR_PREFIX = '*';
28945
+ // TODO(crisbeto): any other tag names that shouldn't be allowed here?
28946
+ const UNSUPPORTED_SELECTORLESS_TAGS = new Set([
28947
+ 'link',
28948
+ 'style',
28949
+ 'script',
28950
+ 'ng-template',
28951
+ 'ng-container',
28952
+ 'ng-content',
28953
+ ]);
28954
+ // TODO(crisbeto): any other attributes that should not be allowed here?
28955
+ const UNSUPPORTED_SELECTORLESS_DIRECTIVE_ATTRS = new Set(['ngProjectAs', 'ngNonBindable']);
28453
28956
  function htmlAstToRender3Ast(htmlNodes, bindingParser, options) {
28454
28957
  const transformer = new HtmlAstToIvyAst(bindingParser, options);
28455
28958
  const ivyNodes = visitAll(transformer, htmlNodes, htmlNodes);
@@ -28513,52 +29016,8 @@ class HtmlAstToIvyAst {
28513
29016
  }
28514
29017
  // Whether the element is a `<ng-template>`
28515
29018
  const isTemplateElement = isNgTemplate(element.name);
28516
- const parsedProperties = [];
28517
- const boundEvents = [];
28518
- const variables = [];
28519
- const references = [];
28520
- const attributes = [];
28521
- const i18nAttrsMeta = {};
28522
- const templateParsedProperties = [];
28523
- const templateVariables = [];
28524
- // Whether the element has any *-attribute
28525
- let elementHasInlineTemplate = false;
28526
- for (const attribute of element.attrs) {
28527
- let hasBinding = false;
28528
- const normalizedName = normalizeAttributeName(attribute.name);
28529
- // `*attr` defines template bindings
28530
- let isTemplateBinding = false;
28531
- if (attribute.i18n) {
28532
- i18nAttrsMeta[attribute.name] = attribute.i18n;
28533
- }
28534
- if (normalizedName.startsWith(TEMPLATE_ATTR_PREFIX)) {
28535
- // *-attributes
28536
- if (elementHasInlineTemplate) {
28537
- this.reportError(`Can't have multiple template bindings on one element. Use only one attribute prefixed with *`, attribute.sourceSpan);
28538
- }
28539
- isTemplateBinding = true;
28540
- elementHasInlineTemplate = true;
28541
- const templateValue = attribute.value;
28542
- const templateKey = normalizedName.substring(TEMPLATE_ATTR_PREFIX.length);
28543
- const parsedVariables = [];
28544
- const absoluteValueOffset = attribute.valueSpan
28545
- ? attribute.valueSpan.start.offset
28546
- : // If there is no value span the attribute does not have a value, like `attr` in
28547
- //`<div attr></div>`. In this case, point to one character beyond the last character of
28548
- // the attribute name.
28549
- attribute.sourceSpan.start.offset + attribute.name.length;
28550
- this.bindingParser.parseInlineTemplateBinding(templateKey, templateValue, attribute.sourceSpan, absoluteValueOffset, [], templateParsedProperties, parsedVariables, true /* isIvyAst */);
28551
- templateVariables.push(...parsedVariables.map((v) => new Variable(v.name, v.value, v.sourceSpan, v.keySpan, v.valueSpan)));
28552
- }
28553
- else {
28554
- // Check for variables, events, property bindings, interpolation
28555
- hasBinding = this.parseAttribute(isTemplateElement, attribute, [], parsedProperties, boundEvents, variables, references);
28556
- }
28557
- if (!hasBinding && !isTemplateBinding) {
28558
- // don't include the bindings as attributes as well in the AST
28559
- attributes.push(this.visitAttribute(attribute));
28560
- }
28561
- }
29019
+ const { attributes, boundEvents, references, variables, templateVariables, elementHasInlineTemplate, parsedProperties, templateParsedProperties, i18nAttrsMeta, } = this.prepareAttributes(element.attrs, isTemplateElement);
29020
+ const directives = this.extractDirectives(element);
28562
29021
  let children;
28563
29022
  if (preparsedElement.nonBindable) {
28564
29023
  // The `NonBindableVisitor` may need to return an array of nodes for blocks so we need
@@ -28573,44 +29032,24 @@ class HtmlAstToIvyAst {
28573
29032
  if (preparsedElement.type === PreparsedElementType.NG_CONTENT) {
28574
29033
  const selector = preparsedElement.selectAttr;
28575
29034
  const attrs = element.attrs.map((attr) => this.visitAttribute(attr));
28576
- parsedElement = new Content(selector, attrs, children, element.sourceSpan, element.i18n);
29035
+ parsedElement = new Content(selector, attrs, children, element.sourceSpan, element.startSourceSpan, element.endSourceSpan, element.i18n);
28577
29036
  this.ngContentSelectors.push(selector);
28578
29037
  }
28579
29038
  else if (isTemplateElement) {
28580
29039
  // `<ng-template>`
28581
- const attrs = this.extractAttributes(element.name, parsedProperties, i18nAttrsMeta);
28582
- parsedElement = new Template(element.name, attributes, attrs.bound, boundEvents, [
29040
+ const attrs = this.categorizePropertyAttributes(element.name, parsedProperties, i18nAttrsMeta);
29041
+ parsedElement = new Template(element.name, attributes, attrs.bound, boundEvents, directives, [
28583
29042
  /* no template attributes */
28584
29043
  ], children, references, variables, element.sourceSpan, element.startSourceSpan, element.endSourceSpan, element.i18n);
28585
29044
  }
28586
29045
  else {
28587
- const attrs = this.extractAttributes(element.name, parsedProperties, i18nAttrsMeta);
28588
- parsedElement = new Element$1(element.name, attributes, attrs.bound, boundEvents, children, references, element.sourceSpan, element.startSourceSpan, element.endSourceSpan, element.i18n);
29046
+ const attrs = this.categorizePropertyAttributes(element.name, parsedProperties, i18nAttrsMeta);
29047
+ parsedElement = new Element$1(element.name, attributes, attrs.bound, boundEvents, directives, children, references, element.sourceSpan, element.startSourceSpan, element.endSourceSpan, element.i18n);
28589
29048
  }
28590
29049
  if (elementHasInlineTemplate) {
28591
29050
  // If this node is an inline-template (e.g. has *ngFor) then we need to create a template
28592
29051
  // node that contains this node.
28593
- // Moreover, if the node is an element, then we need to hoist its attributes to the template
28594
- // node for matching against content projection selectors.
28595
- const attrs = this.extractAttributes('ng-template', templateParsedProperties, i18nAttrsMeta);
28596
- const templateAttrs = [];
28597
- attrs.literal.forEach((attr) => templateAttrs.push(attr));
28598
- attrs.bound.forEach((attr) => templateAttrs.push(attr));
28599
- const hoistedAttrs = parsedElement instanceof Element$1
28600
- ? {
28601
- attributes: parsedElement.attributes,
28602
- inputs: parsedElement.inputs,
28603
- outputs: parsedElement.outputs,
28604
- }
28605
- : { attributes: [], inputs: [], outputs: [] };
28606
- // For <ng-template>s with structural directives on them, avoid passing i18n information to
28607
- // the wrapping template to prevent unnecessary i18n instructions from being generated. The
28608
- // necessary i18n meta information will be extracted from child elements.
28609
- const i18n = isTemplateElement && isI18nRootElement ? undefined : element.i18n;
28610
- const name = parsedElement instanceof Template ? null : parsedElement.name;
28611
- parsedElement = new Template(name, hoistedAttrs.attributes, hoistedAttrs.inputs, hoistedAttrs.outputs, templateAttrs, [parsedElement], [
28612
- /* no references */
28613
- ], templateVariables, element.sourceSpan, element.startSourceSpan, element.endSourceSpan, i18n);
29052
+ parsedElement = this.wrapInTemplate(parsedElement, templateParsedProperties, templateVariables, i18nAttrsMeta, isTemplateElement, isI18nRootElement);
28614
29053
  }
28615
29054
  if (isI18nRootElement) {
28616
29055
  this.inI18nBlock = false;
@@ -28674,6 +29113,43 @@ class HtmlAstToIvyAst {
28674
29113
  }
28675
29114
  return new LetDeclaration$1(decl.name, value, decl.sourceSpan, decl.nameSpan, decl.valueSpan);
28676
29115
  }
29116
+ visitComponent(component) {
29117
+ const isI18nRootElement = isI18nRootNode(component.i18n);
29118
+ if (isI18nRootElement) {
29119
+ if (this.inI18nBlock) {
29120
+ this.reportError('Cannot mark a component as translatable inside of a translatable section. Please remove the nested i18n marker.', component.sourceSpan);
29121
+ }
29122
+ this.inI18nBlock = true;
29123
+ }
29124
+ if (component.tagName !== null && UNSUPPORTED_SELECTORLESS_TAGS.has(component.tagName)) {
29125
+ this.reportError(`Tag name "${component.tagName}" cannot be used as a component tag`, component.startSourceSpan);
29126
+ return null;
29127
+ }
29128
+ const { attributes, boundEvents, references, templateVariables, elementHasInlineTemplate, parsedProperties, templateParsedProperties, i18nAttrsMeta, } = this.prepareAttributes(component.attrs, false);
29129
+ const directives = this.extractDirectives(component);
29130
+ let children;
29131
+ if (component.attrs.find((attr) => attr.name === 'ngNonBindable')) {
29132
+ // The `NonBindableVisitor` may need to return an array of nodes for blocks so we need
29133
+ // to flatten the array here. Avoid doing this for the `HtmlAstToIvyAst` since `flat` creates
29134
+ // a new array.
29135
+ children = visitAll(NON_BINDABLE_VISITOR, component.children).flat(Infinity);
29136
+ }
29137
+ else {
29138
+ children = visitAll(this, component.children, component.children);
29139
+ }
29140
+ const attrs = this.categorizePropertyAttributes(component.tagName, parsedProperties, i18nAttrsMeta);
29141
+ let node = new Component$1(component.componentName, component.tagName, component.fullName, attributes, attrs.bound, boundEvents, directives, children, references, component.sourceSpan, component.startSourceSpan, component.endSourceSpan, component.i18n);
29142
+ if (elementHasInlineTemplate) {
29143
+ node = this.wrapInTemplate(node, templateParsedProperties, templateVariables, i18nAttrsMeta, false, isI18nRootElement);
29144
+ }
29145
+ if (isI18nRootElement) {
29146
+ this.inI18nBlock = false;
29147
+ }
29148
+ return node;
29149
+ }
29150
+ visitDirective() {
29151
+ return null;
29152
+ }
28677
29153
  visitBlockParameter() {
28678
29154
  return null;
28679
29155
  }
@@ -28750,8 +29226,8 @@ class HtmlAstToIvyAst {
28750
29226
  }
28751
29227
  return relatedBlocks;
28752
29228
  }
28753
- // convert view engine `ParsedProperty` to a format suitable for IVY
28754
- extractAttributes(elementName, properties, i18nPropsMeta) {
29229
+ /** Splits up the property attributes depending on whether they're static or bound. */
29230
+ categorizePropertyAttributes(elementName, properties, i18nPropsMeta) {
28755
29231
  const bound = [];
28756
29232
  const literal = [];
28757
29233
  properties.forEach((prop) => {
@@ -28771,6 +29247,65 @@ class HtmlAstToIvyAst {
28771
29247
  });
28772
29248
  return { bound, literal };
28773
29249
  }
29250
+ prepareAttributes(attrs, isTemplateElement) {
29251
+ const parsedProperties = [];
29252
+ const boundEvents = [];
29253
+ const variables = [];
29254
+ const references = [];
29255
+ const attributes = [];
29256
+ const i18nAttrsMeta = {};
29257
+ const templateParsedProperties = [];
29258
+ const templateVariables = [];
29259
+ // Whether the element has any *-attribute
29260
+ let elementHasInlineTemplate = false;
29261
+ for (const attribute of attrs) {
29262
+ let hasBinding = false;
29263
+ const normalizedName = normalizeAttributeName(attribute.name);
29264
+ // `*attr` defines template bindings
29265
+ let isTemplateBinding = false;
29266
+ if (attribute.i18n) {
29267
+ i18nAttrsMeta[attribute.name] = attribute.i18n;
29268
+ }
29269
+ if (normalizedName.startsWith(TEMPLATE_ATTR_PREFIX)) {
29270
+ // *-attributes
29271
+ if (elementHasInlineTemplate) {
29272
+ this.reportError(`Can't have multiple template bindings on one element. Use only one attribute prefixed with *`, attribute.sourceSpan);
29273
+ }
29274
+ isTemplateBinding = true;
29275
+ elementHasInlineTemplate = true;
29276
+ const templateValue = attribute.value;
29277
+ const templateKey = normalizedName.substring(TEMPLATE_ATTR_PREFIX.length);
29278
+ const parsedVariables = [];
29279
+ const absoluteValueOffset = attribute.valueSpan
29280
+ ? attribute.valueSpan.start.offset
29281
+ : // If there is no value span the attribute does not have a value, like `attr` in
29282
+ //`<div attr></div>`. In this case, point to one character beyond the last character of
29283
+ // the attribute name.
29284
+ attribute.sourceSpan.start.offset + attribute.name.length;
29285
+ this.bindingParser.parseInlineTemplateBinding(templateKey, templateValue, attribute.sourceSpan, absoluteValueOffset, [], templateParsedProperties, parsedVariables, true /* isIvyAst */);
29286
+ templateVariables.push(...parsedVariables.map((v) => new Variable(v.name, v.value, v.sourceSpan, v.keySpan, v.valueSpan)));
29287
+ }
29288
+ else {
29289
+ // Check for variables, events, property bindings, interpolation
29290
+ hasBinding = this.parseAttribute(isTemplateElement, attribute, [], parsedProperties, boundEvents, variables, references);
29291
+ }
29292
+ if (!hasBinding && !isTemplateBinding) {
29293
+ // don't include the bindings as attributes as well in the AST
29294
+ attributes.push(this.visitAttribute(attribute));
29295
+ }
29296
+ }
29297
+ return {
29298
+ attributes,
29299
+ boundEvents,
29300
+ references,
29301
+ variables,
29302
+ templateVariables,
29303
+ elementHasInlineTemplate,
29304
+ parsedProperties,
29305
+ templateParsedProperties,
29306
+ i18nAttrsMeta,
29307
+ };
29308
+ }
28774
29309
  parseAttribute(isTemplateElement, attribute, matchableAttributes, parsedProperties, boundEvents, variables, references) {
28775
29310
  const name = normalizeAttributeName(attribute.name);
28776
29311
  const value = attribute.value;
@@ -28869,6 +29404,81 @@ class HtmlAstToIvyAst {
28869
29404
  const hasBinding = this.bindingParser.parsePropertyInterpolation(name, value, srcSpan, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan, attribute.valueTokens ?? null);
28870
29405
  return hasBinding;
28871
29406
  }
29407
+ extractDirectives(node) {
29408
+ const elementName = node instanceof Component ? node.tagName : node.name;
29409
+ const directives = [];
29410
+ const seenDirectives = new Set();
29411
+ for (const directive of node.directives) {
29412
+ let invalid = false;
29413
+ for (const attr of directive.attrs) {
29414
+ if (attr.name.startsWith(TEMPLATE_ATTR_PREFIX)) {
29415
+ invalid = true;
29416
+ this.reportError(`Shorthand template syntax "${attr.name}" is not supported inside a directive context`, attr.sourceSpan);
29417
+ }
29418
+ else if (UNSUPPORTED_SELECTORLESS_DIRECTIVE_ATTRS.has(attr.name)) {
29419
+ invalid = true;
29420
+ this.reportError(`Attribute "${attr.name}" is not supported in a directive context`, attr.sourceSpan);
29421
+ }
29422
+ }
29423
+ if (!invalid && seenDirectives.has(directive.name)) {
29424
+ invalid = true;
29425
+ this.reportError(`Cannot apply directive "${directive.name}" multiple times on the same element`, directive.sourceSpan);
29426
+ }
29427
+ if (invalid) {
29428
+ continue;
29429
+ }
29430
+ const { attributes, parsedProperties, boundEvents, references, i18nAttrsMeta } = this.prepareAttributes(directive.attrs, false);
29431
+ const { bound: inputs } = this.categorizePropertyAttributes(elementName, parsedProperties, i18nAttrsMeta);
29432
+ for (const input of inputs) {
29433
+ if (input.type !== exports.BindingType.Property && input.type !== exports.BindingType.TwoWay) {
29434
+ invalid = true;
29435
+ this.reportError('Binding is not supported in a directive context', input.sourceSpan);
29436
+ }
29437
+ }
29438
+ if (invalid) {
29439
+ continue;
29440
+ }
29441
+ seenDirectives.add(directive.name);
29442
+ directives.push(new Directive$1(directive.name, attributes, inputs, boundEvents, references, directive.sourceSpan, directive.startSourceSpan, directive.endSourceSpan, undefined));
29443
+ }
29444
+ return directives;
29445
+ }
29446
+ wrapInTemplate(node, templateProperties, templateVariables, i18nAttrsMeta, isTemplateElement, isI18nRootElement) {
29447
+ // We need to hoist the attributes of the node to the template for content projection purposes.
29448
+ const attrs = this.categorizePropertyAttributes('ng-template', templateProperties, i18nAttrsMeta);
29449
+ const templateAttrs = [];
29450
+ attrs.literal.forEach((attr) => templateAttrs.push(attr));
29451
+ attrs.bound.forEach((attr) => templateAttrs.push(attr));
29452
+ const hoistedAttrs = {
29453
+ attributes: [],
29454
+ inputs: [],
29455
+ outputs: [],
29456
+ };
29457
+ if (node instanceof Element$1 || node instanceof Component$1) {
29458
+ hoistedAttrs.attributes.push(...node.attributes);
29459
+ hoistedAttrs.inputs.push(...node.inputs);
29460
+ hoistedAttrs.outputs.push(...node.outputs);
29461
+ }
29462
+ // For <ng-template>s with structural directives on them, avoid passing i18n information to
29463
+ // the wrapping template to prevent unnecessary i18n instructions from being generated. The
29464
+ // necessary i18n meta information will be extracted from child elements.
29465
+ const i18n = isTemplateElement && isI18nRootElement ? undefined : node.i18n;
29466
+ let name;
29467
+ if (node instanceof Component$1) {
29468
+ name = node.tagName;
29469
+ }
29470
+ else if (node instanceof Template) {
29471
+ name = null;
29472
+ }
29473
+ else {
29474
+ name = node.name;
29475
+ }
29476
+ return new Template(name, hoistedAttrs.attributes, hoistedAttrs.inputs, hoistedAttrs.outputs, [
29477
+ // Do not copy over the directives.
29478
+ ], templateAttrs, [node], [
29479
+ // Do not copy over the references.
29480
+ ], templateVariables, node.sourceSpan, node.startSourceSpan, node.endSourceSpan, i18n);
29481
+ }
28872
29482
  _visitTextWithInterpolation(value, sourceSpan, interpolatedTokens, i18n) {
28873
29483
  const valueNoNgsp = replaceNgsp(value);
28874
29484
  const expr = this.bindingParser.parseInterpolation(valueNoNgsp, sourceSpan, interpolatedTokens);
@@ -28919,7 +29529,8 @@ class NonBindableVisitor {
28919
29529
  const children = visitAll(this, ast.children, null);
28920
29530
  return new Element$1(ast.name, visitAll(this, ast.attrs),
28921
29531
  /* inputs */ [],
28922
- /* outputs */ [], children,
29532
+ /* outputs */ [],
29533
+ /* directives */ [], children,
28923
29534
  /* references */ [], ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan);
28924
29535
  }
28925
29536
  visitComment(comment) {
@@ -28955,6 +29566,17 @@ class NonBindableVisitor {
28955
29566
  visitLetDeclaration(decl, context) {
28956
29567
  return new Text$3(`@let ${decl.name} = ${decl.value};`, decl.sourceSpan);
28957
29568
  }
29569
+ visitComponent(ast, context) {
29570
+ const children = visitAll(this, ast.children, null);
29571
+ return new Element$1(ast.fullName, visitAll(this, ast.attrs),
29572
+ /* inputs */ [],
29573
+ /* outputs */ [],
29574
+ /* directives */ [], children,
29575
+ /* references */ [], ast.sourceSpan, ast.startSourceSpan, ast.endSourceSpan);
29576
+ }
29577
+ visitDirective(directive, context) {
29578
+ return null;
29579
+ }
28958
29580
  }
28959
29581
  const NON_BINDABLE_VISITOR = new NonBindableVisitor();
28960
29582
  function normalizeAttributeName(attrName) {
@@ -29730,6 +30352,7 @@ class Scope {
29730
30352
  }
29731
30353
  }
29732
30354
  visitElement(element) {
30355
+ element.directives.forEach((node) => node.visit(this));
29733
30356
  // `Element`s in the template may have `Reference`s which are captured in the scope.
29734
30357
  element.references.forEach((node) => this.visitReference(node));
29735
30358
  // Recurse into the `Element`'s children.
@@ -29737,6 +30360,7 @@ class Scope {
29737
30360
  this.elementsInScope.add(element);
29738
30361
  }
29739
30362
  visitTemplate(template) {
30363
+ template.directives.forEach((node) => node.visit(this));
29740
30364
  // References on a <ng-template> are defined in the outer scope, so capture them before
29741
30365
  // processing the template's child scope.
29742
30366
  template.references.forEach((node) => this.visitReference(node));
@@ -29791,6 +30415,12 @@ class Scope {
29791
30415
  visitLetDeclaration(decl) {
29792
30416
  this.maybeDeclare(decl);
29793
30417
  }
30418
+ visitComponent(component) {
30419
+ throw new Error('TODO');
30420
+ }
30421
+ visitDirective(directive) {
30422
+ throw new Error('TODO');
30423
+ }
29794
30424
  // Unused visitors.
29795
30425
  visitBoundAttribute(attr) { }
29796
30426
  visitBoundEvent(event) { }
@@ -29892,6 +30522,10 @@ class DirectiveBinder {
29892
30522
  // First, determine the HTML shape of the node for the purpose of directive matching.
29893
30523
  // Do this by building up a `CssSelector` for the node.
29894
30524
  const cssSelector = createCssSelectorFromNode(node);
30525
+ // TODO(crisbeto): account for selectorless directives here.
30526
+ if (node.directives.length > 0) {
30527
+ throw new Error('TODO');
30528
+ }
29895
30529
  // Next, use the `SelectorMatcher` to get the list of directives on the node.
29896
30530
  const directives = [];
29897
30531
  this.matcher.match(cssSelector, (_selector, results) => directives.push(...results));
@@ -29991,6 +30625,12 @@ class DirectiveBinder {
29991
30625
  visitContent(content) {
29992
30626
  content.children.forEach((child) => child.visit(this));
29993
30627
  }
30628
+ visitComponent(component) {
30629
+ throw new Error('TODO');
30630
+ }
30631
+ visitDirective(directive) {
30632
+ throw new Error('TODO');
30633
+ }
29994
30634
  // Unused visitors.
29995
30635
  visitVariable(variable) { }
29996
30636
  visitReference(reference) { }
@@ -30121,6 +30761,7 @@ class TemplateBinder extends RecursiveAstVisitor {
30121
30761
  // Visit the inputs, outputs, and children of the element.
30122
30762
  element.inputs.forEach(this.visitNode);
30123
30763
  element.outputs.forEach(this.visitNode);
30764
+ element.directives.forEach(this.visitNode);
30124
30765
  element.children.forEach(this.visitNode);
30125
30766
  element.references.forEach(this.visitNode);
30126
30767
  }
@@ -30128,6 +30769,7 @@ class TemplateBinder extends RecursiveAstVisitor {
30128
30769
  // First, visit inputs, outputs and template attributes of the template node.
30129
30770
  template.inputs.forEach(this.visitNode);
30130
30771
  template.outputs.forEach(this.visitNode);
30772
+ template.directives.forEach(this.visitNode);
30131
30773
  template.templateAttrs.forEach(this.visitNode);
30132
30774
  template.references.forEach(this.visitNode);
30133
30775
  // Next, recurse into the template.
@@ -30145,6 +30787,12 @@ class TemplateBinder extends RecursiveAstVisitor {
30145
30787
  this.symbols.set(reference, this.rootNode);
30146
30788
  }
30147
30789
  }
30790
+ visitComponent(component) {
30791
+ throw new Error('TODO');
30792
+ }
30793
+ visitDirective(directive) {
30794
+ throw new Error('TODO');
30795
+ }
30148
30796
  // Unused template visitors
30149
30797
  visitText(text) { }
30150
30798
  visitTextAttribute(attribute) { }
@@ -31197,7 +31845,7 @@ class _Visitor {
31197
31845
  this._init(_VisitorMode.Merge, interpolationConfig);
31198
31846
  this._translations = translations;
31199
31847
  // Construct a single fake root element
31200
- const wrapper = new Element('wrapper', [], nodes, undefined, undefined, undefined);
31848
+ const wrapper = new Element('wrapper', [], [], nodes, undefined, undefined, undefined);
31201
31849
  const translatedNode = wrapper.visit(this, null);
31202
31850
  if (this._inI18nBlock) {
31203
31851
  this._reportError(nodes[nodes.length - 1], 'Unclosed block');
@@ -31283,7 +31931,41 @@ class _Visitor {
31283
31931
  return text;
31284
31932
  }
31285
31933
  visitElement(el, context) {
31286
- this._mayBeAddBlockChildren(el);
31934
+ return this._visitElementLike(el, context);
31935
+ }
31936
+ visitAttribute(attribute, context) {
31937
+ throw new Error('unreachable code');
31938
+ }
31939
+ visitBlock(block, context) {
31940
+ visitAll(this, block.children, context);
31941
+ }
31942
+ visitBlockParameter(parameter, context) { }
31943
+ visitLetDeclaration(decl, context) { }
31944
+ visitComponent(component, context) {
31945
+ return this._visitElementLike(component, context);
31946
+ }
31947
+ visitDirective(directive, context) {
31948
+ throw new Error('unreachable code');
31949
+ }
31950
+ _init(mode, interpolationConfig) {
31951
+ this._mode = mode;
31952
+ this._inI18nBlock = false;
31953
+ this._inI18nNode = false;
31954
+ this._depth = 0;
31955
+ this._inIcu = false;
31956
+ this._msgCountAtSectionStart = undefined;
31957
+ this._errors = [];
31958
+ this._messages = [];
31959
+ this._inImplicitNode = false;
31960
+ this._createI18nMessage = createI18nMessageFactory(interpolationConfig, DEFAULT_CONTAINER_BLOCKS,
31961
+ // When dropping significant whitespace we need to retain whitespace tokens or
31962
+ // else we won't be able to reuse source spans because empty tokens would be
31963
+ // removed and cause a mismatch.
31964
+ /* retainEmptyTokens */ !this._preserveSignificantWhitespace,
31965
+ /* preserveExpressionWhitespace */ this._preserveSignificantWhitespace);
31966
+ }
31967
+ _visitElementLike(node, context) {
31968
+ this._mayBeAddBlockChildren(node);
31287
31969
  this._depth++;
31288
31970
  const wasInI18nNode = this._inI18nNode;
31289
31971
  const wasInImplicitNode = this._inImplicitNode;
@@ -31292,9 +31974,10 @@ class _Visitor {
31292
31974
  // Extract:
31293
31975
  // - top level nodes with the (implicit) "i18n" attribute if not already in a section
31294
31976
  // - ICU messages
31295
- const i18nAttr = _getI18nAttr(el);
31977
+ const nodeName = node instanceof Component ? node.tagName : node.name;
31978
+ const i18nAttr = _getI18nAttr(node);
31296
31979
  const i18nMeta = i18nAttr ? i18nAttr.value : '';
31297
- const isImplicit = this._implicitTags.some((tag) => el.name === tag) &&
31980
+ const isImplicit = this._implicitTags.some((tag) => nodeName === tag) &&
31298
31981
  !this._inIcu &&
31299
31982
  !this._isInTranslatableSection;
31300
31983
  const isTopLevelImplicit = !wasInImplicitNode && isImplicit;
@@ -31302,29 +31985,29 @@ class _Visitor {
31302
31985
  if (!this._isInTranslatableSection && !this._inIcu) {
31303
31986
  if (i18nAttr || isTopLevelImplicit) {
31304
31987
  this._inI18nNode = true;
31305
- const message = this._addMessage(el.children, i18nMeta);
31306
- translatedChildNodes = this._translateMessage(el, message);
31988
+ const message = this._addMessage(node.children, i18nMeta);
31989
+ translatedChildNodes = this._translateMessage(node, message);
31307
31990
  }
31308
31991
  if (this._mode == _VisitorMode.Extract) {
31309
31992
  const isTranslatable = i18nAttr || isTopLevelImplicit;
31310
31993
  if (isTranslatable)
31311
- this._openTranslatableSection(el);
31312
- visitAll(this, el.children);
31994
+ this._openTranslatableSection(node);
31995
+ visitAll(this, node.children);
31313
31996
  if (isTranslatable)
31314
- this._closeTranslatableSection(el, el.children);
31997
+ this._closeTranslatableSection(node, node.children);
31315
31998
  }
31316
31999
  }
31317
32000
  else {
31318
32001
  if (i18nAttr || isTopLevelImplicit) {
31319
- this._reportError(el, 'Could not mark an element as translatable inside a translatable section');
32002
+ this._reportError(node, 'Could not mark an element as translatable inside a translatable section');
31320
32003
  }
31321
32004
  if (this._mode == _VisitorMode.Extract) {
31322
32005
  // Descend into child nodes for extraction
31323
- visitAll(this, el.children);
32006
+ visitAll(this, node.children);
31324
32007
  }
31325
32008
  }
31326
32009
  if (this._mode === _VisitorMode.Merge) {
31327
- const visitNodes = translatedChildNodes || el.children;
32010
+ const visitNodes = translatedChildNodes || node.children;
31328
32011
  visitNodes.forEach((child) => {
31329
32012
  const visited = child.visit(this, context);
31330
32013
  if (visited && !this._isInTranslatableSection) {
@@ -31334,48 +32017,29 @@ class _Visitor {
31334
32017
  }
31335
32018
  });
31336
32019
  }
31337
- this._visitAttributesOf(el);
32020
+ this._visitAttributesOf(node);
31338
32021
  this._depth--;
31339
32022
  this._inI18nNode = wasInI18nNode;
31340
32023
  this._inImplicitNode = wasInImplicitNode;
31341
32024
  if (this._mode === _VisitorMode.Merge) {
31342
- const translatedAttrs = this._translateAttributes(el);
31343
- return new Element(el.name, translatedAttrs, childNodes, el.sourceSpan, el.startSourceSpan, el.endSourceSpan);
32025
+ if (node instanceof Element) {
32026
+ return new Element(node.name, this._translateAttributes(node), this._translateDirectives(node), childNodes, node.sourceSpan, node.startSourceSpan, node.endSourceSpan);
32027
+ }
32028
+ else {
32029
+ return new Component(node.componentName, node.tagName, node.fullName, this._translateAttributes(node), this._translateDirectives(node), childNodes, node.sourceSpan, node.startSourceSpan, node.endSourceSpan);
32030
+ }
31344
32031
  }
31345
32032
  return null;
31346
32033
  }
31347
- visitAttribute(attribute, context) {
31348
- throw new Error('unreachable code');
31349
- }
31350
- visitBlock(block, context) {
31351
- visitAll(this, block.children, context);
31352
- }
31353
- visitBlockParameter(parameter, context) { }
31354
- visitLetDeclaration(decl, context) { }
31355
- _init(mode, interpolationConfig) {
31356
- this._mode = mode;
31357
- this._inI18nBlock = false;
31358
- this._inI18nNode = false;
31359
- this._depth = 0;
31360
- this._inIcu = false;
31361
- this._msgCountAtSectionStart = undefined;
31362
- this._errors = [];
31363
- this._messages = [];
31364
- this._inImplicitNode = false;
31365
- this._createI18nMessage = createI18nMessageFactory(interpolationConfig, DEFAULT_CONTAINER_BLOCKS,
31366
- // When dropping significant whitespace we need to retain whitespace tokens or
31367
- // else we won't be able to reuse source spans because empty tokens would be
31368
- // removed and cause a mismatch.
31369
- /* retainEmptyTokens */ !this._preserveSignificantWhitespace,
31370
- /* preserveExpressionWhitespace */ this._preserveSignificantWhitespace);
31371
- }
31372
32034
  // looks for translatable attributes
31373
32035
  _visitAttributesOf(el) {
31374
32036
  const explicitAttrNameToValue = {};
31375
- const implicitAttrNames = this._implicitAttrs[el.name] || [];
32037
+ const implicitAttrNames = this._implicitAttrs[el instanceof Component ? el.tagName || '' : el.name] || [];
31376
32038
  el.attrs
31377
- .filter((attr) => attr.name.startsWith(_I18N_ATTR_PREFIX))
31378
- .forEach((attr) => (explicitAttrNameToValue[attr.name.slice(_I18N_ATTR_PREFIX.length)] = attr.value));
32039
+ .filter((attr) => attr instanceof Attribute && attr.name.startsWith(_I18N_ATTR_PREFIX))
32040
+ .forEach((attr) => {
32041
+ explicitAttrNameToValue[attr.name.slice(_I18N_ATTR_PREFIX.length)] = attr.value;
32042
+ });
31379
32043
  el.attrs.forEach((attr) => {
31380
32044
  if (attr.name in explicitAttrNameToValue) {
31381
32045
  this._addMessage([attr], explicitAttrNameToValue[attr.name]);
@@ -31448,16 +32112,15 @@ class _Visitor {
31448
32112
  return [];
31449
32113
  }
31450
32114
  // translate the attributes of an element and remove i18n specific attributes
31451
- _translateAttributes(el) {
31452
- const attributes = el.attrs;
32115
+ _translateAttributes(node) {
31453
32116
  const i18nParsedMessageMeta = {};
31454
- attributes.forEach((attr) => {
32117
+ const translatedAttributes = [];
32118
+ node.attrs.forEach((attr) => {
31455
32119
  if (attr.name.startsWith(_I18N_ATTR_PREFIX)) {
31456
32120
  i18nParsedMessageMeta[attr.name.slice(_I18N_ATTR_PREFIX.length)] = _parseMessageMeta(attr.value);
31457
32121
  }
31458
32122
  });
31459
- const translatedAttributes = [];
31460
- attributes.forEach((attr) => {
32123
+ node.attrs.forEach((attr) => {
31461
32124
  if (attr.name === _I18N_ATTR || attr.name.startsWith(_I18N_ATTR_PREFIX)) {
31462
32125
  // strip i18n specific attributes
31463
32126
  return;
@@ -31475,11 +32138,11 @@ class _Visitor {
31475
32138
  translatedAttributes.push(new Attribute(attr.name, value, attr.sourceSpan, undefined /* keySpan */, undefined /* valueSpan */, undefined /* valueTokens */, undefined /* i18n */));
31476
32139
  }
31477
32140
  else {
31478
- this._reportError(el, `Unexpected translation for attribute "${attr.name}" (id="${id || this._translations.digest(message)}")`);
32141
+ this._reportError(node, `Unexpected translation for attribute "${attr.name}" (id="${id || this._translations.digest(message)}")`);
31479
32142
  }
31480
32143
  }
31481
32144
  else {
31482
- this._reportError(el, `Translation unavailable for attribute "${attr.name}" (id="${id || this._translations.digest(message)}")`);
32145
+ this._reportError(node, `Translation unavailable for attribute "${attr.name}" (id="${id || this._translations.digest(message)}")`);
31483
32146
  }
31484
32147
  }
31485
32148
  else {
@@ -31488,6 +32151,9 @@ class _Visitor {
31488
32151
  });
31489
32152
  return translatedAttributes;
31490
32153
  }
32154
+ _translateDirectives(node) {
32155
+ return node.directives.map((dir) => new Directive(dir.name, this._translateAttributes(dir), dir.sourceSpan, dir.startSourceSpan, dir.endSourceSpan));
32156
+ }
31491
32157
  /**
31492
32158
  * Add the node as a child of the block when:
31493
32159
  * - we are in a block,
@@ -31563,7 +32229,7 @@ function _isClosingComment(n) {
31563
32229
  return !!(n instanceof Comment && n.value && n.value === '/i18n');
31564
32230
  }
31565
32231
  function _getI18nAttr(p) {
31566
- return p.attrs.find((attr) => attr.name === _I18N_ATTR) || null;
32232
+ return (p.attrs.find((attr) => attr instanceof Attribute && attr.name === _I18N_ATTR) || null);
31567
32233
  }
31568
32234
  function _parseMessageMeta(i18n) {
31569
32235
  if (!i18n)
@@ -31597,7 +32263,7 @@ var FactoryTarget;
31597
32263
  * @description
31598
32264
  * Entry point for all public APIs of the compiler package.
31599
32265
  */
31600
- new Version('20.0.0-next.5');
32266
+ new Version('20.0.0-next.7');
31601
32267
 
31602
32268
  //////////////////////////////////////
31603
32269
  // THIS FILE HAS GLOBAL SIDE EFFECT //