Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ import {JSONFile} from '@schematics/angular/utility/json-file';

export function migrate(): Rule {
return async (tree: Tree) => {
const {buildPaths, testPaths} = await getProjectTsConfigPaths(tree);
const {buildPaths, testPaths} = await getProjectTsConfigPaths(tree, {
angularBuildersOnly: true,
});
const allPaths = [...new Set([...buildPaths, ...testPaths])];

for (const tsconfigPath of allPaths) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ describe('strict-safe-navigation-narrow migration', () => {
root: '',
architect: {
build: {
builder: '@angular/build:application',
options: {
tsConfig: './tsconfig.json',
},
Expand Down Expand Up @@ -138,4 +139,44 @@ describe('strict-safe-navigation-narrow migration', () => {
tsconfig.angularCompilerOptions.extendedDiagnostics.checks.nullishCoalescingNotNullable,
).toBe('suppress');
});

it('should not migrate tsconfig files of projects that do not use an Angular builder', async () => {
tree.overwrite(
'/angular.json',
JSON.stringify({
version: 1,
projects: {
'angular-app': {
root: '',
architect: {
build: {
builder: '@angular/build:application',
options: {tsConfig: './tsconfig.app.json'},
},
},
},
'node-lib': {
root: '',
architect: {
build: {
builder: '@nx/js:tsc',
options: {tsConfig: './tsconfig.lib.json'},
},
},
},
},
}),
);
tree.create('/tsconfig.app.json', JSON.stringify({compilerOptions: {target: 'es2020'}}));
tree.create('/tsconfig.lib.json', JSON.stringify({compilerOptions: {target: 'es2020'}}));

const runMigration = migrate();
await runMigration(tree, {} as any);

const appTsconfig = parseConfig(tree, '/tsconfig.app.json');
expect(
appTsconfig.angularCompilerOptions.extendedDiagnostics.checks.nullishCoalescingNotNullable,
).toBe('suppress');
expect(parseConfig(tree, '/tsconfig.lib.json').angularCompilerOptions).toBeUndefined();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@ function getResolvedAngularCompilerOptions(tree: any, tsconfigPath: string): Rec
*/
export function migrate(): Rule {
return async (tree) => {
const {buildPaths, testPaths} = await getProjectTsConfigPaths(tree);
const {buildPaths, testPaths} = await getProjectTsConfigPaths(tree, {
angularBuildersOnly: true,
});
const allPaths = [...new Set([...buildPaths, ...testPaths])];

for (const tsconfigPath of allPaths) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ describe('strict-templates-default migration', () => {
root: '',
architect: {
build: {
builder: '@angular/build:application',
options: {
tsConfig: './tsconfig.json',
},
Expand Down Expand Up @@ -197,4 +198,41 @@ describe('strict-templates-default migration', () => {
expect(baseTsconfig.angularCompilerOptions).toBeDefined();
expect(appTsconfig.angularCompilerOptions).toBeUndefined();
});

it('should not migrate tsconfig files of projects that do not use an Angular builder', async () => {
tree.overwrite(
'/angular.json',
JSON.stringify({
version: 1,
projects: {
'angular-app': {
root: '',
architect: {
build: {
builder: '@angular/build:application',
options: {tsConfig: './tsconfig.app.json'},
},
},
},
'node-lib': {
root: '',
architect: {
build: {
builder: '@nx/js:tsc',
options: {tsConfig: './tsconfig.lib.json'},
},
},
},
},
}),
);
tree.create('/tsconfig.app.json', JSON.stringify({compilerOptions: {target: 'es2020'}}));
tree.create('/tsconfig.lib.json', JSON.stringify({compilerOptions: {target: 'es2020'}}));

await migrate()(tree, {} as any);

const appTsconfig = parseConfig(tree, '/tsconfig.app.json');
expect(appTsconfig.angularCompilerOptions.strictTemplates).toBe(false);
expect(parseConfig(tree, '/tsconfig.lib.json').angularCompilerOptions).toBeUndefined();
});
});
55 changes: 55 additions & 0 deletions packages/core/schematics/test/project_tsconfig_paths_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,4 +100,59 @@ describe('project tsconfig paths', () => {

expect((await getProjectTsConfigPaths(testTree)).buildPaths).toEqual(['tsconfig.json']);
});

it('should only return tsconfig paths of Angular builders when `angularBuildersOnly` is set', async () => {
testTree.create('/tsconfig.app.json', '');
testTree.create('/tsconfig.nx.json', '');
testTree.create('/tsconfig.lib.json', '');
testTree.create('/tsconfig.ngx-build-plus.json', '');
testTree.create(
'/angular.json',
JSON.stringify({
version: 1,
projects: {
angular_app: {
root: '',
architect: {
build: {
builder: '@angular/build:application',
options: {tsConfig: './tsconfig.app.json'},
},
},
},
nx_angular_app: {
root: '',
architect: {
build: {
builder: '@nx/angular:webpack-browser',
options: {tsConfig: './tsconfig.nx.json'},
},
},
},
node_lib: {
root: '',
architect: {
build: {builder: '@nx/js:tsc', options: {tsConfig: './tsconfig.lib.json'}},
},
},
ngx_build_plus_app: {
root: '',
architect: {
build: {
builder: 'ngx-build-plus:browser',
options: {tsConfig: './tsconfig.ngx-build-plus.json'},
},
},
},
},
}),
);

const {buildPaths} = await getProjectTsConfigPaths(testTree, {angularBuildersOnly: true});
expect(buildPaths).toEqual([
'tsconfig.app.json',
'tsconfig.nx.json',
'tsconfig.ngx-build-plus.json',
]);
});
});
25 changes: 25 additions & 0 deletions packages/core/schematics/utils/project_tsconfig_paths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,14 @@ import {Tree} from '@angular-devkit/schematics';
/**
* Gets all tsconfig paths from a CLI project by reading the workspace configuration
* and looking for common tsconfig locations.
*
* When `angularBuildersOnly` is set, only targets that use an Angular builder are considered.
* This avoids picking up tsconfig files of non-Angular projects in a mixed workspace (e.g. an Nx
* monorepo), which should not be touched by Angular migrations.
*/
export async function getProjectTsConfigPaths(
tree: Tree,
{angularBuildersOnly = false}: {angularBuildersOnly?: boolean} = {},
): Promise<{buildPaths: string[]; testPaths: string[]}> {
// Start with some tsconfig paths that are generally used within CLI projects. Note
// that we are not interested in IDE-specific tsconfig files (e.g. /tsconfig.json)
Expand All @@ -24,6 +29,10 @@ export async function getProjectTsConfigPaths(
const workspace = await getWorkspace(tree);
for (const [, project] of workspace.projects) {
for (const [name, target] of project.targets) {
if (angularBuildersOnly && !isAngularBuilder(target.builder)) {
continue;
}

for (const [, options] of allTargetOptions(target)) {
const tsConfig = options['tsConfig'];
// Filter out tsconfig files that don't exist in the CLI project.
Expand All @@ -46,6 +55,22 @@ export async function getProjectTsConfigPaths(
};
}

/** Prefixes of Angular builders, including common community builders (e.g. Nx). */
const angularBuilderPrefixes = [
'@angular-devkit/build-angular:',
'@angular/build:',
'@nx/angular:',
'@angular-builders/',
Comment thread
JeanMeche marked this conversation as resolved.
'ngx-build-plus:',
];

/** Whether the given builder is a recognized Angular builder. */
function isAngularBuilder(builder: string | undefined): boolean {
return (
builder !== undefined && angularBuilderPrefixes.some((prefix) => builder.startsWith(prefix))
);
}

/** Get options for all configurations for the passed builder target. */
function* allTargetOptions(
target: workspaces.TargetDefinition,
Expand Down
Loading