@angular/core 22.0.0-next.1 → 22.0.0-next.11

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 (83) hide show
  1. package/fesm2022/_attribute-chunk.mjs +1 -1
  2. package/fesm2022/_attribute-chunk.mjs.map +1 -1
  3. package/fesm2022/_debug_node-chunk.mjs +2440 -2014
  4. package/fesm2022/_debug_node-chunk.mjs.map +1 -1
  5. package/fesm2022/_effect-chunk.mjs +3 -5
  6. package/fesm2022/_effect-chunk.mjs.map +1 -1
  7. package/fesm2022/_not_found-chunk.mjs +1 -1
  8. package/fesm2022/_not_found-chunk.mjs.map +1 -1
  9. package/fesm2022/_pending_tasks-chunk.mjs +145 -44
  10. package/fesm2022/_pending_tasks-chunk.mjs.map +1 -1
  11. package/fesm2022/_resource-chunk.mjs +108 -33
  12. package/fesm2022/_resource-chunk.mjs.map +1 -1
  13. package/fesm2022/_untracked-chunk.mjs +10 -7
  14. package/fesm2022/_untracked-chunk.mjs.map +1 -1
  15. package/fesm2022/_weak_ref-chunk.mjs +1 -1
  16. package/fesm2022/_weak_ref-chunk.mjs.map +1 -1
  17. package/fesm2022/core.mjs +2368 -1892
  18. package/fesm2022/core.mjs.map +1 -1
  19. package/fesm2022/primitives-di.mjs +1 -1
  20. package/fesm2022/primitives-di.mjs.map +1 -1
  21. package/fesm2022/primitives-event-dispatch.mjs +2 -24
  22. package/fesm2022/primitives-event-dispatch.mjs.map +1 -1
  23. package/fesm2022/primitives-signals.mjs +2 -2
  24. package/fesm2022/primitives-signals.mjs.map +1 -1
  25. package/fesm2022/rxjs-interop.mjs +6 -3
  26. package/fesm2022/rxjs-interop.mjs.map +1 -1
  27. package/fesm2022/testing.mjs +56 -25
  28. package/fesm2022/testing.mjs.map +1 -1
  29. package/package.json +7 -3
  30. package/schematics/bundles/apply_import_manager-CxA_YYgB.cjs +1 -1
  31. package/schematics/bundles/can-match-snapshot-required.cjs +104 -0
  32. package/schematics/bundles/change-detection-eager.cjs +1 -1
  33. package/schematics/bundles/cleanup-unused-imports.cjs +1 -1
  34. package/schematics/bundles/common-to-standalone-migration.cjs +1 -1
  35. package/schematics/bundles/compiler_host-CY14HvaP.cjs +1 -1
  36. package/schematics/bundles/control-flow-migration.cjs +1 -1
  37. package/schematics/bundles/http-xhr-backend.cjs +6 -11
  38. package/schematics/bundles/imports-CKV-ITqD.cjs +1 -1
  39. package/schematics/bundles/incremental-hydration.cjs +94 -0
  40. package/schematics/bundles/{index-BtLcQH8g.cjs → index-DcezkXLN.cjs} +10 -3
  41. package/schematics/bundles/inject-migration.cjs +26 -18
  42. package/schematics/bundles/json-file-Drblb4E1.cjs +1916 -0
  43. package/schematics/bundles/leading_space-BTPRV0wu.cjs +1 -1
  44. package/schematics/bundles/{migrate_ts_type_references-MWoZx-Cb.cjs → migrate_ts_type_references-xRTTASnu.cjs} +48 -28
  45. package/schematics/bundles/model-output.cjs +200 -0
  46. package/schematics/bundles/ng_component_template-DPAF1aEA.cjs +1 -1
  47. package/schematics/bundles/ng_decorators-IVztR9rk.cjs +1 -1
  48. package/schematics/bundles/ngclass-to-class-migration.cjs +14 -6
  49. package/schematics/bundles/ngstyle-to-style-migration.cjs +14 -6
  50. package/schematics/bundles/nodes-ZSQ7WZRB.cjs +1 -1
  51. package/schematics/bundles/output-migration.cjs +2 -2
  52. package/schematics/bundles/parse_html-C8eKA9px.cjs +1 -1
  53. package/schematics/bundles/project_paths-D2V-Uh2L.cjs +1 -1
  54. package/schematics/bundles/project_tsconfig_paths-DkkMibv-.cjs +1 -1
  55. package/schematics/bundles/property_name-BCpALNpZ.cjs +1 -1
  56. package/schematics/bundles/route-lazy-loading.cjs +22 -2
  57. package/schematics/bundles/router-testing-module-migration.cjs +1 -1
  58. package/schematics/bundles/safe-optional-chaining.cjs +571 -0
  59. package/schematics/bundles/self-closing-tags-migration.cjs +1 -1
  60. package/schematics/bundles/signal-input-migration.cjs +3 -3
  61. package/schematics/bundles/signal-queries-migration.cjs +3 -3
  62. package/schematics/bundles/signals.cjs +3 -3
  63. package/schematics/bundles/standalone-migration.cjs +29 -17
  64. package/schematics/bundles/strict-safe-navigation-narrow.cjs +32 -0
  65. package/schematics/bundles/strict-templates-default.cjs +63 -0
  66. package/schematics/migrations.json +30 -0
  67. package/third_party/@mcp-b/webmcp-types/LICENSE +21 -0
  68. package/third_party/@mcp-b/webmcp-types/dist/common.d.ts +297 -0
  69. package/third_party/@mcp-b/webmcp-types/dist/json-schema.d.ts +196 -0
  70. package/third_party/@mcp-b/webmcp-types/index.d.ts +9 -0
  71. package/types/_api-chunk.d.ts +44 -17
  72. package/types/_chrome_dev_tools_performance-chunk.d.ts +4 -3
  73. package/types/{_discovery-chunk.d.ts → _debug_node-chunk.d.ts} +2157 -2258
  74. package/types/_effect-chunk.d.ts +1 -1
  75. package/types/_event_dispatcher-chunk.d.ts +1 -1
  76. package/types/_formatter-chunk.d.ts +1 -1
  77. package/types/_weak_ref-chunk.d.ts +1 -1
  78. package/types/core.d.ts +6833 -6485
  79. package/types/primitives-di.d.ts +1 -1
  80. package/types/primitives-event-dispatch.d.ts +1 -1
  81. package/types/primitives-signals.d.ts +1 -1
  82. package/types/rxjs-interop.d.ts +5 -4
  83. package/types/testing.d.ts +6 -6
