@angular/core 19.1.0-next.4 → 19.1.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 +85 -91
- 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/rxjs-interop.mjs.map +1 -1
- package/fesm2022/testing.mjs +4 -4
- package/index.d.ts +41 -36
- 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-5082ccea.js +732 -0
- package/schematics/bundles/{checker-884633eb.js → checker-aa999c96.js} +50 -25
- package/schematics/bundles/cleanup-unused-imports.js +295 -0
- package/schematics/bundles/{compiler_host-22f6513d.js → compiler_host-f0b570c8.js} +2 -2
- package/schematics/bundles/control-flow-migration.js +3 -3
- package/schematics/bundles/explicit-standalone-flag.js +5 -5
- package/schematics/bundles/{imports-abe29092.js → imports-31a38653.js} +1 -1
- package/schematics/bundles/index-02a11f43.js +30 -0
- package/schematics/bundles/{combine_units-4a95b1b9.js → index-15b61bae.js} +10 -723
- package/schematics/bundles/inject-migration.js +6 -6
- package/schematics/bundles/{leading_space-d190b83b.js → leading_space-6e7a8ec6.js} +1 -1
- package/schematics/bundles/{migrate_ts_type_references-4b11f3f2.js → migrate_ts_type_references-042ca765.js} +30 -29
- package/schematics/bundles/{nodes-a9f0b985.js → nodes-88c2157f.js} +2 -2
- package/schematics/bundles/output-migration.js +27 -26
- package/schematics/bundles/pending-tasks.js +5 -5
- package/schematics/bundles/{program-094352ba.js → program-393ca8f3.js} +106 -46
- package/schematics/bundles/{project_tsconfig_paths-e9ccccbf.js → project_tsconfig_paths-6c9cde78.js} +1 -1
- package/schematics/bundles/provide-initializer.js +5 -5
- package/schematics/bundles/route-lazy-loading.js +4 -4
- package/schematics/bundles/signal-input-migration.js +33 -32
- package/schematics/bundles/signal-queries-migration.js +56 -49
- package/schematics/bundles/signals.js +8 -7
- package/schematics/bundles/standalone-migration.js +12 -28
- 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
|
|
4
4
|
* (c) 2010-2024 Google LLC. https://angular.io/
|
|
5
5
|
* License: MIT
|
|
6
6
|
*/
|
|
@@ -4285,7 +4285,7 @@ class TypeofExpression extends AST {
|
|
|
4285
4285
|
this.expression = expression;
|
|
4286
4286
|
}
|
|
4287
4287
|
visit(visitor, context = null) {
|
|
4288
|
-
return visitor.
|
|
4288
|
+
return visitor.visitTypeofExpression(this, context);
|
|
4289
4289
|
}
|
|
4290
4290
|
}
|
|
4291
4291
|
class NonNullAssert extends AST {
|
|
@@ -4445,7 +4445,7 @@ class RecursiveAstVisitor {
|
|
|
4445
4445
|
visitPrefixNot(ast, context) {
|
|
4446
4446
|
this.visit(ast.expression, context);
|
|
4447
4447
|
}
|
|
4448
|
-
|
|
4448
|
+
visitTypeofExpression(ast, context) {
|
|
4449
4449
|
this.visit(ast.expression, context);
|
|
4450
4450
|
}
|
|
4451
4451
|
visitNonNullAssert(ast, context) {
|
|
@@ -7350,7 +7350,7 @@ class ShadowCss {
|
|
|
7350
7350
|
*
|
|
7351
7351
|
* For example, we convert this css:
|
|
7352
7352
|
*
|
|
7353
|
-
* ```
|
|
7353
|
+
* ```scss
|
|
7354
7354
|
* .box {
|
|
7355
7355
|
* animation: box-animation 1s forwards;
|
|
7356
7356
|
* }
|
|
@@ -7364,7 +7364,7 @@ class ShadowCss {
|
|
|
7364
7364
|
*
|
|
7365
7365
|
* to this:
|
|
7366
7366
|
*
|
|
7367
|
-
* ```
|
|
7367
|
+
* ```scss
|
|
7368
7368
|
* .box {
|
|
7369
7369
|
* animation: scopeName_box-animation 1s forwards;
|
|
7370
7370
|
* }
|
|
@@ -7393,7 +7393,7 @@ class ShadowCss {
|
|
|
7393
7393
|
*
|
|
7394
7394
|
* For example, it takes a rule such as:
|
|
7395
7395
|
*
|
|
7396
|
-
* ```
|
|
7396
|
+
* ```scss
|
|
7397
7397
|
* @keyframes box-animation {
|
|
7398
7398
|
* to {
|
|
7399
7399
|
* background-color: green;
|
|
@@ -7403,7 +7403,7 @@ class ShadowCss {
|
|
|
7403
7403
|
*
|
|
7404
7404
|
* and returns:
|
|
7405
7405
|
*
|
|
7406
|
-
* ```
|
|
7406
|
+
* ```scss
|
|
7407
7407
|
* @keyframes scopeName_box-animation {
|
|
7408
7408
|
* to {
|
|
7409
7409
|
* background-color: green;
|
|
@@ -17764,7 +17764,7 @@ class Parser {
|
|
|
17764
17764
|
* parsing errors in case the given expression is invalid.
|
|
17765
17765
|
*
|
|
17766
17766
|
* For example,
|
|
17767
|
-
* ```
|
|
17767
|
+
* ```html
|
|
17768
17768
|
* <div *ngFor="let item of items">
|
|
17769
17769
|
* ^ ^ absoluteValueOffset for `templateValue`
|
|
17770
17770
|
* absoluteKeyOffset for `templateKey`
|
|
@@ -17775,7 +17775,7 @@ class Parser {
|
|
|
17775
17775
|
* 3. ngForOf -> items
|
|
17776
17776
|
*
|
|
17777
17777
|
* This is apparent from the de-sugared template:
|
|
17778
|
-
* ```
|
|
17778
|
+
* ```html
|
|
17779
17779
|
* <ng-template ngFor let-item [ngForOf]="items">
|
|
17780
17780
|
* ```
|
|
17781
17781
|
*
|
|
@@ -18632,7 +18632,7 @@ class _ParseAST {
|
|
|
18632
18632
|
* parsing errors in case the given expression is invalid.
|
|
18633
18633
|
*
|
|
18634
18634
|
* For example,
|
|
18635
|
-
* ```
|
|
18635
|
+
* ```html
|
|
18636
18636
|
* <div *ngFor="let item of items; index as i; trackBy: func">
|
|
18637
18637
|
* ```
|
|
18638
18638
|
* contains five bindings:
|
|
@@ -19023,7 +19023,7 @@ class SerializeExpressionVisitor {
|
|
|
19023
19023
|
.map((e) => e.visit(this, context))
|
|
19024
19024
|
.join(', ')})`;
|
|
19025
19025
|
}
|
|
19026
|
-
|
|
19026
|
+
visitTypeofExpression(ast, context) {
|
|
19027
19027
|
return `typeof ${ast.expression.visit(this, context)}`;
|
|
19028
19028
|
}
|
|
19029
19029
|
visitASTWithSource(ast, context) {
|
|
@@ -26382,7 +26382,7 @@ class BindingParser {
|
|
|
26382
26382
|
}
|
|
26383
26383
|
/**
|
|
26384
26384
|
* Parses the bindings in a microsyntax expression, e.g.
|
|
26385
|
-
* ```
|
|
26385
|
+
* ```html
|
|
26386
26386
|
* <tag *tplKey="let value1 = prop; let value2 = localVar">
|
|
26387
26387
|
* ```
|
|
26388
26388
|
*
|
|
@@ -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');
|
|
30414
30422
|
|
|
30415
30423
|
const _I18N_ATTR = 'i18n';
|
|
30416
30424
|
const _I18N_ATTR_PREFIX = 'i18n-';
|
|
@@ -31114,7 +31122,7 @@ exports.ErrorCode = void 0;
|
|
|
31114
31122
|
* The left-hand side of an assignment expression was a template variable. Effectively, the
|
|
31115
31123
|
* template looked like:
|
|
31116
31124
|
*
|
|
31117
|
-
* ```
|
|
31125
|
+
* ```html
|
|
31118
31126
|
* <ng-template let-something>
|
|
31119
31127
|
* <button (click)="something = ...">...</button>
|
|
31120
31128
|
* </ng-template>
|
|
@@ -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-aa999c96.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)
|
|
@@ -33146,13 +33161,13 @@ class TypeScriptReflectionHost {
|
|
|
33146
33161
|
*
|
|
33147
33162
|
* For example, if the identifier is the `Directive` part of a qualified type chain like:
|
|
33148
33163
|
*
|
|
33149
|
-
* ```
|
|
33164
|
+
* ```ts
|
|
33150
33165
|
* core.Directive
|
|
33151
33166
|
* ```
|
|
33152
33167
|
*
|
|
33153
33168
|
* then it might be that `core` is a namespace import such as:
|
|
33154
33169
|
*
|
|
33155
|
-
* ```
|
|
33170
|
+
* ```ts
|
|
33156
33171
|
* import * as core from 'tslib';
|
|
33157
33172
|
* ```
|
|
33158
33173
|
*
|
|
@@ -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),
|
|
@@ -33531,6 +33547,14 @@ function propertyNameToString(node) {
|
|
|
33531
33547
|
return null;
|
|
33532
33548
|
}
|
|
33533
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
|
+
}
|
|
33534
33558
|
/**
|
|
33535
33559
|
* Compute the left most identifier in a qualified type chain. E.g. the `a` of `a.b.c.SomeType`.
|
|
33536
33560
|
* @param qualifiedName The starting property access expression from which we want to compute
|
|
@@ -41051,7 +41075,7 @@ class AstTranslator {
|
|
|
41051
41075
|
addParseSpanInfo(node, ast.sourceSpan);
|
|
41052
41076
|
return node;
|
|
41053
41077
|
}
|
|
41054
|
-
|
|
41078
|
+
visitTypeofExpression(ast) {
|
|
41055
41079
|
const expression = wrapForDiagnostics(this.translate(ast.expression));
|
|
41056
41080
|
const node = ts__default["default"].factory.createTypeOfExpression(expression);
|
|
41057
41081
|
addParseSpanInfo(node, ast.sourceSpan);
|
|
@@ -41265,7 +41289,7 @@ class VeSafeLhsInferenceBugDetector {
|
|
|
41265
41289
|
visitPrefixNot(ast) {
|
|
41266
41290
|
return ast.expression.visit(this);
|
|
41267
41291
|
}
|
|
41268
|
-
|
|
41292
|
+
visitTypeofExpression(ast) {
|
|
41269
41293
|
return ast.expression.visit(this);
|
|
41270
41294
|
}
|
|
41271
41295
|
visitNonNullAssert(ast) {
|
|
@@ -45961,6 +45985,7 @@ exports.IcuPlaceholder = IcuPlaceholder;
|
|
|
45961
45985
|
exports.Identifiers = Identifiers;
|
|
45962
45986
|
exports.ImplicitReceiver = ImplicitReceiver;
|
|
45963
45987
|
exports.ImportManager = ImportManager;
|
|
45988
|
+
exports.InstantiateExpr = InstantiateExpr;
|
|
45964
45989
|
exports.Interpolation = Interpolation$1;
|
|
45965
45990
|
exports.InterpolationConfig = InterpolationConfig;
|
|
45966
45991
|
exports.InvokeFunctionExpr = InvokeFunctionExpr;
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
/**
|
|
3
|
+
* @license Angular v19.1.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-6c9cde78.js');
|
|
13
|
+
var apply_import_manager = require('./apply_import_manager-5082ccea.js');
|
|
14
|
+
require('os');
|
|
15
|
+
var ts = require('typescript');
|
|
16
|
+
var checker = require('./checker-aa999c96.js');
|
|
17
|
+
var program = require('./program-393ca8f3.js');
|
|
18
|
+
require('path');
|
|
19
|
+
require('./index-02a11f43.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
|
+
.insertRight(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
|
|
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-aa999c96.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
|
|
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-f0b570c8.js');
|
|
14
|
+
var checker = require('./checker-aa999c96.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
|
|
4
4
|
* (c) 2010-2024 Google LLC. https://angular.io/
|
|
5
5
|
* License: MIT
|
|
6
6
|
*/
|
|
@@ -10,12 +10,12 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
10
10
|
|
|
11
11
|
var schematics = require('@angular-devkit/schematics');
|
|
12
12
|
var p = require('path');
|
|
13
|
-
var project_tsconfig_paths = require('./project_tsconfig_paths-
|
|
14
|
-
var compiler_host = require('./compiler_host-
|
|
13
|
+
var project_tsconfig_paths = require('./project_tsconfig_paths-6c9cde78.js');
|
|
14
|
+
var compiler_host = require('./compiler_host-f0b570c8.js');
|
|
15
15
|
var ts = require('typescript');
|
|
16
|
-
var imports = require('./imports-
|
|
16
|
+
var imports = require('./imports-31a38653.js');
|
|
17
17
|
require('@angular-devkit/core');
|
|
18
|
-
require('./checker-
|
|
18
|
+
require('./checker-aa999c96.js');
|
|
19
19
|
require('os');
|
|
20
20
|
require('fs');
|
|
21
21
|
require('module');
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
/**
|
|
3
|
+
* @license Angular v19.1.0
|
|
4
|
+
* (c) 2010-2024 Google LLC. https://angular.io/
|
|
5
|
+
* License: MIT
|
|
6
|
+
*/
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
require('os');
|
|
10
|
+
require('typescript');
|
|
11
|
+
var checker = require('./checker-aa999c96.js');
|
|
12
|
+
require('./program-393ca8f3.js');
|
|
13
|
+
require('path');
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @module
|
|
17
|
+
* @description
|
|
18
|
+
* Entry point for all public APIs of the compiler-cli package.
|
|
19
|
+
*/
|
|
20
|
+
new checker.Version('19.1.0');
|
|
21
|
+
|
|
22
|
+
var LogLevel;
|
|
23
|
+
(function (LogLevel) {
|
|
24
|
+
LogLevel[LogLevel["debug"] = 0] = "debug";
|
|
25
|
+
LogLevel[LogLevel["info"] = 1] = "info";
|
|
26
|
+
LogLevel[LogLevel["warn"] = 2] = "warn";
|
|
27
|
+
LogLevel[LogLevel["error"] = 3] = "error";
|
|
28
|
+
})(LogLevel || (LogLevel = {}));
|
|
29
|
+
|
|
30
|
+
checker.setFileSystem(new checker.NodeJSFileSystem());
|