@angular/core 19.0.0-next.8 → 19.0.0-next.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/core.mjs +93 -13
- package/fesm2022/core.mjs.map +1 -1
- package/fesm2022/primitives/event-dispatch.mjs +1 -1
- package/fesm2022/primitives/signals.mjs +1 -1
- package/fesm2022/primitives/signals.mjs.map +1 -1
- package/fesm2022/rxjs-interop.mjs +1 -1
- package/fesm2022/testing.mjs +6 -4
- package/fesm2022/testing.mjs.map +1 -1
- package/index.d.ts +41 -18
- package/package.json +1 -1
- package/primitives/event-dispatch/index.d.ts +1 -1
- package/primitives/signals/index.d.ts +5 -1
- package/rxjs-interop/index.d.ts +1 -1
- package/schematics/bundles/{checker-e68dd7ce.js → checker-3b2ea20f.js} +124 -76
- package/schematics/bundles/{compiler_host-9a4d0c2b.js → compiler_host-b4ba5a28.js} +2 -2
- package/schematics/bundles/control-flow-migration.js +3 -3
- package/schematics/bundles/explicit-standalone-flag.js +3 -3
- package/schematics/bundles/{group_replacements-472b2387.js → group_replacements-e1b5cbf8.js} +234 -108
- package/schematics/bundles/imports-4ac08251.js +1 -1
- package/schematics/bundles/inject-migration.js +3 -3
- package/schematics/bundles/leading_space-d190b83b.js +1 -1
- package/schematics/bundles/nodes-0e7d45ca.js +1 -1
- package/schematics/bundles/pending-tasks.js +3 -3
- package/schematics/bundles/{program-105283c5.js → program-6534a30a.js} +67 -21
- package/schematics/bundles/project_tsconfig_paths-e9ccccbf.js +1 -1
- package/schematics/bundles/route-lazy-loading.js +3 -3
- package/schematics/bundles/signal-input-migration.js +147 -171
- package/schematics/bundles/signal-queries-migration.js +93 -74
- package/schematics/bundles/standalone-migration.js +5 -5
- package/testing/index.d.ts +3 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
/**
|
|
3
|
-
* @license Angular v19.0.0-next.
|
|
3
|
+
* @license Angular v19.0.0-next.9
|
|
4
4
|
* (c) 2010-2024 Google LLC. https://angular.io/
|
|
5
5
|
* License: MIT
|
|
6
6
|
*/
|
|
@@ -9,17 +9,17 @@
|
|
|
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-
|
|
12
|
+
var group_replacements = require('./group_replacements-e1b5cbf8.js');
|
|
13
13
|
var ts = require('typescript');
|
|
14
14
|
require('os');
|
|
15
|
-
var checker = require('./checker-
|
|
16
|
-
var program = require('./program-
|
|
15
|
+
var checker = require('./checker-3b2ea20f.js');
|
|
16
|
+
var program = require('./program-6534a30a.js');
|
|
17
17
|
require('path');
|
|
18
18
|
var assert = require('assert');
|
|
19
19
|
var leading_space = require('./leading_space-d190b83b.js');
|
|
20
20
|
var project_tsconfig_paths = require('./project_tsconfig_paths-e9ccccbf.js');
|
|
21
21
|
require('@angular-devkit/core');
|
|
22
|
-
require('node:path');
|
|
22
|
+
require('node:path/posix');
|
|
23
23
|
require('fs');
|
|
24
24
|
require('module');
|
|
25
25
|
require('url');
|
|
@@ -29,100 +29,6 @@ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'defau
|
|
|
29
29
|
var ts__default = /*#__PURE__*/_interopDefaultLegacy(ts);
|
|
30
30
|
var assert__default = /*#__PURE__*/_interopDefaultLegacy(assert);
|
|
31
31
|
|
|
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
32
|
/**
|
|
127
33
|
* Class that holds information about a given directive and its input fields.
|
|
128
34
|
*/
|
|
@@ -145,11 +51,13 @@ class DirectiveInfo {
|
|
|
145
51
|
this.incompatible = null;
|
|
146
52
|
}
|
|
147
53
|
/**
|
|
148
|
-
* Checks whether there are any
|
|
54
|
+
* Checks whether there are any migrated inputs for the
|
|
149
55
|
* given class.
|
|
56
|
+
*
|
|
57
|
+
* Returns `false` if all inputs are incompatible.
|
|
150
58
|
*/
|
|
151
|
-
|
|
152
|
-
return Array.from(this.inputFields.values()).some(({ descriptor }) => this.isInputMemberIncompatible(descriptor));
|
|
59
|
+
hasMigratedFields() {
|
|
60
|
+
return Array.from(this.inputFields.values()).some(({ descriptor }) => !this.isInputMemberIncompatible(descriptor));
|
|
153
61
|
}
|
|
154
62
|
/**
|
|
155
63
|
* Whether the given input member is incompatible. If the class is incompatible,
|
|
@@ -300,8 +208,8 @@ class KnownInputs {
|
|
|
300
208
|
const inputInfo = this.knownInputIds.get(input.key);
|
|
301
209
|
const existingIncompatibility = inputInfo.container.getInputMemberIncompatibility(input);
|
|
302
210
|
// Ensure an existing more significant incompatibility is not overridden.
|
|
303
|
-
if (existingIncompatibility !== null && group_replacements.
|
|
304
|
-
incompatibility = group_replacements.
|
|
211
|
+
if (existingIncompatibility !== null && group_replacements.isFieldIncompatibility(existingIncompatibility)) {
|
|
212
|
+
incompatibility = group_replacements.pickFieldIncompatibility(existingIncompatibility, incompatibility);
|
|
305
213
|
}
|
|
306
214
|
this.knownInputIds
|
|
307
215
|
.get(input.key)
|
|
@@ -329,13 +237,13 @@ class KnownInputs {
|
|
|
329
237
|
captureUnknownDerivedField(field) {
|
|
330
238
|
this.markFieldIncompatible(field, {
|
|
331
239
|
context: null,
|
|
332
|
-
reason: group_replacements.
|
|
240
|
+
reason: group_replacements.FieldIncompatibilityReason.OverriddenByDerivedClass,
|
|
333
241
|
});
|
|
334
242
|
}
|
|
335
243
|
captureUnknownParentField(field) {
|
|
336
244
|
this.markFieldIncompatible(field, {
|
|
337
245
|
context: null,
|
|
338
|
-
reason: group_replacements.
|
|
246
|
+
reason: group_replacements.FieldIncompatibilityReason.TypeConflictWithBaseClass,
|
|
339
247
|
});
|
|
340
248
|
}
|
|
341
249
|
}
|
|
@@ -527,7 +435,7 @@ function prepareAndCheckForConversion(node, metadata, checker, options) {
|
|
|
527
435
|
if (ts__default["default"].isAccessor(node)) {
|
|
528
436
|
return {
|
|
529
437
|
context: node,
|
|
530
|
-
reason: group_replacements.
|
|
438
|
+
reason: group_replacements.FieldIncompatibilityReason.Accessor,
|
|
531
439
|
};
|
|
532
440
|
}
|
|
533
441
|
assert__default["default"](metadata.inputDecorator !== null, 'Expected an input decorator for inputs that are being migrated.');
|
|
@@ -566,7 +474,7 @@ function prepareAndCheckForConversion(node, metadata, checker, options) {
|
|
|
566
474
|
if (typeToAdd === undefined) {
|
|
567
475
|
return {
|
|
568
476
|
context: node,
|
|
569
|
-
reason: group_replacements.
|
|
477
|
+
reason: group_replacements.FieldIncompatibilityReason.SignalInput__QuestionMarkButNoGoodExplicitTypeExtractable,
|
|
570
478
|
};
|
|
571
479
|
}
|
|
572
480
|
if (!checker.isTypeAssignableTo(checker.getUndefinedType(), checker.getTypeFromTypeNode(typeToAdd))) {
|
|
@@ -607,7 +515,7 @@ function prepareAndCheckForConversion(node, metadata, checker, options) {
|
|
|
607
515
|
// the generated type might depend on imports that we cannot add here (nor want).
|
|
608
516
|
return {
|
|
609
517
|
context: node,
|
|
610
|
-
reason: group_replacements.
|
|
518
|
+
reason: group_replacements.FieldIncompatibilityReason.SignalInput__RequiredButNoGoodExplicitTypeExtractable,
|
|
611
519
|
};
|
|
612
520
|
}
|
|
613
521
|
}
|
|
@@ -667,7 +575,7 @@ function pass1__IdentifySourceFileAndDeclarationInputs(sf, host, checker, reflec
|
|
|
667
575
|
// these are then later migrated in the migration phase.
|
|
668
576
|
if (decoratorInput.inSourceFile && host.isSourceFileForCurrentMigration(sf)) {
|
|
669
577
|
const conversionPreparation = prepareAndCheckForConversion(node, decoratorInput, checker, host.compilerOptions);
|
|
670
|
-
if (group_replacements.
|
|
578
|
+
if (group_replacements.isFieldIncompatibility(conversionPreparation)) {
|
|
671
579
|
knownDecoratorInputs.markFieldIncompatible(inputDescr, conversionPreparation);
|
|
672
580
|
result.sourceInputs.set(inputDescr, null);
|
|
673
581
|
}
|
|
@@ -761,14 +669,14 @@ function executeAnalysisPhase(host, knownInputs, result, { sourceFiles, fullProg
|
|
|
761
669
|
for (const reference of result.references) {
|
|
762
670
|
if (group_replacements.isTsReference(reference) && reference.from.isWrite) {
|
|
763
671
|
knownInputs.markFieldIncompatible(reference.target, {
|
|
764
|
-
reason: group_replacements.
|
|
672
|
+
reason: group_replacements.FieldIncompatibilityReason.WriteAssignment,
|
|
765
673
|
context: reference.from.node,
|
|
766
674
|
});
|
|
767
675
|
}
|
|
768
676
|
if (group_replacements.isTemplateReference(reference) || group_replacements.isHostBindingReference(reference)) {
|
|
769
677
|
if (reference.from.isWrite) {
|
|
770
678
|
knownInputs.markFieldIncompatible(reference.target, {
|
|
771
|
-
reason: group_replacements.
|
|
679
|
+
reason: group_replacements.FieldIncompatibilityReason.WriteAssignment,
|
|
772
680
|
// No TS node context available for template or host bindings.
|
|
773
681
|
context: null,
|
|
774
682
|
});
|
|
@@ -779,7 +687,7 @@ function executeAnalysisPhase(host, knownInputs, result, { sourceFiles, fullProg
|
|
|
779
687
|
if (group_replacements.isTemplateReference(reference)) {
|
|
780
688
|
if (reference.from.isLikelyPartOfNarrowing) {
|
|
781
689
|
knownInputs.markFieldIncompatible(reference.target, {
|
|
782
|
-
reason: group_replacements.
|
|
690
|
+
reason: group_replacements.FieldIncompatibilityReason.PotentiallyNarrowedInTemplateButNoSupportYet,
|
|
783
691
|
context: null,
|
|
784
692
|
});
|
|
785
693
|
}
|
|
@@ -921,7 +829,7 @@ function mergeCompilationUnitData(metadataFiles) {
|
|
|
921
829
|
else {
|
|
922
830
|
// Input might not be incompatible in one target, but others might invalidate it.
|
|
923
831
|
// merge the incompatibility state.
|
|
924
|
-
existing.memberIncompatibility = group_replacements.
|
|
832
|
+
existing.memberIncompatibility = group_replacements.pickFieldIncompatibility({ reason: info.memberIncompatibility, context: null }, { reason: existing.memberIncompatibility, context: null }).reason;
|
|
925
833
|
}
|
|
926
834
|
}
|
|
927
835
|
// Merge incompatibility of the class owning the input.
|
|
@@ -953,7 +861,7 @@ function mergeCompilationUnitData(metadataFiles) {
|
|
|
953
861
|
// If parent is incompatible and not migrated, then this input
|
|
954
862
|
// cannot be migrated either. Try propagating parent incompatibility then.
|
|
955
863
|
if (isNodeIncompatible(parent.data)) {
|
|
956
|
-
node.data.info.memberIncompatibility = group_replacements.
|
|
864
|
+
node.data.info.memberIncompatibility = group_replacements.pickFieldIncompatibility({ reason: group_replacements.FieldIncompatibilityReason.ParentIsIncompatible, context: null }, existingMemberIncompatibility).reason;
|
|
957
865
|
break;
|
|
958
866
|
}
|
|
959
867
|
}
|
|
@@ -965,7 +873,7 @@ function mergeCompilationUnitData(metadataFiles) {
|
|
|
965
873
|
const existingMemberIncompatibility = info.memberIncompatibility !== null
|
|
966
874
|
? { reason: info.memberIncompatibility, context: null }
|
|
967
875
|
: null;
|
|
968
|
-
info.memberIncompatibility = group_replacements.
|
|
876
|
+
info.memberIncompatibility = group_replacements.pickFieldIncompatibility({ reason: group_replacements.FieldIncompatibilityReason.OutsideOfMigrationScope, context: null }, existingMemberIncompatibility).reason;
|
|
969
877
|
}
|
|
970
878
|
}
|
|
971
879
|
return result;
|
|
@@ -1154,6 +1062,102 @@ function extractTransformOfInput(transform, resolvedType, checker) {
|
|
|
1154
1062
|
};
|
|
1155
1063
|
}
|
|
1156
1064
|
|
|
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
|
+
|
|
1157
1161
|
/**
|
|
1158
1162
|
* Inserts a TODO for the incompatibility blocking the given node
|
|
1159
1163
|
* from being migrated.
|
|
@@ -1165,14 +1169,15 @@ function insertTodoForIncompatibility(node, programInfo, input) {
|
|
|
1165
1169
|
}
|
|
1166
1170
|
// If an input is skipped via config filter or outside migration scope, do not
|
|
1167
1171
|
// insert TODOs, as this could results in lots of unnecessary comments.
|
|
1168
|
-
if (group_replacements.
|
|
1169
|
-
(incompatibility.reason === group_replacements.
|
|
1170
|
-
incompatibility.reason === group_replacements.
|
|
1172
|
+
if (group_replacements.isFieldIncompatibility(incompatibility) &&
|
|
1173
|
+
(incompatibility.reason === group_replacements.FieldIncompatibilityReason.SkippedViaConfigFilter ||
|
|
1174
|
+
incompatibility.reason === group_replacements.FieldIncompatibilityReason.OutsideOfMigrationScope)) {
|
|
1171
1175
|
return [];
|
|
1172
1176
|
}
|
|
1173
|
-
const message = group_replacements.
|
|
1174
|
-
?
|
|
1175
|
-
|
|
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;
|
|
1176
1181
|
const lines = cutStringToLineLimit(message, 70);
|
|
1177
1182
|
return [
|
|
1178
1183
|
insertPrecedingLine(node, programInfo, `// TODO: Skipped for migration because:`),
|
|
@@ -1306,43 +1311,7 @@ function pass8__migrateHostBindings(host, references, info) {
|
|
|
1306
1311
|
in Catalyst test files.
|
|
1307
1312
|
*/
|
|
1308
1313
|
function pass9__migrateTypeScriptTypeReferences(host, references, importManager, info) {
|
|
1309
|
-
|
|
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
|
-
}
|
|
1314
|
+
group_replacements.migrateTypeScriptTypeReferences(host, references, importManager, info);
|
|
1346
1315
|
}
|
|
1347
1316
|
|
|
1348
1317
|
/**
|
|
@@ -1366,7 +1335,7 @@ function executeMigrationPhase(host, knownInputs, result, info) {
|
|
|
1366
1335
|
replacements: result.replacements,
|
|
1367
1336
|
shouldMigrateReferencesToField: (inputDescr) => knownInputs.has(inputDescr) && knownInputs.get(inputDescr).isIncompatible() === false,
|
|
1368
1337
|
shouldMigrateReferencesToClass: (clazz) => knownInputs.getDirectiveInfoForClass(clazz) !== undefined &&
|
|
1369
|
-
knownInputs.getDirectiveInfoForClass(clazz).
|
|
1338
|
+
knownInputs.getDirectiveInfoForClass(clazz).hasMigratedFields(),
|
|
1370
1339
|
};
|
|
1371
1340
|
// Migrate passes.
|
|
1372
1341
|
pass5__migrateTypeScriptReferences(referenceMigrationHost, result.references, typeChecker, info);
|
|
@@ -1380,13 +1349,14 @@ function executeMigrationPhase(host, knownInputs, result, info) {
|
|
|
1380
1349
|
/** Input reasons that cannot be ignored. */
|
|
1381
1350
|
const nonIgnorableInputIncompatibilities = [
|
|
1382
1351
|
// Outside of scope inputs should not be migrated. E.g. references to inputs in `node_modules/`.
|
|
1383
|
-
group_replacements.
|
|
1352
|
+
group_replacements.FieldIncompatibilityReason.OutsideOfMigrationScope,
|
|
1384
1353
|
// Explicitly filtered inputs cannot be skipped via best effort mode.
|
|
1385
|
-
group_replacements.
|
|
1354
|
+
group_replacements.FieldIncompatibilityReason.SkippedViaConfigFilter,
|
|
1386
1355
|
// There is no good output for accessor inputs.
|
|
1387
|
-
group_replacements.
|
|
1356
|
+
group_replacements.FieldIncompatibilityReason.Accessor,
|
|
1388
1357
|
// There is no good output for such inputs. We can't perform "conversion".
|
|
1389
|
-
group_replacements.
|
|
1358
|
+
group_replacements.FieldIncompatibilityReason.SignalInput__RequiredButNoGoodExplicitTypeExtractable,
|
|
1359
|
+
group_replacements.FieldIncompatibilityReason.SignalInput__QuestionMarkButNoGoodExplicitTypeExtractable,
|
|
1390
1360
|
];
|
|
1391
1361
|
/** Filters ignorable input incompatibilities when best effort mode is enabled. */
|
|
1392
1362
|
function filterIncompatibilitiesForBestEffortMode(knownInputs) {
|
|
@@ -1472,7 +1442,7 @@ class SignalInputMigration extends group_replacements.TsurgeComplexMigration {
|
|
|
1472
1442
|
// Non-batch mode!
|
|
1473
1443
|
if (this.config.upgradeAnalysisPhaseToAvoidBatch) {
|
|
1474
1444
|
const merged = await this.merge([unitData]);
|
|
1475
|
-
const replacements = await this.migrate(merged, info, {
|
|
1445
|
+
const { replacements } = await this.migrate(merged, info, {
|
|
1476
1446
|
knownInputs,
|
|
1477
1447
|
result,
|
|
1478
1448
|
host,
|
|
@@ -1507,7 +1477,7 @@ class SignalInputMigration extends group_replacements.TsurgeComplexMigration {
|
|
|
1507
1477
|
}
|
|
1508
1478
|
this.config.reportProgressFn?.(60, 'Collecting migration changes..');
|
|
1509
1479
|
executeMigrationPhase(host, knownInputs, result, analysisDeps);
|
|
1510
|
-
return result.replacements;
|
|
1480
|
+
return { replacements: result.replacements };
|
|
1511
1481
|
}
|
|
1512
1482
|
async stats(globalMetadata) {
|
|
1513
1483
|
let fullCompilationInputs = 0;
|
|
@@ -1517,14 +1487,20 @@ class SignalInputMigration extends group_replacements.TsurgeComplexMigration {
|
|
|
1517
1487
|
const classIncompatibleCounts = {};
|
|
1518
1488
|
for (const [id, input] of Object.entries(globalMetadata.knownInputs)) {
|
|
1519
1489
|
fullCompilationInputs++;
|
|
1520
|
-
|
|
1521
|
-
|
|
1490
|
+
const isConsideredSourceInput = input.seenAsSourceInput &&
|
|
1491
|
+
input.memberIncompatibility !== group_replacements.FieldIncompatibilityReason.OutsideOfMigrationScope &&
|
|
1492
|
+
input.memberIncompatibility !== group_replacements.FieldIncompatibilityReason.SkippedViaConfigFilter;
|
|
1493
|
+
// We won't track incompatibilities to inputs that aren't considered source inputs.
|
|
1494
|
+
// Tracking their statistics wouldn't provide any value.
|
|
1495
|
+
if (!isConsideredSourceInput) {
|
|
1496
|
+
continue;
|
|
1522
1497
|
}
|
|
1498
|
+
sourceInputs++;
|
|
1523
1499
|
if (input.memberIncompatibility !== null || input.owningClassIncompatibility !== null) {
|
|
1524
1500
|
incompatibleInputs++;
|
|
1525
1501
|
}
|
|
1526
1502
|
if (input.memberIncompatibility !== null) {
|
|
1527
|
-
const reasonName = group_replacements.
|
|
1503
|
+
const reasonName = group_replacements.FieldIncompatibilityReason[input.memberIncompatibility];
|
|
1528
1504
|
const key = `input-field-incompatibility-${reasonName}`;
|
|
1529
1505
|
fieldIncompatibleCounts[key] ??= 0;
|
|
1530
1506
|
fieldIncompatibleCounts[key]++;
|
|
@@ -1562,7 +1538,7 @@ function filterInputsViaConfig(result, knownInputs, config) {
|
|
|
1562
1538
|
skippedInputs.add(input.descriptor.key);
|
|
1563
1539
|
knownInputs.markFieldIncompatible(input.descriptor, {
|
|
1564
1540
|
context: null,
|
|
1565
|
-
reason: group_replacements.
|
|
1541
|
+
reason: group_replacements.FieldIncompatibilityReason.SkippedViaConfigFilter,
|
|
1566
1542
|
});
|
|
1567
1543
|
}
|
|
1568
1544
|
}
|
|
@@ -1622,7 +1598,7 @@ function migrate(options) {
|
|
|
1622
1598
|
const replacementsPerFile = new Map();
|
|
1623
1599
|
for (const { info, tsconfigPath } of programInfos) {
|
|
1624
1600
|
context.logger.info(`Migrating: ${tsconfigPath}..`);
|
|
1625
|
-
const replacements = await migration.migrate(merged, info);
|
|
1601
|
+
const { replacements } = await migration.migrate(merged, info);
|
|
1626
1602
|
const changesPerFile = group_replacements.groupReplacementsByFile(replacements);
|
|
1627
1603
|
for (const [file, changes] of changesPerFile) {
|
|
1628
1604
|
if (!replacementsPerFile.has(file)) {
|