@@ -0,0 +1,571 @@
1
+ 'use strict';
2
+ /**
3
+ * @license Angular v22.0.0-next.11
4
+ * (c) 2010-2026 Google LLC. https://angular.dev/
5
+ * License: MIT
6
+ */
7
+ 'use strict';
8
+
9
+ require('@angular-devkit/core');
10
+ require('node:path/posix');
11
+ var project_paths = require('./project_paths-D2V-Uh2L.cjs');
12
+ var compiler = require('@angular/compiler');
13
+ var ts = require('typescript');
14
+ var ng_component_template = require('./ng_component_template-DPAF1aEA.cjs');
15
+ var ng_decorators = require('./ng_decorators-IVztR9rk.cjs');
16
+ require('@angular/compiler-cli');
17
+ require('@angular/compiler-cli/private/migrations');
18
+ require('node:path');
19
+ var property_name = require('./property_name-BCpALNpZ.cjs');
20
+ require('@angular-devkit/schematics');
21
+ require('./project_tsconfig_paths-DkkMibv-.cjs');
22
+ require('./imports-CKV-ITqD.cjs');
23
+
24
+ import('@angular/compiler');
25
+ /**
26
+ * This migration wraps optional chaining expressions in Angular templates with a call to the
27
+ * `$safeNavigationMigration()` magic function. This function doesn't exist at runtime, but is
28
+ * used as a marker for the Angular compiler to transform the expression to keep the legacy
29
+ * behavior of returning `null`.
30
+ *
31
+ * The migration uses a top-down "sink" approach: each expression is visited with a boolean
32
+ * `nullSensitive` context that indicates whether the consumer of the expression's value
33
+ * distinguishes between `null` and `undefined`. Safe navigation operators (`?.`) wrap themselves
34
+ * when their sink is null-sensitive.
35
+ */
36
+ class SafeOptionalChainingMigration extends project_paths.TsurgeFunnelMigration {
37
+ config;
38
+ constructor(config = {}) {
39
+ super();
40
+ this.config = config;
41
+ }
42
+ async analyze(info) {
43
+ const replacements = [];
44
+ // Template Iteration
45
+ const templateVisitor = new ng_component_template.NgComponentTemplateVisitor(info.program.getTypeChecker());
46
+ for (const sourceFile of info.sourceFiles) {
47
+ templateVisitor.visitNode(sourceFile);
48
+ }
49
+ for (const template of templateVisitor.resolvedTemplates) {
50
+ const nodes = compiler.parseTemplate(template.content, template.filePath.toString(), {
51
+ preserveWhitespaces: true,
52
+ preserveLineEndings: true,
53
+ preserveSignificantWhitespace: true,
54
+ leadingTriviaChars: [],
55
+ }).nodes;
56
+ const file = template.inline
57
+ ? project_paths.projectFile(template.container.getSourceFile(), info)
58
+ : project_paths.projectFile(template.filePath, info);
59
+ const exprMigrator = new ExpressionMigrator(file, template.start);
60
+ const visitor = new TmplVisitor(exprMigrator);
61
+ for (const node of nodes) {
62
+ if (node.visit) {
63
+ node.visit(visitor);
64
+ }
65
+ }
66
+ replacements.push(...exprMigrator.replacements);
67
+ }
68
+ for (const sourceFile of info.sourceFiles) {
69
+ replacements.push(...migrateHostBindingsInSourceFile(sourceFile, info));
70
+ }
71
+ return project_paths.confirmAsSerializable({ replacements });
72
+ }
73
+ async combine(unitA, unitB) {
74
+ const seen = new Set();
75
+ const deduped = [];
76
+ for (const r of [...unitA.replacements, ...unitB.replacements]) {
77
+ const key = `${r.projectFile.rootRelativePath}:${r.update.data.position}:${r.update.data.end}:${r.update.data.toInsert}`;
78
+ if (!seen.has(key)) {
79
+ seen.add(key);
80
+ deduped.push(r);
81
+ }
82
+ }
83
+ return project_paths.confirmAsSerializable({ replacements: deduped });
84
+ }
85
+ async globalMeta(data) {
86
+ return project_paths.confirmAsSerializable(data);
87
+ }
88
+ async stats(data) {
89
+ return project_paths.confirmAsSerializable({});
90
+ }
91
+ async migrate(data) {
92
+ return { replacements: data.replacements };
93
+ }
94
+ }
95
+ function migrateHostBindingsInSourceFile(sourceFile, info) {
96
+ const replacements = [];
97
+ const file = project_paths.projectFile(sourceFile, info);
98
+ const typeChecker = info.program.getTypeChecker();
99
+ const visitNode = (node) => {
100
+ if (ts.isClassDeclaration(node)) {
101
+ const decorators = ts.getDecorators(node) ?? [];
102
+ const ngDecorators = ng_decorators.getAngularDecorators(typeChecker, decorators);
103
+ for (const decorator of ngDecorators) {
104
+ if (decorator.name !== 'Component' && decorator.name !== 'Directive') {
105
+ continue;
106
+ }
107
+ const metadata = decorator.node.expression.arguments[0];
108
+ if (!metadata || !ts.isObjectLiteralExpression(metadata)) {
109
+ continue;
110
+ }
111
+ for (const prop of metadata.properties) {
112
+ if (!ts.isPropertyAssignment(prop)) {
113
+ continue;
114
+ }
115
+ const propName = property_name.getPropertyNameText(prop.name);
116
+ if (propName !== 'host' || !ts.isObjectLiteralExpression(prop.initializer)) {
117
+ continue;
118
+ }
119
+ for (const hostProp of prop.initializer.properties) {
120
+ if (!ts.isPropertyAssignment(hostProp) ||
121
+ !ts.isStringLiteralLike(hostProp.initializer)) {
122
+ continue;
123
+ }
124
+ const hostKey = property_name.getPropertyNameText(hostProp.name);
125
+ if (hostKey === null || (!hostKey.startsWith('[') && !hostKey.startsWith('('))) {
126
+ continue;
127
+ }
128
+ // Preserve raw text between quotes/backticks so source offsets stay aligned.
129
+ const hostExpression = hostProp.initializer.getText().slice(1, -1);
130
+ const fakeTemplatePrefix = `<div ${hostKey}="`;
131
+ const fakeTemplate = `${fakeTemplatePrefix}${hostExpression}"></div>`;
132
+ const parsedNodes = compiler.parseTemplate(fakeTemplate, sourceFile.fileName, {
133
+ preserveWhitespaces: true,
134
+ }).nodes;
135
+ const hostExpressionStart = hostProp.initializer.getStart() + 1;
136
+ const exprMigrator = new ExpressionMigrator(file, hostExpressionStart - fakeTemplatePrefix.length);
137
+ const visitor = new HostBindingVisitor(exprMigrator);
138
+ for (const parsedNode of parsedNodes) {
139
+ if (parsedNode.visit) {
140
+ parsedNode.visit(visitor);
141
+ }
142
+ }
143
+ replacements.push(...exprMigrator.replacements);
144
+ }
145
+ }
146
+ }
147
+ }
148
+ ts.forEachChild(node, visitNode);
149
+ };
150
+ visitNode(sourceFile);
151
+ return replacements;
152
+ }
153
+ /** Returns whether the given attribute is a class, style, or attribute binding. */
154
+ function isClassStyleOrAttrBinding(attribute) {
155
+ return (attribute.type === compiler.BindingType.Class ||
156
+ attribute.type === compiler.BindingType.Style ||
157
+ attribute.type === compiler.BindingType.Attribute ||
158
+ (attribute.type === compiler.BindingType.Property &&
159
+ (attribute.name === 'class' || attribute.name === 'className' || attribute.name === 'style')));
160
+ }
161
+ class HostBindingVisitor extends compiler.TmplAstRecursiveVisitor {
162
+ hostExprMigrator;
163
+ constructor(hostExprMigrator) {
164
+ super();
165
+ this.hostExprMigrator = hostExprMigrator;
166
+ }
167
+ visitBoundAttribute(attribute) {
168
+ if (isClassStyleOrAttrBinding(attribute)) {
169
+ // Class/style/attr bindings use truthiness — not null-sensitive by default.
170
+ // Safe navs inside function calls or pipes will still be wrapped by the migrator.
171
+ attribute.value.visit(this.hostExprMigrator, false);
172
+ }
173
+ else {
174
+ // Regular property bindings are null-sensitive.
175
+ attribute.value.visit(this.hostExprMigrator, true);
176
+ }
177
+ super.visitBoundAttribute(attribute);
178
+ }
179
+ visitBoundEvent(event) {
180
+ if (event.handler && event.handler.visit) {
181
+ // Event handlers are not null-sensitive; safe navs inside function calls will
182
+ // still be wrapped because function arguments are always null-sensitive.
183
+ event.handler.visit(this.hostExprMigrator, false);
184
+ }
185
+ super.visitBoundEvent(event);
186
+ }
187
+ }
188
+ /**
189
+ * Visits template expressions and inserts `$safeNavigationMigration(…)` wrappers around safe
190
+ * navigation chains whose result is consumed by a null-sensitive sink.
191
+ *
192
+ * The `context` parameter at every visit call is a boolean `nullSensitive` flag that answers:
193
+ * "does the parent care whether this expression's value is `null` vs. `undefined`?"
194
+ *
195
+ * Propagation rules:
196
+ * - `||`, `&&`, `??`: children are **not** null-sensitive (both normalise null/undefined).
197
+ * - `===` / `!==` with a nullish literal on one side: the other operand **is** null-sensitive.
198
+ * - All other binary operators: propagate the parent's null-sensitivity.
199
+ * - `!` (prefix not): operand is **not** null-sensitive (uses truthiness).
200
+ * - Ternary condition: **not** null-sensitive; branches inherit the parent's null-sensitivity.
201
+ * - Function call arguments and pipe inputs: **always** null-sensitive.
202
+ * - Receivers of property/keyed/call reads: **not** null-sensitive by default.
203
+ * For property/keyed/call continuations, when the receiver is a safe node and the parent sink
204
+ * is null-sensitive, wrap the full continuation node (e.g. `foo?.bar.baz`, `foo?.save()`) rather
205
+ * than the inner safe receiver (`foo?.bar`, `foo?.save`) so runtime behavior is preserved.
206
+ * - `NonNullAssert` (`!`): propagates the parent's null-sensitivity (type-only assertion).
207
+ */
208
+ class ExpressionMigrator extends compiler.RecursiveAstVisitor {
209
+ file;
210
+ templateStart;
211
+ replacements = [];
212
+ constructor(file, templateStart) {
213
+ super();
214
+ this.file = file;
215
+ this.templateStart = templateStart;
216
+ }
217
+ // ---------------------------------------------------------------------------
218
+ // Safe navigation nodes — wrap when the sink is null-sensitive
219
+ // ---------------------------------------------------------------------------
220
+ visitSafePropertyRead(ast, nullSensitive) {
221
+ if (nullSensitive) {
222
+ this.addReplacement(ast);
223
+ }
224
+ // Receiver: not null-sensitive (further access on null/undefined throws either way).
225
+ this.visit(ast.receiver, false);
226
+ }
227
+ visitSafeKeyedRead(ast, nullSensitive) {
228
+ if (nullSensitive) {
229
+ this.addReplacement(ast);
230
+ }
231
+ this.visit(ast.receiver, false);
232
+ this.visit(ast.key, false);
233
+ }
234
+ visitSafeCall(ast, nullSensitive) {
235
+ if (nullSensitive) {
236
+ this.addReplacement(ast);
237
+ }
238
+ this.visit(ast.receiver, false);
239
+ this.visitAll(ast.args, true);
240
+ }
241
+ hasSafeReceiver(receiver) {
242
+ if (receiver instanceof compiler.SafePropertyRead ||
243
+ receiver instanceof compiler.SafeKeyedRead ||
244
+ receiver instanceof compiler.SafeCall) {
245
+ return true;
246
+ }
247
+ if (receiver instanceof compiler.NonNullAssert) {
248
+ return this.hasSafeReceiver(receiver.expression);
249
+ }
250
+ return false;
251
+ }
252
+ // ---------------------------------------------------------------------------
253
+ // Non-safe access — receiver is never null-sensitive
254
+ // ---------------------------------------------------------------------------
255
+ visitPropertyRead(ast, nullSensitive) {
256
+ if (nullSensitive && this.hasSafeReceiver(ast.receiver)) {
257
+ this.addReplacement(ast);
258
+ }
259
+ this.visit(ast.receiver, false);
260
+ }
261
+ visitKeyedRead(ast, nullSensitive) {
262
+ if (nullSensitive && this.hasSafeReceiver(ast.receiver)) {
263
+ this.addReplacement(ast);
264
+ }
265
+ this.visit(ast.receiver, false);
266
+ this.visit(ast.key, false);
267
+ }
268
+ // ---------------------------------------------------------------------------
269
+ // Function calls — arguments are always null-sensitive
270
+ // ---------------------------------------------------------------------------
271
+ visitCall(ast, nullSensitive) {
272
+ if (nullSensitive && this.hasSafeReceiver(ast.receiver)) {
273
+ this.addReplacement(ast);
274
+ }
275
+ this.visit(ast.receiver, false);
276
+ this.visitAll(ast.args, true);
277
+ }
278
+ // ---------------------------------------------------------------------------
279
+ // Pipes — input and arguments are always null-sensitive
280
+ // ---------------------------------------------------------------------------
281
+ visitPipe(ast, _nullSensitive) {
282
+ this.visit(ast.exp, true);
283
+ this.visitAll(ast.args, true);
284
+ }
285
+ // ---------------------------------------------------------------------------
286
+ // Binary operators
287
+ // ---------------------------------------------------------------------------
288
+ visitBinary(ast, nullSensitive) {
289
+ if (ast.operation === '||' || ast.operation === '&&' || ast.operation === '??') {
290
+ // These operators normalise null and undefined (both produce the same result),
291
+ // so the operands are not null-sensitive.
292
+ this.visit(ast.left, false);
293
+ this.visit(ast.right, false);
294
+ }
295
+ else if (ast.operation === '===' || ast.operation === '!==') {
296
+ // A strict comparison with a nullish literal makes the other side null-sensitive
297
+ // (null === null is true but undefined === null is false).
298
+ const leftIsNullish = isNullishLiteralAST(ast.left);
299
+ const rightIsNullish = isNullishLiteralAST(ast.right);
300
+ this.visit(ast.left, rightIsNullish);
301
+ this.visit(ast.right, leftIsNullish);
302
+ }
303
+ else {
304
+ // All other binary operators (<, >, +, -, …): propagate the parent's null-sensitivity
305
+ // because null and undefined can produce different numeric results (null → 0,
306
+ // undefined → NaN for arithmetic/comparison).
307
+ this.visit(ast.left, nullSensitive);
308
+ this.visit(ast.right, nullSensitive);
309
+ }
310
+ }
311
+ // ---------------------------------------------------------------------------
312
+ // Unary / logical operators
313
+ // ---------------------------------------------------------------------------
314
+ visitPrefixNot(ast, _nullSensitive) {
315
+ // Logical negation uses truthiness — null and undefined are both falsy.
316
+ this.visit(ast.expression, false);
317
+ }
318
+ // ---------------------------------------------------------------------------
319
+ // Ternary
320
+ // ---------------------------------------------------------------------------
321
+ visitConditional(ast, nullSensitive) {
322
+ // The condition is evaluated as a boolean — not null-sensitive.
323
+ this.visit(ast.condition, false);
324
+ // The result of a branch is consumed by the parent, so it inherits null-sensitivity.
325
+ this.visit(ast.trueExp, nullSensitive);
326
+ this.visit(ast.falseExp, nullSensitive);
327
+ }
328
+ // ---------------------------------------------------------------------------
329
+ // NonNullAssert — a compile-time type annotation, no runtime effect
330
+ // ---------------------------------------------------------------------------
331
+ visitNonNullAssert(ast, nullSensitive) {
332
+ this.visit(ast.expression, nullSensitive);
333
+ }
334
+ // ---------------------------------------------------------------------------
335
+ // Chain (event handler statements) — never null-sensitive
336
+ // ---------------------------------------------------------------------------
337
+ visitChain(ast, _nullSensitive) {
338
+ this.visitAll(ast.expressions, false);
339
+ }
340
+ // ---------------------------------------------------------------------------
341
+ // Interpolation — coerces to string, so null and undefined produce identical
342
+ // output; sub-expressions are therefore never null-sensitive.
343
+ // ---------------------------------------------------------------------------
344
+ visitInterpolation(ast, _nullSensitive) {
345
+ this.visitAll(ast.expressions, false);
346
+ }
347
+ // ---------------------------------------------------------------------------
348
+ // All other nodes (LiteralArray, LiteralMap, Unary, …) use the default
349
+ // RecursiveAstVisitor which propagates the current context to every child —
350
+ // the correct "inherit parent null-sensitivity" fallback.
351
+ // ---------------------------------------------------------------------------
352
+ addReplacement(ast) {
353
+ const startArg = ast.sourceSpan.start;
354
+ const endArg = ast.sourceSpan.end;
355
+ this.replacements.push(new project_paths.Replacement(this.file, new project_paths.TextUpdate({
356
+ position: this.templateStart + endArg,
357
+ end: this.templateStart + endArg,
358
+ toInsert: ')',
359
+ })), new project_paths.Replacement(this.file, new project_paths.TextUpdate({
360
+ position: this.templateStart + startArg,
361
+ end: this.templateStart + startArg,
362
+ toInsert: '$safeNavigationMigration(',
363
+ })));
364
+ }
365
+ }
366
+ // ---------------------------------------------------------------------------
367
+ // Helper utilities
368
+ // ---------------------------------------------------------------------------
369
+ /** Returns true if the AST node is a literal `null` or `undefined`. */
370
+ function isNullishLiteralAST(ast) {
371
+ const innerAst = ast instanceof compiler.ASTWithSource ? ast.ast : ast;
372
+ return (innerAst instanceof compiler.LiteralPrimitive &&
373
+ (innerAst.value === null || innerAst.value === undefined));
374
+ }
375
+ /** Returns true if the AST node is a non-null, non-undefined primitive literal. */
376
+ function isNonNullishLiteralAST(ast) {
377
+ const innerAst = ast instanceof compiler.ASTWithSource ? ast.ast : ast;
378
+ return (innerAst instanceof compiler.LiteralPrimitive && innerAst.value !== null && innerAst.value !== undefined);
379
+ }
380
+ // ---------------------------------------------------------------------------
381
+ // Template visitor
382
+ // ---------------------------------------------------------------------------
383
+ /**
384
+ * Returns true if all *ngSwitchCase bindings in the given nodes (and their children)
385
+ * are non-null/non-undefined literal expressions — meaning the switch expression
386
+ * doesn't need null-sensitivity migration.
387
+ */
388
+ function allNgSwitchCasesAreLiterals(nodes) {
389
+ for (const node of nodes) {
390
+ for (const input of node.inputs) {
391
+ if (input.name === 'ngSwitchCase' && input.value) {
392
+ if (!isNonNullishLiteralAST(input.value)) {
393
+ return false;
394
+ }
395
+ }
396
+ }
397
+ if (node instanceof compiler.TmplAstTemplate) {
398
+ for (const attr of node.templateAttrs) {
399
+ if (attr instanceof compiler.TmplAstBoundAttribute && attr.name === 'ngSwitchCase' && attr.value) {
400
+ if (!isNonNullishLiteralAST(attr.value)) {
401
+ return false;
402
+ }
403
+ }
404
+ }
405
+ }
406
+ const childHosts = node.children.filter((child) => child instanceof compiler.TmplAstElement || child instanceof compiler.TmplAstTemplate);
407
+ if (!allNgSwitchCasesAreLiterals(childHosts)) {
408
+ return false;
409
+ }
410
+ }
411
+ return true;
412
+ }
413
+ class TmplVisitor extends compiler.TmplAstRecursiveVisitor {
414
+ exprMigrator;
415
+ migratableSwitchCases = new WeakSet();
416
+ /**
417
+ * Stack tracking whether the current ngSwitch context should be migrated.
418
+ * False when all *ngSwitchCase expressions are non-null literals.
419
+ */
420
+ ngSwitchShouldMigrateStack = [];
421
+ constructor(exprMigrator) {
422
+ super();
423
+ this.exprMigrator = exprMigrator;
424
+ }
425
+ shouldMigrateCurrentNgSwitchContext() {
426
+ return this.ngSwitchShouldMigrateStack[this.ngSwitchShouldMigrateStack.length - 1] ?? true;
427
+ }
428
+ hasNgSwitchBinding(node) {
429
+ return (node.inputs.some((attr) => attr.name === 'ngSwitch') ||
430
+ (node instanceof compiler.TmplAstTemplate &&
431
+ node.templateAttrs.some((attr) => attr.name === 'ngSwitch')));
432
+ }
433
+ visitElement(element) {
434
+ const hasNgSwitch = this.hasNgSwitchBinding(element);
435
+ if (hasNgSwitch) {
436
+ const childHosts = element.children.filter((child) => child instanceof compiler.TmplAstElement || child instanceof compiler.TmplAstTemplate);
437
+ this.ngSwitchShouldMigrateStack.push(!allNgSwitchCasesAreLiterals(childHosts));
438
+ }
439
+ super.visitElement(element);
440
+ if (hasNgSwitch) {
441
+ this.ngSwitchShouldMigrateStack.pop();
442
+ }
443
+ }
444
+ visitBoundAttribute(attribute) {
445
+ if (attribute.name === 'ngForOf') {
446
+ // ngFor/@for item expressions are not null-sensitive by default.
447
+ // Still visit so inner null-sensitive sinks (e.g. pipes/functions) are migrated.
448
+ attribute.value.visit(this.exprMigrator, false);
449
+ }
450
+ else if (attribute.name === 'ngIf') {
451
+ // ngIf evaluates as a boolean; null-sensitivity only arises when the expression
452
+ // itself contains a strict null comparison (handled inside ExpressionMigrator).
453
+ attribute.value.visit(this.exprMigrator, false);
454
+ }
455
+ else if (attribute.name === 'ngSwitch' || attribute.name === 'ngSwitchCase') {
456
+ attribute.value.visit(this.exprMigrator, this.shouldMigrateCurrentNgSwitchContext());
457
+ }
458
+ else if (isClassStyleOrAttrBinding(attribute)) {
459
+ // Class/style/attr bindings use truthiness — not null-sensitive by default.
460
+ attribute.value.visit(this.exprMigrator, false);
461
+ }
462
+ else {
463
+ // Regular property bindings are null-sensitive.
464
+ attribute.value.visit(this.exprMigrator, true);
465
+ }
466
+ super.visitBoundAttribute(attribute);
467
+ }
468
+ visitBoundEvent(event) {
469
+ if (event.handler && event.handler.visit) {
470
+ // Event handlers are not null-sensitive. Safe navs inside function calls
471
+ // (e.g. `compute(user?.save())`) are still migrated because function arguments
472
+ // are always null-sensitive in ExpressionMigrator.
473
+ event.handler.visit(this.exprMigrator, false);
474
+ }
475
+ super.visitBoundEvent(event);
476
+ }
477
+ visitBoundText(text) {
478
+ // Interpolation text is not null-sensitive by default; null-sensitivity is
479
+ // introduced by pipes, function calls, or strict null comparisons inside
480
+ // the expression, all handled by ExpressionMigrator.
481
+ text.value.visit(this.exprMigrator, false);
482
+ super.visitBoundText(text);
483
+ }
484
+ visitTemplate(template) {
485
+ const hasNgSwitch = this.hasNgSwitchBinding(template);
486
+ if (hasNgSwitch) {
487
+ const childHosts = template.children.filter((child) => child instanceof compiler.TmplAstElement || child instanceof compiler.TmplAstTemplate);
488
+ this.ngSwitchShouldMigrateStack.push(!allNgSwitchCasesAreLiterals(childHosts));
489
+ }
490
+ for (const attr of template.templateAttrs) {
491
+ if (!(attr instanceof compiler.TmplAstBoundAttribute)) {
492
+ continue;
493
+ }
494
+ if (attr.name === 'ngIf') {
495
+ attr.value.visit(this.exprMigrator, false);
496
+ }
497
+ else if (attr.name === 'ngSwitch' || attr.name === 'ngSwitchCase') {
498
+ attr.value.visit(this.exprMigrator, this.shouldMigrateCurrentNgSwitchContext());
499
+ }
500
+ else if (attr.name === 'ngForOf') {
501
+ // ngFor microsyntax expressions are not null-sensitive by default.
502
+ // Still visit so nested null-sensitive sinks are handled.
503
+ attr.value.visit(this.exprMigrator, false);
504
+ }
505
+ else {
506
+ attr.value.visit(this.exprMigrator, true);
507
+ }
508
+ }
509
+ super.visitTemplate(template);
510
+ if (hasNgSwitch) {
511
+ this.ngSwitchShouldMigrateStack.pop();
512
+ }
513
+ }
514
+ visitIfBlockBranch(block) {
515
+ if (block.expression) {
516
+ // @if condition: not null-sensitive by default (same logic as ngIf).
517
+ block.expression.visit(this.exprMigrator, false);
518
+ }
519
+ super.visitIfBlockBranch(block);
520
+ }
521
+ visitForLoopBlock(block) {
522
+ block.expression.visit(this.exprMigrator, false);
523
+ block.trackBy.visit(this.exprMigrator, false);
524
+ super.visitForLoopBlock(block);
525
+ }
526
+ visitLetDeclaration(decl) {
527
+ // @let value is assigned directly — null-sensitive.
528
+ decl.value.visit(this.exprMigrator, true);
529
+ super.visitLetDeclaration(decl);
530
+ }
531
+ visitSwitchBlock(block) {
532
+ const switchCases = block.groups.flatMap((group) => group.cases);
533
+ // Don't migrate if every case expression is a non-null/non-undefined literal
534
+ // (e.g. strings, numbers, booleans). In that case null-vs-undefined can never
535
+ // match a case, so wrapping the switch expression would be pointless.
536
+ const shouldMigrate = !switchCases
537
+ .filter((switchCase) => switchCase.expression)
538
+ .every((switchCase) => isNonNullishLiteralAST(switchCase.expression));
539
+ if (shouldMigrate) {
540
+ block.expression.visit(this.exprMigrator, true);
541
+ for (const switchCase of switchCases) {
542
+ this.migratableSwitchCases.add(switchCase);
543
+ }
544
+ }
545
+ super.visitSwitchBlock(block);
546
+ }
547
+ visitSwitchBlockCase(block) {
548
+ if (this.migratableSwitchCases.has(block) && block.expression) {
549
+ block.expression.visit(this.exprMigrator, true);
550
+ }
551
+ super.visitSwitchBlockCase(block);
552
+ }
553
+ visitDeferredTrigger(trigger) {
554
+ if (trigger instanceof compiler.TmplAstBoundDeferredTrigger) {
555
+ // @defer (when …): same logic as @if — not null-sensitive by default.
556
+ trigger.value.visit(this.exprMigrator, false);
557
+ }
558
+ super.visitDeferredTrigger(trigger);
559
+ }
560
+ }
561
+
562
+ function migrate() {
563
+ return async (tree) => {
564
+ await project_paths.runMigrationInDevkit({
565
+ tree,
566
+ getMigration: () => new SafeOptionalChainingMigration(),
567
+ });
568
+ };
569
+ }
570
+
571
+ exports.migrate = migrate;
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
  /**
3
- * @license Angular v22.0.0-next.1
3
+ * @license Angular v22.0.0-next.11
4
4
  * (c) 2010-2026 Google LLC. https://angular.dev/
5
5
  * License: MIT
6
6
  */
