@angular/core 19.0.0-next.8 → 19.0.0-rc.0

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 (42) hide show
  1. package/fesm2022/core.mjs +21591 -19590
  2. package/fesm2022/core.mjs.map +1 -1
  3. package/fesm2022/primitives/event-dispatch.mjs +71 -47
  4. package/fesm2022/primitives/event-dispatch.mjs.map +1 -1
  5. package/fesm2022/primitives/signals.mjs +8 -6
  6. package/fesm2022/primitives/signals.mjs.map +1 -1
  7. package/fesm2022/rxjs-interop.mjs +90 -10
  8. package/fesm2022/rxjs-interop.mjs.map +1 -1
  9. package/fesm2022/testing.mjs +177 -114
  10. package/fesm2022/testing.mjs.map +1 -1
  11. package/index.d.ts +591 -101
  12. package/package.json +1 -1
  13. package/primitives/event-dispatch/index.d.ts +7 -4
  14. package/primitives/signals/index.d.ts +7 -1
  15. package/rxjs-interop/index.d.ts +35 -4
  16. package/schematics/bundles/{checker-e68dd7ce.js → checker-2451e7c5.js} +2464 -1132
  17. package/schematics/bundles/{group_replacements-472b2387.js → combine_units-c52492ab.js} +1964 -2207
  18. package/schematics/bundles/{compiler_host-9a4d0c2b.js → compiler_host-f54f8309.js} +2 -2
  19. package/schematics/bundles/control-flow-migration.js +3 -3
  20. package/schematics/bundles/explicit-standalone-flag.js +31 -11
  21. package/schematics/bundles/{imports-4ac08251.js → imports-44987700.js} +1 -1
  22. package/schematics/bundles/inject-migration.js +122 -48
  23. package/schematics/bundles/{leading_space-d190b83b.js → leading_space-6e7a8ec6.js} +1 -1
  24. package/schematics/bundles/migrate_ts_type_references-ab18a7c3.js +1463 -0
  25. package/schematics/bundles/{nodes-0e7d45ca.js → ng_decorators-3ad437d2.js} +2 -15
  26. package/schematics/bundles/nodes-ffdce442.js +27 -0
  27. package/schematics/bundles/output-migration.js +7450 -0
  28. package/schematics/bundles/pending-tasks.js +5 -5
  29. package/schematics/bundles/{program-105283c5.js → program-58424797.js} +1359 -455
  30. package/schematics/bundles/{project_tsconfig_paths-e9ccccbf.js → project_tsconfig_paths-6c9cde78.js} +1 -1
  31. package/schematics/bundles/provide-initializer.js +190 -0
  32. package/schematics/bundles/route-lazy-loading.js +4 -4
  33. package/schematics/bundles/signal-input-migration.js +197 -349
  34. package/schematics/bundles/signal-queries-migration.js +462 -185
  35. package/schematics/bundles/signals.js +54 -0
  36. package/schematics/bundles/standalone-migration.js +38 -20
  37. package/schematics/collection.json +11 -0
  38. package/schematics/migrations.json +7 -1
  39. package/schematics/ng-generate/output-migration/schema.json +19 -0
  40. package/schematics/ng-generate/signal-queries-migration/schema.json +11 -0
  41. package/schematics/ng-generate/signals/schema.json +65 -0
  42. package/testing/index.d.ts +3 -1
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
  /**
3
- * @license Angular v19.0.0-next.8
3
+ * @license Angular v19.0.0-rc.0
4
4
  * (c) 2010-2024 Google LLC. https://angular.io/
5
5
  * License: MIT
6
6
  */
@@ -9,147 +9,57 @@
9
9
  Object.defineProperty(exports, '__esModule', { value: true });
10
10
 
11
11
  var schematics = require('@angular-devkit/schematics');
12
- var group_replacements = require('./group_replacements-472b2387.js');
12
+ var migrate_ts_type_references = require('./migrate_ts_type_references-ab18a7c3.js');
13
13
  var ts = require('typescript');
14
14
  require('os');
15
- var checker = require('./checker-e68dd7ce.js');
16
- var program = require('./program-105283c5.js');
15
+ var checker = require('./checker-2451e7c5.js');
16
+ var program = require('./program-58424797.js');
17
17
  require('path');
18
+ var combine_units = require('./combine_units-c52492ab.js');
18
19
  var assert = require('assert');
19
- var leading_space = require('./leading_space-d190b83b.js');
20
- var project_tsconfig_paths = require('./project_tsconfig_paths-e9ccccbf.js');
21
- require('@angular-devkit/core');
22
- require('node:path');
20
+ var project_tsconfig_paths = require('./project_tsconfig_paths-6c9cde78.js');
21
+ require('./leading_space-6e7a8ec6.js');
23
22
  require('fs');
24
23
  require('module');
25
24
  require('url');
25
+ require('@angular-devkit/core');
26
+ require('node:path/posix');
26
27
 
27
28
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
28
29
 
29
30
  var ts__default = /*#__PURE__*/_interopDefaultLegacy(ts);
30
31
  var assert__default = /*#__PURE__*/_interopDefaultLegacy(assert);
31
32
 
32
- /**
33
- * Gets human-readable message information for the given input incompatibility.
34
- * This text will be used by the language service, or CLI-based migration.
35
- */
36
- function getMessageForInputIncompatibility(reason) {
37
- switch (reason) {
38
- case group_replacements.InputIncompatibilityReason.Accessor:
39
- return {
40
- short: 'Accessor inputs cannot be migrated as they are too complex.',
41
- extra: 'The migration potentially requires usage of `effect` or `computed`, but ' +
42
- 'the intent is unclear. The migration cannot safely migrate.',
43
- };
44
- case group_replacements.InputIncompatibilityReason.OverriddenByDerivedClass:
45
- return {
46
- short: 'The input cannot be migrated because the field is overridden by a subclass.',
47
- extra: 'The field in the subclass is not an input, so migrating would break your build.',
48
- };
49
- case group_replacements.InputIncompatibilityReason.ParentIsIncompatible:
50
- return {
51
- short: 'This input is inherited from a superclass, but the parent cannot be migrated.',
52
- extra: 'Migrating this input would cause your build to fail.',
53
- };
54
- case group_replacements.InputIncompatibilityReason.PotentiallyNarrowedInTemplateButNoSupportYet:
55
- return {
56
- short: 'This input is used in a control flow expression (e.g. `@if` or `*ngIf`) and ' +
57
- 'migrating would break narrowing currently.',
58
- extra: `In the future, Angular intends to support narrowing of signals.`,
59
- };
60
- case group_replacements.InputIncompatibilityReason.RedeclaredViaDerivedClassInputsArray:
61
- return {
62
- short: 'The input is overridden by a subclass that cannot be migrated.',
63
- extra: 'The subclass re-declares this input via the `inputs` array in @Directive/@Component. ' +
64
- 'Migrating this input would break your build because the subclass input cannot be migrated.',
65
- };
66
- case group_replacements.InputIncompatibilityReason.RequiredInputButNoGoodExplicitTypeExtractable:
67
- return {
68
- short: `Input is required, but the migration cannot determine a good type for the input.`,
69
- extra: 'Consider adding an explicit type to make the migration possible.',
70
- };
71
- case group_replacements.InputIncompatibilityReason.InputWithQuestionMarkButNoGoodExplicitTypeExtractable:
72
- return {
73
- short: `Input is marked with a question mark. Migration could not determine a good type for the input.`,
74
- extra: 'The migration needs to be able to resolve a type, so that it can include `undefined` in your type. ' +
75
- 'Consider adding an explicit type to make the migration possible.',
76
- };
77
- case group_replacements.InputIncompatibilityReason.SkippedViaConfigFilter:
78
- return {
79
- short: `This input is not part of the current migration scope.`,
80
- extra: 'Skipped via migration config.',
81
- };
82
- case group_replacements.InputIncompatibilityReason.SpyOnThatOverwritesField:
83
- return {
84
- short: 'A jasmine `spyOn` call spies on this input. This breaks with signal inputs.',
85
- extra: `Migration cannot safely migrate as "spyOn" writes to the input. Signal inputs are readonly.`,
86
- };
87
- case group_replacements.InputIncompatibilityReason.TypeConflictWithBaseClass:
88
- return {
89
- short: 'This input overrides a field from a superclass, while the superclass field is not migrated.',
90
- extra: 'Migrating the input would break your build because of a type conflict then.',
91
- };
92
- case group_replacements.InputIncompatibilityReason.WriteAssignment:
93
- return {
94
- short: 'Your application code writes to the input. This prevents migration.',
95
- extra: 'Signal inputs are readonly, so migrating would break your build.',
96
- };
97
- case group_replacements.InputIncompatibilityReason.OutsideOfMigrationScope:
98
- return {
99
- short: 'This input is not part of any source files in your project.',
100
- extra: 'The migration excludes inputs if no source file declaring the input was seen.',
101
- };
102
- }
103
- }
104
- /**
105
- * Gets human-readable message information for the given input class incompatibility.
106
- * This text will be used by the language service, or CLI-based migration.
107
- */
108
- function getMessageForClassIncompatibility(reason) {
109
- switch (reason) {
110
- case group_replacements.ClassIncompatibilityReason.InputOwningClassReferencedInClassProperty:
111
- return {
112
- short: 'Class of this input is referenced in the signature of another class.',
113
- extra: 'The other class is likely typed to expect a non-migrated field, so ' +
114
- 'migration is skipped to not break your build.',
115
- };
116
- case group_replacements.ClassIncompatibilityReason.ClassManuallyInstantiated:
117
- return {
118
- short: 'Class of this input is manually instantiated. ' +
119
- 'This is discouraged and prevents migration',
120
- extra: 'Signal inputs require a DI injection context. Manually instantiating ' +
121
- 'breaks this requirement in some cases, so the migration is skipped.',
122
- };
123
- }
124
- }
125
-
126
33
  /**
127
34
  * Class that holds information about a given directive and its input fields.
128
35
  */
