Skip to content

Commit 73d6b01

Browse files
aparzileonsenft
authored andcommitted
fix(migrations): inject migration not work in multi-project workspace with option path
Fix inject migration in multi-project workspace. The inject migration doesn't work when targeting one of the projects due to either not finding any files in other projects or considering them external thus it throws a SchematicsException Fixes: #66074 (cherry picked from commit a73b4b7)
1 parent 3838554 commit 73d6b01

File tree

2 files changed

+107
-29
lines changed

2 files changed

+107
-29
lines changed

packages/core/schematics/ng-generate/inject-migration/index.ts

Lines changed: 37 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@
66
* found in the LICENSE file at https://angular.dev/license
77
*/
88

9-
import {Rule, SchematicsException, Tree} from '@angular-devkit/schematics';
9+
import {Rule, SchematicContext, SchematicsException, Tree} from '@angular-devkit/schematics';
1010
import {join, relative} from 'path';
11+
import ts from 'typescript';
1112

1213
import {normalizePath} from '../../utils/change_tracker';
1314
import {canMigrateFile, createMigrationProgram} from '../../utils/typescript/compiler_host';
@@ -21,49 +22,54 @@ interface Options extends MigrationOptions {
2122
}
2223

2324
export function migrate(options: Options): Rule {
24-
return async (tree: Tree) => {
25-
const {buildPaths, testPaths} = await getProjectTsConfigPaths(tree);
25+
return async (tree: Tree, context: SchematicContext) => {
2626
const basePath = process.cwd();
27+
let pathToMigrate: string | undefined;
28+
if (options.path) {
29+
if (options.path.startsWith('..')) {
30+
throw new SchematicsException(
31+
'Cannot run inject migration outside of the current project.',
32+
);
33+
}
34+
pathToMigrate = normalizePath(join(basePath, options.path));
35+
}
36+
37+
const {buildPaths, testPaths} = await getProjectTsConfigPaths(tree);
2738
const allPaths = [...buildPaths, ...testPaths];
28-
const pathToMigrate = normalizePath(join(basePath, options.path));
2939

3040
if (!allPaths.length) {
31-
throw new SchematicsException(
32-
'Could not find any tsconfig file. Cannot run the inject migration.',
33-
);
41+
context.logger.warn('Could not find any tsconfig file. Cannot run the inject migration.');
42+
return;
3443
}
3544

45+
let sourceFilesCount = 0;
46+
3647
for (const tsconfigPath of allPaths) {
37-
runInjectMigration(tree, tsconfigPath, basePath, pathToMigrate, options);
48+
const program = createMigrationProgram(tree, tsconfigPath, basePath);
49+
const sourceFiles = program
50+
.getSourceFiles()
51+
.filter(
52+
(sourceFile) =>
53+
(pathToMigrate ? sourceFile.fileName.startsWith(pathToMigrate) : true) &&
54+
canMigrateFile(basePath, sourceFile, program),
55+
);
56+
57+
sourceFilesCount += runInjectMigration(tree, sourceFiles, basePath, options);
58+
}
59+
60+
if (sourceFilesCount === 0) {
61+
context.logger.warn('Inject migration did not find any files to migrate');
3862
}
3963
};
4064
}
4165

4266
function runInjectMigration(
4367
tree: Tree,
44-
tsconfigPath: string,
68+
sourceFiles: ts.SourceFile[],
4569
basePath: string,
46-
pathToMigrate: string,
4770
schematicOptions: Options,
48-
): void {
49-
if (schematicOptions.path.startsWith('..')) {
50-
throw new SchematicsException('Cannot run inject migration outside of the current project.');
51-
}
52-
53-
const program = createMigrationProgram(tree, tsconfigPath, basePath);
54-
const sourceFiles = program
55-
.getSourceFiles()
56-
.filter(
57-
(sourceFile) =>
58-
sourceFile.fileName.startsWith(pathToMigrate) &&
59-
canMigrateFile(basePath, sourceFile, program),
60-
);
61-
62-
if (sourceFiles.length === 0) {
63-
throw new SchematicsException(
64-
`Could not find any files to migrate under the path ${pathToMigrate}. Cannot run the inject migration.`,
65-
);
66-
}
71+
): number {
72+
let migratedFiles = 0;
6773

6874
for (const sourceFile of sourceFiles) {
6975
const changes = migrateFile(sourceFile, schematicOptions);
@@ -79,6 +85,8 @@ function runInjectMigration(
7985
}
8086

8187
tree.commitUpdate(update);
88+
migratedFiles++;
8289
}
8390
}
91+
return migratedFiles;
8492
}

packages/core/schematics/test/inject_migration_spec.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,76 @@ describe('inject migration', () => {
384384
]);
385385
});
386386

387+
it('should migrate files present in other workspace projects', async () => {
388+
writeFile('/tsconfig.json', '{}');
389+
390+
// Multiple projects...
391+
writeFile(
392+
'/angular.json',
393+
JSON.stringify({
394+
version: 1,
395+
projects: {
396+
app: {root: '', architect: {build: {options: {tsConfig: './tsconfig.json'}}}},
397+
lib: {root: 'lib', architect: {build: {options: {tsConfig: './lib/tsconfig.json'}}}},
398+
},
399+
}),
400+
);
401+
402+
// The lib tsconfig includes only its own folder so the second program does see the file.
403+
writeFile('/lib/tsconfig.json', JSON.stringify({include: ['**/*.ts']}));
404+
405+
// File that should be migrated exists only under the second project's folder.
406+
writeFile(
407+
'/lib/should-migrate/dir.ts',
408+
[
409+
`import { Directive } from '@angular/core';`,
410+
`import { Foo } from 'foo';`,
411+
``,
412+
`@Directive()`,
413+
`class MyDir {`,
414+
` constructor(private foo: Foo) {}`,
415+
`}`,
416+
].join('\n'),
417+
);
418+
419+
// Unrelated file outside the specified path should remain unchanged.
420+
writeFile(
421+
'/other.ts',
422+
[
423+
`import { Directive } from '@angular/core';`,
424+
`import { Foo } from 'foo';`,
425+
``,
426+
`@Directive()`,
427+
`class Other {`,
428+
` constructor(private foo: Foo) {}`,
429+
`}`,
430+
].join('\n'),
431+
);
432+
433+
// Files should be migrated under the path
434+
await runMigration({path: 'lib/should-migrate'});
435+
436+
expect(tree.readContent('/lib/should-migrate/dir.ts').split('\n')).toEqual([
437+
`import { Directive, inject } from '@angular/core';`,
438+
`import { Foo } from 'foo';`,
439+
``,
440+
`@Directive()`,
441+
`class MyDir {`,
442+
` private foo = inject(Foo);`,
443+
`}`,
444+
]);
445+
446+
expect(tree.readContent('/other.ts').split('\n')).toEqual([
447+
`import { Directive } from '@angular/core';`,
448+
`import { Foo } from 'foo';`,
449+
``,
450+
`@Directive()`,
451+
`class Other {`,
452+
` constructor(private foo: Foo) {}`,
453+
`}`,
454+
]);
455+
});
456+
387457
it('should only migrate the specified file', async () => {
388458
writeFile(
389459
'/dir.ts',

0 commit comments

Comments
 (0)