@@ -1,18 +1,18 @@
1
1
  'use strict';
2
2
  /**
3
- * @license Angular v22.0.0-next.1
3
+ * @license Angular v22.0.0-next.11
4
4
  * (c) 2010-2026 Google LLC. https://angular.dev/
5
5
  * License: MIT
6
6
  */
7
7
  'use strict';
8
8
 
9
- var migrate_ts_type_references = require('./migrate_ts_type_references-MWoZx-Cb.cjs');
9
+ var migrate_ts_type_references = require('./migrate_ts_type_references-xRTTASnu.cjs');
10
10
  var ts = require('typescript');
11
11
  require('@angular/compiler-cli');
12
12
  var migrations = require('@angular/compiler-cli/private/migrations');
13
13
  require('node:path');
14
14
  var project_paths = require('./project_paths-D2V-Uh2L.cjs');
15
- var index = require('./index-BtLcQH8g.cjs');
15
+ var index = require('./index-DcezkXLN.cjs');
16
16
  var assert = require('assert');
17
17
  var apply_import_manager = require('./apply_import_manager-CxA_YYgB.cjs');
18
18
  require('@angular-devkit/core');
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
  /**
3
- * @license Angular v22.0.0-next.1
3
+ * @license Angular v22.0.0-next.11
4
4
  * (c) 2010-2026 Google LLC. https://angular.dev/
5
5
  * License: MIT
6
6
  */
