@angular/core 19.0.0-next.9 → 19.0.0-rc.1

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 +21183 -19410
  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 +174 -113
  10. package/fesm2022/testing.mjs.map +1 -1
  11. package/index.d.ts +524 -92
  12. package/package.json +1 -1
  13. package/primitives/event-dispatch/index.d.ts +7 -4
  14. package/primitives/signals/index.d.ts +3 -1
  15. package/rxjs-interop/index.d.ts +35 -4
  16. package/schematics/bundles/{checker-3b2ea20f.js → checker-9ca42e51.js} +2303 -1006
  17. package/schematics/bundles/combine_units-a16385aa.js +1634 -0
  18. package/schematics/bundles/{compiler_host-b4ba5a28.js → compiler_host-31afa4ed.js} +8 -5
  19. package/schematics/bundles/control-flow-migration.js +3 -3
  20. package/schematics/bundles/explicit-standalone-flag.js +29 -9
  21. package/schematics/bundles/imports-4ac08251.js +1 -1
  22. package/schematics/bundles/inject-migration.js +222 -54
  23. package/schematics/bundles/leading_space-d190b83b.js +1 -1
  24. package/schematics/bundles/migrate_ts_type_references-b2a28742.js +1463 -0
  25. package/schematics/bundles/nodes-0e7d45ca.js +1 -1
  26. package/schematics/bundles/output-migration.js +575 -0
  27. package/schematics/bundles/pending-tasks.js +3 -3
  28. package/schematics/bundles/{program-6534a30a.js → program-71beec0b.js} +1385 -460
  29. package/schematics/bundles/project_tsconfig_paths-e9ccccbf.js +1 -1
  30. package/schematics/bundles/provide-initializer.js +179 -0
  31. package/schematics/bundles/route-lazy-loading.js +9 -4
  32. package/schematics/bundles/signal-input-migration.js +183 -311
  33. package/schematics/bundles/signal-queries-migration.js +404 -146
  34. package/schematics/bundles/signals.js +54 -0
  35. package/schematics/bundles/standalone-migration.js +29 -12
  36. package/schematics/collection.json +11 -0
  37. package/schematics/migrations.json +7 -1
  38. package/schematics/ng-generate/output-migration/schema.json +19 -0
  39. package/schematics/ng-generate/signal-queries-migration/schema.json +11 -0
  40. package/schematics/ng-generate/signals/schema.json +65 -0
  41. package/testing/index.d.ts +1 -1
  42. package/schematics/bundles/group_replacements-e1b5cbf8.js +0 -31571
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
  /**
3
- * @license Angular v19.0.0-next.9
3
+ * @license Angular v19.0.0-rc.1
4
4
  * (c) 2010-2024 Google LLC. https://angular.io/
5
5
  * License: MIT
6
6
  */
@@ -9,20 +9,21 @@
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-e1b5cbf8.js');
12
+ var migrate_ts_type_references = require('./migrate_ts_type_references-b2a28742.js');
13
13
  var ts = require('typescript');
14
14
  require('os');
15
- var checker = require('./checker-3b2ea20f.js');
16
- var program = require('./program-6534a30a.js');
15
+ var checker = require('./checker-9ca42e51.js');
16
+ var program = require('./program-71beec0b.js');
17
17
  require('path');
18
+ var combine_units = require('./combine_units-a16385aa.js');
18
19
  var assert = require('assert');
19
- var leading_space = require('./leading_space-d190b83b.js');
20
20
  var project_tsconfig_paths = require('./project_tsconfig_paths-e9ccccbf.js');