129
36
  class DirectiveInfo {
37
+ clazz;
38
+ /**
39
+ * Map of inputs detected in the given class.
40
+ * Maps string-based input ids to the detailed input metadata.
41
+ */
42
+ inputFields = new Map();
43
+ /** Map of input IDs and their incompatibilities. */
44
+ memberIncompatibility = new Map();
45
+ /**
46
+ * Whether the whole class is incompatible.
47
+ *
48
+ * Class incompatibility precedes individual member incompatibility.
49
+ * All members in the class are considered incompatible.
50
+ */
51
+ incompatible = null;
130
52
  constructor(clazz) {
131
53
  this.clazz = clazz;
132
- /**
133
- * Map of inputs detected in the given class.
134
- * Maps string-based input ids to the detailed input metadata.
135
- */
136
- this.inputFields = new Map();
137
- /** Map of input IDs and their incompatibilities. */
138
- this.memberIncompatibility = new Map();
139
- /**
140
- * Whether the whole class is incompatible.
141
- *
142
- * Class incompatibility precedes individual member incompatibility.
143
- * All members in the class are considered incompatible.
144
- */
145
- this.incompatible = null;
146
54
  }
147
55
  /**
148
- * Checks whether there are any incompatible inputs for the
56
+ * Checks whether there are any migrated inputs for the
149
57
  * given class.
58
+ *
59
+ * Returns `false` if all inputs are incompatible.
150
60
  */
151
- hasIncompatibleMembers() {
152
- return Array.from(this.inputFields.values()).some(({ descriptor }) => this.isInputMemberIncompatible(descriptor));
61
+ hasMigratedFields() {
62
+ return Array.from(this.inputFields.values()).some(({ descriptor }) => !this.isInputMemberIncompatible(descriptor));
153
63
  }
154
64
  /**
155
65
  * Whether the given input member is incompatible. If the class is incompatible,
@@ -170,6 +80,11 @@ class DirectiveInfo {
170
80
  * the whole migration.
171
81
  */
172
82
  class MigrationHost {
83
+ isMigratingCore;
84
+ programInfo;
85
+ config;
86
+ _sourceFiles;
87
+ compilerOptions;
173
88
  constructor(isMigratingCore, programInfo, config, sourceFiles) {
174
89
  this.isMigratingCore = isMigratingCore;
175
90
  this.programInfo = programInfo;
@@ -192,7 +107,7 @@ function getInputDescriptor(hostOrInfo, node) {
192
107
  className = node.parent.name?.text ?? '<anonymous>';
193
108
  }
194
109
  const info = hostOrInfo instanceof MigrationHost ? hostOrInfo.programInfo : hostOrInfo;
195
- const file = group_replacements.projectFile(node.getSourceFile(), info);
110
+ const file = combine_units.projectFile(node.getSourceFile(), info);
196
111
  // Inputs may be detected in `.d.ts` files. Ensure that if the file IDs
197
112
  // match regardless of extension. E.g. `/google3/blaze-out/bin/my_file.ts` should
198
113
  // have the same ID as `/google3/my_file.ts`.
@@ -202,11 +117,6 @@ function getInputDescriptor(hostOrInfo, node) {
202
117
  node,
203
118
  };
204
119
  }
205
- /** Whether the given value is an input descriptor. */
206
- function isInputDescriptor(v) {
207
- return (v.key !== undefined &&
208
- v.node !== undefined);
209
- }
210
120
 
211
121
  /**
212
122
  * Attempts to resolve the known `@Input` metadata for the given
@@ -216,11 +126,11 @@ function attemptRetrieveInputFromSymbol(programInfo, memberSymbol, knownInputs)
216
126
  // Even for declared classes from `.d.ts`, the value declaration
217
127
  // should exist and point to the property declaration.
218
128
  if (memberSymbol.valueDeclaration !== undefined &&
219
- group_replacements.isInputContainerNode(memberSymbol.valueDeclaration)) {
129
+ combine_units.isInputContainerNode(memberSymbol.valueDeclaration)) {
220
130
  const member = memberSymbol.valueDeclaration;
221
131
  // If the member itself is an input that is being migrated, we
222
132
  // do not need to check, as overriding would be fine then— like before.
223
- const memberInputDescr = group_replacements.isInputContainerNode(member)
133
+ const memberInputDescr = combine_units.isInputContainerNode(member)
224
134
  ? getInputDescriptor(programInfo, member)
225
135
  : null;
226
136
  return memberInputDescr !== null ? (knownInputs.get(memberInputDescr) ?? null) : null;
@@ -235,17 +145,19 @@ function attemptRetrieveInputFromSymbol(programInfo, memberSymbol, knownInputs)
235
145
  * loaded into the program.
236
146
  */
237
147
  class KnownInputs {
148
+ programInfo;
149
+ config;
150
+ /**
151
+ * Known inputs from the whole program.
152
+ */
153
+ knownInputIds = new Map();
154
+ /** Known container classes of inputs. */
155
+ _allClasses = new Set();
156
+ /** Maps classes to their directive info. */
157
+ _classToDirectiveInfo = new Map();
238
158
  constructor(programInfo, config) {
239
159
  this.programInfo = programInfo;
240
160
  this.config = config;
241
- /**
242
- * Known inputs from the whole program.
243
- */
244
- this.knownInputIds = new Map();
245
- /** Known container classes of inputs. */
246
- this._allClasses = new Set();
247
- /** Maps classes to their directive info. */
248
- this._classToDirectiveInfo = new Map();
249
161
  }
250
162
  /** Whether the given input exists. */
251
163
  has(descr) {
@@ -274,7 +186,7 @@ class KnownInputs {
274
186
  }
275
187
  const directiveInfo = this._classToDirectiveInfo.get(data.node.parent);
276
188
  const inputInfo = {
277
- file: group_replacements.projectFile(data.node.getSourceFile(), this.programInfo),
189
+ file: combine_units.projectFile(data.node.getSourceFile(), this.programInfo),
278
190
  metadata: data.metadata,
279
191
  descriptor: data.descriptor,
280
192
  container: directiveInfo,
@@ -300,8 +212,8 @@ class KnownInputs {
300
212
  const inputInfo = this.knownInputIds.get(input.key);
301
213
  const existingIncompatibility = inputInfo.container.getInputMemberIncompatibility(input);
302
214
  // Ensure an existing more significant incompatibility is not overridden.
303
- if (existingIncompatibility !== null && group_replacements.isInputMemberIncompatibility(existingIncompatibility)) {
304
- incompatibility = group_replacements.pickInputIncompatibility(existingIncompatibility, incompatibility);
215
+ if (existingIncompatibility !== null && migrate_ts_type_references.isFieldIncompatibility(existingIncompatibility)) {
216
+ incompatibility = migrate_ts_type_references.pickFieldIncompatibility(existingIncompatibility, incompatibility);
305
217
  }
306
218
  this.knownInputIds
307
219
  .get(input.key)
@@ -329,13 +241,13 @@ class KnownInputs {
329
241
  captureUnknownDerivedField(field) {
330
242
  this.markFieldIncompatible(field, {
331
243
  context: null,
332
- reason: group_replacements.InputIncompatibilityReason.OverriddenByDerivedClass,
244
+ reason: migrate_ts_type_references.FieldIncompatibilityReason.OverriddenByDerivedClass,
333
245
  });
334
246
  }
335
247
  captureUnknownParentField(field) {
336
248
  this.markFieldIncompatible(field, {
337
249
  context: null,
338
- reason: group_replacements.InputIncompatibilityReason.TypeConflictWithBaseClass,
250
+ reason: migrate_ts_type_references.FieldIncompatibilityReason.TypeConflictWithBaseClass,
339
251
  });
340
252
  }
341
253
  }
@@ -391,17 +303,15 @@ function prepareAnalysisInfo(userProgram, compiler, programAbsoluteRootPaths) {
391
303
  * - imports that may need to be updated.
392
304
  */
393
305
  class MigrationResult {
394
- constructor() {
395
- this.printer = ts__default["default"].createPrinter({ newLine: ts__default["default"].NewLineKind.LineFeed });
396
- // May be `null` if the input cannot be converted. This is also
397
- // signified by an incompatibility- but the input is tracked here as it
398
- // still is a "source input".
399
- this.sourceInputs = new Map();
400
- this.references = [];
401
- // Execution data
402
- this.replacements = [];
403
- this.inputDecoratorSpecifiers = new Map();
404
- }
306
+ printer = ts__default["default"].createPrinter({ newLine: ts__default["default"].NewLineKind.LineFeed });
307
+ // May be `null` if the input cannot be converted. This is also
308
+ // signified by an incompatibility- but the input is tracked here as it
309
+ // still is a "source input".
310
+ sourceInputs = new Map();
311
+ references = [];
312
+ // Execution data
313
+ replacements = [];
314
+ inputDecoratorSpecifiers = new Map();
405
315
  }
406
316
 
407
317
  /** Attempts to extract metadata of a potential TypeScript `@Input()` declaration. */
@@ -414,7 +324,7 @@ function extractDecoratorInput(node, host, reflector, metadataReader, evaluator,
414
324
  * part of a `.d.ts` file.
415
325
  */
416
326
  function extractDtsInput(node, metadataReader) {
417
- if (!group_replacements.isInputContainerNode(node) ||
327
+ if (!combine_units.isInputContainerNode(node) ||
418
328
  !ts__default["default"].isIdentifier(node.name) ||
419
329
  !node.getSourceFile().isDeclarationFile) {
420
330
  return null;
@@ -425,7 +335,18 @@ function extractDtsInput(node, metadataReader) {
425
335
  !ts__default["default"].isIdentifier(node.parent.name)) {
426
336
  return null;
427
337
  }
428
- const directiveMetadata = metadataReader.getDirectiveMetadata(new checker.Reference(node.parent));
338
+ let directiveMetadata = null;
339
+ // Getting directive metadata can throw errors when e.g. types referenced
340
+ // in the `.d.ts` aren't resolvable. This seems to be unexpected and shouldn't
341
+ // result in the entire migration to be failing.
342
+ try {
343
+ directiveMetadata = metadataReader.getDirectiveMetadata(new checker.Reference(node.parent));
344
+ }
345
+ catch (e) {
346
+ console.error('Unexpected error. Gracefully ignoring.');
347
+ console.error('Could not parse directive metadata:', e);
348
+ return null;
349
+ }
429
350
  const inputMapping = directiveMetadata?.inputs.getByClassPropertyName(node.name.text);
430
351
  // Signal inputs are never tracked and migrated.
431
352
  if (inputMapping?.isSignal) {
@@ -437,6 +358,8 @@ function extractDtsInput(node, metadataReader) {
437
358
  ...inputMapping,
438
359
  inputDecorator: null,
439
360
  inSourceFile: false,
361
+ // Inputs from `.d.ts` cannot have any field decorators applied.
362
+ fieldDecorators: [],
440
363
  };
441
364
  }
442
365
  /**
@@ -444,7 +367,7 @@ function extractDtsInput(node, metadataReader) {
444
367
  * directly defined inside a source file (`.ts`).
445
368
  */
446
369
  function extractSourceCodeInput(node, host, reflector, evaluator, refEmitter) {
447
- if (!group_replacements.isInputContainerNode(node) ||
370
+ if (!combine_units.isInputContainerNode(node) ||
448
371
  !ts__default["default"].isIdentifier(node.name) ||
449
372
  node.getSourceFile().isDeclarationFile) {
450
373
  return null;
@@ -488,6 +411,7 @@ function extractSourceCodeInput(node, host, reflector, evaluator, refEmitter) {
488
411
  inSourceFile: true,
489
412
  transform: transformResult,
490
413
  inputDecorator,
414
+ fieldDecorators: decorators,
491
415
  };
492
416
  }
493
417
  /**
@@ -527,14 +451,19 @@ function prepareAndCheckForConversion(node, metadata, checker, options) {
527
451
  if (ts__default["default"].isAccessor(node)) {
528
452
  return {
529
453
  context: node,
530
- reason: group_replacements.InputIncompatibilityReason.Accessor,
454
+ reason: migrate_ts_type_references.FieldIncompatibilityReason.Accessor,
531
455
  };
532
456
  }
533
457
  assert__default["default"](metadata.inputDecorator !== null, 'Expected an input decorator for inputs that are being migrated.');
534
458
  let initialValue = node.initializer;
535
459
  let isUndefinedInitialValue = node.initializer === undefined ||
536
460
  (ts__default["default"].isIdentifier(node.initializer) && node.initializer.text === 'undefined');
537
- const loosePropertyInitializationWithStrictNullChecks = options.strict !== true && options.strictPropertyInitialization !== true;
461
+ const strictNullChecksEnabled = options.strict === true || options.strictNullChecks === true;
462
+ const strictPropertyInitialization = options.strict === true || options.strictPropertyInitialization === true;
463
+ // Shorthand should never be used, as would expand the type of `T` to be `T|undefined`.
464
+ // This wouldn't matter with strict null checks disabled, but it can break if this is
465
+ // a library that is later consumed with strict null checks enabled.
466
+ const avoidTypeExpansion = !strictNullChecksEnabled;
538
467
  // If an input can be required, due to the non-null assertion on the property,
539
468
  // make it required if there is no initializer.
540
469
  if (node.exclamationToken !== undefined && initialValue === undefined) {
@@ -548,7 +477,7 @@ function prepareAndCheckForConversion(node, metadata, checker, options) {
548
477
  if (!metadata.required &&
549
478
  node.type !== undefined &&
550
479
  isUndefinedInitialValue &&
551
- !loosePropertyInitializationWithStrictNullChecks) {
480
+ !avoidTypeExpansion) {
552
481
  preferShorthandIfPossible = { originalType: node.type };
553
482
  }
554
483
  // If the input is using `@Input() bla?: string;` with the "optional question mark",
@@ -566,7 +495,7 @@ function prepareAndCheckForConversion(node, metadata, checker, options) {
566
495
  if (typeToAdd === undefined) {
567
496
  return {
568
497
  context: node,
569
- reason: group_replacements.InputIncompatibilityReason.InputWithQuestionMarkButNoGoodExplicitTypeExtractable,
498
+ reason: migrate_ts_type_references.FieldIncompatibilityReason.SignalInput__QuestionMarkButNoGoodExplicitTypeExtractable,
570
499
  };
571
500
  }
572
501
  if (!checker.isTypeAssignableTo(checker.getUndefinedType(), checker.getTypeFromTypeNode(typeToAdd))) {
@@ -581,8 +510,8 @@ function prepareAndCheckForConversion(node, metadata, checker, options) {
581
510
  // is disabled, while strict null checks are enabled; then we know that `undefined`
582
511
  // cannot be used as initial value, nor do we want to expand the input's type magically.
583
512
  // Instead, we detect this case and migrate to `undefined!` which leaves the behavior unchanged.
584
- // TODO: This would be a good spot for a clean-up TODO.
585
- if (loosePropertyInitializationWithStrictNullChecks &&
513
+ if (strictNullChecksEnabled &&
514
+ !strictPropertyInitialization &&
586
515
  node.initializer === undefined &&
587
516
  node.type !== undefined &&
588
517
  node.questionToken === undefined &&
@@ -607,7 +536,7 @@ function prepareAndCheckForConversion(node, metadata, checker, options) {
607
536
  // the generated type might depend on imports that we cannot add here (nor want).
608
537
  return {
609
538
  context: node,
610
- reason: group_replacements.InputIncompatibilityReason.RequiredInputButNoGoodExplicitTypeExtractable,
539
+ reason: migrate_ts_type_references.FieldIncompatibilityReason.SignalInput__RequiredButNoGoodExplicitTypeExtractable,
611
540
  };
612
541
  }
613
542
  }
@@ -659,7 +588,7 @@ function pass1__IdentifySourceFileAndDeclarationInputs(sf, host, checker, reflec
659
588
  const visitor = (node) => {
660
589
  const decoratorInput = extractDecoratorInput(node, host, reflector, dtsMetadataReader, evaluator, refEmitter);
661
590
  if (decoratorInput !== null) {
662
- assert__default["default"](group_replacements.isInputContainerNode(node), 'Expected input to be declared on accessor or property.');
591
+ assert__default["default"](combine_units.isInputContainerNode(node), 'Expected input to be declared on accessor or property.');
663
592
  const inputDescr = getInputDescriptor(host, node);
664
593
  // track all inputs, even from declarations for reference resolution.
665
594
  knownDecoratorInputs.register({ descriptor: inputDescr, metadata: decoratorInput, node });
@@ -667,7 +596,7 @@ function pass1__IdentifySourceFileAndDeclarationInputs(sf, host, checker, reflec
667
596
  // these are then later migrated in the migration phase.
668
597
  if (decoratorInput.inSourceFile && host.isSourceFileForCurrentMigration(sf)) {
669
598
  const conversionPreparation = prepareAndCheckForConversion(node, decoratorInput, checker, host.compilerOptions);
670
- if (group_replacements.isInputMemberIncompatibility(conversionPreparation)) {
599
+ if (migrate_ts_type_references.isFieldIncompatibility(conversionPreparation)) {
671
600
  knownDecoratorInputs.markFieldIncompatible(inputDescr, conversionPreparation);
672
601
  result.sourceInputs.set(inputDescr, null);
673
602
  }
@@ -706,8 +635,17 @@ function pass1__IdentifySourceFileAndDeclarationInputs(sf, host, checker, reflec
706
635
  * In addition, spying onto an input may be problematic- so we skip migrating
707
636
  * such.
708
637
  */
709
- function pass3__checkIncompatiblePatterns(inheritanceGraph, checker, groupedTsAstVisitor, knownInputs) {
710
- group_replacements.checkIncompatiblePatterns(inheritanceGraph, checker, groupedTsAstVisitor, knownInputs, () => knownInputs.getAllInputContainingClasses());
638
+ function pass3__checkIncompatiblePatterns(host, inheritanceGraph, checker$1, groupedTsAstVisitor, knownInputs) {
639
+ migrate_ts_type_references.checkIncompatiblePatterns(inheritanceGraph, checker$1, groupedTsAstVisitor, knownInputs, () => knownInputs.getAllInputContainingClasses());
640
+ for (const input of knownInputs.knownInputIds.values()) {
641
+ const hostBindingDecorators = checker.getAngularDecorators(input.metadata.fieldDecorators, ['HostBinding'], host.isMigratingCore);
642
+ if (hostBindingDecorators.length > 0) {
643
+ knownInputs.markFieldIncompatible(input.descriptor, {
644
+ context: hostBindingDecorators[0].node,
645
+ reason: migrate_ts_type_references.FieldIncompatibilityReason.SignalIncompatibleWithHostBinding,
646
+ });
647
+ }
648
+ }
711
649
  }
712
650
 
713
651
  /**
@@ -721,7 +659,7 @@ function pass3__checkIncompatiblePatterns(inheritanceGraph, checker, groupedTsAs
721
659
  * such.
722
660
  */
723
661
  function pass2_IdentifySourceFileReferences(programInfo, checker, reflector, resourceLoader, evaluator, templateTypeChecker, groupedTsAstVisitor, knownInputs, result, fieldNamesToConsiderForReferenceLookup) {
724
- groupedTsAstVisitor.register(group_replacements.createFindAllSourceFileReferencesVisitor(programInfo, checker, reflector, resourceLoader, evaluator, templateTypeChecker, knownInputs, fieldNamesToConsiderForReferenceLookup, result).visitor);
662
+ groupedTsAstVisitor.register(combine_units.createFindAllSourceFileReferencesVisitor(programInfo, checker, reflector, resourceLoader, evaluator, templateTypeChecker, knownInputs, fieldNamesToConsiderForReferenceLookup, result).visitor);
725
663
  }
726
664
 
727
665
  /**
@@ -749,26 +687,26 @@ function executeAnalysisPhase(host, knownInputs, result, { sourceFiles, fullProg
749
687
  }
750
688
  // A graph starting with source files is sufficient. We will resolve into
751
689
  // declaration files if a source file depends on such.
752
- const inheritanceGraph = new group_replacements.InheritanceGraph(typeChecker).expensivePopulate(sourceFiles);
753
- const pass2And3SourceFileVisitor = new group_replacements.GroupedTsAstVisitor(sourceFiles);
690
+ const inheritanceGraph = new migrate_ts_type_references.InheritanceGraph(typeChecker).expensivePopulate(sourceFiles);
691
+ const pass2And3SourceFileVisitor = new migrate_ts_type_references.GroupedTsAstVisitor(sourceFiles);
754
692
  // Register pass 2. Find all source file references.
755
693
  pass2_IdentifySourceFileReferences(host.programInfo, typeChecker, reflector, resourceLoader, evaluator, templateTypeChecker, pass2And3SourceFileVisitor, knownInputs, result, fieldNamesToConsiderForReferenceLookup);
756
694
  // Register pass 3. Check incompatible patterns pass.
757
- pass3__checkIncompatiblePatterns(inheritanceGraph, typeChecker, pass2And3SourceFileVisitor, knownInputs);
695
+ pass3__checkIncompatiblePatterns(host, inheritanceGraph, typeChecker, pass2And3SourceFileVisitor, knownInputs);
758
696
  // Perform Pass 2 and Pass 3, efficiently in one pass.
759
697
  pass2And3SourceFileVisitor.execute();
760
698
  // Determine incompatible inputs based on resolved references.
761
699
  for (const reference of result.references) {
762
- if (group_replacements.isTsReference(reference) && reference.from.isWrite) {
700
+ if (combine_units.isTsReference(reference) && reference.from.isWrite) {
763
701
  knownInputs.markFieldIncompatible(reference.target, {
764
- reason: group_replacements.InputIncompatibilityReason.WriteAssignment,
702
+ reason: migrate_ts_type_references.FieldIncompatibilityReason.WriteAssignment,
765
703
  context: reference.from.node,
766
704
  });
767
705
  }
768
- if (group_replacements.isTemplateReference(reference) || group_replacements.isHostBindingReference(reference)) {
706
+ if (combine_units.isTemplateReference(reference) || combine_units.isHostBindingReference(reference)) {
769
707
  if (reference.from.isWrite) {
770
708
  knownInputs.markFieldIncompatible(reference.target, {
771
- reason: group_replacements.InputIncompatibilityReason.WriteAssignment,
709
+ reason: migrate_ts_type_references.FieldIncompatibilityReason.WriteAssignment,
772
710
  // No TS node context available for template or host bindings.
773
711
  context: null,
774
712
  });
@@ -776,10 +714,10 @@ function executeAnalysisPhase(host, knownInputs, result, { sourceFiles, fullProg
776
714
  }
777
715
  // TODO: Remove this when we support signal narrowing in templates.
778
716
  // https://github.com/angular/angular/pull/55456.
779
- if (group_replacements.isTemplateReference(reference)) {
717
+ if (combine_units.isTemplateReference(reference)) {
780
718
  if (reference.from.isLikelyPartOfNarrowing) {
781
719
  knownInputs.markFieldIncompatible(reference.target, {
782
- reason: group_replacements.InputIncompatibilityReason.PotentiallyNarrowedInTemplateButNoSupportYet,
720
+ reason: migrate_ts_type_references.FieldIncompatibilityReason.PotentiallyNarrowedInTemplateButNoSupportYet,
783
721
  context: null,
784
722
  });
785
723
  }
@@ -809,7 +747,7 @@ function executeAnalysisPhase(host, knownInputs, result, { sourceFiles, fullProg
809
747
  * would then have other derived classes as well, it would propagate the status.
810
748
  */
811
749
  function pass4__checkInheritanceOfInputs(inheritanceGraph, metaRegistry, knownInputs) {
812
- group_replacements.checkInheritanceOfKnownFields(inheritanceGraph, metaRegistry, knownInputs, {
750
+ migrate_ts_type_references.checkInheritanceOfKnownFields(inheritanceGraph, metaRegistry, knownInputs, {
813
751
  isClassWithKnownFields: (clazz) => knownInputs.isInputContainingClass(clazz),
814
752
  getFieldsForClass: (clazz) => {
815
753
  const directiveInfo = knownInputs.getDirectiveInfoForClass(clazz);
@@ -885,25 +823,15 @@ function topologicalSort(graph) {
885
823
  }
886
824
 
887
825
  /** Merges a list of compilation units into a combined unit. */
888
- function mergeCompilationUnitData(metadataFiles) {
826
+ function combineCompilationUnitData(unitA, unitB) {
889
827
  const result = {
890
828
  knownInputs: {},
891
829
  };
892
- const idToGraphNode = new Map();
893
- const inheritanceGraph = [];
894
- const isNodeIncompatible = (node) => node.info.memberIncompatibility !== null || node.info.owningClassIncompatibility !== null;
895
- for (const file of metadataFiles) {
830
+ for (const file of [unitA, unitB]) {
896
831
  for (const [key, info] of Object.entries(file.knownInputs)) {
897
832
  const existing = result.knownInputs[key];
898
833
  if (existing === undefined) {
899
834
  result.knownInputs[key] = info;
900
- const node = {
901
- incoming: new Set(),
902
- outgoing: new Set(),
903
- data: { info, key },
904
- };
905
- inheritanceGraph.push(node);
906
- idToGraphNode.set(key, node);
907
835
  continue;
908
836
  }
909
837
  // Merge metadata.
@@ -921,7 +849,7 @@ function mergeCompilationUnitData(metadataFiles) {
921
849
  else {
922
850
  // Input might not be incompatible in one target, but others might invalidate it.
923
851
  // merge the incompatibility state.
924
- existing.memberIncompatibility = group_replacements.pickInputIncompatibility({ reason: info.memberIncompatibility, context: null }, { reason: existing.memberIncompatibility, context: null }).reason;
852
+ existing.memberIncompatibility = migrate_ts_type_references.pickFieldIncompatibility({ reason: info.memberIncompatibility, context: null }, { reason: existing.memberIncompatibility, context: null }).reason;
925
853
  }
926
854
  }
927
855
  // Merge incompatibility of the class owning the input.
@@ -933,7 +861,30 @@ function mergeCompilationUnitData(metadataFiles) {
933
861
  }
934
862
  }
935
863
  }
936
- for (const [key, info] of Object.entries(result.knownInputs)) {
864
+ return result;
865
+ }
866
+ function convertToGlobalMeta(combinedData) {
867
+ const globalMeta = {
868
+ knownInputs: {},
869
+ };
870
+ const idToGraphNode = new Map();
871
+ const inheritanceGraph = [];
872
+ const isNodeIncompatible = (node) => node.info.memberIncompatibility !== null || node.info.owningClassIncompatibility !== null;
873
+ for (const [key, info] of Object.entries(combinedData.knownInputs)) {
874
+ const existing = globalMeta.knownInputs[key];
875
+ if (existing !== undefined) {
876
+ continue;
877
+ }
878
+ const node = {
879
+ incoming: new Set(),
880
+ outgoing: new Set(),
881
+ data: { info, key },
882
+ };
883
+ inheritanceGraph.push(node);
884
+ idToGraphNode.set(key, node);
885
+ globalMeta.knownInputs[key] = info;
886
+ }
887
+ for (const [key, info] of Object.entries(globalMeta.knownInputs)) {
937
888
  if (info.extendsFrom !== null) {
938
889
  const from = idToGraphNode.get(key);
939
890
  const target = idToGraphNode.get(info.extendsFrom);
@@ -953,22 +904,22 @@ function mergeCompilationUnitData(metadataFiles) {
953
904
  // If parent is incompatible and not migrated, then this input
954
905
  // cannot be migrated either. Try propagating parent incompatibility then.
955
906
  if (isNodeIncompatible(parent.data)) {
956
- node.data.info.memberIncompatibility = group_replacements.pickInputIncompatibility({ reason: group_replacements.InputIncompatibilityReason.ParentIsIncompatible, context: null }, existingMemberIncompatibility).reason;
907
+ node.data.info.memberIncompatibility = migrate_ts_type_references.pickFieldIncompatibility({ reason: migrate_ts_type_references.FieldIncompatibilityReason.ParentIsIncompatible, context: null }, existingMemberIncompatibility).reason;
957
908
  break;
958
909
  }
959
910
  }
960
911
  }
961
- for (const info of Object.values(result.knownInputs)) {
912
+ for (const info of Object.values(combinedData.knownInputs)) {
962
913
  // We never saw a source file for this input, globally. Try marking it as incompatible,
963
914
  // so that all references and inheritance checks can propagate accordingly.
964
915
  if (!info.seenAsSourceInput) {
965
916
  const existingMemberIncompatibility = info.memberIncompatibility !== null
966
917
  ? { reason: info.memberIncompatibility, context: null }
967
918
  : null;
968
- info.memberIncompatibility = group_replacements.pickInputIncompatibility({ reason: group_replacements.InputIncompatibilityReason.OutsideOfMigrationScope, context: null }, existingMemberIncompatibility).reason;
919
+ info.memberIncompatibility = migrate_ts_type_references.pickFieldIncompatibility({ reason: migrate_ts_type_references.FieldIncompatibilityReason.OutsideOfMigrationScope, context: null }, existingMemberIncompatibility).reason;
969
920
  }
970
921
  }
971
- return result;
922
+ return globalMeta;
972
923
  }
973
924
 
974
925
  function populateKnownInputsFromGlobalData(knownInputs, globalData) {
@@ -992,46 +943,6 @@ function populateKnownInputsFromGlobalData(knownInputs, globalData) {
992
943
  }
993
944
  }
994
945
 
995
- /**
996
- * Inserts a leading string for the given node, respecting
997
- * indentation of the given anchor node.
998
- *
999
- * Useful for inserting TODOs.
1000
- */
1001
- function insertPrecedingLine(node, info, text) {
1002
- const leadingSpace = leading_space.getLeadingLineWhitespaceOfNode(node);
1003
- return new group_replacements.Replacement(group_replacements.projectFile(node.getSourceFile(), info), new group_replacements.TextUpdate({
1004
- position: node.getStart(),
1005
- end: node.getStart(),
1006
- toInsert: `${text}\n${leadingSpace}`,
1007
- }));
1008
- }
1009
-
1010
- /**
1011
- * Cuts the given string into lines basing around the specified
1012
- * line length limit. This function breaks the string on a per-word basis.
1013
- */
1014
- function cutStringToLineLimit(str, limit) {
1015
- const words = str.split(' ');
1016
- const chunks = [];
1017
- let chunkIdx = 0;
1018
- while (words.length) {
1019
- // New line if we exceed limit.
1020
- if (chunks[chunkIdx] !== undefined && chunks[chunkIdx].length > limit) {
1021
- chunkIdx++;
1022
- }
1023
- // Ensure line is initialized for the given index.
1024
- if (chunks[chunkIdx] === undefined) {
1025
- chunks[chunkIdx] = '';
1026
- }
1027
- const word = words.shift();
1028
- const needsSpace = chunks[chunkIdx].length > 0;
1029
- // Insert word. Add space before, if the line already contains text.
1030
- chunks[chunkIdx] += `${needsSpace ? ' ' : ''}${word}`;
1031
- }
1032
- return chunks;
1033
- }
1034
-
1035
946
  // TODO: Consider initializations inside the constructor. Those are not migrated right now
1036
947
  // though, as they are writes.
1037
948
  /**
@@ -1073,7 +984,7 @@ function convertToSignalInput(node, { resolvedMetadata: metadata, resolvedType,
1073
984
  // When using the `input()` shorthand, try cutting of `undefined` from potential
1074
985
  // union types. `undefined` will be automatically included in the type.
1075
986
  if (ts__default["default"].isUnionTypeNode(resolvedType)) {
1076
- resolvedType = group_replacements.removeFromUnionIfPossible(resolvedType, (t) => t.kind !== ts__default["default"].SyntaxKind.UndefinedKeyword);
987
+ resolvedType = migrate_ts_type_references.removeFromUnionIfPossible(resolvedType, (t) => t.kind !== ts__default["default"].SyntaxKind.UndefinedKeyword);
1077
988
  }
1078
989
  }
1079
990
  }
@@ -1113,9 +1024,9 @@ function convertToSignalInput(node, { resolvedMetadata: metadata, resolvedType,
1113
1024
  const newPropertyText = result.printer.printNode(ts__default["default"].EmitHint.Unspecified, newNode, node.getSourceFile());
1114
1025
  const replacements = [];
1115
1026
  if (leadingTodoText !== null) {
1116
- replacements.push(insertPrecedingLine(node, info, '// TODO: Notes from signal input migration:'), ...cutStringToLineLimit(leadingTodoText, 70).map((line) => insertPrecedingLine(node, info, `// ${line}`)));
1027
+ replacements.push(migrate_ts_type_references.insertPrecedingLine(node, info, '// TODO: Notes from signal input migration:'), ...migrate_ts_type_references.cutStringToLineLimit(leadingTodoText, 70).map((line) => migrate_ts_type_references.insertPrecedingLine(node, info, `// ${line}`)));
1117
1028
  }
1118
- replacements.push(new group_replacements.Replacement(group_replacements.projectFile(node.getSourceFile(), info), new group_replacements.TextUpdate({
1029
+ replacements.push(new combine_units.Replacement(combine_units.projectFile(node.getSourceFile(), info), new combine_units.TextUpdate({
1119
1030
  position: node.getStart(),
1120
1031
  end: node.getEnd(),
1121
1032
  toInsert: newPropertyText,
@@ -1154,32 +1065,6 @@ function extractTransformOfInput(transform, resolvedType, checker) {
1154
1065
  };
1155
1066
  }
1156
1067
 
1157
- /**
1158
- * Inserts a TODO for the incompatibility blocking the given node
1159
- * from being migrated.
1160
- */
1161
- function insertTodoForIncompatibility(node, programInfo, input) {
1162
- const incompatibility = input.container.getInputMemberIncompatibility(input.descriptor);
1163
- if (incompatibility === null) {
1164
- return [];
1165
- }
1166
- // If an input is skipped via config filter or outside migration scope, do not
1167
- // insert TODOs, as this could results in lots of unnecessary comments.
1168
- if (group_replacements.isInputMemberIncompatibility(incompatibility) &&
1169
- (incompatibility.reason === group_replacements.InputIncompatibilityReason.SkippedViaConfigFilter ||
1170
- incompatibility.reason === group_replacements.InputIncompatibilityReason.OutsideOfMigrationScope)) {
1171
- return [];
1172
- }
1173
- const message = group_replacements.isInputMemberIncompatibility(incompatibility)
1174
- ? getMessageForInputIncompatibility(incompatibility.reason).short
1175
- : getMessageForClassIncompatibility(incompatibility).short;
1176
- const lines = cutStringToLineLimit(message, 70);
1177
- return [
1178
- insertPrecedingLine(node, programInfo, `// TODO: Skipped for migration because:`),
1179
- ...lines.map((line) => insertPrecedingLine(node, programInfo, `// ${line}`)),
1180
- ];
1181
- }
1182
-
1183
1068
  /**
1184
1069
  * Phase that migrates `@Input()` declarations to signal inputs and
1185
1070
  * manages imports within the given file.
@@ -1192,9 +1077,13 @@ function pass6__migrateInputDeclarations(host, checker, result, knownInputs, imp
1192
1077
  const inputInfo = knownInputs.get(input);
1193
1078
  // Do not migrate incompatible inputs.
1194
1079
  if (inputInfo.isIncompatible()) {
1080
+ const incompatibilityReason = inputInfo.container.getInputMemberIncompatibility(input);
1195
1081
  // Add a TODO for the incompatible input, if desired.
1196
- if (host.config.insertTodosForSkippedFields) {
1197
- result.replacements.push(...insertTodoForIncompatibility(input.node, info, inputInfo));
1082
+ if (incompatibilityReason !== null && host.config.insertTodosForSkippedFields) {
1083
+ result.replacements.push(...migrate_ts_type_references.insertTodoForIncompatibility(input.node, info, incompatibilityReason, {
1084
+ single: 'input',
1085
+ plural: 'inputs',
1086
+ }));
1198
1087
  }
1199
1088
  filesWithIncompatibleInputs.add(sf);
1200
1089
  continue;
@@ -1217,7 +1106,7 @@ function pass6__migrateInputDeclarations(host, checker, result, knownInputs, imp
1217
1106
  * previous migrate phases.
1218
1107
  */
1219
1108
  function pass10_applyImportManager(importManager, result, sourceFiles, info) {
1220
- group_replacements.applyImportManagerChanges(importManager, result.replacements, sourceFiles, info);
1109
+ combine_units.applyImportManagerChanges(importManager, result.replacements, sourceFiles, info);
1221
1110
  }
1222
1111
 
1223
1112
  /**
@@ -1227,7 +1116,7 @@ function pass10_applyImportManager(importManager, result, sourceFiles, info) {
1227
1116
  * where needed to ensure narrowing continues to work. E.g.
1228
1117
  */
1229
1118
  function pass5__migrateTypeScriptReferences(host, references, checker, info) {
1230
- group_replacements.migrateTypeScriptReferences(host, references, checker, info);
1119
+ migrate_ts_type_references.migrateTypeScriptReferences(host, references, checker, info);
1231
1120
  }
1232
1121
 
1233
1122
  /**
@@ -1238,7 +1127,7 @@ function pass7__migrateTemplateReferences(host, references) {
1238
1127
  const seenFileReferences = new Set();
1239
1128
  for (const reference of references) {
1240
1129
  // This pass only deals with HTML template references.
1241
- if (!group_replacements.isTemplateReference(reference)) {
1130
+ if (!combine_units.isTemplateReference(reference)) {
1242
1131
  continue;
1243
1132
  }
1244
1133
  // Skip references to incompatible inputs.
@@ -1255,7 +1144,7 @@ function pass7__migrateTemplateReferences(host, references) {
1255
1144
  const appendText = reference.from.isObjectShorthandExpression
1256
1145
  ? `: ${reference.from.read.name}()`
1257
1146
  : `()`;
1258
- host.replacements.push(new group_replacements.Replacement(reference.from.templateFile, new group_replacements.TextUpdate({
1147
+ host.replacements.push(new combine_units.Replacement(reference.from.templateFile, new combine_units.TextUpdate({
1259
1148
  position: reference.from.read.sourceSpan.end,
1260
1149
  end: reference.from.read.sourceSpan.end,
1261
1150
  toInsert: appendText,
@@ -1271,7 +1160,7 @@ function pass8__migrateHostBindings(host, references, info) {
1271
1160
  const seenReferences = new WeakMap();
1272
1161
  for (const reference of references) {
1273
1162
  // This pass only deals with host binding references.
1274
- if (!group_replacements.isHostBindingReference(reference)) {
1163
+ if (!combine_units.isHostBindingReference(reference)) {
1275
1164
  continue;
1276
1165
  }
1277
1166
  // Skip references to incompatible inputs.
@@ -1295,7 +1184,7 @@ function pass8__migrateHostBindings(host, references, info) {
1295
1184
  const appendText = reference.from.isObjectShorthandExpression
1296
1185
  ? `: ${reference.from.read.name}()`
1297
1186
  : `()`;
1298
- host.replacements.push(new group_replacements.Replacement(group_replacements.projectFile(bindingField.getSourceFile(), info), new group_replacements.TextUpdate({ position: readEndPos, end: readEndPos, toInsert: appendText })));
1187
+ host.replacements.push(new combine_units.Replacement(combine_units.projectFile(bindingField.getSourceFile(), info), new combine_units.TextUpdate({ position: readEndPos, end: readEndPos, toInsert: appendText })));
1299
1188
  }
1300
1189
  }
1301
1190
 
@@ -1306,43 +1195,7 @@ function pass8__migrateHostBindings(host, references, info) {
1306
1195
  in Catalyst test files.
1307
1196
  */
1308
1197
  function pass9__migrateTypeScriptTypeReferences(host, references, importManager, info) {
1309
- const seenTypeNodes = new WeakSet();
1310
- for (const reference of references) {
1311
- // This pass only deals with TS input class type references.
1312
- if (!group_replacements.isTsClassTypeReference(reference)) {
1313
- continue;
1314
- }
1315
- // Skip references to classes that are not fully migrated.
1316
- if (!host.shouldMigrateReferencesToClass(reference.target)) {
1317
- continue;
1318
- }
1319
- // Skip duplicate references. E.g. in batching.
1320
- if (seenTypeNodes.has(reference.from.node)) {
1321
- continue;
1322
- }
1323
- seenTypeNodes.add(reference.from.node);
1324
- if (reference.isPartialReference && reference.isPartOfCatalystFile) {
1325
- assert__default["default"](reference.from.node.typeArguments, 'Expected type arguments for partial reference.');
1326
- assert__default["default"](reference.from.node.typeArguments.length === 1, 'Expected an argument for reference.');
1327
- const firstArg = reference.from.node.typeArguments[0];
1328
- const sf = firstArg.getSourceFile();
1329
- // Naive detection of the import. Sufficient for this test file migration.
1330
- const catalystImport = sf.text.includes('google3/javascript/angular2/testing/catalyst/fake_async')
1331
- ? 'google3/javascript/angular2/testing/catalyst/fake_async'
1332
- : 'google3/javascript/angular2/testing/catalyst/async';
1333
- const unwrapImportExpr = importManager.addImport({
1334
- exportModuleSpecifier: catalystImport,
1335
- exportSymbolName: 'UnwrapSignalInputs',
1336
- requestedFile: sf,
1337
- });
1338
- host.replacements.push(new group_replacements.Replacement(group_replacements.projectFile(sf, info), new group_replacements.TextUpdate({
1339
- position: firstArg.getStart(),
1340
- end: firstArg.getStart(),
1341
- toInsert: `${host.printer.printNode(ts__default["default"].EmitHint.Unspecified, unwrapImportExpr, sf)}<`,
1342
- })));
1343
- host.replacements.push(new group_replacements.Replacement(group_replacements.projectFile(sf, info), new group_replacements.TextUpdate({ position: firstArg.getEnd(), end: firstArg.getEnd(), toInsert: '>' })));
1344
- }
1345
- }
1198
+ migrate_ts_type_references.migrateTypeScriptTypeReferences(host, references, importManager, info);
1346
1199
  }
1347
1200
 
1348
1201
  /**
@@ -1366,7 +1219,7 @@ function executeMigrationPhase(host, knownInputs, result, info) {
1366
1219
  replacements: result.replacements,
1367
1220
  shouldMigrateReferencesToField: (inputDescr) => knownInputs.has(inputDescr) && knownInputs.get(inputDescr).isIncompatible() === false,
1368
1221
  shouldMigrateReferencesToClass: (clazz) => knownInputs.getDirectiveInfoForClass(clazz) !== undefined &&
1369
- knownInputs.getDirectiveInfoForClass(clazz).hasIncompatibleMembers() === false,
1222
+ knownInputs.getDirectiveInfoForClass(clazz).hasMigratedFields(),
1370
1223
  };
1371
1224
  // Migrate passes.
1372
1225
  pass5__migrateTypeScriptReferences(referenceMigrationHost, result.references, typeChecker, info);
@@ -1377,24 +1230,13 @@ function executeMigrationPhase(host, knownInputs, result, info) {
1377
1230
  pass10_applyImportManager(importManager, result, sourceFiles, info);
1378
1231
  }
1379
1232
 
1380
- /** Input reasons that cannot be ignored. */
1381
- const nonIgnorableInputIncompatibilities = [
1382
- // Outside of scope inputs should not be migrated. E.g. references to inputs in `node_modules/`.
1383
- group_replacements.InputIncompatibilityReason.OutsideOfMigrationScope,
1384
- // Explicitly filtered inputs cannot be skipped via best effort mode.
1385
- group_replacements.InputIncompatibilityReason.SkippedViaConfigFilter,
1386
- // There is no good output for accessor inputs.
1387
- group_replacements.InputIncompatibilityReason.Accessor,
1388
- // There is no good output for such inputs. We can't perform "conversion".
1389
- group_replacements.InputIncompatibilityReason.RequiredInputButNoGoodExplicitTypeExtractable,
1390
- ];
1391
1233
  /** Filters ignorable input incompatibilities when best effort mode is enabled. */
1392
1234
  function filterIncompatibilitiesForBestEffortMode(knownInputs) {
1393
1235
  knownInputs.knownInputIds.forEach(({ container: c }) => {
1394
1236
  // All class incompatibilities are "filterable" right now.
1395
1237
  c.incompatible = null;
1396
1238
  for (const [key, i] of c.memberIncompatibility.entries()) {
1397
- if (!nonIgnorableInputIncompatibilities.includes(i.reason)) {
1239
+ if (!migrate_ts_type_references.nonIgnorableFieldIncompatibilities.includes(i.reason)) {
1398
1240
  c.memberIncompatibility.delete(key);
1399
1241
  }
1400
1242
  }
@@ -1405,15 +1247,16 @@ function filterIncompatibilitiesForBestEffortMode(knownInputs) {
1405
1247
  * Tsurge migration for migrating Angular `@Input()` declarations to
1406
1248
  * signal inputs, with support for batch execution.
1407
1249
  */
1408
- class SignalInputMigration extends group_replacements.TsurgeComplexMigration {
1250
+ class SignalInputMigration extends combine_units.TsurgeComplexMigration {
1251
+ config;
1252
+ upgradedAnalysisPhaseResults = null;
1409
1253
  constructor(config = {}) {
1410
1254
  super();
1411
1255
  this.config = config;
1412
- this.upgradedAnalysisPhaseResults = null;
1413
1256
  }
1414
1257
  // Override the default ngtsc program creation, to add extra flags.
1415
1258
  createProgram(tsconfigAbsPath, fs) {
1416
- return group_replacements.createNgtscProgram(tsconfigAbsPath, fs, {
1259
+ return combine_units.createNgtscProgram(tsconfigAbsPath, fs, {
1417
1260
  _compilePoisonedComponents: true,
1418
1261
  // We want to migrate non-exported classes too.
1419
1262
  compileNonExportedClasses: true,
@@ -1471,8 +1314,8 @@ class SignalInputMigration extends group_replacements.TsurgeComplexMigration {
1471
1314
  const unitData = getCompilationUnitMetadata(knownInputs);
1472
1315
  // Non-batch mode!
1473
1316
  if (this.config.upgradeAnalysisPhaseToAvoidBatch) {
1474
- const merged = await this.merge([unitData]);
1475
- const replacements = await this.migrate(merged, info, {
1317
+ const globalMeta = await this.globalMeta(unitData);
1318
+ const { replacements } = await this.migrate(globalMeta, info, {
1476
1319
  knownInputs,
1477
1320
  result,
1478
1321
  host,
@@ -1486,10 +1329,13 @@ class SignalInputMigration extends group_replacements.TsurgeComplexMigration {
1486
1329
  knownInputs,
1487
1330
  };
1488
1331
  }
1489
- return group_replacements.confirmAsSerializable(unitData);
1332
+ return combine_units.confirmAsSerializable(unitData);
1333
+ }
1334
+ async combine(unitA, unitB) {
1335
+ return combine_units.confirmAsSerializable(combineCompilationUnitData(unitA, unitB));
1490
1336
  }
1491
- async merge(units) {
1492
- return group_replacements.confirmAsSerializable(mergeCompilationUnitData(units));
1337
+ async globalMeta(combinedData) {
1338
+ return combine_units.confirmAsSerializable(convertToGlobalMeta(combinedData));
1493
1339
  }
1494
1340
  async migrate(globalMetadata, info, nonBatchData) {
1495
1341
  const knownInputs = nonBatchData?.knownInputs ?? new KnownInputs(info, this.config);
@@ -1507,7 +1353,7 @@ class SignalInputMigration extends group_replacements.TsurgeComplexMigration {
1507
1353
  }
1508
1354
  this.config.reportProgressFn?.(60, 'Collecting migration changes..');
1509
1355
  executeMigrationPhase(host, knownInputs, result, analysisDeps);
1510
- return result.replacements;
1356
+ return { replacements: result.replacements };
1511
1357
  }
1512
1358
  async stats(globalMetadata) {
1513
1359
  let fullCompilationInputs = 0;
@@ -1517,20 +1363,26 @@ class SignalInputMigration extends group_replacements.TsurgeComplexMigration {
1517
1363
  const classIncompatibleCounts = {};
1518
1364
  for (const [id, input] of Object.entries(globalMetadata.knownInputs)) {
1519
1365
  fullCompilationInputs++;
1520
- if (input.seenAsSourceInput) {
1521
- sourceInputs++;
1366
+ const isConsideredSourceInput = input.seenAsSourceInput &&
1367
+ input.memberIncompatibility !== migrate_ts_type_references.FieldIncompatibilityReason.OutsideOfMigrationScope &&
1368
+ input.memberIncompatibility !== migrate_ts_type_references.FieldIncompatibilityReason.SkippedViaConfigFilter;
1369
+ // We won't track incompatibilities to inputs that aren't considered source inputs.
1370
+ // Tracking their statistics wouldn't provide any value.
1371
+ if (!isConsideredSourceInput) {
1372
+ continue;
1522
1373
  }
1374
+ sourceInputs++;
1523
1375
  if (input.memberIncompatibility !== null || input.owningClassIncompatibility !== null) {
1524
1376
  incompatibleInputs++;
1525
1377
  }
1526
1378
  if (input.memberIncompatibility !== null) {
1527
- const reasonName = group_replacements.InputIncompatibilityReason[input.memberIncompatibility];
1379
+ const reasonName = migrate_ts_type_references.FieldIncompatibilityReason[input.memberIncompatibility];
1528
1380
  const key = `input-field-incompatibility-${reasonName}`;
1529
1381
  fieldIncompatibleCounts[key] ??= 0;
1530
1382
  fieldIncompatibleCounts[key]++;
1531
1383
  }
1532
1384
  if (input.owningClassIncompatibility !== null) {
1533
- const reasonName = group_replacements.ClassIncompatibilityReason[input.owningClassIncompatibility];
1385
+ const reasonName = migrate_ts_type_references.ClassIncompatibilityReason[input.owningClassIncompatibility];
1534
1386
  const key = `input-owning-class-incompatibility-${reasonName}`;
1535
1387
  classIncompatibleCounts[key] ??= 0;
1536
1388
  classIncompatibleCounts[key]++;
@@ -1562,19 +1414,10 @@ function filterInputsViaConfig(result, knownInputs, config) {
1562
1414
  skippedInputs.add(input.descriptor.key);
1563
1415
  knownInputs.markFieldIncompatible(input.descriptor, {
1564
1416
  context: null,
1565
- reason: group_replacements.InputIncompatibilityReason.SkippedViaConfigFilter,
1417
+ reason: migrate_ts_type_references.FieldIncompatibilityReason.SkippedViaConfigFilter,
1566
1418
  });
1567
1419
  }
1568
1420
  }
1569
- result.references = result.references.filter((reference) => {
1570
- if (isInputDescriptor(reference.target)) {
1571
- // Only migrate the reference if the target is NOT skipped.
1572
- return !skippedInputs.has(reference.target.key);
1573
- }
1574
- // Class references may be migrated. This is up to the logic handling
1575
- // the class reference. E.g. it may not migrate if any member is incompatible.
1576
- return true;
1577
- });
1578
1421
  }
1579
1422
  function createMigrationHost(info, config) {
1580
1423
  return new MigrationHost(/* isMigratingCore */ false, info, config, info.sourceFiles);
@@ -1586,7 +1429,7 @@ function migrate(options) {
1586
1429
  if (!buildPaths.length && !testPaths.length) {
1587
1430
  throw new schematics.SchematicsException('Could not find any tsconfig file. Cannot run signal input migration.');
1588
1431
  }
1589
- const fs = new group_replacements.DevkitMigrationFilesystem(tree);
1432
+ const fs = new combine_units.DevkitMigrationFilesystem(tree);
1590
1433
  checker.setFileSystem(fs);
1591
1434
  const migration = new SignalInputMigration({
1592
1435
  bestEffortMode: options.bestEffortMode,
@@ -1618,12 +1461,17 @@ function migrate(options) {
1618
1461
  context.logger.info(``);
1619
1462
  context.logger.info(`Processing analysis data between targets..`);
1620
1463
  context.logger.info(``);
1621
- const merged = await migration.merge(unitResults);
1464
+ const combined = await combine_units.synchronouslyCombineUnitData(migration, unitResults);
1465
+ if (combined === null) {
1466
+ context.logger.error('Migration failed unexpectedly with no analysis data');
1467
+ return;
1468
+ }
1469
+ const globalMeta = await migration.globalMeta(combined);
1622
1470
  const replacementsPerFile = new Map();
1623
1471
  for (const { info, tsconfigPath } of programInfos) {
1624
1472
  context.logger.info(`Migrating: ${tsconfigPath}..`);
1625
- const replacements = await migration.migrate(merged, info);
1626
- const changesPerFile = group_replacements.groupReplacementsByFile(replacements);
1473
+ const { replacements } = await migration.migrate(globalMeta, info);
1474
+ const changesPerFile = combine_units.groupReplacementsByFile(replacements);
1627
1475
  for (const [file, changes] of changesPerFile) {
1628
1476
  if (!replacementsPerFile.has(file)) {
1629
1477
  replacementsPerFile.set(file, changes);
@@ -1640,7 +1488,7 @@ function migrate(options) {
1640
1488
  }
1641
1489
  tree.commitUpdate(recorder);
1642
1490
  }
1643
- const { counters } = await migration.stats(merged);
1491
+ const { counters } = await migration.stats(globalMeta);
1644
1492
  const migratedInputs = counters.sourceInputs - counters.incompatibleInputs;
1645
1493
  context.logger.info('');
1646
1494
  context.logger.info(`Successfully migrated to signal inputs 🎉`);