@angular/core 19.1.0-next.3 → 19.1.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/LICENSE +1 -1
- package/fesm2022/core.mjs +516 -266
- package/fesm2022/core.mjs.map +1 -1
- package/fesm2022/primitives/event-dispatch.mjs +1 -24
- package/fesm2022/primitives/event-dispatch.mjs.map +1 -1
- package/fesm2022/primitives/signals.mjs +1 -1
- package/fesm2022/rxjs-interop.mjs +1 -1
- package/fesm2022/testing.mjs +5 -5
- package/index.d.ts +169 -17
- package/package.json +1 -1
- package/primitives/event-dispatch/index.d.ts +1 -1
- package/primitives/signals/index.d.ts +1 -1
- package/rxjs-interop/index.d.ts +1 -1
- package/schematics/bundles/apply_import_manager-6508401d.js +732 -0
- package/schematics/bundles/{checker-228cb8a8.js → checker-24b68d23.js} +75 -27
- package/schematics/bundles/cleanup-unused-imports.js +295 -0
- package/schematics/bundles/{compiler_host-fc806dbe.js → compiler_host-5f693799.js} +2 -2
- package/schematics/bundles/control-flow-migration.js +3 -3
- package/schematics/bundles/explicit-standalone-flag.js +3 -3
- package/schematics/bundles/imports-abe29092.js +1 -1
- package/schematics/bundles/{combine_units-5d6a7099.js → index-767e341d.js} +10 -723
- package/schematics/bundles/index-b1033cf0.js +30 -0
- package/schematics/bundles/inject-migration.js +3 -3
- package/schematics/bundles/leading_space-d190b83b.js +1 -1
- package/schematics/bundles/{migrate_ts_type_references-d02c6750.js → migrate_ts_type_references-bc7d8784.js} +29 -28
- package/schematics/bundles/nodes-a9f0b985.js +1 -1
- package/schematics/bundles/output-migration.js +26 -25
- package/schematics/bundles/pending-tasks.js +3 -3
- package/schematics/bundles/{program-1591ec8f.js → program-c810a4c2.js} +81 -40
- package/schematics/bundles/project_tsconfig_paths-e9ccccbf.js +1 -1
- package/schematics/bundles/provide-initializer.js +3 -3
- package/schematics/bundles/route-lazy-loading.js +3 -3
- package/schematics/bundles/signal-input-migration.js +31 -30
- package/schematics/bundles/signal-queries-migration.js +48 -47
- package/schematics/bundles/signals.js +6 -5
- package/schematics/bundles/standalone-migration.js +9 -25
- package/schematics/collection.json +5 -0
- package/schematics/ng-generate/cleanup-unused-imports/schema.json +7 -0
- package/testing/index.d.ts +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
/**
|
|
3
|
-
* @license Angular v19.1.0-
|
|
3
|
+
* @license Angular v19.1.0-rc.0
|
|
4
4
|
* (c) 2010-2024 Google LLC. https://angular.io/
|
|
5
5
|
* License: MIT
|
|
6
6
|
*/
|
|
@@ -26642,6 +26642,14 @@ class BindingParser {
|
|
|
26642
26642
|
if (ast instanceof NonNullAssert) {
|
|
26643
26643
|
return this._isAllowedAssignmentEvent(ast.expression);
|
|
26644
26644
|
}
|
|
26645
|
+
if (ast instanceof Call &&
|
|
26646
|
+
ast.args.length === 1 &&
|
|
26647
|
+
ast.receiver instanceof PropertyRead &&
|
|
26648
|
+
ast.receiver.name === '$any' &&
|
|
26649
|
+
ast.receiver.receiver instanceof ImplicitReceiver &&
|
|
26650
|
+
!(ast.receiver.receiver instanceof ThisReceiver)) {
|
|
26651
|
+
return this._isAllowedAssignmentEvent(ast.args[0]);
|
|
26652
|
+
}
|
|
26645
26653
|
if (ast instanceof PropertyRead || ast instanceof KeyedRead) {
|
|
26646
26654
|
return true;
|
|
26647
26655
|
}
|
|
@@ -26968,7 +26976,7 @@ function parseForLoopParameters(block, errors, bindingParser) {
|
|
|
26968
26976
|
}
|
|
26969
26977
|
continue;
|
|
26970
26978
|
}
|
|
26971
|
-
errors.push(new ParseError(param.sourceSpan, `Unrecognized @for loop
|
|
26979
|
+
errors.push(new ParseError(param.sourceSpan, `Unrecognized @for loop parameter "${param.expression}"`));
|
|
26972
26980
|
}
|
|
26973
26981
|
return result;
|
|
26974
26982
|
}
|
|
@@ -27115,7 +27123,7 @@ function parseConditionalBlockParameters(block, errors, bindingParser) {
|
|
|
27115
27123
|
// For now conditionals can only have an `as` parameter.
|
|
27116
27124
|
// We may want to rework this later if we add more.
|
|
27117
27125
|
if (aliasMatch === null) {
|
|
27118
|
-
errors.push(new ParseError(param.sourceSpan, `Unrecognized conditional
|
|
27126
|
+
errors.push(new ParseError(param.sourceSpan, `Unrecognized conditional parameter "${param.expression}"`));
|
|
27119
27127
|
}
|
|
27120
27128
|
else if (block.name !== 'if') {
|
|
27121
27129
|
errors.push(new ParseError(param.sourceSpan, '"as" expression is only allowed on the primary @if block'));
|
|
@@ -30410,7 +30418,7 @@ function publishFacade(global) {
|
|
|
30410
30418
|
* @description
|
|
30411
30419
|
* Entry point for all public APIs of the compiler package.
|
|
30412
30420
|
*/
|
|
30413
|
-
new Version('19.1.0-
|
|
30421
|
+
new Version('19.1.0-rc.0');
|
|
30414
30422
|
|
|
30415
30423
|
const _I18N_ATTR = 'i18n';
|
|
30416
30424
|
const _I18N_ATTR_PREFIX = 'i18n-';
|
|
@@ -31818,7 +31826,7 @@ class NodeJSPathManipulation {
|
|
|
31818
31826
|
// G3-ESM-MARKER: G3 uses CommonJS, but externally everything in ESM.
|
|
31819
31827
|
// CommonJS/ESM interop for determining the current file name and containing dir.
|
|
31820
31828
|
const isCommonJS = typeof __filename !== 'undefined';
|
|
31821
|
-
const currentFileUrl = isCommonJS ? null : (typeof document === 'undefined' ? new (require('u' + 'rl').URL)('file:' + __filename).href : (document.currentScript && document.currentScript.tagName.toUpperCase() === 'SCRIPT' && document.currentScript.src || new URL('checker-
|
|
31829
|
+
const currentFileUrl = isCommonJS ? null : (typeof document === 'undefined' ? new (require('u' + 'rl').URL)('file:' + __filename).href : (document.currentScript && document.currentScript.tagName.toUpperCase() === 'SCRIPT' && document.currentScript.src || new URL('checker-24b68d23.js', document.baseURI).href));
|
|
31822
31830
|
const currentFileName = isCommonJS ? __filename : url.fileURLToPath(currentFileUrl);
|
|
31823
31831
|
/**
|
|
31824
31832
|
* A wrapper around the Node.js file-system that supports readonly operations and path manipulation.
|
|
@@ -32917,9 +32925,16 @@ function classMemberAccessLevelToString(level) {
|
|
|
32917
32925
|
class TypeScriptReflectionHost {
|
|
32918
32926
|
checker;
|
|
32919
32927
|
isLocalCompilation;
|
|
32920
|
-
|
|
32928
|
+
skipPrivateValueDeclarationTypes;
|
|
32929
|
+
/**
|
|
32930
|
+
* @param skipPrivateValueDeclarationTypes Avoids using a value declaration that is considered private (using a ɵ-prefix),
|
|
32931
|
+
* instead using the first available declaration. This is needed for the {@link FormControl} API of
|
|
32932
|
+
* which the type declaration documents the type and the value declaration corresponds with an implementation detail.
|
|
32933
|
+
*/
|
|
32934
|
+
constructor(checker, isLocalCompilation = false, skipPrivateValueDeclarationTypes = false) {
|
|
32921
32935
|
this.checker = checker;
|
|
32922
32936
|
this.isLocalCompilation = isLocalCompilation;
|
|
32937
|
+
this.skipPrivateValueDeclarationTypes = skipPrivateValueDeclarationTypes;
|
|
32923
32938
|
}
|
|
32924
32939
|
getDecoratorsOfDeclaration(declaration) {
|
|
32925
32940
|
const decorators = ts__default["default"].canHaveDecorators(declaration)
|
|
@@ -33218,9 +33233,10 @@ class TypeScriptReflectionHost {
|
|
|
33218
33233
|
while (symbol.flags & ts__default["default"].SymbolFlags.Alias) {
|
|
33219
33234
|
symbol = this.checker.getAliasedSymbol(symbol);
|
|
33220
33235
|
}
|
|
33221
|
-
// Look at the resolved Symbol's declarations and pick one of them to return.
|
|
33222
|
-
// are given precedence over type declarations
|
|
33223
|
-
if (symbol.valueDeclaration !== undefined
|
|
33236
|
+
// Look at the resolved Symbol's declarations and pick one of them to return.
|
|
33237
|
+
// Value declarations are given precedence over type declarations if not specified otherwise
|
|
33238
|
+
if (symbol.valueDeclaration !== undefined &&
|
|
33239
|
+
(!this.skipPrivateValueDeclarationTypes || !isPrivateSymbol(this.checker, symbol))) {
|
|
33224
33240
|
return {
|
|
33225
33241
|
node: symbol.valueDeclaration,
|
|
33226
33242
|
viaModule: this._viaModule(symbol.valueDeclaration, originalId, importInfo),
|
|
@@ -33315,6 +33331,13 @@ class TypeScriptReflectionHost {
|
|
|
33315
33331
|
}
|
|
33316
33332
|
}
|
|
33317
33333
|
class TypeEntityToDeclarationError extends Error {
|
|
33334
|
+
constructor(message) {
|
|
33335
|
+
super(message);
|
|
33336
|
+
// Extending `Error` ends up breaking some internal tests. This appears to be a known issue
|
|
33337
|
+
// when extending errors in TS and the workaround is to explicitly set the prototype.
|
|
33338
|
+
// https://stackoverflow.com/questions/41102060/typescript-extending-error-class
|
|
33339
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
33340
|
+
}
|
|
33318
33341
|
}
|
|
33319
33342
|
/**
|
|
33320
33343
|
* @throws {TypeEntityToDeclarationError} if the type cannot be converted
|
|
@@ -33524,6 +33547,14 @@ function propertyNameToString(node) {
|
|
|
33524
33547
|
return null;
|
|
33525
33548
|
}
|
|
33526
33549
|
}
|
|
33550
|
+
/** Determines whether a given symbol represents a private API (symbols with names that start with `ɵ`) */
|
|
33551
|
+
function isPrivateSymbol(typeChecker, symbol) {
|
|
33552
|
+
if (symbol.valueDeclaration !== undefined) {
|
|
33553
|
+
const symbolType = typeChecker.getTypeOfSymbolAtLocation(symbol, symbol.valueDeclaration);
|
|
33554
|
+
return symbolType?.symbol?.name.startsWith('ɵ') === true;
|
|
33555
|
+
}
|
|
33556
|
+
return false;
|
|
33557
|
+
}
|
|
33527
33558
|
/**
|
|
33528
33559
|
* Compute the left most identifier in a qualified type chain. E.g. the `a` of `a.b.c.SomeType`.
|
|
33529
33560
|
* @param qualifiedName The starting property access expression from which we want to compute
|
|
@@ -38512,7 +38543,7 @@ class Chunk {
|
|
|
38512
38543
|
// ' test'.trim()
|
|
38513
38544
|
// split -> ' ' + 'test'
|
|
38514
38545
|
// ✔️ edit -> '' + 'test'
|
|
38515
|
-
// ✖️ edit -> 'test' + ''
|
|
38546
|
+
// ✖️ edit -> 'test' + ''
|
|
38516
38547
|
// TODO is this block necessary?...
|
|
38517
38548
|
newChunk.edit('', false);
|
|
38518
38549
|
this.content = '';
|
|
@@ -38759,6 +38790,7 @@ class Mappings {
|
|
|
38759
38790
|
this.raw[this.generatedCodeLine] = this.rawSegments = [];
|
|
38760
38791
|
this.generatedCodeColumn = 0;
|
|
38761
38792
|
first = true;
|
|
38793
|
+
charInHiresBoundary = false;
|
|
38762
38794
|
} else {
|
|
38763
38795
|
if (this.hires || first || sourcemapLocations.has(originalCharIndex)) {
|
|
38764
38796
|
const segment = [this.generatedCodeColumn, sourceIndex, loc.line, loc.column];
|
|
@@ -38836,6 +38868,7 @@ class MagicString {
|
|
|
38836
38868
|
storedNames: { writable: true, value: {} },
|
|
38837
38869
|
indentStr: { writable: true, value: undefined },
|
|
38838
38870
|
ignoreList: { writable: true, value: options.ignoreList },
|
|
38871
|
+
offset: { writable: true, value: options.offset || 0 },
|
|
38839
38872
|
});
|
|
38840
38873
|
|
|
38841
38874
|
this.byStart[0] = chunk;
|
|
@@ -38854,6 +38887,8 @@ class MagicString {
|
|
|
38854
38887
|
}
|
|
38855
38888
|
|
|
38856
38889
|
appendLeft(index, content) {
|
|
38890
|
+
index = index + this.offset;
|
|
38891
|
+
|
|
38857
38892
|
if (typeof content !== 'string') throw new TypeError('inserted content must be a string');
|
|
38858
38893
|
|
|
38859
38894
|
this._split(index);
|
|
@@ -38869,6 +38904,8 @@ class MagicString {
|
|
|
38869
38904
|
}
|
|
38870
38905
|
|
|
38871
38906
|
appendRight(index, content) {
|
|
38907
|
+
index = index + this.offset;
|
|
38908
|
+
|
|
38872
38909
|
if (typeof content !== 'string') throw new TypeError('inserted content must be a string');
|
|
38873
38910
|
|
|
38874
38911
|
this._split(index);
|
|
@@ -38884,7 +38921,7 @@ class MagicString {
|
|
|
38884
38921
|
}
|
|
38885
38922
|
|
|
38886
38923
|
clone() {
|
|
38887
|
-
const cloned = new MagicString(this.original, { filename: this.filename });
|
|
38924
|
+
const cloned = new MagicString(this.original, { filename: this.filename, offset: this.offset });
|
|
38888
38925
|
|
|
38889
38926
|
let originalChunk = this.firstChunk;
|
|
38890
38927
|
let clonedChunk = (cloned.firstChunk = cloned.lastSearchedChunk = originalChunk.clone());
|
|
@@ -39082,7 +39119,7 @@ class MagicString {
|
|
|
39082
39119
|
if (!warned.insertLeft) {
|
|
39083
39120
|
console.warn(
|
|
39084
39121
|
'magicString.insertLeft(...) is deprecated. Use magicString.appendLeft(...) instead',
|
|
39085
|
-
);
|
|
39122
|
+
);
|
|
39086
39123
|
warned.insertLeft = true;
|
|
39087
39124
|
}
|
|
39088
39125
|
|
|
@@ -39093,7 +39130,7 @@ class MagicString {
|
|
|
39093
39130
|
if (!warned.insertRight) {
|
|
39094
39131
|
console.warn(
|
|
39095
39132
|
'magicString.insertRight(...) is deprecated. Use magicString.prependRight(...) instead',
|
|
39096
|
-
);
|
|
39133
|
+
);
|
|
39097
39134
|
warned.insertRight = true;
|
|
39098
39135
|
}
|
|
39099
39136
|
|
|
@@ -39101,6 +39138,10 @@ class MagicString {
|
|
|
39101
39138
|
}
|
|
39102
39139
|
|
|
39103
39140
|
move(start, end, index) {
|
|
39141
|
+
start = start + this.offset;
|
|
39142
|
+
end = end + this.offset;
|
|
39143
|
+
index = index + this.offset;
|
|
39144
|
+
|
|
39104
39145
|
if (index >= start && index <= end) throw new Error('Cannot move a selection inside itself');
|
|
39105
39146
|
|
|
39106
39147
|
this._split(start);
|
|
@@ -39143,6 +39184,9 @@ class MagicString {
|
|
|
39143
39184
|
}
|
|
39144
39185
|
|
|
39145
39186
|
update(start, end, content, options) {
|
|
39187
|
+
start = start + this.offset;
|
|
39188
|
+
end = end + this.offset;
|
|
39189
|
+
|
|
39146
39190
|
if (typeof content !== 'string') throw new TypeError('replacement content must be a string');
|
|
39147
39191
|
|
|
39148
39192
|
if (this.original.length !== 0) {
|
|
@@ -39163,7 +39207,7 @@ class MagicString {
|
|
|
39163
39207
|
if (!warned.storeName) {
|
|
39164
39208
|
console.warn(
|
|
39165
39209
|
'The final argument to magicString.overwrite(...) should be an options object. See https://github.com/rich-harris/magic-string',
|
|
39166
|
-
);
|
|
39210
|
+
);
|
|
39167
39211
|
warned.storeName = true;
|
|
39168
39212
|
}
|
|
39169
39213
|
|
|
@@ -39214,6 +39258,8 @@ class MagicString {
|
|
|
39214
39258
|
}
|
|
39215
39259
|
|
|
39216
39260
|
prependLeft(index, content) {
|
|
39261
|
+
index = index + this.offset;
|
|
39262
|
+
|
|
39217
39263
|
if (typeof content !== 'string') throw new TypeError('inserted content must be a string');
|
|
39218
39264
|
|
|
39219
39265
|
this._split(index);
|
|
@@ -39229,6 +39275,8 @@ class MagicString {
|
|
|
39229
39275
|
}
|
|
39230
39276
|
|
|
39231
39277
|
prependRight(index, content) {
|
|
39278
|
+
index = index + this.offset;
|
|
39279
|
+
|
|
39232
39280
|
if (typeof content !== 'string') throw new TypeError('inserted content must be a string');
|
|
39233
39281
|
|
|
39234
39282
|
this._split(index);
|
|
@@ -39244,6 +39292,9 @@ class MagicString {
|
|
|
39244
39292
|
}
|
|
39245
39293
|
|
|
39246
39294
|
remove(start, end) {
|
|
39295
|
+
start = start + this.offset;
|
|
39296
|
+
end = end + this.offset;
|
|
39297
|
+
|
|
39247
39298
|
if (this.original.length !== 0) {
|
|
39248
39299
|
while (start < 0) start += this.original.length;
|
|
39249
39300
|
while (end < 0) end += this.original.length;
|
|
@@ -39270,6 +39321,9 @@ class MagicString {
|
|
|
39270
39321
|
}
|
|
39271
39322
|
|
|
39272
39323
|
reset(start, end) {
|
|
39324
|
+
start = start + this.offset;
|
|
39325
|
+
end = end + this.offset;
|
|
39326
|
+
|
|
39273
39327
|
if (this.original.length !== 0) {
|
|
39274
39328
|
while (start < 0) start += this.original.length;
|
|
39275
39329
|
while (end < 0) end += this.original.length;
|
|
@@ -39334,7 +39388,10 @@ class MagicString {
|
|
|
39334
39388
|
return this.intro + lineStr;
|
|
39335
39389
|
}
|
|
39336
39390
|
|
|
39337
|
-
slice(start = 0, end = this.original.length) {
|
|
39391
|
+
slice(start = 0, end = this.original.length - this.offset) {
|
|
39392
|
+
start = start + this.offset;
|
|
39393
|
+
end = end + this.offset;
|
|
39394
|
+
|
|
39338
39395
|
if (this.original.length !== 0) {
|
|
39339
39396
|
while (start < 0) start += this.original.length;
|
|
39340
39397
|
while (end < 0) end += this.original.length;
|
|
@@ -39570,11 +39627,7 @@ class MagicString {
|
|
|
39570
39627
|
if (match.index != null) {
|
|
39571
39628
|
const replacement = getReplacement(match, this.original);
|
|
39572
39629
|
if (replacement !== match[0]) {
|
|
39573
|
-
this.overwrite(
|
|
39574
|
-
match.index,
|
|
39575
|
-
match.index + match[0].length,
|
|
39576
|
-
replacement
|
|
39577
|
-
);
|
|
39630
|
+
this.overwrite(match.index, match.index + match[0].length, replacement);
|
|
39578
39631
|
}
|
|
39579
39632
|
}
|
|
39580
39633
|
});
|
|
@@ -39583,11 +39636,7 @@ class MagicString {
|
|
|
39583
39636
|
if (match && match.index != null) {
|
|
39584
39637
|
const replacement = getReplacement(match, this.original);
|
|
39585
39638
|
if (replacement !== match[0]) {
|
|
39586
|
-
this.overwrite(
|
|
39587
|
-
match.index,
|
|
39588
|
-
match.index + match[0].length,
|
|
39589
|
-
replacement
|
|
39590
|
-
);
|
|
39639
|
+
this.overwrite(match.index, match.index + match[0].length, replacement);
|
|
39591
39640
|
}
|
|
39592
39641
|
}
|
|
39593
39642
|
}
|
|
@@ -39622,8 +39671,7 @@ class MagicString {
|
|
|
39622
39671
|
index = original.indexOf(string, index + stringLength)
|
|
39623
39672
|
) {
|
|
39624
39673
|
const previous = original.slice(index, index + stringLength);
|
|
39625
|
-
if (previous !== replacement)
|
|
39626
|
-
this.overwrite(index, index + stringLength, replacement);
|
|
39674
|
+
if (previous !== replacement) this.overwrite(index, index + stringLength, replacement);
|
|
39627
39675
|
}
|
|
39628
39676
|
|
|
39629
39677
|
return this;
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
/**
|
|
3
|
+
* @license Angular v19.1.0-rc.0
|
|
4
|
+
* (c) 2010-2024 Google LLC. https://angular.io/
|
|
5
|
+
* License: MIT
|
|
6
|
+
*/
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
10
|
+
|
|
11
|
+
var schematics = require('@angular-devkit/schematics');
|
|
12
|
+
var project_tsconfig_paths = require('./project_tsconfig_paths-e9ccccbf.js');
|
|
13
|
+
var apply_import_manager = require('./apply_import_manager-6508401d.js');
|
|
14
|
+
require('os');
|
|
15
|
+
var ts = require('typescript');
|
|
16
|
+
var checker = require('./checker-24b68d23.js');
|
|
17
|
+
var program = require('./program-c810a4c2.js');
|
|
18
|
+
require('path');
|
|
19
|
+
require('./index-b1033cf0.js');
|
|
20
|
+
require('@angular-devkit/core');
|
|
21
|
+
require('node:path/posix');
|
|
22
|
+
require('fs');
|
|
23
|
+
require('module');
|
|
24
|
+
require('url');
|
|
25
|
+
|
|
26
|
+
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
27
|
+
|
|
28
|
+
var ts__default = /*#__PURE__*/_interopDefaultLegacy(ts);
|
|
29
|
+
|
|
30
|
+
/** Migration that cleans up unused imports from a project. */
|
|
31
|
+
class UnusedImportsMigration extends apply_import_manager.TsurgeFunnelMigration {
|
|
32
|
+
printer = ts__default["default"].createPrinter();
|
|
33
|
+
createProgram(tsconfigAbsPath, fs) {
|
|
34
|
+
return super.createProgram(tsconfigAbsPath, fs, {
|
|
35
|
+
extendedDiagnostics: {
|
|
36
|
+
checks: {
|
|
37
|
+
// Ensure that the diagnostic is enabled.
|
|
38
|
+
unusedStandaloneImports: program.DiagnosticCategoryLabel.Warning,
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
async analyze(info) {
|
|
44
|
+
const nodePositions = new Map();
|
|
45
|
+
const replacements = [];
|
|
46
|
+
let removedImports = 0;
|
|
47
|
+
let changedFiles = 0;
|
|
48
|
+
info.ngCompiler?.getDiagnostics().forEach((diag) => {
|
|
49
|
+
if (diag.file !== undefined &&
|
|
50
|
+
diag.start !== undefined &&
|
|
51
|
+
diag.length !== undefined &&
|
|
52
|
+
diag.code === checker.ngErrorCode(checker.ErrorCode.UNUSED_STANDALONE_IMPORTS)) {
|
|
53
|
+
if (!nodePositions.has(diag.file)) {
|
|
54
|
+
nodePositions.set(diag.file, new Set());
|
|
55
|
+
}
|
|
56
|
+
nodePositions.get(diag.file).add(this.getNodeKey(diag.start, diag.length));
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
nodePositions.forEach((locations, sourceFile) => {
|
|
60
|
+
const resolvedLocations = this.resolveRemovalLocations(sourceFile, locations);
|
|
61
|
+
const usageAnalysis = this.analyzeUsages(sourceFile, resolvedLocations);
|
|
62
|
+
if (resolvedLocations.allRemovedIdentifiers.size > 0) {
|
|
63
|
+
removedImports += resolvedLocations.allRemovedIdentifiers.size;
|
|
64
|
+
changedFiles++;
|
|
65
|
+
}
|
|
66
|
+
this.generateReplacements(sourceFile, resolvedLocations, usageAnalysis, info, replacements);
|
|
67
|
+
});
|
|
68
|
+
return apply_import_manager.confirmAsSerializable({ replacements, removedImports, changedFiles });
|
|
69
|
+
}
|
|
70
|
+
async migrate(globalData) {
|
|
71
|
+
return apply_import_manager.confirmAsSerializable(globalData);
|
|
72
|
+
}
|
|
73
|
+
async combine(unitA, unitB) {
|
|
74
|
+
return apply_import_manager.confirmAsSerializable({
|
|
75
|
+
replacements: [...unitA.replacements, ...unitB.replacements],
|
|
76
|
+
removedImports: unitA.removedImports + unitB.removedImports,
|
|
77
|
+
changedFiles: unitA.changedFiles + unitB.changedFiles,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
async globalMeta(combinedData) {
|
|
81
|
+
return apply_import_manager.confirmAsSerializable(combinedData);
|
|
82
|
+
}
|
|
83
|
+
async stats(globalMetadata) {
|
|
84
|
+
return {
|
|
85
|
+
counters: {
|
|
86
|
+
removedImports: globalMetadata.removedImports,
|
|
87
|
+
changedFiles: globalMetadata.changedFiles,
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
/** Gets a key that can be used to look up a node based on its location. */
|
|
92
|
+
getNodeKey(start, length) {
|
|
93
|
+
return `${start}/${length}`;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Resolves a set of node locations to the actual AST nodes that need to be migrated.
|
|
97
|
+
* @param sourceFile File in which to resolve the locations.
|
|
98
|
+
* @param locations Location keys that should be resolved.
|
|
99
|
+
*/
|
|
100
|
+
resolveRemovalLocations(sourceFile, locations) {
|
|
101
|
+
const result = {
|
|
102
|
+
fullRemovals: new Set(),
|
|
103
|
+
partialRemovals: new Map(),
|
|
104
|
+
allRemovedIdentifiers: new Set(),
|
|
105
|
+
};
|
|
106
|
+
const walk = (node) => {
|
|
107
|
+
if (!ts__default["default"].isIdentifier(node)) {
|
|
108
|
+
node.forEachChild(walk);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
// The TS typings don't reflect that the parent can be undefined.
|
|
112
|
+
const parent = node.parent;
|
|
113
|
+
if (!parent) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
if (locations.has(this.getNodeKey(node.getStart(), node.getWidth()))) {
|
|
117
|
+
// When the entire array needs to be cleared, the diagnostic is
|
|
118
|
+
// reported on the property assignment, rather than an array element.
|
|
119
|
+
if (ts__default["default"].isPropertyAssignment(parent) &&
|
|
120
|
+
parent.name === node &&
|
|
121
|
+
ts__default["default"].isArrayLiteralExpression(parent.initializer)) {
|
|
122
|
+
result.fullRemovals.add(parent.initializer);
|
|
123
|
+
parent.initializer.elements.forEach((element) => {
|
|
124
|
+
if (ts__default["default"].isIdentifier(element)) {
|
|
125
|
+
result.allRemovedIdentifiers.add(element.text);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
else if (ts__default["default"].isArrayLiteralExpression(parent)) {
|
|
130
|
+
if (!result.partialRemovals.has(parent)) {
|
|
131
|
+
result.partialRemovals.set(parent, new Set());
|
|
132
|
+
}
|
|
133
|
+
result.partialRemovals.get(parent).add(node);
|
|
134
|
+
result.allRemovedIdentifiers.add(node.text);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
walk(sourceFile);
|
|
139
|
+
return result;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Analyzes how identifiers are used across a file.
|
|
143
|
+
* @param sourceFile File to be analyzed.
|
|
144
|
+
* @param locations Locations that will be changed as a part of this migration.
|
|
145
|
+
*/
|
|
146
|
+
analyzeUsages(sourceFile, locations) {
|
|
147
|
+
const { partialRemovals, fullRemovals } = locations;
|
|
148
|
+
const result = {
|
|
149
|
+
importedSymbols: new Map(),
|
|
150
|
+
identifierCounts: new Map(),
|
|
151
|
+
};
|
|
152
|
+
const walk = (node) => {
|
|
153
|
+
if (ts__default["default"].isIdentifier(node) &&
|
|
154
|
+
node.parent &&
|
|
155
|
+
// Don't track individual identifiers marked for removal.
|
|
156
|
+
(!ts__default["default"].isArrayLiteralExpression(node.parent) ||
|
|
157
|
+
!partialRemovals.has(node.parent) ||
|
|
158
|
+
!partialRemovals.get(node.parent).has(node))) {
|
|
159
|
+
result.identifierCounts.set(node.text, (result.identifierCounts.get(node.text) ?? 0) + 1);
|
|
160
|
+
}
|
|
161
|
+
// Don't track identifiers in array literals that are about to be removed.
|
|
162
|
+
if (ts__default["default"].isArrayLiteralExpression(node) && fullRemovals.has(node)) {
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
if (ts__default["default"].isImportDeclaration(node)) {
|
|
166
|
+
const namedBindings = node.importClause?.namedBindings;
|
|
167
|
+
const moduleName = ts__default["default"].isStringLiteral(node.moduleSpecifier)
|
|
168
|
+
? node.moduleSpecifier.text
|
|
169
|
+
: null;
|
|
170
|
+
if (namedBindings && ts__default["default"].isNamedImports(namedBindings) && moduleName !== null) {
|
|
171
|
+
namedBindings.elements.forEach((imp) => {
|
|
172
|
+
if (!result.importedSymbols.has(moduleName)) {
|
|
173
|
+
result.importedSymbols.set(moduleName, new Map());
|
|
174
|
+
}
|
|
175
|
+
const symbolName = (imp.propertyName || imp.name).text;
|
|
176
|
+
const localName = imp.name.text;
|
|
177
|
+
result.importedSymbols.get(moduleName).set(localName, symbolName);
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
// Don't track identifiers in imports.
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
// Track identifiers in all other node kinds.
|
|
184
|
+
node.forEachChild(walk);
|
|
185
|
+
};
|
|
186
|
+
walk(sourceFile);
|
|
187
|
+
return result;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Generates text replacements based on the data produced by the migration.
|
|
191
|
+
* @param sourceFile File being migrated.
|
|
192
|
+
* @param removalLocations Data about nodes being removed.
|
|
193
|
+
* @param usages Data about identifier usage.
|
|
194
|
+
* @param info Information about the current program.
|
|
195
|
+
* @param replacements Array tracking all text replacements.
|
|
196
|
+
*/
|
|
197
|
+
generateReplacements(sourceFile, removalLocations, usages, info, replacements) {
|
|
198
|
+
const { fullRemovals, partialRemovals, allRemovedIdentifiers } = removalLocations;
|
|
199
|
+
const { importedSymbols, identifierCounts } = usages;
|
|
200
|
+
const importManager = new checker.ImportManager();
|
|
201
|
+
// Replace full arrays with empty ones. This allows preserves more of the user's formatting.
|
|
202
|
+
fullRemovals.forEach((node) => {
|
|
203
|
+
replacements.push(new apply_import_manager.Replacement(apply_import_manager.projectFile(sourceFile, info), new apply_import_manager.TextUpdate({
|
|
204
|
+
position: node.getStart(),
|
|
205
|
+
end: node.getEnd(),
|
|
206
|
+
toInsert: '[]',
|
|
207
|
+
})));
|
|
208
|
+
});
|
|
209
|
+
// Filter out the unused identifiers from an array.
|
|
210
|
+
partialRemovals.forEach((toRemove, node) => {
|
|
211
|
+
const newNode = ts__default["default"].factory.updateArrayLiteralExpression(node, node.elements.filter((el) => !toRemove.has(el)));
|
|
212
|
+
replacements.push(new apply_import_manager.Replacement(apply_import_manager.projectFile(sourceFile, info), new apply_import_manager.TextUpdate({
|
|
213
|
+
position: node.getStart(),
|
|
214
|
+
end: node.getEnd(),
|
|
215
|
+
toInsert: this.printer.printNode(ts__default["default"].EmitHint.Unspecified, newNode, sourceFile),
|
|
216
|
+
})));
|
|
217
|
+
});
|
|
218
|
+
// Attempt to clean up unused import declarations. Note that this isn't foolproof, because we
|
|
219
|
+
// do the matching based on identifier text, rather than going through the type checker which
|
|
220
|
+
// can be expensive. This should be enough for the vast majority of cases in this schematic
|
|
221
|
+
// since we're dealing exclusively with directive/pipe class names which tend to be very
|
|
222
|
+
// specific. In the worst case we may end up not removing an import declaration which would
|
|
223
|
+
// still be valid code that the user can clean up themselves.
|
|
224
|
+
importedSymbols.forEach((names, moduleName) => {
|
|
225
|
+
names.forEach((symbolName, localName) => {
|
|
226
|
+
// Note that in the `identifierCounts` lookup both zero and undefined
|
|
227
|
+
// are valid and mean that the identifiers isn't being used anymore.
|
|
228
|
+
if (allRemovedIdentifiers.has(localName) && !identifierCounts.get(localName)) {
|
|
229
|
+
importManager.removeImport(sourceFile, symbolName, moduleName);
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
apply_import_manager.applyImportManagerChanges(importManager, replacements, [sourceFile], info);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function migrate() {
|
|
238
|
+
return async (tree, context) => {
|
|
239
|
+
const { buildPaths, testPaths } = await project_tsconfig_paths.getProjectTsConfigPaths(tree);
|
|
240
|
+
if (!buildPaths.length && !testPaths.length) {
|
|
241
|
+
throw new schematics.SchematicsException('Could not find any tsconfig file. Cannot clean up unused imports.');
|
|
242
|
+
}
|
|
243
|
+
const fs = new apply_import_manager.DevkitMigrationFilesystem(tree);
|
|
244
|
+
checker.setFileSystem(fs);
|
|
245
|
+
const migration = new UnusedImportsMigration();
|
|
246
|
+
const unitResults = [];
|
|
247
|
+
const programInfos = [...buildPaths, ...testPaths].map((tsconfigPath) => {
|
|
248
|
+
context.logger.info(`Preparing analysis for ${tsconfigPath}`);
|
|
249
|
+
const baseInfo = migration.createProgram(tsconfigPath, fs);
|
|
250
|
+
const info = migration.prepareProgram(baseInfo);
|
|
251
|
+
return { info, tsconfigPath };
|
|
252
|
+
});
|
|
253
|
+
for (const { info, tsconfigPath } of programInfos) {
|
|
254
|
+
context.logger.info(`Scanning for unused imports using ${tsconfigPath}`);
|
|
255
|
+
unitResults.push(await migration.analyze(info));
|
|
256
|
+
}
|
|
257
|
+
const combined = await apply_import_manager.synchronouslyCombineUnitData(migration, unitResults);
|
|
258
|
+
if (combined === null) {
|
|
259
|
+
context.logger.error('Schematic failed unexpectedly with no analysis data');
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
const globalMeta = await migration.globalMeta(combined);
|
|
263
|
+
const replacementsPerFile = new Map();
|
|
264
|
+
const { replacements } = await migration.migrate(globalMeta);
|
|
265
|
+
const changesPerFile = apply_import_manager.groupReplacementsByFile(replacements);
|
|
266
|
+
for (const [file, changes] of changesPerFile) {
|
|
267
|
+
if (!replacementsPerFile.has(file)) {
|
|
268
|
+
replacementsPerFile.set(file, changes);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
for (const [file, changes] of replacementsPerFile) {
|
|
272
|
+
const recorder = tree.beginUpdate(file);
|
|
273
|
+
for (const c of changes) {
|
|
274
|
+
recorder
|
|
275
|
+
.remove(c.data.position, c.data.end - c.data.position)
|
|
276
|
+
.insertLeft(c.data.position, c.data.toInsert);
|
|
277
|
+
}
|
|
278
|
+
tree.commitUpdate(recorder);
|
|
279
|
+
}
|
|
280
|
+
const { counters: { removedImports, changedFiles }, } = await migration.stats(globalMeta);
|
|
281
|
+
let statsMessage;
|
|
282
|
+
if (removedImports === 0) {
|
|
283
|
+
statsMessage = 'Schematic could not find unused imports in the project';
|
|
284
|
+
}
|
|
285
|
+
else {
|
|
286
|
+
statsMessage =
|
|
287
|
+
`Removed ${removedImports} import${removedImports !== 1 ? 's' : ''} ` +
|
|
288
|
+
`in ${changedFiles} file${changedFiles !== 1 ? 's' : ''}`;
|
|
289
|
+
}
|
|
290
|
+
context.logger.info('');
|
|
291
|
+
context.logger.info(statsMessage);
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
exports.migrate = migrate;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
/**
|
|
3
|
-
* @license Angular v19.1.0-
|
|
3
|
+
* @license Angular v19.1.0-rc.0
|
|
4
4
|
* (c) 2010-2024 Google LLC. https://angular.io/
|
|
5
5
|
* License: MIT
|
|
6
6
|
*/
|
|
7
7
|
'use strict';
|
|
8
8
|
|
|
9
9
|
var ts = require('typescript');
|
|
10
|
-
var checker = require('./checker-
|
|
10
|
+
var checker = require('./checker-24b68d23.js');
|
|
11
11
|
require('os');
|
|
12
12
|
var p = require('path');
|
|
13
13
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
/**
|
|
3
|
-
* @license Angular v19.1.0-
|
|
3
|
+
* @license Angular v19.1.0-rc.0
|
|
4
4
|
* (c) 2010-2024 Google LLC. https://angular.io/
|
|
5
5
|
* License: MIT
|
|
6
6
|
*/
|
|
@@ -10,8 +10,8 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
10
10
|
|
|
11
11
|
var schematics = require('@angular-devkit/schematics');
|
|
12
12
|
var p = require('path');
|
|
13
|
-
var compiler_host = require('./compiler_host-
|
|
14
|
-
var checker = require('./checker-
|
|
13
|
+
var compiler_host = require('./compiler_host-5f693799.js');
|
|
14
|
+
var checker = require('./checker-24b68d23.js');
|
|
15
15
|
var ts = require('typescript');
|
|
16
16
|
require('os');
|
|
17
17
|
require('fs');
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
/**
|
|
3
|
-
* @license Angular v19.1.0-
|
|
3
|
+
* @license Angular v19.1.0-rc.0
|
|
4
4
|
* (c) 2010-2024 Google LLC. https://angular.io/
|
|
5
5
|
* License: MIT
|
|
6
6
|
*/
|
|
@@ -11,11 +11,11 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
11
11
|
var schematics = require('@angular-devkit/schematics');
|
|
12
12
|
var p = require('path');
|
|
13
13
|
var project_tsconfig_paths = require('./project_tsconfig_paths-e9ccccbf.js');
|
|
14
|
-
var compiler_host = require('./compiler_host-
|
|
14
|
+
var compiler_host = require('./compiler_host-5f693799.js');
|
|
15
15
|
var ts = require('typescript');
|
|
16
16
|
var imports = require('./imports-abe29092.js');
|
|
17
17
|
require('@angular-devkit/core');
|
|
18
|
-
require('./checker-
|
|
18
|
+
require('./checker-24b68d23.js');
|
|
19
19
|
require('os');
|
|
20
20
|
require('fs');
|
|
21
21
|
require('module');
|