21
- require('@angular-devkit/core');
22
- require('node:path/posix');
21
+ require('./leading_space-d190b83b.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
 
@@ -33,22 +34,23 @@ var assert__default = /*#__PURE__*/_interopDefaultLegacy(assert);
33
34
  * Class that holds information about a given directive and its input fields.
34
35
  */
35
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;
36
52
  constructor(clazz) {
37
53
  this.clazz = clazz;
38
- /**
39
- * Map of inputs detected in the given class.
40
- * Maps string-based input ids to the detailed input metadata.
41
- */
42
- this.inputFields = new Map();
43
- /** Map of input IDs and their incompatibilities. */
44
- this.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
- this.incompatible = null;
52
54
  }
53
55
  /**
54
56
  * Checks whether there are any migrated inputs for the
@@ -78,6 +80,11 @@ class DirectiveInfo {
78
80
  * the whole migration.
79
81
  */
80
82
  class MigrationHost {
83
+ isMigratingCore;
84
+ programInfo;
85
+ config;
86
+ _sourceFiles;
87
+ compilerOptions;
81
88
  constructor(isMigratingCore, programInfo, config, sourceFiles) {
82
89
  this.isMigratingCore = isMigratingCore;
83
90
  this.programInfo = programInfo;
@@ -100,7 +107,7 @@ function getInputDescriptor(hostOrInfo, node) {
100
107
  className = node.parent.name?.text ?? '<anonymous>';
101
108
  }
102
109
  const info = hostOrInfo instanceof MigrationHost ? hostOrInfo.programInfo : hostOrInfo;
103
- const file = group_replacements.projectFile(node.getSourceFile(), info);
110
+ const file = combine_units.projectFile(node.getSourceFile(), info);
104
111
  // Inputs may be detected in `.d.ts` files. Ensure that if the file IDs
105
112
  // match regardless of extension. E.g. `/google3/blaze-out/bin/my_file.ts` should
106
113
  // have the same ID as `/google3/my_file.ts`.
@@ -110,11 +117,6 @@ function getInputDescriptor(hostOrInfo, node) {
110
117
  node,
111
118
  };
112
119
  }
113
- /** Whether the given value is an input descriptor. */
114
- function isInputDescriptor(v) {
115
- return (v.key !== undefined &&
116
- v.node !== undefined);
117
- }
118
120
 
119
121
  /**
120
122
  * Attempts to resolve the known `@Input` metadata for the given
@@ -124,11 +126,11 @@ function attemptRetrieveInputFromSymbol(programInfo, memberSymbol, knownInputs)
124
126
  // Even for declared classes from `.d.ts`, the value declaration
125
127
  // should exist and point to the property declaration.
126
128
  if (memberSymbol.valueDeclaration !== undefined &&
127
- group_replacements.isInputContainerNode(memberSymbol.valueDeclaration)) {
129
+ combine_units.isInputContainerNode(memberSymbol.valueDeclaration)) {
128
130
  const member = memberSymbol.valueDeclaration;
129
131
  // If the member itself is an input that is being migrated, we
130
132
  // do not need to check, as overriding would be fine then— like before.
131
- const memberInputDescr = group_replacements.isInputContainerNode(member)
133
+ const memberInputDescr = combine_units.isInputContainerNode(member)
132
134
  ? getInputDescriptor(programInfo, member)
133
135
  : null;
134
136
  return memberInputDescr !== null ? (knownInputs.get(memberInputDescr) ?? null) : null;
@@ -143,17 +145,19 @@ function attemptRetrieveInputFromSymbol(programInfo, memberSymbol, knownInputs)
143
145
  * loaded into the program.
144
146
  */
145
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();
146
158
  constructor(programInfo, config) {
147
159
  this.programInfo = programInfo;
148
160
  this.config = config;
149
- /**
150
- * Known inputs from the whole program.
151
- */
152
- this.knownInputIds = new Map();
153
- /** Known container classes of inputs. */
154
- this._allClasses = new Set();
155
- /** Maps classes to their directive info. */
156
- this._classToDirectiveInfo = new Map();
157
161
  }
158
162
  /** Whether the given input exists. */
159
163
  has(descr) {
@@ -182,7 +186,7 @@ class KnownInputs {
182
186
  }
183
187
  const directiveInfo = this._classToDirectiveInfo.get(data.node.parent);
184
188
  const inputInfo = {
185
- file: group_replacements.projectFile(data.node.getSourceFile(), this.programInfo),
189
+ file: combine_units.projectFile(data.node.getSourceFile(), this.programInfo),
186
190
  metadata: data.metadata,
187
191
  descriptor: data.descriptor,
188
192
  container: directiveInfo,
@@ -208,8 +212,8 @@ class KnownInputs {
208
212
  const inputInfo = this.knownInputIds.get(input.key);
209
213
  const existingIncompatibility = inputInfo.container.getInputMemberIncompatibility(input);
210
214
  // Ensure an existing more significant incompatibility is not overridden.
211
- if (existingIncompatibility !== null && group_replacements.isFieldIncompatibility(existingIncompatibility)) {
212
- incompatibility = group_replacements.pickFieldIncompatibility(existingIncompatibility, incompatibility);
215
+ if (existingIncompatibility !== null && migrate_ts_type_references.isFieldIncompatibility(existingIncompatibility)) {
216
+ incompatibility = migrate_ts_type_references.pickFieldIncompatibility(existingIncompatibility, incompatibility);
213
217
  }
214
218
  this.knownInputIds
215
219
  .get(input.key)
@@ -237,13 +241,13 @@ class KnownInputs {
237
241
  captureUnknownDerivedField(field) {
238
242
  this.markFieldIncompatible(field, {
239
243
  context: null,
240
- reason: group_replacements.FieldIncompatibilityReason.OverriddenByDerivedClass,
244
+ reason: migrate_ts_type_references.FieldIncompatibilityReason.OverriddenByDerivedClass,
241
245
  });
242
246
  }
243
247
  captureUnknownParentField(field) {
244
248
  this.markFieldIncompatible(field, {
245
249
  context: null,
246
- reason: group_replacements.FieldIncompatibilityReason.TypeConflictWithBaseClass,
250
+ reason: migrate_ts_type_references.FieldIncompatibilityReason.TypeConflictWithBaseClass,
247
251
  });
248
252
  }
249
253
  }
@@ -299,17 +303,15 @@ function prepareAnalysisInfo(userProgram, compiler, programAbsoluteRootPaths) {
299
303
  * - imports that may need to be updated.
300
304
  */
301
305
  class MigrationResult {
302
- constructor() {
303
- this.printer = ts__default["default"].createPrinter({ newLine: ts__default["default"].NewLineKind.LineFeed });
304
- // May be `null` if the input cannot be converted. This is also
305
- // signified by an incompatibility- but the input is tracked here as it
306
- // still is a "source input".
307
- this.sourceInputs = new Map();
308
- this.references = [];
309
- // Execution data
310
- this.replacements = [];
311
- this.inputDecoratorSpecifiers = new Map();
312
- }
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();
313
315
  }
314
316
 
315
317
  /** Attempts to extract metadata of a potential TypeScript `@Input()` declaration. */
@@ -322,7 +324,7 @@ function extractDecoratorInput(node, host, reflector, metadataReader, evaluator,
322
324
  * part of a `.d.ts` file.
323
325
  */
324
326
  function extractDtsInput(node, metadataReader) {
325
- if (!group_replacements.isInputContainerNode(node) ||
327
+ if (!combine_units.isInputContainerNode(node) ||
326
328
  !ts__default["default"].isIdentifier(node.name) ||
327
329
  !node.getSourceFile().isDeclarationFile) {
328
330
  return null;
@@ -333,7 +335,18 @@ function extractDtsInput(node, metadataReader) {
333
335
  !ts__default["default"].isIdentifier(node.parent.name)) {
334
336
  return null;
335
337
  }
336
- 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
+ }
337
350
  const inputMapping = directiveMetadata?.inputs.getByClassPropertyName(node.name.text);
338
351
  // Signal inputs are never tracked and migrated.
339
352
  if (inputMapping?.isSignal) {
@@ -345,6 +358,8 @@ function extractDtsInput(node, metadataReader) {
345
358
  ...inputMapping,
346
359
  inputDecorator: null,
347
360
  inSourceFile: false,
361
+ // Inputs from `.d.ts` cannot have any field decorators applied.
362
+ fieldDecorators: [],
348
363
  };
349
364
  }
350
365
  /**
@@ -352,7 +367,7 @@ function extractDtsInput(node, metadataReader) {
352
367
  * directly defined inside a source file (`.ts`).
353
368
  */
354
369
  function extractSourceCodeInput(node, host, reflector, evaluator, refEmitter) {
355
- if (!group_replacements.isInputContainerNode(node) ||
370
+ if (!combine_units.isInputContainerNode(node) ||
356
371
  !ts__default["default"].isIdentifier(node.name) ||
357
372
  node.getSourceFile().isDeclarationFile) {
358
373
  return null;
@@ -396,6 +411,7 @@ function extractSourceCodeInput(node, host, reflector, evaluator, refEmitter) {
396
411
  inSourceFile: true,
397
412
  transform: transformResult,
398
413
  inputDecorator,
414
+ fieldDecorators: decorators,
399
415
  };
400
416
  }
401
417
  /**
@@ -435,14 +451,19 @@ function prepareAndCheckForConversion(node, metadata, checker, options) {
435
451
  if (ts__default["default"].isAccessor(node)) {
436
452
  return {
437
453
  context: node,
438
- reason: group_replacements.FieldIncompatibilityReason.Accessor,
454
+ reason: migrate_ts_type_references.FieldIncompatibilityReason.Accessor,
439
455
  };
440
456
  }
441
457
  assert__default["default"](metadata.inputDecorator !== null, 'Expected an input decorator for inputs that are being migrated.');
442
458
  let initialValue = node.initializer;
443
459
  let isUndefinedInitialValue = node.initializer === undefined ||
444
460
  (ts__default["default"].isIdentifier(node.initializer) && node.initializer.text === 'undefined');
445
- 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;
446
467
  // If an input can be required, due to the non-null assertion on the property,
447
468
  // make it required if there is no initializer.
448
469
  if (node.exclamationToken !== undefined && initialValue === undefined) {
@@ -456,7 +477,7 @@ function prepareAndCheckForConversion(node, metadata, checker, options) {
456
477
  if (!metadata.required &&
457
478
  node.type !== undefined &&
458
479
  isUndefinedInitialValue &&
459
- !loosePropertyInitializationWithStrictNullChecks) {
480
+ !avoidTypeExpansion) {
460
481
  preferShorthandIfPossible = { originalType: node.type };
461
482
  }
462
483
  // If the input is using `@Input() bla?: string;` with the "optional question mark",
@@ -474,7 +495,7 @@ function prepareAndCheckForConversion(node, metadata, checker, options) {
474
495
  if (typeToAdd === undefined) {
475
496
  return {
476
497
  context: node,
477
- reason: group_replacements.FieldIncompatibilityReason.SignalInput__QuestionMarkButNoGoodExplicitTypeExtractable,
498
+ reason: migrate_ts_type_references.FieldIncompatibilityReason.SignalInput__QuestionMarkButNoGoodExplicitTypeExtractable,
478
499
  };
479
500
  }
480
501
  if (!checker.isTypeAssignableTo(checker.getUndefinedType(), checker.getTypeFromTypeNode(typeToAdd))) {
@@ -489,8 +510,8 @@ function prepareAndCheckForConversion(node, metadata, checker, options) {
489
510
  // is disabled, while strict null checks are enabled; then we know that `undefined`
490
511
  // cannot be used as initial value, nor do we want to expand the input's type magically.
491
512
  // Instead, we detect this case and migrate to `undefined!` which leaves the behavior unchanged.
492
- // TODO: This would be a good spot for a clean-up TODO.
493
- if (loosePropertyInitializationWithStrictNullChecks &&
513
+ if (strictNullChecksEnabled &&
514
+ !strictPropertyInitialization &&
494
515
  node.initializer === undefined &&
495
516
  node.type !== undefined &&
496
517
  node.questionToken === undefined &&
@@ -515,7 +536,7 @@ function prepareAndCheckForConversion(node, metadata, checker, options) {
515
536
  // the generated type might depend on imports that we cannot add here (nor want).
516
537
  return {
517
538
  context: node,
518
- reason: group_replacements.FieldIncompatibilityReason.SignalInput__RequiredButNoGoodExplicitTypeExtractable,
539
+ reason: migrate_ts_type_references.FieldIncompatibilityReason.SignalInput__RequiredButNoGoodExplicitTypeExtractable,
519
540
  };
520
541
  }
521
542
  }
@@ -567,7 +588,7 @@ function pass1__IdentifySourceFileAndDeclarationInputs(sf, host, checker, reflec
567
588
  const visitor = (node) => {
568
589
  const decoratorInput = extractDecoratorInput(node, host, reflector, dtsMetadataReader, evaluator, refEmitter);
569
590
  if (decoratorInput !== null) {
570
- 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.');
571
592
  const inputDescr = getInputDescriptor(host, node);
572
593
  // track all inputs, even from declarations for reference resolution.
573
594
  knownDecoratorInputs.register({ descriptor: inputDescr, metadata: decoratorInput, node });
@@ -575,7 +596,7 @@ function pass1__IdentifySourceFileAndDeclarationInputs(sf, host, checker, reflec
575
596
  // these are then later migrated in the migration phase.
576
597
  if (decoratorInput.inSourceFile && host.isSourceFileForCurrentMigration(sf)) {
577
598
  const conversionPreparation = prepareAndCheckForConversion(node, decoratorInput, checker, host.compilerOptions);
578
- if (group_replacements.isFieldIncompatibility(conversionPreparation)) {
599
+ if (migrate_ts_type_references.isFieldIncompatibility(conversionPreparation)) {
579
600
  knownDecoratorInputs.markFieldIncompatible(inputDescr, conversionPreparation);
580
601
  result.sourceInputs.set(inputDescr, null);
581
602
  }
@@ -614,8 +635,17 @@ function pass1__IdentifySourceFileAndDeclarationInputs(sf, host, checker, reflec
614
635
  * In addition, spying onto an input may be problematic- so we skip migrating
615
636
  * such.
616
637
  */
617
- function pass3__checkIncompatiblePatterns(inheritanceGraph, checker, groupedTsAstVisitor, knownInputs) {
618
- 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
+ }
619
649
  }
620
650
 
621
651
  /**
@@ -629,7 +659,7 @@ function pass3__checkIncompatiblePatterns(inheritanceGraph, checker, groupedTsAs
629
659
  * such.
630
660
  */
631
661
  function pass2_IdentifySourceFileReferences(programInfo, checker, reflector, resourceLoader, evaluator, templateTypeChecker, groupedTsAstVisitor, knownInputs, result, fieldNamesToConsiderForReferenceLookup) {
632
- 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);
633
663
  }
634
664
 
635
665
  /**
@@ -657,26 +687,26 @@ function executeAnalysisPhase(host, knownInputs, result, { sourceFiles, fullProg
657
687
  }
658
688
  // A graph starting with source files is sufficient. We will resolve into
659
689
  // declaration files if a source file depends on such.
660
- const inheritanceGraph = new group_replacements.InheritanceGraph(typeChecker).expensivePopulate(sourceFiles);
661
- 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);
662
692
  // Register pass 2. Find all source file references.
663
693
  pass2_IdentifySourceFileReferences(host.programInfo, typeChecker, reflector, resourceLoader, evaluator, templateTypeChecker, pass2And3SourceFileVisitor, knownInputs, result, fieldNamesToConsiderForReferenceLookup);
664
694
  // Register pass 3. Check incompatible patterns pass.
665
- pass3__checkIncompatiblePatterns(inheritanceGraph, typeChecker, pass2And3SourceFileVisitor, knownInputs);
695
+ pass3__checkIncompatiblePatterns(host, inheritanceGraph, typeChecker, pass2And3SourceFileVisitor, knownInputs);
666
696
  // Perform Pass 2 and Pass 3, efficiently in one pass.
667
697
  pass2And3SourceFileVisitor.execute();
668
698
  // Determine incompatible inputs based on resolved references.
669
699
  for (const reference of result.references) {
670
- if (group_replacements.isTsReference(reference) && reference.from.isWrite) {
700
+ if (combine_units.isTsReference(reference) && reference.from.isWrite) {
671
701
  knownInputs.markFieldIncompatible(reference.target, {
672
- reason: group_replacements.FieldIncompatibilityReason.WriteAssignment,
702
+ reason: migrate_ts_type_references.FieldIncompatibilityReason.WriteAssignment,
673
703
  context: reference.from.node,
674
704
  });
675
705
  }
676
- if (group_replacements.isTemplateReference(reference) || group_replacements.isHostBindingReference(reference)) {
706
+ if (combine_units.isTemplateReference(reference) || combine_units.isHostBindingReference(reference)) {
677
707
  if (reference.from.isWrite) {
678
708
  knownInputs.markFieldIncompatible(reference.target, {
679
- reason: group_replacements.FieldIncompatibilityReason.WriteAssignment,
709
+ reason: migrate_ts_type_references.FieldIncompatibilityReason.WriteAssignment,
680
710
  // No TS node context available for template or host bindings.
681
711
  context: null,
682
712
  });
@@ -684,10 +714,10 @@ function executeAnalysisPhase(host, knownInputs, result, { sourceFiles, fullProg
684
714
  }
685
715
  // TODO: Remove this when we support signal narrowing in templates.
686
716
  // https://github.com/angular/angular/pull/55456.
687
- if (group_replacements.isTemplateReference(reference)) {
717
+ if (combine_units.isTemplateReference(reference)) {
688
718
  if (reference.from.isLikelyPartOfNarrowing) {
689
719
  knownInputs.markFieldIncompatible(reference.target, {
690
- reason: group_replacements.FieldIncompatibilityReason.PotentiallyNarrowedInTemplateButNoSupportYet,
720
+ reason: migrate_ts_type_references.FieldIncompatibilityReason.PotentiallyNarrowedInTemplateButNoSupportYet,
691
721
  context: null,
692
722
  });
693
723
  }
@@ -717,7 +747,7 @@ function executeAnalysisPhase(host, knownInputs, result, { sourceFiles, fullProg
717
747
  * would then have other derived classes as well, it would propagate the status.
718
748
  */
719
749
  function pass4__checkInheritanceOfInputs(inheritanceGraph, metaRegistry, knownInputs) {
720
- group_replacements.checkInheritanceOfKnownFields(inheritanceGraph, metaRegistry, knownInputs, {
750
+ migrate_ts_type_references.checkInheritanceOfKnownFields(inheritanceGraph, metaRegistry, knownInputs, {
721
751
  isClassWithKnownFields: (clazz) => knownInputs.isInputContainingClass(clazz),
722
752
  getFieldsForClass: (clazz) => {
723
753
  const directiveInfo = knownInputs.getDirectiveInfoForClass(clazz);
@@ -793,25 +823,15 @@ function topologicalSort(graph) {
793
823
  }
794
824
 
795
825
  /** Merges a list of compilation units into a combined unit. */
796
- function mergeCompilationUnitData(metadataFiles) {
826
+ function combineCompilationUnitData(unitA, unitB) {
797
827
  const result = {
798
828
  knownInputs: {},
799
829
  };
800
- const idToGraphNode = new Map();
801
- const inheritanceGraph = [];
802
- const isNodeIncompatible = (node) => node.info.memberIncompatibility !== null || node.info.owningClassIncompatibility !== null;
803
- for (const file of metadataFiles) {
830
+ for (const file of [unitA, unitB]) {
804
831
  for (const [key, info] of Object.entries(file.knownInputs)) {
805
832
  const existing = result.knownInputs[key];
806
833
  if (existing === undefined) {
807
834
  result.knownInputs[key] = info;
808
- const node = {
809
- incoming: new Set(),
810
- outgoing: new Set(),
811
- data: { info, key },
812
- };
813
- inheritanceGraph.push(node);
814
- idToGraphNode.set(key, node);
815
835
  continue;
816
836
  }
817
837
  // Merge metadata.
@@ -829,7 +849,7 @@ function mergeCompilationUnitData(metadataFiles) {
829
849
  else {
830
850
  // Input might not be incompatible in one target, but others might invalidate it.
831
851
  // merge the incompatibility state.
832
- existing.memberIncompatibility = group_replacements.pickFieldIncompatibility({ 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;
833
853
  }
834
854
  }
835
855
  // Merge incompatibility of the class owning the input.
@@ -841,7 +861,30 @@ function mergeCompilationUnitData(metadataFiles) {
841
861
  }
842
862
  }
843
863
  }
844
- 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)) {
845
888
  if (info.extendsFrom !== null) {
846
889
  const from = idToGraphNode.get(key);
847
890
  const target = idToGraphNode.get(info.extendsFrom);
@@ -861,22 +904,22 @@ function mergeCompilationUnitData(metadataFiles) {
861
904
  // If parent is incompatible and not migrated, then this input
862
905
  // cannot be migrated either. Try propagating parent incompatibility then.
863
906
  if (isNodeIncompatible(parent.data)) {
864
- node.data.info.memberIncompatibility = group_replacements.pickFieldIncompatibility({ reason: group_replacements.FieldIncompatibilityReason.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;
865
908
  break;
866
909
  }
867
910
  }
868
911
  }
869
- for (const info of Object.values(result.knownInputs)) {
912
+ for (const info of Object.values(combinedData.knownInputs)) {
870
913
  // We never saw a source file for this input, globally. Try marking it as incompatible,
871
914
  // so that all references and inheritance checks can propagate accordingly.
872
915
  if (!info.seenAsSourceInput) {
873
916
  const existingMemberIncompatibility = info.memberIncompatibility !== null
874
917
  ? { reason: info.memberIncompatibility, context: null }
875
918
  : null;
876
- info.memberIncompatibility = group_replacements.pickFieldIncompatibility({ reason: group_replacements.FieldIncompatibilityReason.OutsideOfMigrationScope, context: null }, existingMemberIncompatibility).reason;
919
+ info.memberIncompatibility = migrate_ts_type_references.pickFieldIncompatibility({ reason: migrate_ts_type_references.FieldIncompatibilityReason.OutsideOfMigrationScope, context: null }, existingMemberIncompatibility).reason;
877
920
  }
878
921
  }
879
- return result;
922
+ return globalMeta;
880
923
  }
881
924
 
882
925
  function populateKnownInputsFromGlobalData(knownInputs, globalData) {
@@ -900,46 +943,6 @@ function populateKnownInputsFromGlobalData(knownInputs, globalData) {
900
943
  }
901
944
  }
902
945
 
903
- /**
904
- * Inserts a leading string for the given node, respecting
905
- * indentation of the given anchor node.
906
- *
907
- * Useful for inserting TODOs.
908
- */
909
- function insertPrecedingLine(node, info, text) {
910
- const leadingSpace = leading_space.getLeadingLineWhitespaceOfNode(node);
911
- return new group_replacements.Replacement(group_replacements.projectFile(node.getSourceFile(), info), new group_replacements.TextUpdate({
912
- position: node.getStart(),
913
- end: node.getStart(),
914
- toInsert: `${text}\n${leadingSpace}`,
915
- }));
916
- }
917
-
918
- /**
919
- * Cuts the given string into lines basing around the specified
920
- * line length limit. This function breaks the string on a per-word basis.
921
- */
922
- function cutStringToLineLimit(str, limit) {
923
- const words = str.split(' ');
924
- const chunks = [];
925
- let chunkIdx = 0;
926
- while (words.length) {
927
- // New line if we exceed limit.
928
- if (chunks[chunkIdx] !== undefined && chunks[chunkIdx].length > limit) {
929
- chunkIdx++;
930
- }
931
- // Ensure line is initialized for the given index.
932
- if (chunks[chunkIdx] === undefined) {
933
- chunks[chunkIdx] = '';
934
- }
935
- const word = words.shift();
936
- const needsSpace = chunks[chunkIdx].length > 0;
937
- // Insert word. Add space before, if the line already contains text.
938
- chunks[chunkIdx] += `${needsSpace ? ' ' : ''}${word}`;
939
- }
940
- return chunks;
941
- }
942
-
943
946
  // TODO: Consider initializations inside the constructor. Those are not migrated right now
944
947
  // though, as they are writes.
945
948
  /**
@@ -981,7 +984,7 @@ function convertToSignalInput(node, { resolvedMetadata: metadata, resolvedType,
981
984
  // When using the `input()` shorthand, try cutting of `undefined` from potential
982
985
  // union types. `undefined` will be automatically included in the type.
983
986
  if (ts__default["default"].isUnionTypeNode(resolvedType)) {
984
- 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);
985
988
  }
986
989
  }
987
990
  }
@@ -1021,9 +1024,9 @@ function convertToSignalInput(node, { resolvedMetadata: metadata, resolvedType,
1021
1024
  const newPropertyText = result.printer.printNode(ts__default["default"].EmitHint.Unspecified, newNode, node.getSourceFile());
1022
1025
  const replacements = [];
1023
1026
  if (leadingTodoText !== null) {
1024
- 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}`)));
1025
1028
  }
1026
- 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({
1027
1030
  position: node.getStart(),
1028
1031
  end: node.getEnd(),
1029
1032
  toInsert: newPropertyText,
@@ -1062,129 +1065,6 @@ function extractTransformOfInput(transform, resolvedType, checker) {
1062
1065
  };
1063
1066
  }
1064
1067
 
1065
- /**
1066
- * Gets human-readable message information for the given field incompatibility.
1067
- * This text will be used by the language service, or CLI-based migration.
1068
- */
1069
- function getMessageForFieldIncompatibility(reason, fieldName) {
1070
- switch (reason) {
1071
- case group_replacements.FieldIncompatibilityReason.Accessor:
1072
- return {
1073
- short: `Accessor ${fieldName.plural} cannot be migrated as they are too complex.`,
1074
- extra: 'The migration potentially requires usage of `effect` or `computed`, but ' +
1075
- 'the intent is unclear. The migration cannot safely migrate.',
1076
- };
1077
- case group_replacements.FieldIncompatibilityReason.OverriddenByDerivedClass:
1078
- return {
1079
- short: `The ${fieldName.single} cannot be migrated because the field is overridden by a subclass.`,
1080
- extra: 'The field in the subclass is not a signal, so migrating would break your build.',
1081
- };
1082
- case group_replacements.FieldIncompatibilityReason.ParentIsIncompatible:
1083
- return {
1084
- short: `This ${fieldName.single} is inherited from a superclass, but the parent cannot be migrated.`,
1085
- extra: 'Migrating this field would cause your build to fail.',
1086
- };
1087
- case group_replacements.FieldIncompatibilityReason.PotentiallyNarrowedInTemplateButNoSupportYet:
1088
- return {
1089
- short: `This ${fieldName.single} is used in a control flow expression (e.g. \`@if\` or \`*ngIf\`) and ` +
1090
- 'migrating would break narrowing currently.',
1091
- extra: `In the future, Angular intends to support narrowing of signals.`,
1092
- };
1093
- case group_replacements.FieldIncompatibilityReason.RedeclaredViaDerivedClassInputsArray:
1094
- return {
1095
- short: `The ${fieldName.single} is overridden by a subclass that cannot be migrated.`,
1096
- extra: `The subclass overrides this ${fieldName.single} via the \`inputs\` array in @Directive/@Component. ` +
1097
- 'Migrating the field would break your build because the subclass field cannot be a signal.',
1098
- };
1099
- case group_replacements.FieldIncompatibilityReason.SignalInput__RequiredButNoGoodExplicitTypeExtractable:
1100
- return {
1101
- short: `Input is required, but the migration cannot determine a good type for the input.`,
1102
- extra: 'Consider adding an explicit type to make the migration possible.',
1103
- };
1104
- case group_replacements.FieldIncompatibilityReason.SignalInput__QuestionMarkButNoGoodExplicitTypeExtractable:
1105
- return {
1106
- short: `Input is marked with a question mark. Migration could not determine a good type for the input.`,
1107
- extra: 'The migration needs to be able to resolve a type, so that it can include `undefined` in your type. ' +
1108
- 'Consider adding an explicit type to make the migration possible.',
1109
- };
1110
- case group_replacements.FieldIncompatibilityReason.SkippedViaConfigFilter:
1111
- return {
1112
- short: `This ${fieldName.single} is not part of the current migration scope.`,
1113
- extra: 'Skipped via migration config.',
1114
- };
1115
- case group_replacements.FieldIncompatibilityReason.SpyOnThatOverwritesField:
1116
- return {
1117
- short: 'A jasmine `spyOn` call spies on this field. This breaks with signals.',
1118
- extra: `Migration cannot safely migrate as "spyOn" writes to the ${fieldName.single}. ` +
1119
- `Signal ${fieldName.plural} are readonly.`,
1120
- };
1121
- case group_replacements.FieldIncompatibilityReason.TypeConflictWithBaseClass:
1122
- return {
1123
- short: `This ${fieldName.single} overrides a field from a superclass, while the superclass ` +
1124
- `field is not migrated.`,
1125
- extra: 'Migrating the field would break your build because of a type conflict.',
1126
- };
1127
- case group_replacements.FieldIncompatibilityReason.WriteAssignment:
1128
- return {
1129
- short: `Your application code writes to the ${fieldName.single}. This prevents migration.`,
1130
- extra: `Signal ${fieldName.plural} are readonly, so migrating would break your build.`,
1131
- };
1132
- case group_replacements.FieldIncompatibilityReason.OutsideOfMigrationScope:
1133
- return {
1134
- short: `This ${fieldName.single} is not part of any source files in your project.`,
1135
- extra: `The migration excludes ${fieldName.plural} if no source file declaring them was seen.`,
1136
- };
1137
- }
1138
- }
1139
- /**
1140
- * Gets human-readable message information for the given class incompatibility.
1141
- * This text will be used by the language service, or CLI-based migration.
1142
- */
1143
- function getMessageForClassIncompatibility(reason, fieldName) {
1144
- switch (reason) {
1145
- case group_replacements.ClassIncompatibilityReason.OwningClassReferencedInClassProperty:
1146
- return {
1147
- short: `Class of this ${fieldName.single} is referenced in the signature of another class.`,
1148
- extra: 'The other class is likely typed to expect a non-migrated field, so ' +
1149
- 'migration is skipped to not break your build.',
1150
- };
1151
- case group_replacements.ClassIncompatibilityReason.ClassManuallyInstantiated:
1152
- return {
1153
- short: `Class of this ${fieldName.single} is manually instantiated. ` +
1154
- 'This is discouraged and prevents migration',
1155
- extra: `Signal ${fieldName.plural} require a DI injection context. Manually instantiating ` +
1156
- 'breaks this requirement in some cases, so the migration is skipped.',
1157
- };
1158
- }
1159
- }
1160
-
1161
- /**
1162
- * Inserts a TODO for the incompatibility blocking the given node
1163
- * from being migrated.
1164
- */
1165
- function insertTodoForIncompatibility(node, programInfo, input) {
1166
- const incompatibility = input.container.getInputMemberIncompatibility(input.descriptor);
1167
- if (incompatibility === null) {
1168
- return [];
1169
- }
1170
- // If an input is skipped via config filter or outside migration scope, do not
1171
- // insert TODOs, as this could results in lots of unnecessary comments.
1172
- if (group_replacements.isFieldIncompatibility(incompatibility) &&
1173
- (incompatibility.reason === group_replacements.FieldIncompatibilityReason.SkippedViaConfigFilter ||
1174
- incompatibility.reason === group_replacements.FieldIncompatibilityReason.OutsideOfMigrationScope)) {
1175
- return [];
1176
- }
1177
- const message = group_replacements.isFieldIncompatibility(incompatibility)
1178
- ? getMessageForFieldIncompatibility(incompatibility.reason, { single: 'input', plural: 'inputs' })
1179
- .short
1180
- : getMessageForClassIncompatibility(incompatibility, { single: 'input', plural: 'inputs' }).short;
1181
- const lines = cutStringToLineLimit(message, 70);
1182
- return [
1183
- insertPrecedingLine(node, programInfo, `// TODO: Skipped for migration because:`),
1184
- ...lines.map((line) => insertPrecedingLine(node, programInfo, `// ${line}`)),
1185
- ];
1186
- }
1187
-
1188
1068
  /**
1189
1069
  * Phase that migrates `@Input()` declarations to signal inputs and
1190
1070
  * manages imports within the given file.
@@ -1197,9 +1077,13 @@ function pass6__migrateInputDeclarations(host, checker, result, knownInputs, imp
1197
1077
  const inputInfo = knownInputs.get(input);
1198
1078
  // Do not migrate incompatible inputs.
1199
1079
  if (inputInfo.isIncompatible()) {
1080
+ const incompatibilityReason = inputInfo.container.getInputMemberIncompatibility(input);
1200
1081
  // Add a TODO for the incompatible input, if desired.
1201
- if (host.config.insertTodosForSkippedFields) {
1202
- 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
+ }));
1203
1087
  }
1204
1088
  filesWithIncompatibleInputs.add(sf);
1205
1089
  continue;
@@ -1222,7 +1106,7 @@ function pass6__migrateInputDeclarations(host, checker, result, knownInputs, imp
1222
1106
  * previous migrate phases.
1223
1107
  */
1224
1108
  function pass10_applyImportManager(importManager, result, sourceFiles, info) {
1225
- group_replacements.applyImportManagerChanges(importManager, result.replacements, sourceFiles, info);
1109
+ combine_units.applyImportManagerChanges(importManager, result.replacements, sourceFiles, info);
1226
1110
  }
1227
1111
 
1228
1112
  /**
@@ -1232,7 +1116,7 @@ function pass10_applyImportManager(importManager, result, sourceFiles, info) {
1232
1116
  * where needed to ensure narrowing continues to work. E.g.
1233
1117
  */
1234
1118
  function pass5__migrateTypeScriptReferences(host, references, checker, info) {
1235
- group_replacements.migrateTypeScriptReferences(host, references, checker, info);
1119
+ migrate_ts_type_references.migrateTypeScriptReferences(host, references, checker, info);
1236
1120
  }
1237
1121
 
1238
1122
  /**
@@ -1243,7 +1127,7 @@ function pass7__migrateTemplateReferences(host, references) {
1243
1127
  const seenFileReferences = new Set();
1244
1128
  for (const reference of references) {
1245
1129
  // This pass only deals with HTML template references.
1246
- if (!group_replacements.isTemplateReference(reference)) {
1130
+ if (!combine_units.isTemplateReference(reference)) {
1247
1131
  continue;
1248
1132
  }
1249
1133
  // Skip references to incompatible inputs.
@@ -1260,7 +1144,7 @@ function pass7__migrateTemplateReferences(host, references) {
1260
1144
  const appendText = reference.from.isObjectShorthandExpression
1261
1145
  ? `: ${reference.from.read.name}()`
1262
1146
  : `()`;
1263
- 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({
1264
1148
  position: reference.from.read.sourceSpan.end,
1265
1149
  end: reference.from.read.sourceSpan.end,
1266
1150
  toInsert: appendText,
@@ -1276,7 +1160,7 @@ function pass8__migrateHostBindings(host, references, info) {
1276
1160
  const seenReferences = new WeakMap();
1277
1161
  for (const reference of references) {
1278
1162
  // This pass only deals with host binding references.
1279
- if (!group_replacements.isHostBindingReference(reference)) {
1163
+ if (!combine_units.isHostBindingReference(reference)) {
1280
1164
  continue;
1281
1165
  }
1282
1166
  // Skip references to incompatible inputs.
@@ -1300,7 +1184,7 @@ function pass8__migrateHostBindings(host, references, info) {
1300
1184
  const appendText = reference.from.isObjectShorthandExpression
1301
1185
  ? `: ${reference.from.read.name}()`
1302
1186
  : `()`;
1303
- 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 })));
1304
1188
  }
1305
1189
  }
1306
1190
 
@@ -1311,7 +1195,7 @@ function pass8__migrateHostBindings(host, references, info) {
1311
1195
  in Catalyst test files.
1312
1196
  */
1313
1197
  function pass9__migrateTypeScriptTypeReferences(host, references, importManager, info) {
1314
- group_replacements.migrateTypeScriptTypeReferences(host, references, importManager, info);
1198
+ migrate_ts_type_references.migrateTypeScriptTypeReferences(host, references, importManager, info);
1315
1199
  }
1316
1200
 
1317
1201
  /**
@@ -1346,25 +1230,13 @@ function executeMigrationPhase(host, knownInputs, result, info) {
1346
1230
  pass10_applyImportManager(importManager, result, sourceFiles, info);
1347
1231
  }
1348
1232
 
1349
- /** Input reasons that cannot be ignored. */
1350
- const nonIgnorableInputIncompatibilities = [
1351
- // Outside of scope inputs should not be migrated. E.g. references to inputs in `node_modules/`.
1352
- group_replacements.FieldIncompatibilityReason.OutsideOfMigrationScope,
1353
- // Explicitly filtered inputs cannot be skipped via best effort mode.
1354
- group_replacements.FieldIncompatibilityReason.SkippedViaConfigFilter,
1355
- // There is no good output for accessor inputs.
1356
- group_replacements.FieldIncompatibilityReason.Accessor,
1357
- // There is no good output for such inputs. We can't perform "conversion".
1358
- group_replacements.FieldIncompatibilityReason.SignalInput__RequiredButNoGoodExplicitTypeExtractable,
1359
- group_replacements.FieldIncompatibilityReason.SignalInput__QuestionMarkButNoGoodExplicitTypeExtractable,
1360
- ];
1361
1233
  /** Filters ignorable input incompatibilities when best effort mode is enabled. */
1362
1234
  function filterIncompatibilitiesForBestEffortMode(knownInputs) {
1363
1235
  knownInputs.knownInputIds.forEach(({ container: c }) => {
1364
1236
  // All class incompatibilities are "filterable" right now.
1365
1237
  c.incompatible = null;
1366
1238
  for (const [key, i] of c.memberIncompatibility.entries()) {
1367
- if (!nonIgnorableInputIncompatibilities.includes(i.reason)) {
1239
+ if (!migrate_ts_type_references.nonIgnorableFieldIncompatibilities.includes(i.reason)) {
1368
1240
  c.memberIncompatibility.delete(key);
1369
1241
  }
1370
1242
  }
@@ -1375,15 +1247,16 @@ function filterIncompatibilitiesForBestEffortMode(knownInputs) {
1375
1247
  * Tsurge migration for migrating Angular `@Input()` declarations to
1376
1248
  * signal inputs, with support for batch execution.
1377
1249
  */
1378
- class SignalInputMigration extends group_replacements.TsurgeComplexMigration {
1250
+ class SignalInputMigration extends combine_units.TsurgeComplexMigration {
1251
+ config;
1252
+ upgradedAnalysisPhaseResults = null;
1379
1253
  constructor(config = {}) {
1380
1254
  super();
1381
1255
  this.config = config;
1382
- this.upgradedAnalysisPhaseResults = null;
1383
1256
  }
1384
1257
  // Override the default ngtsc program creation, to add extra flags.
1385
1258
  createProgram(tsconfigAbsPath, fs) {
1386
- return group_replacements.createNgtscProgram(tsconfigAbsPath, fs, {
1259
+ return combine_units.createNgtscProgram(tsconfigAbsPath, fs, {
1387
1260
  _compilePoisonedComponents: true,
1388
1261
  // We want to migrate non-exported classes too.
1389
1262
  compileNonExportedClasses: true,
@@ -1441,8 +1314,8 @@ class SignalInputMigration extends group_replacements.TsurgeComplexMigration {
1441
1314
  const unitData = getCompilationUnitMetadata(knownInputs);
1442
1315
  // Non-batch mode!
1443
1316
  if (this.config.upgradeAnalysisPhaseToAvoidBatch) {
1444
- const merged = await this.merge([unitData]);
1445
- const { replacements } = await this.migrate(merged, info, {
1317
+ const globalMeta = await this.globalMeta(unitData);
1318
+ const { replacements } = await this.migrate(globalMeta, info, {
1446
1319
  knownInputs,
1447
1320
  result,
1448
1321
  host,
@@ -1456,10 +1329,13 @@ class SignalInputMigration extends group_replacements.TsurgeComplexMigration {
1456
1329
  knownInputs,
1457
1330
  };
1458
1331
  }
1459
- 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));
1460
1336
  }
1461
- async merge(units) {
1462
- return group_replacements.confirmAsSerializable(mergeCompilationUnitData(units));
1337
+ async globalMeta(combinedData) {
1338
+ return combine_units.confirmAsSerializable(convertToGlobalMeta(combinedData));
1463
1339
  }
1464
1340
  async migrate(globalMetadata, info, nonBatchData) {
1465
1341
  const knownInputs = nonBatchData?.knownInputs ?? new KnownInputs(info, this.config);
@@ -1488,8 +1364,8 @@ class SignalInputMigration extends group_replacements.TsurgeComplexMigration {
1488
1364
  for (const [id, input] of Object.entries(globalMetadata.knownInputs)) {
1489
1365
  fullCompilationInputs++;
1490
1366
  const isConsideredSourceInput = input.seenAsSourceInput &&
1491
- input.memberIncompatibility !== group_replacements.FieldIncompatibilityReason.OutsideOfMigrationScope &&
1492
- input.memberIncompatibility !== group_replacements.FieldIncompatibilityReason.SkippedViaConfigFilter;
1367
+ input.memberIncompatibility !== migrate_ts_type_references.FieldIncompatibilityReason.OutsideOfMigrationScope &&
1368
+ input.memberIncompatibility !== migrate_ts_type_references.FieldIncompatibilityReason.SkippedViaConfigFilter;
1493
1369
  // We won't track incompatibilities to inputs that aren't considered source inputs.
1494
1370
  // Tracking their statistics wouldn't provide any value.
1495
1371
  if (!isConsideredSourceInput) {
@@ -1500,13 +1376,13 @@ class SignalInputMigration extends group_replacements.TsurgeComplexMigration {
1500
1376
  incompatibleInputs++;
1501
1377
  }
1502
1378
  if (input.memberIncompatibility !== null) {
1503
- const reasonName = group_replacements.FieldIncompatibilityReason[input.memberIncompatibility];
1379
+ const reasonName = migrate_ts_type_references.FieldIncompatibilityReason[input.memberIncompatibility];
1504
1380
  const key = `input-field-incompatibility-${reasonName}`;
1505
1381
  fieldIncompatibleCounts[key] ??= 0;
1506
1382
  fieldIncompatibleCounts[key]++;
1507
1383
  }
1508
1384
  if (input.owningClassIncompatibility !== null) {
1509
- const reasonName = group_replacements.ClassIncompatibilityReason[input.owningClassIncompatibility];
1385
+ const reasonName = migrate_ts_type_references.ClassIncompatibilityReason[input.owningClassIncompatibility];
1510
1386
  const key = `input-owning-class-incompatibility-${reasonName}`;
1511
1387
  classIncompatibleCounts[key] ??= 0;
1512
1388
  classIncompatibleCounts[key]++;
@@ -1538,19 +1414,10 @@ function filterInputsViaConfig(result, knownInputs, config) {
1538
1414
  skippedInputs.add(input.descriptor.key);
1539
1415
  knownInputs.markFieldIncompatible(input.descriptor, {
1540
1416
  context: null,
1541
- reason: group_replacements.FieldIncompatibilityReason.SkippedViaConfigFilter,
1417
+ reason: migrate_ts_type_references.FieldIncompatibilityReason.SkippedViaConfigFilter,
1542
1418
  });
1543
1419
  }
1544
1420
  }
1545
- result.references = result.references.filter((reference) => {
1546
- if (isInputDescriptor(reference.target)) {
1547
- // Only migrate the reference if the target is NOT skipped.
1548
- return !skippedInputs.has(reference.target.key);
1549
- }
1550
- // Class references may be migrated. This is up to the logic handling
1551
- // the class reference. E.g. it may not migrate if any member is incompatible.
1552
- return true;
1553
- });
1554
1421
  }
1555
1422
  function createMigrationHost(info, config) {
1556
1423
  return new MigrationHost(/* isMigratingCore */ false, info, config, info.sourceFiles);
@@ -1562,7 +1429,7 @@ function migrate(options) {
1562
1429
  if (!buildPaths.length && !testPaths.length) {
1563
1430
  throw new schematics.SchematicsException('Could not find any tsconfig file. Cannot run signal input migration.');
1564
1431
  }
1565
- const fs = new group_replacements.DevkitMigrationFilesystem(tree);
1432
+ const fs = new combine_units.DevkitMigrationFilesystem(tree);
1566
1433
  checker.setFileSystem(fs);
1567
1434
  const migration = new SignalInputMigration({
1568
1435
  bestEffortMode: options.bestEffortMode,
@@ -1594,12 +1461,17 @@ function migrate(options) {
1594
1461
  context.logger.info(``);
1595
1462
  context.logger.info(`Processing analysis data between targets..`);
1596
1463
  context.logger.info(``);
1597
- 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);
1598
1470
  const replacementsPerFile = new Map();
1599
1471
  for (const { info, tsconfigPath } of programInfos) {
1600
1472
  context.logger.info(`Migrating: ${tsconfigPath}..`);
1601
- const { replacements } = await migration.migrate(merged, info);
1602
- const changesPerFile = group_replacements.groupReplacementsByFile(replacements);
1473
+ const { replacements } = await migration.migrate(globalMeta, info);
1474
+ const changesPerFile = combine_units.groupReplacementsByFile(replacements);
1603
1475
  for (const [file, changes] of changesPerFile) {
1604
1476
  if (!replacementsPerFile.has(file)) {
1605
1477
  replacementsPerFile.set(file, changes);
@@ -1616,7 +1488,7 @@ function migrate(options) {
1616
1488
  }
1617
1489
  tree.commitUpdate(recorder);
1618
1490
  }
1619
- const { counters } = await migration.stats(merged);
1491
+ const { counters } = await migration.stats(globalMeta);
1620
1492
  const migratedInputs = counters.sourceInputs - counters.incompatibleInputs;
1621
1493
  context.logger.info('');
1622
1494
  context.logger.info(`Successfully migrated to signal inputs 🎉`);