@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.
- package/fesm2022/core.mjs +21591 -19590
- package/fesm2022/core.mjs.map +1 -1
- package/fesm2022/primitives/event-dispatch.mjs +71 -47
- package/fesm2022/primitives/event-dispatch.mjs.map +1 -1
- package/fesm2022/primitives/signals.mjs +8 -6
- package/fesm2022/primitives/signals.mjs.map +1 -1
- package/fesm2022/rxjs-interop.mjs +90 -10
- package/fesm2022/rxjs-interop.mjs.map +1 -1
- package/fesm2022/testing.mjs +177 -114
- package/fesm2022/testing.mjs.map +1 -1
- package/index.d.ts +591 -101
- package/package.json +1 -1
- package/primitives/event-dispatch/index.d.ts +7 -4
- package/primitives/signals/index.d.ts +7 -1
- package/rxjs-interop/index.d.ts +35 -4
- package/schematics/bundles/{checker-e68dd7ce.js → checker-2451e7c5.js} +2464 -1132
- package/schematics/bundles/{group_replacements-472b2387.js → combine_units-c52492ab.js} +1964 -2207
- package/schematics/bundles/{compiler_host-9a4d0c2b.js → compiler_host-f54f8309.js} +2 -2
- package/schematics/bundles/control-flow-migration.js +3 -3
- package/schematics/bundles/explicit-standalone-flag.js +31 -11
- package/schematics/bundles/{imports-4ac08251.js → imports-44987700.js} +1 -1
- package/schematics/bundles/inject-migration.js +122 -48
- package/schematics/bundles/{leading_space-d190b83b.js → leading_space-6e7a8ec6.js} +1 -1
- package/schematics/bundles/migrate_ts_type_references-ab18a7c3.js +1463 -0
- package/schematics/bundles/{nodes-0e7d45ca.js → ng_decorators-3ad437d2.js} +2 -15
- package/schematics/bundles/nodes-ffdce442.js +27 -0
- package/schematics/bundles/output-migration.js +7450 -0
- package/schematics/bundles/pending-tasks.js +5 -5
- package/schematics/bundles/{program-105283c5.js → program-58424797.js} +1359 -455
- package/schematics/bundles/{project_tsconfig_paths-e9ccccbf.js → project_tsconfig_paths-6c9cde78.js} +1 -1
- package/schematics/bundles/provide-initializer.js +190 -0
- package/schematics/bundles/route-lazy-loading.js +4 -4
- package/schematics/bundles/signal-input-migration.js +197 -349
- package/schematics/bundles/signal-queries-migration.js +462 -185
- package/schematics/bundles/signals.js +54 -0
- package/schematics/bundles/standalone-migration.js +38 -20
- package/schematics/collection.json +11 -0
- package/schematics/migrations.json +7 -1
- package/schematics/ng-generate/output-migration/schema.json +19 -0
- package/schematics/ng-generate/signal-queries-migration/schema.json +11 -0
- package/schematics/ng-generate/signals/schema.json +65 -0
- package/testing/index.d.ts +3 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
/**
|
|
3
|
-
* @license Angular v19.0.0-
|
|
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
|
|
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-
|
|
16
|
-
var program = require('./program-
|
|
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
|
|
20
|
-
|
|
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
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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:
|
|
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 &&
|
|
304
|
-
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:
|
|
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:
|
|
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
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
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 (!
|
|
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
|
-
|
|
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 (!
|
|
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:
|
|
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
|
|
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
|
-
!
|
|
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:
|
|
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
|
-
|
|
585
|
-
|
|
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:
|
|
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"](
|
|
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 (
|
|
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
|
-
|
|
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(
|
|
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
|
|
753
|
-
const pass2And3SourceFileVisitor = new
|
|
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 (
|
|
700
|
+
if (combine_units.isTsReference(reference) && reference.from.isWrite) {
|
|
763
701
|
knownInputs.markFieldIncompatible(reference.target, {
|
|
764
|
-
reason:
|
|
702
|
+
reason: migrate_ts_type_references.FieldIncompatibilityReason.WriteAssignment,
|
|
765
703
|
context: reference.from.node,
|
|
766
704
|
});
|
|
767
705
|
}
|
|
768
|
-
if (
|
|
706
|
+
if (combine_units.isTemplateReference(reference) || combine_units.isHostBindingReference(reference)) {
|
|
769
707
|
if (reference.from.isWrite) {
|
|
770
708
|
knownInputs.markFieldIncompatible(reference.target, {
|
|
771
|
-
reason:
|
|
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 (
|
|
717
|
+
if (combine_units.isTemplateReference(reference)) {
|
|
780
718
|
if (reference.from.isLikelyPartOfNarrowing) {
|
|
781
719
|
knownInputs.markFieldIncompatible(reference.target, {
|
|
782
|
-
reason:
|
|
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
|
-
|
|
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
|
|
826
|
+
function combineCompilationUnitData(unitA, unitB) {
|
|
889
827
|
const result = {
|
|
890
828
|
knownInputs: {},
|
|
891
829
|
};
|
|
892
|
-
const
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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(
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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 (!
|
|
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
|
|
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 (!
|
|
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
|
|
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
|
-
|
|
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).
|
|
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 (!
|
|
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
|
|
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
|
|
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
|
|
1475
|
-
const replacements = await this.migrate(
|
|
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
|
|
1332
|
+
return combine_units.confirmAsSerializable(unitData);
|
|
1333
|
+
}
|
|
1334
|
+
async combine(unitA, unitB) {
|
|
1335
|
+
return combine_units.confirmAsSerializable(combineCompilationUnitData(unitA, unitB));
|
|
1490
1336
|
}
|
|
1491
|
-
async
|
|
1492
|
-
return
|
|
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
|
-
|
|
1521
|
-
|
|
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 =
|
|
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 =
|
|
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:
|
|
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
|
|
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
|
|
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(
|
|
1626
|
-
const changesPerFile =
|
|
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(
|
|
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 🎉`);
|