mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
fix(core): add migration away from InjectFlags (#60318)
Adds an automated migration that will switch users from the deprecated `InjectFlags` API to its non-deprecated equivalent. PR Close #60318
This commit is contained in:
parent
9a124c8b5d
commit
e170d24240
8 changed files with 558 additions and 2 deletions
|
|
@ -45,6 +45,7 @@ rollup_bundle(
|
|||
"//packages/core/schematics/ng-generate/signal-queries-migration:index.ts": "signal-queries-migration",
|
||||
"//packages/core/schematics/ng-generate/output-migration:index.ts": "output-migration",
|
||||
"//packages/core/schematics/ng-generate/self-closing-tags-migration:index.ts": "self-closing-tags-migration",
|
||||
"//packages/core/schematics/migrations/inject-flags:index.ts": "inject-flags",
|
||||
},
|
||||
format = "cjs",
|
||||
link_workspace_root = True,
|
||||
|
|
@ -54,6 +55,7 @@ rollup_bundle(
|
|||
"//packages/core/schematics/test:__pkg__",
|
||||
],
|
||||
deps = [
|
||||
"//packages/core/schematics/migrations/inject-flags",
|
||||
"//packages/core/schematics/ng-generate/cleanup-unused-imports",
|
||||
"//packages/core/schematics/ng-generate/control-flow-migration",
|
||||
"//packages/core/schematics/ng-generate/inject-migration",
|
||||
|
|
|
|||
|
|
@ -1,3 +1,9 @@
|
|||
{
|
||||
"schematics": {}
|
||||
"schematics": {
|
||||
"inject-flags": {
|
||||
"version": "20.0.0",
|
||||
"description": "Replaces usages of the deprecated InjectFlags enum",
|
||||
"factory": "./bundles/inject-flags#migrate"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
25
packages/core/schematics/migrations/inject-flags/BUILD.bazel
Normal file
25
packages/core/schematics/migrations/inject-flags/BUILD.bazel
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
load("//tools:defaults.bzl", "ts_library")
|
||||
|
||||
package(
|
||||
default_visibility = [
|
||||
"//packages/core/schematics:__pkg__",
|
||||
"//packages/core/schematics/migrations/google3:__pkg__",
|
||||
"//packages/core/schematics/test:__pkg__",
|
||||
],
|
||||
)
|
||||
|
||||
ts_library(
|
||||
name = "inject-flags",
|
||||
srcs = glob(["**/*.ts"]),
|
||||
tsconfig = "//packages/core/schematics:tsconfig.json",
|
||||
deps = [
|
||||
"//packages/compiler-cli/private",
|
||||
"//packages/compiler-cli/src/ngtsc/file_system",
|
||||
"//packages/core/schematics/utils",
|
||||
"//packages/core/schematics/utils/tsurge",
|
||||
"//packages/core/schematics/utils/tsurge/helpers/angular_devkit",
|
||||
"@npm//@angular-devkit/schematics",
|
||||
"@npm//@types/node",
|
||||
"@npm//typescript",
|
||||
],
|
||||
)
|
||||
23
packages/core/schematics/migrations/inject-flags/README.md
Normal file
23
packages/core/schematics/migrations/inject-flags/README.md
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
## Remove `InjectFlags` migration
|
||||
Replaces the usages of the deprecated `InjectFlags` symbol with its non-deprecated equivalent,
|
||||
for example:
|
||||
|
||||
### Before
|
||||
```typescript
|
||||
import { inject, InjectFlags, Directive, ElementRef } from '@angular/core';
|
||||
|
||||
@Directive()
|
||||
export class Dir {
|
||||
element = inject(ElementRef, InjectFlags.Optional | InjectFlags.Host | InjectFlags.SkipSelf);
|
||||
}
|
||||
```
|
||||
|
||||
### After
|
||||
```typescript
|
||||
import { inject, Directive, ElementRef } from '@angular/core';
|
||||
|
||||
@Directive()
|
||||
export class Dir {
|
||||
element = inject(ElementRef, { optional: true, host: true, skipSelf: true });
|
||||
}
|
||||
```
|
||||
70
packages/core/schematics/migrations/inject-flags/index.ts
Normal file
70
packages/core/schematics/migrations/inject-flags/index.ts
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright Google LLC All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.dev/license
|
||||
*/
|
||||
|
||||
import {Rule, SchematicsException} from '@angular-devkit/schematics';
|
||||
|
||||
import {getProjectTsConfigPaths} from '../../utils/project_tsconfig_paths';
|
||||
import {DevkitMigrationFilesystem} from '../../utils/tsurge/helpers/angular_devkit/devkit_filesystem';
|
||||
import {groupReplacementsByFile} from '../../utils/tsurge/helpers/group_replacements';
|
||||
import {setFileSystem} from '@angular/compiler-cli/src/ngtsc/file_system';
|
||||
import {ProjectRootRelativePath, TextUpdate} from '../../utils/tsurge';
|
||||
import {synchronouslyCombineUnitData} from '../../utils/tsurge/helpers/combine_units';
|
||||
import {CompilationUnitData, InjectFlagsMigration} from './inject_flags_migration';
|
||||
|
||||
export function migrate(): Rule {
|
||||
return async (tree) => {
|
||||
const {buildPaths, testPaths} = await getProjectTsConfigPaths(tree);
|
||||
|
||||
if (!buildPaths.length && !testPaths.length) {
|
||||
throw new SchematicsException(
|
||||
'Could not find any tsconfig file. Cannot replace `InjectFlags` usages.',
|
||||
);
|
||||
}
|
||||
|
||||
const fs = new DevkitMigrationFilesystem(tree);
|
||||
setFileSystem(fs);
|
||||
|
||||
const migration = new InjectFlagsMigration();
|
||||
const unitResults: CompilationUnitData[] = [];
|
||||
const programInfos = [...buildPaths, ...testPaths].map((tsconfigPath) => {
|
||||
const baseInfo = migration.createProgram(tsconfigPath, fs);
|
||||
const info = migration.prepareProgram(baseInfo);
|
||||
return {info, tsconfigPath};
|
||||
});
|
||||
|
||||
for (const {info} of programInfos) {
|
||||
unitResults.push(await migration.analyze(info));
|
||||
}
|
||||
|
||||
const combined = await synchronouslyCombineUnitData(migration, unitResults);
|
||||
if (combined === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const globalMeta = await migration.globalMeta(combined);
|
||||
const replacementsPerFile: Map<ProjectRootRelativePath, TextUpdate[]> = new Map();
|
||||
const {replacements} = await migration.migrate(globalMeta);
|
||||
const changesPerFile = groupReplacementsByFile(replacements);
|
||||
|
||||
for (const [file, changes] of changesPerFile) {
|
||||
if (!replacementsPerFile.has(file)) {
|
||||
replacementsPerFile.set(file, changes);
|
||||
}
|
||||
}
|
||||
|
||||
for (const [file, changes] of replacementsPerFile) {
|
||||
const recorder = tree.beginUpdate(file);
|
||||
for (const c of changes) {
|
||||
recorder
|
||||
.remove(c.data.position, c.data.end - c.data.position)
|
||||
.insertRight(c.data.position, c.data.toInsert);
|
||||
}
|
||||
tree.commitUpdate(recorder);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,193 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright Google LLC All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.dev/license
|
||||
*/
|
||||
|
||||
import ts from 'typescript';
|
||||
import {
|
||||
confirmAsSerializable,
|
||||
ProgramInfo,
|
||||
ProjectFile,
|
||||
projectFile,
|
||||
ProjectFileID,
|
||||
Replacement,
|
||||
Serializable,
|
||||
TextUpdate,
|
||||
TsurgeFunnelMigration,
|
||||
} from '../../utils/tsurge';
|
||||
import {ImportManager} from '@angular/compiler-cli/private/migrations';
|
||||
import {applyImportManagerChanges} from '../../utils/tsurge/helpers/apply_import_manager';
|
||||
import {getImportSpecifier} from '../../utils/typescript/imports';
|
||||
|
||||
export interface CompilationUnitData {
|
||||
/** Tracks information about `InjectFlags` binary expressions and how they should be replaced. */
|
||||
locations: Record<NodeID, ReplacementLocation>;
|
||||
|
||||
/** Tracks files and their import removal replacements, */
|
||||
importRemovals: Record<ProjectFileID, Replacement[]>;
|
||||
}
|
||||
|
||||
/** Information about a single `InjectFlags` expression. */
|
||||
interface ReplacementLocation {
|
||||
/** File in which the expression is defined. */
|
||||
file: ProjectFile;
|
||||
|
||||
/** `InjectFlags` used in the expression. */
|
||||
flags: string[];
|
||||
|
||||
/** Start of the expression. */
|
||||
position: number;
|
||||
|
||||
/** End of the expression. */
|
||||
end: number;
|
||||
}
|
||||
|
||||
/** Mapping between `InjectFlag` enum members to their object literal equvalients. */
|
||||
const FLAGS_TO_FIELDS: Record<string, string> = {
|
||||
'Default': 'default',
|
||||
'Host': 'host',
|
||||
'Optional': 'optional',
|
||||
'Self': 'self',
|
||||
'SkipSelf': 'skipSelf',
|
||||
};
|
||||
|
||||
/** ID of a node based on its location. */
|
||||
type NodeID = string & {__nodeID: true};
|
||||
|
||||
/** Migration that replaces `InjectFlags` usages with object literals. */
|
||||
export class InjectFlagsMigration extends TsurgeFunnelMigration<
|
||||
CompilationUnitData,
|
||||
CompilationUnitData
|
||||
> {
|
||||
override async analyze(info: ProgramInfo): Promise<Serializable<CompilationUnitData>> {
|
||||
const locations: Record<NodeID, ReplacementLocation> = {};
|
||||
const importRemovals: Record<ProjectFileID, Replacement[]> = {};
|
||||
|
||||
for (const sourceFile of info.sourceFiles) {
|
||||
const specifier = getImportSpecifier(sourceFile, '@angular/core', 'InjectFlags');
|
||||
|
||||
if (specifier === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const file = projectFile(sourceFile, info);
|
||||
const importManager = new ImportManager();
|
||||
const importReplacements: Replacement[] = [];
|
||||
|
||||
// Always remove the `InjectFlags` since it has been removed from Angular.
|
||||
// Note that it be better to do this inside of `migrate`, but we don't have AST access there.
|
||||
importManager.removeImport(sourceFile, 'InjectFlags', '@angular/core');
|
||||
applyImportManagerChanges(importManager, importReplacements, [sourceFile], info);
|
||||
importRemovals[file.id] = importReplacements;
|
||||
|
||||
sourceFile.forEachChild(function walk(node) {
|
||||
if (
|
||||
// Note: we don't use the type checker for matching here, because
|
||||
// the `InjectFlags` will be removed which can break the lookup.
|
||||
ts.isPropertyAccessExpression(node) &&
|
||||
ts.isIdentifier(node.expression) &&
|
||||
node.expression.text === specifier.name.text &&
|
||||
FLAGS_TO_FIELDS.hasOwnProperty(node.name.text)
|
||||
) {
|
||||
const root = getInjectFlagsRootExpression(node);
|
||||
|
||||
if (root !== null) {
|
||||
const flagName = FLAGS_TO_FIELDS[node.name.text];
|
||||
const id = getNodeID(file, root);
|
||||
locations[id] ??= {file, flags: [], position: root.getStart(), end: root.getEnd()};
|
||||
|
||||
// The flags can't be a set here, because they need to be serializable.
|
||||
if (!locations[id].flags.includes(flagName)) {
|
||||
locations[id].flags.push(flagName);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
node.forEachChild(walk);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return confirmAsSerializable({locations, importRemovals});
|
||||
}
|
||||
|
||||
override async migrate(globalData: CompilationUnitData) {
|
||||
const replacements: Replacement[] = [];
|
||||
|
||||
for (const removals of Object.values(globalData.importRemovals)) {
|
||||
replacements.push(...removals);
|
||||
}
|
||||
|
||||
for (const {file, position, end, flags} of Object.values(globalData.locations)) {
|
||||
// Declare a property for each flag, except for `default` which does not have a flag.
|
||||
const properties = flags.filter((flag) => flag !== 'default').map((flag) => `${flag}: true`);
|
||||
const toInsert = properties.length ? `{ ${properties.join(', ')} }` : '{}';
|
||||
replacements.push(new Replacement(file, new TextUpdate({position, end, toInsert})));
|
||||
}
|
||||
|
||||
return confirmAsSerializable({replacements});
|
||||
}
|
||||
|
||||
override async combine(
|
||||
unitA: CompilationUnitData,
|
||||
unitB: CompilationUnitData,
|
||||
): Promise<Serializable<CompilationUnitData>> {
|
||||
return confirmAsSerializable({
|
||||
locations: {
|
||||
...unitA.locations,
|
||||
...unitB.locations,
|
||||
},
|
||||
importRemovals: {
|
||||
...unitA.importRemovals,
|
||||
...unitB.importRemovals,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
override async globalMeta(
|
||||
combinedData: CompilationUnitData,
|
||||
): Promise<Serializable<CompilationUnitData>> {
|
||||
return confirmAsSerializable(combinedData);
|
||||
}
|
||||
|
||||
override async stats() {
|
||||
return {counters: {}};
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets an ID that can be used to look up a node based on its location. */
|
||||
function getNodeID(file: ProjectFile, node: ts.Node): NodeID {
|
||||
return `${file.id}/${node.getStart()}/${node.getWidth()}` as NodeID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the root expression of an `InjectFlags` usage. For example given `InjectFlags.Optional`.
|
||||
* in `InjectFlags.Host | InjectFlags.Optional | InjectFlags.SkipSelf`, the function will return
|
||||
* the top-level binary expression.
|
||||
* @param start Node from which to start searching.
|
||||
*/
|
||||
function getInjectFlagsRootExpression(start: ts.Expression): ts.Expression | null {
|
||||
let current = start as ts.Node | undefined;
|
||||
let parent = current?.parent;
|
||||
|
||||
while (parent && (ts.isBinaryExpression(parent) || ts.isParenthesizedExpression(parent))) {
|
||||
current = parent;
|
||||
parent = current.parent;
|
||||
}
|
||||
|
||||
// Only allow allow expressions that are call parameters, variable initializer or parameter
|
||||
// initializers which are the only officially supported usages of `InjectFlags`.
|
||||
if (
|
||||
current &&
|
||||
parent &&
|
||||
((ts.isCallExpression(parent) && parent.arguments.includes(current as ts.Expression)) ||
|
||||
(ts.isVariableDeclaration(parent) && parent.initializer === current) ||
|
||||
(ts.isParameter(parent) && parent.initializer === current))
|
||||
) {
|
||||
return current as ts.Expression;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
237
packages/core/schematics/test/inject_flags_spec.ts
Normal file
237
packages/core/schematics/test/inject_flags_spec.ts
Normal file
|
|
@ -0,0 +1,237 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright Google LLC All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.dev/license
|
||||
*/
|
||||
|
||||
import {getSystemPath, normalize, virtualFs} from '@angular-devkit/core';
|
||||
import {TempScopedNodeJsSyncHost} from '@angular-devkit/core/node/testing';
|
||||
import {HostTree} from '@angular-devkit/schematics';
|
||||
import {SchematicTestRunner, UnitTestTree} from '@angular-devkit/schematics/testing';
|
||||
import {runfiles} from '@bazel/runfiles';
|
||||
import shx from 'shelljs';
|
||||
|
||||
describe('inject-flags migration', () => {
|
||||
let runner: SchematicTestRunner;
|
||||
let host: TempScopedNodeJsSyncHost;
|
||||
let tree: UnitTestTree;
|
||||
let tmpDirPath: string;
|
||||
|
||||
function writeFile(filePath: string, contents: string) {
|
||||
host.sync.write(normalize(filePath), virtualFs.stringToFileBuffer(contents));
|
||||
}
|
||||
|
||||
function runMigration() {
|
||||
return runner.runSchematic('inject-flags', {}, tree);
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
runner = new SchematicTestRunner('test', runfiles.resolvePackageRelative('../migrations.json'));
|
||||
host = new TempScopedNodeJsSyncHost();
|
||||
tree = new UnitTestTree(new HostTree(host));
|
||||
tmpDirPath = getSystemPath(host.root);
|
||||
|
||||
writeFile('/tsconfig.json', '{}');
|
||||
writeFile(
|
||||
'/angular.json',
|
||||
JSON.stringify({
|
||||
version: 1,
|
||||
projects: {t: {root: '', architect: {build: {options: {tsConfig: './tsconfig.json'}}}}},
|
||||
}),
|
||||
);
|
||||
|
||||
shx.cd(tmpDirPath);
|
||||
});
|
||||
|
||||
it('should migrate a single InjectFlags usage', async () => {
|
||||
writeFile(
|
||||
'/test.ts',
|
||||
`
|
||||
import { inject, InjectFlags, Directive, ElementRef } from '@angular/core';
|
||||
|
||||
@Directive()
|
||||
export class Dir {
|
||||
el = inject(ElementRef, InjectFlags.Optional);
|
||||
}
|
||||
`,
|
||||
);
|
||||
|
||||
await runMigration();
|
||||
const content = tree.readContent('/test.ts');
|
||||
expect(content).toContain(`import { inject, Directive, ElementRef } from '@angular/core';`);
|
||||
expect(content).toContain(`el = inject(ElementRef, { optional: true });`);
|
||||
});
|
||||
|
||||
it('should migrate multiple InjectFlags', async () => {
|
||||
writeFile(
|
||||
'/test.ts',
|
||||
`
|
||||
import { inject, InjectFlags, Directive, ElementRef } from '@angular/core';
|
||||
|
||||
@Directive()
|
||||
export class Dir {
|
||||
el = inject(ElementRef, InjectFlags.Optional | InjectFlags.Host | InjectFlags.SkipSelf);
|
||||
}
|
||||
`,
|
||||
);
|
||||
|
||||
await runMigration();
|
||||
const content = tree.readContent('/test.ts');
|
||||
expect(content).toContain(`import { inject, Directive, ElementRef } from '@angular/core';`);
|
||||
expect(content).toContain(
|
||||
`el = inject(ElementRef, { optional: true, host: true, skipSelf: true });`,
|
||||
);
|
||||
});
|
||||
|
||||
it('should not generate a property for InjectFlags.Default', async () => {
|
||||
writeFile(
|
||||
'/test.ts',
|
||||
`
|
||||
import { inject, InjectFlags, Directive, ElementRef } from '@angular/core';
|
||||
|
||||
@Directive()
|
||||
export class Dir {
|
||||
el = inject(ElementRef, InjectFlags.Default);
|
||||
}
|
||||
`,
|
||||
);
|
||||
|
||||
await runMigration();
|
||||
const content = tree.readContent('/test.ts');
|
||||
expect(content).toContain(`import { inject, Directive, ElementRef } from '@angular/core';`);
|
||||
expect(content).toContain(`el = inject(ElementRef, {});`);
|
||||
});
|
||||
|
||||
it('should migrate InjectFlags used in a variable', async () => {
|
||||
writeFile(
|
||||
'/test.ts',
|
||||
`
|
||||
import { inject, InjectFlags, Directive, ElementRef } from '@angular/core';
|
||||
|
||||
const flags = InjectFlags.SkipSelf | InjectFlags.Optional;
|
||||
|
||||
@Directive()
|
||||
export class Dir {
|
||||
el = inject(ElementRef, flags);
|
||||
}
|
||||
`,
|
||||
);
|
||||
|
||||
await runMigration();
|
||||
const content = tree.readContent('/test.ts');
|
||||
expect(content).toContain(`import { inject, Directive, ElementRef } from '@angular/core';`);
|
||||
expect(content).toContain(`const flags = { skipSelf: true, optional: true };`);
|
||||
});
|
||||
|
||||
it('should migrate InjectFlags used in a function initializer', async () => {
|
||||
writeFile(
|
||||
'/test.ts',
|
||||
`
|
||||
import { inject, InjectFlags, Directive, ElementRef } from '@angular/core';
|
||||
|
||||
function injectEl(flags = InjectFlags.SkipSelf | InjectFlags.Optional) {
|
||||
return inject(ElementRef, flags);
|
||||
}
|
||||
|
||||
@Directive()
|
||||
export class Dir {
|
||||
el = injectEl();
|
||||
}
|
||||
`,
|
||||
);
|
||||
|
||||
await runMigration();
|
||||
const content = tree.readContent('/test.ts');
|
||||
expect(content).toContain(`import { inject, Directive, ElementRef } from '@angular/core';`);
|
||||
expect(content).toContain(`function injectEl(flags = { skipSelf: true, optional: true })`);
|
||||
});
|
||||
|
||||
it('should remove InjectFlags import even if InjectFlags is not used', async () => {
|
||||
writeFile(
|
||||
'/test.ts',
|
||||
`
|
||||
import { inject, InjectFlags, Directive, ElementRef } from '@angular/core';
|
||||
|
||||
@Directive()
|
||||
export class Dir {}
|
||||
`,
|
||||
);
|
||||
|
||||
await runMigration();
|
||||
const content = tree.readContent('/test.ts');
|
||||
expect(content).not.toContain('InjectFlags');
|
||||
});
|
||||
|
||||
it('should migrate InjectFlags within a parenthesized expression', async () => {
|
||||
writeFile(
|
||||
'/test.ts',
|
||||
`
|
||||
import { inject, InjectFlags, Directive, ElementRef } from '@angular/core';
|
||||
|
||||
@Directive()
|
||||
export class Dir {
|
||||
el = inject(ElementRef, ((InjectFlags.Optional) | InjectFlags.SkipSelf));
|
||||
}
|
||||
`,
|
||||
);
|
||||
|
||||
await runMigration();
|
||||
const content = tree.readContent('/test.ts');
|
||||
expect(content).toContain(`import { inject, Directive, ElementRef } from '@angular/core';`);
|
||||
expect(content).toContain(`el = inject(ElementRef, { optional: true, skipSelf: true });`);
|
||||
});
|
||||
|
||||
it('should handle a file that is present in multiple projects', async () => {
|
||||
writeFile('/tsconfig-2.json', '{}');
|
||||
writeFile(
|
||||
'/angular.json',
|
||||
JSON.stringify({
|
||||
version: 1,
|
||||
projects: {
|
||||
a: {root: '', architect: {build: {options: {tsConfig: './tsconfig.json'}}}},
|
||||
b: {root: '', architect: {build: {options: {tsConfig: './tsconfig-2.json'}}}},
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
writeFile(
|
||||
'test.ts',
|
||||
`
|
||||
import { inject, InjectFlags, Directive, ElementRef } from '@angular/core';
|
||||
|
||||
@Directive()
|
||||
export class Dir {
|
||||
el = inject(ElementRef, InjectFlags.Optional | InjectFlags.SkipSelf);
|
||||
}
|
||||
`,
|
||||
);
|
||||
|
||||
await runMigration();
|
||||
const content = tree.readContent('/test.ts');
|
||||
expect(content).toContain(`import { inject, Directive, ElementRef } from '@angular/core';`);
|
||||
expect(content).toContain(`el = inject(ElementRef, { optional: true, skipSelf: true });`);
|
||||
});
|
||||
|
||||
it('should handle aliased InjectFlags', async () => {
|
||||
writeFile(
|
||||
'/test.ts',
|
||||
`
|
||||
import { inject, InjectFlags as Foo, Directive, ElementRef } from '@angular/core';
|
||||
|
||||
@Directive()
|
||||
export class Dir {
|
||||
el = inject(ElementRef, Foo.Optional | Foo.Host | Foo.SkipSelf);
|
||||
}
|
||||
`,
|
||||
);
|
||||
|
||||
await runMigration();
|
||||
const content = tree.readContent('/test.ts');
|
||||
expect(content).toContain(`import { inject, Directive, ElementRef } from '@angular/core';`);
|
||||
expect(content).toContain(
|
||||
`el = inject(ElementRef, { optional: true, host: true, skipSelf: true });`,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
load("//tools:defaults.bzl", "ts_library")
|
||||
|
||||
package(default_visibility = ["//packages/core/schematics/ng-generate:__subpackages__"])
|
||||
package(default_visibility = ["//packages/core/schematics:__subpackages__"])
|
||||
|
||||
ts_library(
|
||||
name = "angular_devkit",
|
||||
|
|
|
|||
Loading…
Reference in a new issue