@@ -12,9 +12,9 @@ require('@angular/compiler-cli');
12
12
  require('node:path');
13
13
  var project_paths = require('./project_paths-D2V-Uh2L.cjs');
14
14
  var apply_import_manager = require('./apply_import_manager-CxA_YYgB.cjs');
15
- var migrate_ts_type_references = require('./migrate_ts_type_references-MWoZx-Cb.cjs');
15
+ var migrate_ts_type_references = require('./migrate_ts_type_references-xRTTASnu.cjs');
16
16
  var assert = require('assert');
17
- var index = require('./index-BtLcQH8g.cjs');
17
+ var index = require('./index-DcezkXLN.cjs');
18
18
  var compiler = require('@angular/compiler');
19
19
  require('@angular-devkit/core');
20
20
  require('node:path/posix');
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
  /**
3
- * @license Angular v22.0.0-next.1
3
+ * @license Angular v22.0.0-next.11
4
4
  * (c) 2010-2026 Google LLC. https://angular.dev/
5
5
  * License: MIT
6
6
  */
@@ -19,9 +19,9 @@ require('@angular-devkit/core');
19
19
  require('node:path/posix');
20
20
  require('./project_tsconfig_paths-DkkMibv-.cjs');
21
21
  require('./apply_import_manager-CxA_YYgB.cjs');
22
- require('./migrate_ts_type_references-MWoZx-Cb.cjs');
22
+ require('./migrate_ts_type_references-xRTTASnu.cjs');
23
23
  require('assert');
24
- require('./index-BtLcQH8g.cjs');
24
+ require('./index-DcezkXLN.cjs');
25
25
  require('@angular/compiler');
26
26
  require('./leading_space-BTPRV0wu.cjs');
27
27