diff --git a/packages/angular/src/generators/application/application.spec.ts b/packages/angular/src/generators/application/application.spec.ts index 36a9a6f1646050..b6852588fc1024 100644 --- a/packages/angular/src/generators/application/application.spec.ts +++ b/packages/angular/src/generators/application/application.spec.ts @@ -185,6 +185,27 @@ describe('app', () => { const workspaceJson = readJson(appTree, '/workspace.json'); expect(workspaceJson.projects['app'].projectType).toEqual('application'); }); + + it('should extend from tsconfig.base.json', async () => { + // ACT + await generateApp(appTree, 'app'); + + // ASSERT + const appTsConfig = readJson(appTree, 'apps/app/tsconfig.json'); + expect(appTsConfig.extends).toBe('../../tsconfig.base.json'); + }); + + it('should support a root tsconfig.json instead of tsconfig.base.json', async () => { + // ARRANGE + appTree.rename('tsconfig.base.json', 'tsconfig.json'); + + // ACT + await generateApp(appTree, 'app'); + + // ASSERT + const appTsConfig = readJson(appTree, 'apps/app/tsconfig.json'); + expect(appTsConfig.extends).toBe('../../tsconfig.json'); + }); }); describe('nested', () => { @@ -258,6 +279,27 @@ describe('app', () => { }, ].forEach(hasJsonValue); }); + + it('should extend from tsconfig.base.json', async () => { + // ACT + await generateApp(appTree, 'app', { directory: 'myDir' }); + + // ASSERT + const appTsConfig = readJson(appTree, 'apps/my-dir/app/tsconfig.json'); + expect(appTsConfig.extends).toBe('../../../tsconfig.base.json'); + }); + + it('should support a root tsconfig.json instead of tsconfig.base.json', async () => { + // ARRANGE + appTree.rename('tsconfig.base.json', 'tsconfig.json'); + + // ACT + await generateApp(appTree, 'app', { directory: 'myDir' }); + + // ASSERT + const appTsConfig = readJson(appTree, 'apps/my-dir/app/tsconfig.json'); + expect(appTsConfig.extends).toBe('../../../tsconfig.json'); + }); }); describe('at the root', () => { diff --git a/packages/angular/src/generators/application/files/tsconfig.json b/packages/angular/src/generators/application/files/tsconfig.json index 795ebf01cbe3c6..595598eddbf3e9 100644 --- a/packages/angular/src/generators/application/files/tsconfig.json +++ b/packages/angular/src/generators/application/files/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "<%= offsetFromRoot %>tsconfig.base.json", + "extends": "<%= rootTsConfigPath %>", "files": [], "include": [], "references": [ diff --git a/packages/angular/src/generators/application/lib/create-files.ts b/packages/angular/src/generators/application/lib/create-files.ts index a4b64f2836095a..fc86855f1f6712 100644 --- a/packages/angular/src/generators/application/lib/create-files.ts +++ b/packages/angular/src/generators/application/lib/create-files.ts @@ -1,7 +1,7 @@ import type { Tree } from '@nrwl/devkit'; -import type { NormalizedSchema } from './normalized-schema'; - import { generateFiles, joinPathFragments, offsetFromRoot } from '@nrwl/devkit'; +import { getRootTsConfigPathInTree } from '@nrwl/workspace/src/utilities/typescript'; +import type { NormalizedSchema } from './normalized-schema'; export function createFiles( host: Tree, @@ -16,7 +16,9 @@ export function createFiles( options.appProjectRoot, { ...options, - offsetFromRoot: offsetFromRoot(options.appProjectRoot), + rootTsConfigPath: `${offsetFromRoot( + options.appProjectRoot + )}${getRootTsConfigPathInTree(host)}`, tpl: '', } ); diff --git a/packages/angular/src/generators/application/lib/update-e2e-project.ts b/packages/angular/src/generators/application/lib/update-e2e-project.ts index 90a1df3996bab5..3d5ee890f97d80 100644 --- a/packages/angular/src/generators/application/lib/update-e2e-project.ts +++ b/packages/angular/src/generators/application/lib/update-e2e-project.ts @@ -6,6 +6,7 @@ import { updateJson, updateProjectConfiguration, } from '@nrwl/devkit'; +import { getRootTsConfigPathInTree } from '@nrwl/workspace/src/utilities/typescript'; import type { NormalizedSchema } from './normalized-schema'; export function updateE2eProject(tree: Tree, options: NormalizedSchema) { @@ -60,7 +61,9 @@ export function updateE2eProject(tree: Tree, options: NormalizedSchema) { updateJson(tree, `${options.e2eProjectRoot}/tsconfig.json`, (json) => { return { ...json, - extends: `${offsetFromRoot(options.e2eProjectRoot)}tsconfig.base.json`, + extends: `${offsetFromRoot( + options.e2eProjectRoot + )}${getRootTsConfigPathInTree(tree)}`, }; }); } diff --git a/packages/angular/src/generators/library-secondary-entry-point/lib/add-path-mapping.ts b/packages/angular/src/generators/library-secondary-entry-point/lib/add-path-mapping.ts index e54c0ec1cfe5a9..2168c9405f602e 100644 --- a/packages/angular/src/generators/library-secondary-entry-point/lib/add-path-mapping.ts +++ b/packages/angular/src/generators/library-secondary-entry-point/lib/add-path-mapping.ts @@ -1,11 +1,12 @@ import { Tree, updateJson } from '@nrwl/devkit'; +import { getRootTsConfigPathInTree } from '@nrwl/workspace/src/utilities/typescript'; import { NormalizedGeneratorOptions } from '../schema'; export function addPathMapping( tree: Tree, options: NormalizedGeneratorOptions ): void { - updateJson(tree, 'tsconfig.base.json', (json) => { + updateJson(tree, getRootTsConfigPathInTree(tree), (json) => { const c = json.compilerOptions; c.paths = c.paths || {}; c.paths[options.secondaryEntryPoint] = [ diff --git a/packages/angular/src/generators/library-secondary-entry-point/library-secondary-entry-point.spec.ts b/packages/angular/src/generators/library-secondary-entry-point/library-secondary-entry-point.spec.ts index 4cd7c6693dc394..5f1d4a18200dff 100644 --- a/packages/angular/src/generators/library-secondary-entry-point/library-secondary-entry-point.spec.ts +++ b/packages/angular/src/generators/library-secondary-entry-point/library-secondary-entry-point.spec.ts @@ -123,6 +123,28 @@ describe('librarySecondaryEntryPoint generator', () => { ).toStrictEqual(['libs/lib1/testing/src/index.ts']); }); + it('should support a root tsconfig.json instead of tsconfig.base.json', async () => { + tree.rename('tsconfig.base.json', 'tsconfig.json'); + addProjectConfiguration(tree, 'lib1', { + root: 'libs/lib1', + projectType: 'library', + }); + tree.write( + 'libs/lib1/package.json', + JSON.stringify({ name: '@my-org/lib1' }) + ); + + await librarySecondaryEntryPointGenerator(tree, { + name: 'testing', + library: 'lib1', + }); + + const tsConfig = readJson(tree, 'tsconfig.json'); + expect( + tsConfig.compilerOptions.paths['@my-org/lib1/testing'] + ).toStrictEqual(['libs/lib1/testing/src/index.ts']); + }); + it('should add the entry point file patterns to the lint target', async () => { addProjectConfiguration(tree, 'lib1', { root: 'libs/lib1', diff --git a/packages/angular/src/generators/library/files/lib/tsconfig.json__tpl__ b/packages/angular/src/generators/library/files/lib/tsconfig.json__tpl__ index d4a0841f5b0edf..6feda9591dc035 100644 --- a/packages/angular/src/generators/library/files/lib/tsconfig.json__tpl__ +++ b/packages/angular/src/generators/library/files/lib/tsconfig.json__tpl__ @@ -1,5 +1,5 @@ { - "extends": "<%= offsetFromRoot %>tsconfig.base.json", + "extends": "<%= rootTsConfigPath %>", "files": [], "include": [], "references": [ diff --git a/packages/angular/src/generators/library/lib/update-project.ts b/packages/angular/src/generators/library/lib/update-project.ts index 7c4ec2f870c97d..eff4327fd3c0e9 100644 --- a/packages/angular/src/generators/library/lib/update-project.ts +++ b/packages/angular/src/generators/library/lib/update-project.ts @@ -1,14 +1,15 @@ import { generateFiles, + getWorkspaceLayout, + joinPathFragments, + offsetFromRoot, readProjectConfiguration, Tree, updateJson, updateProjectConfiguration, - getWorkspaceLayout, - offsetFromRoot, - joinPathFragments, } from '@nrwl/devkit'; import { replaceAppNameWithPath } from '@nrwl/workspace'; +import { getRootTsConfigPathInTree } from '@nrwl/workspace/src/utilities/typescript'; import * as path from 'path'; import { NormalizedSchema } from './normalized-schema'; import { updateNgPackage } from './update-ng-package'; @@ -118,7 +119,9 @@ function createFiles(host: Tree, options: NormalizedSchema) { options.projectRoot, { ...options, - offsetFromRoot: offsetFromRoot(options.projectRoot), + rootTsConfigPath: `${offsetFromRoot( + options.projectRoot + )}${getRootTsConfigPathInTree(host)}`, tpl: '', } ); diff --git a/packages/angular/src/generators/library/lib/update-tsconfig.ts b/packages/angular/src/generators/library/lib/update-tsconfig.ts index 10ee0b03b6b359..a3a5d5039a17cd 100644 --- a/packages/angular/src/generators/library/lib/update-tsconfig.ts +++ b/packages/angular/src/generators/library/lib/update-tsconfig.ts @@ -4,10 +4,11 @@ import { Tree, updateJson, } from '@nrwl/devkit'; +import { getRootTsConfigPathInTree } from '@nrwl/workspace/src/utilities/typescript'; import { NormalizedSchema } from './normalized-schema'; function updateRootConfig(host: Tree, options: NormalizedSchema) { - updateJson(host, 'tsconfig.base.json', (json) => { + updateJson(host, getRootTsConfigPathInTree(host), (json) => { const c = json.compilerOptions; c.paths = c.paths || {}; delete c.paths[options.name]; diff --git a/packages/angular/src/generators/library/library.spec.ts b/packages/angular/src/generators/library/library.spec.ts index cec6c69fecdc33..fa70df8956e085 100644 --- a/packages/angular/src/generators/library/library.spec.ts +++ b/packages/angular/src/generators/library/library.spec.ts @@ -305,6 +305,18 @@ describe('lib', () => { }); }); + it('should support a root tsconfig.json instead of tsconfig.base.json', async () => { + // ARRANGE + tree.rename('tsconfig.base.json', 'tsconfig.json'); + + // ACT + await runLibraryGeneratorWithOpts(); + + // ASSERT + const appTsConfig = readJson(tree, 'libs/my-lib/tsconfig.json'); + expect(appTsConfig.extends).toBe('../../tsconfig.json'); + }); + it('should check for existence of spec files before deleting them', async () => { // ARRANGE updateJson( @@ -633,6 +645,18 @@ describe('lib', () => { ], }); }); + + it('should support a root tsconfig.json instead of tsconfig.base.json', async () => { + // ARRANGE + tree.rename('tsconfig.base.json', 'tsconfig.json'); + + // ACT + await runLibraryGeneratorWithOpts({ directory: 'myDir' }); + + // ASSERT + const appTsConfig = readJson(tree, 'libs/my-dir/my-lib/tsconfig.json'); + expect(appTsConfig.extends).toBe('../../../tsconfig.json'); + }); }); describe('at the root', () => { diff --git a/packages/angular/src/generators/setup-mfe/files/webpack/webpack.config.js__tmpl__ b/packages/angular/src/generators/setup-mfe/files/webpack/webpack.config.js__tmpl__ index fe8a6cc9c1e788..cc3f73bc1a9c8b 100644 --- a/packages/angular/src/generators/setup-mfe/files/webpack/webpack.config.js__tmpl__ +++ b/packages/angular/src/generators/setup-mfe/files/webpack/webpack.config.js__tmpl__ @@ -12,7 +12,7 @@ const share = mf.share; * This NX_TSCONFIG_PATH environment variable is set by the @nrwl/angular:webpack-browser and it contains * the location of the generated temporary tsconfig file. */ -const tsConfigPath = process.env.NX_TSCONFIG_PATH ?? path.join(__dirname, '<%= offsetFromRoot %>tsconfig.base.json'); +const tsConfigPath = process.env.NX_TSCONFIG_PATH ?? path.join(__dirname, '<%= rootTsConfigPath %>'); const workspaceRootPath = path.join(__dirname, '<%= offsetFromRoot %>'); const sharedMappings = new mf.SharedMappings(); diff --git a/packages/angular/src/generators/setup-mfe/lib/generate-config.ts b/packages/angular/src/generators/setup-mfe/lib/generate-config.ts index e0e4b2b5a3eda5..a280215885f514 100644 --- a/packages/angular/src/generators/setup-mfe/lib/generate-config.ts +++ b/packages/angular/src/generators/setup-mfe/lib/generate-config.ts @@ -1,11 +1,12 @@ import type { Tree } from '@nrwl/devkit'; -import type { Schema } from '../schema'; import { generateFiles, joinPathFragments, logger, offsetFromRoot, } from '@nrwl/devkit'; +import { getRootTsConfigPathInTree } from '@nrwl/workspace/src/utilities/typescript'; +import type { Schema } from '../schema'; const SHARED_SINGLETON_LIBRARIES = [ '@angular/core', @@ -30,6 +31,8 @@ export function generateWebpackConfig( If this was not the outcome you expected, you can discard the changes we have made, create a backup of your current webpack config, and run the command again.` ); } + + const rootOffset = offsetFromRoot(appRoot); generateFiles( host, joinPathFragments(__dirname, '../files/webpack'), @@ -41,7 +44,8 @@ export function generateWebpackConfig( remotes: remotesWithPorts ?? [], sourceRoot: appRoot, sharedLibraries: SHARED_SINGLETON_LIBRARIES, - offsetFromRoot: offsetFromRoot(appRoot), + offsetFromRoot: rootOffset, + rootTsConfigPath: rootOffset + getRootTsConfigPathInTree(host), } ); } diff --git a/packages/angular/src/generators/setup-mfe/setup-mfe.spec.ts b/packages/angular/src/generators/setup-mfe/setup-mfe.spec.ts index 0f9810681ba43a..2563987c3a4d9b 100644 --- a/packages/angular/src/generators/setup-mfe/setup-mfe.spec.ts +++ b/packages/angular/src/generators/setup-mfe/setup-mfe.spec.ts @@ -43,6 +43,35 @@ describe('Init MFE', () => { } ); + test.each([ + ['app1', 'host'], + ['remote1', 'remote'], + ])( + 'should support a root tsconfig.json instead of tsconfig.base.json', + async (app, type: 'host' | 'remote') => { + // ARRANGE + host.rename('tsconfig.base.json', 'tsconfig.json'); + + // ACT + await setupMfe(host, { + appName: app, + mfeType: type, + }); + + // ASSERT + expect(host.exists(`apps/${app}/webpack.config.js`)).toBeTruthy(); + expect(host.exists(`apps/${app}/webpack.prod.config.js`)).toBeTruthy(); + + const webpackContents = host.read( + `apps/${app}/webpack.config.js`, + 'utf-8' + ); + expect(webpackContents).toContain( + "const tsConfigPath = process.env.NX_TSCONFIG_PATH ?? path.join(__dirname, '../../tsconfig.json');" + ); + } + ); + test.each([ ['app1', 'host'], ['remote1', 'remote'], diff --git a/packages/angular/src/generators/web-worker/lib/update-tsconfig.ts b/packages/angular/src/generators/web-worker/lib/update-tsconfig.ts index 941d6d2ab509fd..05d70469524c78 100644 --- a/packages/angular/src/generators/web-worker/lib/update-tsconfig.ts +++ b/packages/angular/src/generators/web-worker/lib/update-tsconfig.ts @@ -1,21 +1,17 @@ import type { Tree } from '@nrwl/devkit'; import { - getWorkspaceLayout, joinPathFragments, offsetFromRoot, readProjectConfiguration, updateJson, } from '@nrwl/devkit'; +import { getRootTsConfigPathInTree } from '@nrwl/workspace/src/utilities/typescript'; export function updateTsConfig(tree: Tree, project: string): void { - const workerTsConfigPath = joinPathFragments( - getWorkspaceLayout(tree).appsDir, - project, - 'tsconfig.worker.json' - ); const { root } = readProjectConfiguration(tree, project); + const workerTsConfigPath = joinPathFragments(root, 'tsconfig.worker.json'); updateJson(tree, workerTsConfigPath, (json) => { - json.extends = `${offsetFromRoot(root)}tsconfig.base.json`; + json.extends = offsetFromRoot(root) + getRootTsConfigPathInTree(tree); return json; }); } diff --git a/packages/angular/src/generators/web-worker/web-worker.spec.ts b/packages/angular/src/generators/web-worker/web-worker.spec.ts index 829762ba3709a9..3742de6fdf01aa 100644 --- a/packages/angular/src/generators/web-worker/web-worker.spec.ts +++ b/packages/angular/src/generators/web-worker/web-worker.spec.ts @@ -29,6 +29,16 @@ describe('webWorker generator', () => { ).toContain('"extends": "../../tsconfig.base.json"'); }); + it('should extend from tsconfig.json when used instead of tsconfig.base.json', async () => { + tree.rename('tsconfig.base.json', 'tsconfig.json'); + + await webWorkerGenerator(tree, { name: 'test-worker', project: appName }); + + expect( + tree.read(`apps/${appName}/tsconfig.worker.json`, 'utf-8') + ).toContain('"extends": "../../tsconfig.json"'); + }); + it('should format files', async () => { jest.spyOn(devkit, 'formatFiles'); diff --git a/packages/cypress/src/generators/cypress-project/cypress-project.spec.ts b/packages/cypress/src/generators/cypress-project/cypress-project.spec.ts index 1ae61257a8ffc4..783a8cf17cc52e 100644 --- a/packages/cypress/src/generators/cypress-project/cypress-project.spec.ts +++ b/packages/cypress/src/generators/cypress-project/cypress-project.spec.ts @@ -265,6 +265,30 @@ describe('schematic:cypress-project', () => { expect(tsconfigJson).toMatchSnapshot(); }); + it('should extend from tsconfig.base.json', async () => { + await cypressProjectGenerator(tree, { + ...defaultOptions, + name: 'my-app-e2e', + project: 'my-app', + }); + + const tsConfig = readJson(tree, 'apps/my-app-e2e/tsconfig.json'); + expect(tsConfig.extends).toBe('../../tsconfig.base.json'); + }); + + it('should support a root tsconfig.json instead of tsconfig.base.json', async () => { + tree.rename('tsconfig.base.json', 'tsconfig.json'); + + await cypressProjectGenerator(tree, { + ...defaultOptions, + name: 'my-app-e2e', + project: 'my-app', + }); + + const tsConfig = readJson(tree, 'apps/my-app-e2e/tsconfig.json'); + expect(tsConfig.extends).toBe('../../tsconfig.json'); + }); + describe('nested', () => { it('should update workspace.json', async () => { await cypressProjectGenerator(tree, { @@ -343,6 +367,32 @@ describe('schematic:cypress-project', () => { expect(tsconfigJson).toMatchSnapshot(); }); + + it('should extend from tsconfig.base.json', async () => { + await cypressProjectGenerator(tree, { + ...defaultOptions, + name: 'my-app-e2e', + project: 'my-app', + directory: 'my-dir', + }); + + const tsConfig = readJson(tree, 'apps/my-dir/my-app-e2e/tsconfig.json'); + expect(tsConfig.extends).toBe('../../../tsconfig.base.json'); + }); + + it('should support a root tsconfig.json instead of tsconfig.base.json', async () => { + tree.rename('tsconfig.base.json', 'tsconfig.json'); + + await cypressProjectGenerator(tree, { + ...defaultOptions, + name: 'my-app-e2e', + project: 'my-app', + directory: 'my-dir', + }); + + const tsConfig = readJson(tree, 'apps/my-dir/my-app-e2e/tsconfig.json'); + expect(tsConfig.extends).toBe('../../../tsconfig.json'); + }); }); describe('--project', () => { diff --git a/packages/cypress/src/generators/cypress-project/cypress-project.ts b/packages/cypress/src/generators/cypress-project/cypress-project.ts index 0f025e115165ea..0cdc274fc54405 100644 --- a/packages/cypress/src/generators/cypress-project/cypress-project.ts +++ b/packages/cypress/src/generators/cypress-project/cypress-project.ts @@ -16,6 +16,7 @@ import { } from '@nrwl/devkit'; import { Linter, lintProjectGenerator } from '@nrwl/linter'; import { runTasksInSerial } from '@nrwl/workspace/src/utilities/run-tasks-in-serial'; +import { getRootTsConfigPathInTree } from '@nrwl/workspace/src/utilities/typescript'; import { join } from 'path'; // app @@ -33,12 +34,14 @@ export interface CypressProjectSchema extends Schema { } function createFiles(tree: Tree, options: CypressProjectSchema) { + const rootOffset = offsetFromRoot(options.projectRoot); generateFiles(tree, join(__dirname, './files'), options.projectRoot, { tmpl: '', ...options, project: options.project || 'Project', ext: options.js ? 'js' : 'ts', - offsetFromRoot: offsetFromRoot(options.projectRoot), + offsetFromRoot: rootOffset, + rootTsConfigPath: rootOffset + getRootTsConfigPathInTree(tree), }); const cypressVersion = installedCypressVersion(); diff --git a/packages/cypress/src/generators/cypress-project/files/tsconfig.json b/packages/cypress/src/generators/cypress-project/files/tsconfig.json index ab92eb639b4f9b..5af7bb44f8560d 100644 --- a/packages/cypress/src/generators/cypress-project/files/tsconfig.json +++ b/packages/cypress/src/generators/cypress-project/files/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "<%= offsetFromRoot %>tsconfig.base.json", + "extends": "<%= rootTsConfigPath %>", "compilerOptions": { "sourceMap": false, "outDir": "<%= offsetFromRoot %>dist/out-tsc", diff --git a/packages/detox/src/generators/application/application.spec.ts b/packages/detox/src/generators/application/application.spec.ts index a98b83920ef965..f8b07b9525e21c 100644 --- a/packages/detox/src/generators/application/application.spec.ts +++ b/packages/detox/src/generators/application/application.spec.ts @@ -117,4 +117,34 @@ describe('detox application generator', () => { expect(project.implicitDependencies).toEqual(['my-dir-my-app']); }); }); + + describe('tsconfig', () => { + beforeEach(async () => { + addProjectConfiguration(tree, 'my-app', { root: 'my-app' }); + }); + + it('should extend from tsconfig.base.json', async () => { + await detoxApplicationGenerator(tree, { + name: 'my-app-e2e', + project: 'my-app', + linter: Linter.None, + }); + + const tsConfig = readJson(tree, 'apps/my-app-e2e/tsconfig.json'); + expect(tsConfig.extends).toEqual('../../tsconfig.base.json'); + }); + + it('should support a root tsconfig.json instead of tsconfig.base.json', async () => { + tree.rename('tsconfig.base.json', 'tsconfig.json'); + + await detoxApplicationGenerator(tree, { + name: 'my-app-e2e', + project: 'my-app', + linter: Linter.None, + }); + + const tsConfig = readJson(tree, 'apps/my-app-e2e/tsconfig.json'); + expect(tsConfig.extends).toEqual('../../tsconfig.json'); + }); + }); }); diff --git a/packages/detox/src/generators/application/files/app/tsconfig.json b/packages/detox/src/generators/application/files/app/tsconfig.json index c31c52e04ce1a5..eea95f339bebcc 100644 --- a/packages/detox/src/generators/application/files/app/tsconfig.json +++ b/packages/detox/src/generators/application/files/app/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "<%= offsetFromRoot %>tsconfig.base.json", + "extends": "<%= rootTsConfigPath %>", "files": [], "include": [], "references": [ diff --git a/packages/detox/src/generators/application/lib/create-files.ts b/packages/detox/src/generators/application/lib/create-files.ts index c625f987cbf025..7d9e3133220b08 100644 --- a/packages/detox/src/generators/application/lib/create-files.ts +++ b/packages/detox/src/generators/application/lib/create-files.ts @@ -1,11 +1,14 @@ import { generateFiles, offsetFromRoot, toJS, Tree } from '@nrwl/devkit'; +import { getRootTsConfigPathInTree } from '@nrwl/workspace/src/utilities/typescript'; import { join } from 'path'; import { NormalizedSchema } from './normalize-options'; export function createFiles(host: Tree, options: NormalizedSchema) { + const rootOffset = offsetFromRoot(options.projectRoot); generateFiles(host, join(__dirname, '../files/app'), options.projectRoot, { ...options, - offsetFromRoot: offsetFromRoot(options.projectRoot), + offsetFromRoot: rootOffset, + rootTsConfigPath: rootOffset + getRootTsConfigPathInTree(host), }); if (options.js) { toJS(host); diff --git a/packages/devkit/src/generators/format-files.ts b/packages/devkit/src/generators/format-files.ts index b9c87581590f23..d2c21fe9aa0013 100644 --- a/packages/devkit/src/generators/format-files.ts +++ b/packages/devkit/src/generators/format-files.ts @@ -116,16 +116,28 @@ function ensurePropertiesAreInNewLocations(tree: Tree) { function sortTsConfig(tree: Tree) { try { - const tsconfig = readJson(tree, 'tsconfig.base.json'); - const sortedPaths = sortObjectByKeys(tsconfig.compilerOptions.paths); - writeJson(tree, 'tsconfig.base.json', { + const tsConfigPath = getRootTsConfigPath(tree); + if (!tsConfigPath) { + return; + } + updateJson(tree, tsConfigPath, (tsconfig) => ({ ...tsconfig, compilerOptions: { ...tsconfig.compilerOptions, - paths: sortedPaths, + paths: sortObjectByKeys(tsconfig.compilerOptions.paths), }, - }); + })); } catch (e) { // catch noop } } + +function getRootTsConfigPath(tree: Tree): string | undefined { + for (const path of ['tsconfig.base.json', 'tsconfig.json']) { + if (tree.exists(path)) { + return path; + } + } + + return undefined; +} diff --git a/packages/js/src/generators/library/files/tsconfig.json__tmpl__ b/packages/js/src/generators/library/files/tsconfig.json__tmpl__ index f29aa94d03b51e..da6f51affaaa62 100644 --- a/packages/js/src/generators/library/files/tsconfig.json__tmpl__ +++ b/packages/js/src/generators/library/files/tsconfig.json__tmpl__ @@ -1,5 +1,5 @@ { - "extends": "<%= offsetFromRoot %>tsconfig.base.json", + "extends": "<%= rootTsConfigPath %>", "compilerOptions": { "module": "CommonJS"<% if (js) { %>, "allowJs": true<% } %> diff --git a/packages/js/src/generators/library/library.spec.ts b/packages/js/src/generators/library/library.spec.ts index a003b797c9de68..92444092ccd7fb 100644 --- a/packages/js/src/generators/library/library.spec.ts +++ b/packages/js/src/generators/library/library.spec.ts @@ -101,7 +101,7 @@ describe('lib', () => { }); }); - it('should update root tsconfig.json', async () => { + it('should update root tsconfig.base.json', async () => { await libraryGenerator(tree, { ...defaultOptions, name: 'myLib' }); const tsconfigJson = readJson(tree, '/tsconfig.base.json'); expect(tsconfigJson.compilerOptions.paths['@proj/my-lib']).toEqual([ @@ -109,7 +109,18 @@ describe('lib', () => { ]); }); - it('should update root tsconfig.json (no existing path mappings)', async () => { + it('should update root tsconfig.json when no tsconfig.base.json', async () => { + tree.rename('tsconfig.base.json', 'tsconfig.json'); + + await libraryGenerator(tree, { ...defaultOptions, name: 'myLib' }); + + const tsconfigJson = readJson(tree, 'tsconfig.json'); + expect(tsconfigJson.compilerOptions.paths['@proj/my-lib']).toEqual([ + 'libs/my-lib/src/index.ts', + ]); + }); + + it('should update root tsconfig.base.json (no existing path mappings)', async () => { updateJson(tree, 'tsconfig.base.json', (json) => { json.compilerOptions.paths = undefined; return json; @@ -150,6 +161,15 @@ describe('lib', () => { } `); }); + + it('should extend from root tsconfig.json when no tsconfig.base.json', async () => { + tree.rename('tsconfig.base.json', 'tsconfig.json'); + + await libraryGenerator(tree, { ...defaultOptions, name: 'myLib' }); + + const tsconfigJson = readJson(tree, 'libs/my-lib/tsconfig.json'); + expect(tsconfigJson.extends).toBe('../../tsconfig.json'); + }); }); describe('nested', () => { @@ -218,7 +238,7 @@ describe('lib', () => { ); }); - it('should update tsconfig.json', async () => { + it('should update root tsconfig.base.json', async () => { await libraryGenerator(tree, { ...defaultOptions, name: 'myLib', @@ -233,6 +253,24 @@ describe('lib', () => { ).toBeUndefined(); }); + it('should update root tsconfig.json when no tsconfig.base.json', async () => { + tree.rename('tsconfig.base.json', 'tsconfig.json'); + + await libraryGenerator(tree, { + ...defaultOptions, + name: 'myLib', + directory: 'myDir', + }); + + const tsconfigJson = readJson(tree, '/tsconfig.json'); + expect( + tsconfigJson.compilerOptions.paths['@proj/my-dir/my-lib'] + ).toEqual(['libs/my-dir/my-lib/src/index.ts']); + expect( + tsconfigJson.compilerOptions.paths['my-dir-my-lib/*'] + ).toBeUndefined(); + }); + it('should create a local tsconfig.json', async () => { await libraryGenerator(tree, { ...defaultOptions, @@ -250,6 +288,30 @@ describe('lib', () => { }, ]); }); + + it('should extend from root tsconfig.base.json', async () => { + await libraryGenerator(tree, { + ...defaultOptions, + name: 'myLib', + directory: 'myDir', + }); + + const tsconfigJson = readJson(tree, 'libs/my-dir/my-lib/tsconfig.json'); + expect(tsconfigJson.extends).toBe('../../../tsconfig.base.json'); + }); + + it('should extend from root tsconfig.json when no tsconfig.base.json', async () => { + tree.rename('tsconfig.base.json', 'tsconfig.json'); + + await libraryGenerator(tree, { + ...defaultOptions, + name: 'myLib', + directory: 'myDir', + }); + + const tsconfigJson = readJson(tree, 'libs/my-dir/my-lib/tsconfig.json'); + expect(tsconfigJson.extends).toBe('../../../tsconfig.json'); + }); }); describe('--no-strict', () => { diff --git a/packages/js/src/generators/library/library.ts b/packages/js/src/generators/library/library.ts index 2b6a7e9a093cb9..1b69b6c7b630cc 100644 --- a/packages/js/src/generators/library/library.ts +++ b/packages/js/src/generators/library/library.ts @@ -20,6 +20,7 @@ import { Linter, lintProjectGenerator } from '@nrwl/linter'; import { jestProjectGenerator } from '@nrwl/jest'; import { addSwcDependencies } from '../../utils/swc/add-swc-dependencies'; import { addSwcConfig } from '../../utils/swc/add-swc-config'; +import { getRootTsConfigPathInTree } from '@nrwl/workspace/src/utilities/typescript'; export async function libraryGenerator( tree: Tree, @@ -158,6 +159,7 @@ function updateTsConfig(tree: Tree, options: NormalizedSchema) { function createFiles(tree: Tree, options: NormalizedSchema, filesDir: string) { const { className, name, propertyName } = names(options.name); + const rootOffset = offsetFromRoot(options.projectRoot); generateFiles(tree, filesDir, options.projectRoot, { ...options, dot: '.', @@ -168,7 +170,8 @@ function createFiles(tree: Tree, options: NormalizedSchema, filesDir: string) { cliCommand: 'nx', strict: undefined, tmpl: '', - offsetFromRoot: offsetFromRoot(options.projectRoot), + offsetFromRoot: rootOffset, + rootTsConfigPath: rootOffset + getRootTsConfigPathInTree(tree), buildable: options.buildable === true, hasUnitTestRunner: options.unitTestRunner !== 'none', }); @@ -298,7 +301,7 @@ function getCaseAwareFileName(options: { } function updateRootTsConfig(host: Tree, options: NormalizedSchema) { - updateJson(host, 'tsconfig.base.json', (json) => { + updateJson(host, getRootTsConfigPathInTree(host), (json) => { const c = json.compilerOptions; c.paths = c.paths || {}; delete c.paths[options.name]; diff --git a/packages/linter/src/generators/workspace-rules-project/files/tsconfig.json__tmpl__ b/packages/linter/src/generators/workspace-rules-project/files/tsconfig.json__tmpl__ index 10bb5c8c3d42dc..709828769d96ee 100644 --- a/packages/linter/src/generators/workspace-rules-project/files/tsconfig.json__tmpl__ +++ b/packages/linter/src/generators/workspace-rules-project/files/tsconfig.json__tmpl__ @@ -1,5 +1,5 @@ { - "extends": "<%= offsetFromRoot %>tsconfig.base.json", + "extends": "<%= rootTsConfigPath %>", "compilerOptions": { "module": "commonjs" }, diff --git a/packages/linter/src/generators/workspace-rules-project/workspace-rules-project.spec.ts b/packages/linter/src/generators/workspace-rules-project/workspace-rules-project.spec.ts index 026d6dc757d038..164206b967674d 100644 --- a/packages/linter/src/generators/workspace-rules-project/workspace-rules-project.spec.ts +++ b/packages/linter/src/generators/workspace-rules-project/workspace-rules-project.spec.ts @@ -50,6 +50,22 @@ describe('@nrwl/linter:workspace-rules-project', () => { ).toMatchSnapshot(); }); + it('should extend from root tsconfig.base.json', async () => { + await lintWorkspaceRulesProjectGenerator(tree); + + const tsConfig = readJson(tree, 'tools/eslint-rules/tsconfig.json'); + expect(tsConfig.extends).toBe('../../tsconfig.base.json'); + }); + + it('should extend from root tsconfig.json when no tsconfig.base.json', async () => { + tree.rename('tsconfig.base.json', 'tsconfig.json'); + + await lintWorkspaceRulesProjectGenerator(tree); + + const tsConfig = readJson(tree, 'tools/eslint-rules/tsconfig.json'); + expect(tsConfig.extends).toBe('../../tsconfig.json'); + }); + it('should create a project with a test target', async () => { await lintWorkspaceRulesProjectGenerator(tree); diff --git a/packages/linter/src/generators/workspace-rules-project/workspace-rules-project.ts b/packages/linter/src/generators/workspace-rules-project/workspace-rules-project.ts index 7fe2ac9913bb3b..5bbb6f03fdded2 100644 --- a/packages/linter/src/generators/workspace-rules-project/workspace-rules-project.ts +++ b/packages/linter/src/generators/workspace-rules-project/workspace-rules-project.ts @@ -11,6 +11,7 @@ import { updateWorkspaceConfiguration, } from '@nrwl/devkit'; import { addPropertyToJestConfig, jestProjectGenerator } from '@nrwl/jest'; +import { getRootTsConfigPathInTree } from '@nrwl/workspace/src/utilities/typescript'; import { join } from 'path'; import { workspaceLintPluginDir } from '../../utils/workspace-lint-rules'; @@ -33,9 +34,11 @@ export async function lintWorkspaceRulesProjectGenerator(tree: Tree) { }); // Generate the required files + const rootOffset = offsetFromRoot(WORKSPACE_PLUGIN_DIR); generateFiles(tree, join(__dirname, 'files'), workspaceLintPluginDir, { tmpl: '', - offsetFromRoot: offsetFromRoot(WORKSPACE_PLUGIN_DIR), + offsetFromRoot: rootOffset, + rootTsConfigPath: rootOffset + getRootTsConfigPathInTree(tree), }); /** diff --git a/packages/next/src/generators/application/application.spec.ts b/packages/next/src/generators/application/application.spec.ts index 3f1379fd1f7ab8..66cf6e23c3aee0 100644 --- a/packages/next/src/generators/application/application.spec.ts +++ b/packages/next/src/generators/application/application.spec.ts @@ -61,6 +61,30 @@ describe('app', () => { expect(tree.exists('apps/my-app/specs/index.spec.tsx')).toBeTruthy(); expect(tree.exists('apps/my-app/pages/index.module.css')).toBeTruthy(); }); + + it('should extend from root tsconfig.base.json', async () => { + await applicationGenerator(tree, { + name: 'myApp', + style: 'css', + standaloneConfig: false, + }); + + const tsConfig = readJson(tree, 'apps/my-app/tsconfig.json'); + expect(tsConfig.extends).toBe('../../tsconfig.base.json'); + }); + + it('should extend from root tsconfig.json when no tsconfig.base.json', async () => { + tree.rename('tsconfig.base.json', 'tsconfig.json'); + + await applicationGenerator(tree, { + name: 'myApp', + style: 'css', + standaloneConfig: false, + }); + + const tsConfig = readJson(tree, 'apps/my-app/tsconfig.json'); + expect(tsConfig.extends).toBe('../../tsconfig.json'); + }); }); describe('--style scss', () => { diff --git a/packages/next/src/generators/application/files/tsconfig.json__tmpl__ b/packages/next/src/generators/application/files/tsconfig.json__tmpl__ index 1d4d8b2e69b205..1237169a451c5b 100644 --- a/packages/next/src/generators/application/files/tsconfig.json__tmpl__ +++ b/packages/next/src/generators/application/files/tsconfig.json__tmpl__ @@ -1,5 +1,5 @@ { - "extends": "<%= offsetFromRoot %>tsconfig.base.json", + "extends": "<%= rootTsConfigPath %>", "compilerOptions": { "jsx": "preserve", <% if (style === '@emotion/styled') { %>"jsxImportSource": "@emotion/react",<% } %> diff --git a/packages/next/src/generators/application/lib/create-application-files.ts b/packages/next/src/generators/application/lib/create-application-files.ts index 6e1eaecd81aa69..d4d0c832192ac9 100644 --- a/packages/next/src/generators/application/lib/create-application-files.ts +++ b/packages/next/src/generators/application/lib/create-application-files.ts @@ -5,13 +5,15 @@ import { createStyleRules, } from './create-application-files.helpers'; import { generateFiles, names, offsetFromRoot, toJS, Tree } from '@nrwl/devkit'; +import { getRootTsConfigPathInTree } from '@nrwl/workspace/src/utilities/typescript'; export function createApplicationFiles(host: Tree, options: NormalizedSchema) { const templateVariables = { ...names(options.name), ...options, tmpl: '', - offsetFromRoot: offsetFromRoot(options.appProjectRoot), + rootTsConfigPath: + offsetFromRoot(options.appProjectRoot) + getRootTsConfigPathInTree(host), appContent: createAppJsx(options.name), styleContent: createStyleRules(), pageStyleContent: `.page {}`, diff --git a/packages/node/src/executors/webpack/webpack.impl.ts b/packages/node/src/executors/webpack/webpack.impl.ts index 6a10fa798f4d27..e28876e43c86b5 100644 --- a/packages/node/src/executors/webpack/webpack.impl.ts +++ b/packages/node/src/executors/webpack/webpack.impl.ts @@ -7,10 +7,11 @@ import { checkDependentProjectsHaveBeenBuilt, createTmpTsConfig, } from '@nrwl/workspace/src/utilities/buildable-libs-utils'; +import { getRootTsConfigPath } from '@nrwl/workspace/src/utilities/typescript'; import { map, tap } from 'rxjs/operators'; import { eachValueFrom } from 'rxjs-for-await'; -import { join, resolve } from 'path'; +import { resolve } from 'path'; import { register } from 'ts-node'; import { getNodeWebpackConfig } from '../../utils/node.config'; @@ -19,9 +20,6 @@ import { normalizeBuildOptions } from '../../utils/normalize'; import { generatePackageJson } from '../../utils/generate-package-json'; import { runWebpack } from '../../utils/run-webpack'; -import { existsSync } from 'fs'; -import { appRootPath } from '@nrwl/tao/src/utils/app-root'; - export type NodeBuildEvent = { outfile: string; success: boolean; @@ -110,9 +108,9 @@ export async function* webpackExecutor( } function registerTsNode() { - const rootTsConfig = join(appRootPath, 'tsconfig.base.json'); + const rootTsConfig = getRootTsConfigPath(); register({ - ...(existsSync(rootTsConfig) ? { project: rootTsConfig } : null), + ...(rootTsConfig ? { project: rootTsConfig } : null), }); } diff --git a/packages/node/src/generators/application/application.spec.ts b/packages/node/src/generators/application/application.spec.ts index 97259b213eb273..f64cfdb486f707 100644 --- a/packages/node/src/generators/application/application.spec.ts +++ b/packages/node/src/generators/application/application.spec.ts @@ -165,6 +165,18 @@ describe('app', () => { } `); }); + + it('should extend from root tsconfig.json when no tsconfig.base.json', async () => { + tree.rename('tsconfig.base.json', 'tsconfig.json'); + + await applicationGenerator(tree, { + name: 'myNodeApp', + standaloneConfig: false, + }); + + const tsconfig = readJson(tree, 'apps/my-node-app/tsconfig.json'); + expect(tsconfig.extends).toBe('../../tsconfig.json'); + }); }); describe('nested', () => { diff --git a/packages/node/src/generators/application/application.ts b/packages/node/src/generators/application/application.ts index f5ad92e0eabe32..752d349e7fcbb6 100644 --- a/packages/node/src/generators/application/application.ts +++ b/packages/node/src/generators/application/application.ts @@ -28,6 +28,7 @@ import { runTasksInSerial } from '@nrwl/workspace/src/utilities/run-tasks-in-ser import { Schema } from './schema'; import { initGenerator } from '../init/init'; +import { getRootTsConfigPathInTree } from '@nrwl/workspace/src/utilities/typescript'; export interface NormalizedSchema extends Schema { appProjectRoot: string; @@ -108,11 +109,13 @@ function addProject(tree: Tree, options: NormalizedSchema) { } function addAppFiles(tree: Tree, options: NormalizedSchema) { + const offset = offsetFromRoot(options.appProjectRoot); generateFiles(tree, join(__dirname, './files/app'), options.appProjectRoot, { tmpl: '', name: options.name, root: options.appProjectRoot, - offset: offsetFromRoot(options.appProjectRoot), + offset, + rootTsConfigPath: offset + getRootTsConfigPathInTree(tree), }); if (options.js) { toJS(tree); diff --git a/packages/node/src/generators/application/files/app/tsconfig.json b/packages/node/src/generators/application/files/app/tsconfig.json index a6db50db59d2d1..595598eddbf3e9 100644 --- a/packages/node/src/generators/application/files/app/tsconfig.json +++ b/packages/node/src/generators/application/files/app/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "<%= offset %>tsconfig.base.json", + "extends": "<%= rootTsConfigPath %>", "files": [], "include": [], "references": [ diff --git a/packages/nx-plugin/src/generators/e2e-project/e2e.spec.ts b/packages/nx-plugin/src/generators/e2e-project/e2e.spec.ts index 558f82dadfb89b..ffd2484d273d69 100644 --- a/packages/nx-plugin/src/generators/e2e-project/e2e.spec.ts +++ b/packages/nx-plugin/src/generators/e2e-project/e2e.spec.ts @@ -54,6 +54,32 @@ describe('NxPlugin e2e-project Generator', () => { ).toBeTruthy(); }); + it('should extend from root tsconfig.base.json', async () => { + await e2eProjectGenerator(tree, { + pluginName: 'my-plugin', + pluginOutputPath: `dist/libs/my-plugin`, + npmPackageName: '@proj/my-plugin', + standaloneConfig: false, + }); + + const tsConfig = readJson(tree, 'apps/my-plugin-e2e/tsconfig.json'); + expect(tsConfig.extends).toEqual('../../tsconfig.base.json'); + }); + + it('should extend from root tsconfig.json when no tsconfig.base.json', async () => { + tree.rename('tsconfig.base.json', 'tsconfig.json'); + + await e2eProjectGenerator(tree, { + pluginName: 'my-plugin', + pluginOutputPath: `dist/libs/my-plugin`, + npmPackageName: '@proj/my-plugin', + standaloneConfig: false, + }); + + const tsConfig = readJson(tree, 'apps/my-plugin-e2e/tsconfig.json'); + expect(tsConfig.extends).toEqual('../../tsconfig.json'); + }); + it('should set project root with the directory option', async () => { await e2eProjectGenerator(tree, { pluginName: 'my-plugin', diff --git a/packages/nx-plugin/src/generators/e2e-project/e2e.ts b/packages/nx-plugin/src/generators/e2e-project/e2e.ts index 5f02498b414e0c..eabb9caa006d54 100644 --- a/packages/nx-plugin/src/generators/e2e-project/e2e.ts +++ b/packages/nx-plugin/src/generators/e2e-project/e2e.ts @@ -13,6 +13,7 @@ import type { Tree } from '@nrwl/devkit'; import type { Schema } from './schema'; import * as path from 'path'; import { jestProjectGenerator } from '@nrwl/jest'; +import { getRootTsConfigPathInTree } from '@nrwl/workspace/src/utilities/typescript'; interface NormalizedSchema extends Schema { projectRoot: string; @@ -51,7 +52,8 @@ function addFiles(host: Tree, options: NormalizedSchema) { generateFiles(host, path.join(__dirname, './files'), options.projectRoot, { ...options, tmpl: '', - offsetFromRoot: offsetFromRoot(options.projectRoot), + rootTsConfigPath: + offsetFromRoot(options.projectRoot) + getRootTsConfigPathInTree(host), }); } diff --git a/packages/nx-plugin/src/generators/e2e-project/files/tsconfig.json__tmpl__ b/packages/nx-plugin/src/generators/e2e-project/files/tsconfig.json__tmpl__ index c31c52e04ce1a5..eea95f339bebcc 100644 --- a/packages/nx-plugin/src/generators/e2e-project/files/tsconfig.json__tmpl__ +++ b/packages/nx-plugin/src/generators/e2e-project/files/tsconfig.json__tmpl__ @@ -1,5 +1,5 @@ { - "extends": "<%= offsetFromRoot %>tsconfig.base.json", + "extends": "<%= rootTsConfigPath %>", "files": [], "include": [], "references": [ diff --git a/packages/react-native/src/generators/application/application.spec.ts b/packages/react-native/src/generators/application/application.spec.ts index 6485fe26e7db80..149246bbde9613 100644 --- a/packages/react-native/src/generators/application/application.spec.ts +++ b/packages/react-native/src/generators/application/application.spec.ts @@ -62,4 +62,18 @@ describe('app', () => { expect(appTree.exists('apps/my-app/.eslintrc.json')).toBe(true); }); + + it('should extend from root tsconfig.json when no tsconfig.base.json', async () => { + appTree.rename('tsconfig.base.json', 'tsconfig.json'); + + await reactNativeApplicationGenerator(appTree, { + name: 'myApp', + displayName: 'myApp', + linter: Linter.EsLint, + e2eTestRunner: 'none', + }); + + const tsconfig = readJson(appTree, 'apps/my-app/tsconfig.json'); + expect(tsconfig.extends).toEqual('../../tsconfig.json'); + }); }); diff --git a/packages/react-native/src/generators/application/files/app/tsconfig.json.template b/packages/react-native/src/generators/application/files/app/tsconfig.json.template index 6038dc3da8349d..bed1e573009c6e 100644 --- a/packages/react-native/src/generators/application/files/app/tsconfig.json.template +++ b/packages/react-native/src/generators/application/files/app/tsconfig.json.template @@ -1,5 +1,5 @@ { - "extends": "<%= offsetFromRoot %>tsconfig.base.json", + "extends": "<%= rootTsConfigPath %>", "compilerOptions": { "allowSyntheticDefaultImports": true, "jsx": "react-native", diff --git a/packages/react-native/src/generators/application/lib/create-application-files.ts b/packages/react-native/src/generators/application/lib/create-application-files.ts index 22a1fe04f3eeab..5667b5d0a34ff1 100644 --- a/packages/react-native/src/generators/application/lib/create-application-files.ts +++ b/packages/react-native/src/generators/application/lib/create-application-files.ts @@ -1,11 +1,14 @@ import { generateFiles, offsetFromRoot, toJS, Tree } from '@nrwl/devkit'; +import { getRootTsConfigPathInTree } from '@nrwl/workspace/src/utilities/typescript'; import { join } from 'path'; import { NormalizedSchema } from './normalize-options'; export function createApplicationFiles(host: Tree, options: NormalizedSchema) { + const rootOffset = offsetFromRoot(options.appProjectRoot); generateFiles(host, join(__dirname, '../files/app'), options.appProjectRoot, { ...options, - offsetFromRoot: offsetFromRoot(options.appProjectRoot), + offsetFromRoot: rootOffset, + rootTsConfigPath: rootOffset + getRootTsConfigPathInTree(host), }); if (options.unitTestRunner === 'none') { host.delete(join(options.appProjectRoot, `/src/app/App.spec.tsx`)); diff --git a/packages/react-native/src/generators/library/files/lib/tsconfig.json__tmpl__ b/packages/react-native/src/generators/library/files/lib/tsconfig.json__tmpl__ index 514efa55d71046..d9affba903da06 100644 --- a/packages/react-native/src/generators/library/files/lib/tsconfig.json__tmpl__ +++ b/packages/react-native/src/generators/library/files/lib/tsconfig.json__tmpl__ @@ -1,5 +1,5 @@ { - "extends": "<%= offsetFromRoot %>tsconfig.base.json", + "extends": "<%= rootTsConfigPath %>", "compilerOptions": { "jsx": "react-jsx", "allowJs": true, diff --git a/packages/react-native/src/generators/library/library.spec.ts b/packages/react-native/src/generators/library/library.spec.ts index 67f96a2c271755..985673fe9d681f 100644 --- a/packages/react-native/src/generators/library/library.spec.ts +++ b/packages/react-native/src/generators/library/library.spec.ts @@ -37,7 +37,7 @@ describe('lib', () => { expect(workspaceJson.projects['my-lib'].tags).toEqual(['one', 'two']); }); - it('should update tsconfig.base.json', async () => { + it('should update root tsconfig.base.json', async () => { await libraryGenerator(appTree, defaultSchema); const tsconfigJson = readJson(appTree, '/tsconfig.base.json'); expect(tsconfigJson.compilerOptions.paths['@proj/my-lib']).toEqual([ @@ -45,6 +45,17 @@ describe('lib', () => { ]); }); + it('should update root tsconfig.json when no tsconfig.base.json', async () => { + appTree.rename('tsconfig.base.json', 'tsconfig.json'); + + await libraryGenerator(appTree, defaultSchema); + + const tsconfigJson = readJson(appTree, '/tsconfig.json'); + expect(tsconfigJson.compilerOptions.paths['@proj/my-lib']).toEqual([ + 'libs/my-lib/src/index.ts', + ]); + }); + it('should update root tsconfig.base.json (no existing path mappings)', async () => { updateJson(appTree, 'tsconfig.base.json', (json) => { json.compilerOptions.paths = undefined; @@ -60,7 +71,9 @@ describe('lib', () => { it('should create a local tsconfig.json', async () => { await libraryGenerator(appTree, defaultSchema); + const tsconfigJson = readJson(appTree, 'libs/my-lib/tsconfig.json'); + expect(tsconfigJson.extends).toBe('../../tsconfig.base.json'); expect(tsconfigJson.references).toEqual([ { path: './tsconfig.lib.json', @@ -79,6 +92,15 @@ describe('lib', () => { ); }); + it('should extend from root tsconfig.json when no tsconfig.base.json', async () => { + appTree.rename('tsconfig.base.json', 'tsconfig.json'); + + await libraryGenerator(appTree, defaultSchema); + + const tsconfigJson = readJson(appTree, 'libs/my-lib/tsconfig.json'); + expect(tsconfigJson.extends).toBe('../../tsconfig.json'); + }); + it('should extend the local tsconfig.json with tsconfig.spec.json', async () => { await libraryGenerator(appTree, defaultSchema); const tsconfigJson = readJson(appTree, 'libs/my-lib/tsconfig.spec.json'); @@ -140,7 +162,7 @@ describe('lib', () => { }); }); - it('should update tsconfig.base.json', async () => { + it('should update root tsconfig.base.json', async () => { await libraryGenerator(appTree, { ...defaultSchema, directory: 'myDir' }); const tsconfigJson = readJson(appTree, '/tsconfig.base.json'); expect(tsconfigJson.compilerOptions.paths['@proj/my-dir/my-lib']).toEqual( @@ -151,6 +173,20 @@ describe('lib', () => { ).toBeUndefined(); }); + it('should update root tsconfig.json when no tsconfig.base.json', async () => { + appTree.rename('tsconfig.base.json', 'tsconfig.json'); + + await libraryGenerator(appTree, { ...defaultSchema, directory: 'myDir' }); + + const tsconfigJson = readJson(appTree, '/tsconfig.json'); + expect(tsconfigJson.compilerOptions.paths['@proj/my-dir/my-lib']).toEqual( + ['libs/my-dir/my-lib/src/index.ts'] + ); + expect( + tsconfigJson.compilerOptions.paths['my-dir-my-lib/*'] + ).toBeUndefined(); + }); + it('should create a local tsconfig.json', async () => { await libraryGenerator(appTree, { ...defaultSchema, directory: 'myDir' }); @@ -158,6 +194,7 @@ describe('lib', () => { appTree, 'libs/my-dir/my-lib/tsconfig.json' ); + expect(tsconfigJson.extends).toBe('../../../tsconfig.base.json'); expect(tsconfigJson.references).toEqual([ { path: './tsconfig.lib.json', @@ -167,6 +204,18 @@ describe('lib', () => { }, ]); }); + + it('should extend from root tsconfig.json when no tsconfig.base.json', async () => { + appTree.rename('tsconfig.base.json', 'tsconfig.json'); + + await libraryGenerator(appTree, { ...defaultSchema, directory: 'myDir' }); + + const tsconfigJson = readJson( + appTree, + 'libs/my-dir/my-lib/tsconfig.json' + ); + expect(tsconfigJson.extends).toBe('../../../tsconfig.json'); + }); }); describe('--unit-test-runner none', () => { diff --git a/packages/react-native/src/generators/library/library.ts b/packages/react-native/src/generators/library/library.ts index 4715cdde7f640e..01fe14ef91a939 100644 --- a/packages/react-native/src/generators/library/library.ts +++ b/packages/react-native/src/generators/library/library.ts @@ -19,6 +19,7 @@ import { addLinting } from '../../utils/add-linting'; import { addJest } from '../../utils/add-jest'; import { NormalizedSchema, normalizeOptions } from './lib/normalize-options'; import { Schema } from './schema'; +import { getRootTsConfigPathInTree } from '@nrwl/workspace/src/utilities/typescript'; export async function reactNativeLibraryGenerator( host: Tree, @@ -130,7 +131,7 @@ function updateTsConfig(tree: Tree, options: NormalizedSchema) { } function updateBaseTsConfig(host: Tree, options: NormalizedSchema) { - updateJson(host, 'tsconfig.base.json', (json) => { + updateJson(host, getRootTsConfigPathInTree(host), (json) => { const c = json.compilerOptions; c.paths = c.paths || {}; delete c.paths[options.name]; @@ -155,6 +156,7 @@ function updateBaseTsConfig(host: Tree, options: NormalizedSchema) { } function createFiles(host: Tree, options: NormalizedSchema) { + const rootOffset = offsetFromRoot(options.projectRoot); generateFiles( host, joinPathFragments(__dirname, './files/lib'), @@ -163,7 +165,8 @@ function createFiles(host: Tree, options: NormalizedSchema) { ...options, ...names(options.name), tmpl: '', - offsetFromRoot: offsetFromRoot(options.projectRoot), + offsetFromRoot: rootOffset, + rootTsConfigPath: rootOffset + getRootTsConfigPathInTree(host), } ); diff --git a/packages/react/src/generators/application/application.spec.ts b/packages/react/src/generators/application/application.spec.ts index 5661587aee45b4..d03f3d4f2279ef 100644 --- a/packages/react/src/generators/application/application.spec.ts +++ b/packages/react/src/generators/application/application.spec.ts @@ -128,6 +128,22 @@ Object { } `); }); + + it('should extend from root tsconfig.base.json', async () => { + await applicationGenerator(appTree, schema); + + const tsConfig = readJson(appTree, 'apps/my-app/tsconfig.json'); + expect(tsConfig.extends).toEqual('../../tsconfig.base.json'); + }); + + it('should extend from root tsconfig.json when no tsconfig.base.json', async () => { + appTree.rename('tsconfig.base.json', 'tsconfig.json'); + + await applicationGenerator(appTree, schema); + + const tsConfig = readJson(appTree, 'apps/my-app/tsconfig.json'); + expect(tsConfig.extends).toEqual('../../tsconfig.json'); + }); }); describe('nested', () => { diff --git a/packages/react/src/generators/application/files/common/tsconfig.json__tmpl__ b/packages/react/src/generators/application/files/common/tsconfig.json__tmpl__ index 877486407b8df1..fc23421ea4caf0 100644 --- a/packages/react/src/generators/application/files/common/tsconfig.json__tmpl__ +++ b/packages/react/src/generators/application/files/common/tsconfig.json__tmpl__ @@ -1,5 +1,5 @@ { - "extends": "<%= offsetFromRoot %>tsconfig.base.json", + "extends": "<%= rootTsConfigPath %>", "compilerOptions": { "jsx": "react-jsx", <% if (style === '@emotion/styled') { %>"jsxImportSource": "@emotion/react",<% } %> diff --git a/packages/react/src/generators/application/lib/create-application-files.ts b/packages/react/src/generators/application/lib/create-application-files.ts index 303bd263aae511..650978e8b5c851 100644 --- a/packages/react/src/generators/application/lib/create-application-files.ts +++ b/packages/react/src/generators/application/lib/create-application-files.ts @@ -9,6 +9,7 @@ import { updateJson, } from '@nrwl/devkit'; import { join } from 'path'; +import { getRootTsConfigPathInTree } from '@nrwl/workspace/src/utilities/typescript'; function updateTsConfig(host: Tree, options: NormalizedSchema) { updateJson( @@ -46,11 +47,13 @@ export function createApplicationFiles(host: Tree, options: NormalizedSchema) { styleSolutionSpecificAppFiles = '../files/css-module'; } + const rootOffset = offsetFromRoot(options.appProjectRoot); const templateVariables = { ...names(options.name), ...options, tmpl: '', - offsetFromRoot: offsetFromRoot(options.appProjectRoot), + offsetFromRoot: rootOffset, + rootTsConfigPath: rootOffset + getRootTsConfigPathInTree(host), }; generateFiles( diff --git a/packages/react/src/generators/library/files/lib/tsconfig.json__tmpl__ b/packages/react/src/generators/library/files/lib/tsconfig.json__tmpl__ index 1e7db70dbfadf2..c5c5460225c538 100644 --- a/packages/react/src/generators/library/files/lib/tsconfig.json__tmpl__ +++ b/packages/react/src/generators/library/files/lib/tsconfig.json__tmpl__ @@ -1,5 +1,5 @@ { - "extends": "<%= offsetFromRoot %>tsconfig.base.json", + "extends": "<%= rootTsConfigPath %>", "compilerOptions": { "jsx": "react-jsx", <% if (style === '@emotion/styled') { %>"jsxImportSource": "@emotion/react",<% } %> diff --git a/packages/react/src/generators/library/library.spec.ts b/packages/react/src/generators/library/library.spec.ts index 0367541397aa2c..096f89c6ff0e3a 100644 --- a/packages/react/src/generators/library/library.spec.ts +++ b/packages/react/src/generators/library/library.spec.ts @@ -68,7 +68,7 @@ describe('lib', () => { }); }); - it('should update tsconfig.base.json', async () => { + it('should update root tsconfig.base.json', async () => { await libraryGenerator(appTree, defaultSchema); const tsconfigJson = readJson(appTree, '/tsconfig.base.json'); expect(tsconfigJson.compilerOptions.paths['@proj/my-lib']).toEqual([ @@ -76,6 +76,17 @@ describe('lib', () => { ]); }); + it('should update root tsconfig.json when no tsconfig.base.json', async () => { + appTree.rename('tsconfig.base.json', 'tsconfig.json'); + + await libraryGenerator(appTree, defaultSchema); + + const tsconfigJson = readJson(appTree, '/tsconfig.json'); + expect(tsconfigJson.compilerOptions.paths['@proj/my-lib']).toEqual([ + 'libs/my-lib/src/index.ts', + ]); + }); + it('should update root tsconfig.base.json (no existing path mappings)', async () => { updateJson(appTree, 'tsconfig.base.json', (json) => { json.compilerOptions.paths = undefined; @@ -91,7 +102,9 @@ describe('lib', () => { it('should create a local tsconfig.json', async () => { await libraryGenerator(appTree, defaultSchema); + const tsconfigJson = readJson(appTree, 'libs/my-lib/tsconfig.json'); + expect(tsconfigJson.extends).toBe('../../tsconfig.base.json'); expect(tsconfigJson.references).toEqual([ { path: './tsconfig.lib.json', @@ -114,6 +127,15 @@ describe('lib', () => { ); }); + it('should extend from root tsconfig.json when no tsconfig.base.json', async () => { + appTree.rename('tsconfig.base.json', 'tsconfig.json'); + + await libraryGenerator(appTree, defaultSchema); + + const tsconfigJson = readJson(appTree, 'libs/my-lib/tsconfig.json'); + expect(tsconfigJson.extends).toBe('../../tsconfig.json'); + }); + it('should extend the local tsconfig.json with tsconfig.spec.json', async () => { await libraryGenerator(appTree, defaultSchema); const tsconfigJson = readJson(appTree, 'libs/my-lib/tsconfig.spec.json'); @@ -254,7 +276,7 @@ describe('lib', () => { }); }); - it('should update tsconfig.base.json', async () => { + it('should update root tsconfig.base.json', async () => { await libraryGenerator(appTree, { ...defaultSchema, directory: 'myDir' }); const tsconfigJson = readJson(appTree, '/tsconfig.base.json'); expect(tsconfigJson.compilerOptions.paths['@proj/my-dir/my-lib']).toEqual( @@ -265,6 +287,20 @@ describe('lib', () => { ).toBeUndefined(); }); + it('should update root tsconfig.json when no tsconfig.base.json', async () => { + appTree.rename('tsconfig.base.json', 'tsconfig.json'); + + await libraryGenerator(appTree, { ...defaultSchema, directory: 'myDir' }); + + const tsconfigJson = readJson(appTree, '/tsconfig.json'); + expect(tsconfigJson.compilerOptions.paths['@proj/my-dir/my-lib']).toEqual( + ['libs/my-dir/my-lib/src/index.ts'] + ); + expect( + tsconfigJson.compilerOptions.paths['my-dir-my-lib/*'] + ).toBeUndefined(); + }); + it('should create a local tsconfig.json', async () => { await libraryGenerator(appTree, { ...defaultSchema, directory: 'myDir' }); @@ -272,6 +308,7 @@ describe('lib', () => { appTree, 'libs/my-dir/my-lib/tsconfig.json' ); + expect(tsconfigJson.extends).toBe('../../../tsconfig.base.json'); expect(tsconfigJson.references).toEqual([ { path: './tsconfig.lib.json', @@ -281,6 +318,18 @@ describe('lib', () => { }, ]); }); + + it('should extend from root tsconfig.json when no tsconfig.base.json', async () => { + appTree.rename('tsconfig.base.json', 'tsconfig.json'); + + await libraryGenerator(appTree, { ...defaultSchema, directory: 'myDir' }); + + const tsconfigJson = readJson( + appTree, + 'libs/my-dir/my-lib/tsconfig.json' + ); + expect(tsconfigJson.extends).toBe('../../../tsconfig.json'); + }); }); describe('--style scss', () => { diff --git a/packages/react/src/generators/library/library.ts b/packages/react/src/generators/library/library.ts index 1be7064bab4893..d7722615c91c01 100644 --- a/packages/react/src/generators/library/library.ts +++ b/packages/react/src/generators/library/library.ts @@ -20,6 +20,7 @@ import { jestProjectGenerator } from '@nrwl/jest'; import { swcCoreVersion } from '@nrwl/js/src/utils/versions'; import { Linter, lintProjectGenerator } from '@nrwl/linter'; import { runTasksInSerial } from '@nrwl/workspace/src/utilities/run-tasks-in-serial'; +import { getRootTsConfigPathInTree } from '@nrwl/workspace/src/utilities/typescript'; import * as ts from 'typescript'; import { assertValidStyle } from '../../utils/assertion'; import { @@ -239,7 +240,7 @@ function updateTsConfig(tree: Tree, options: NormalizedSchema) { } function updateBaseTsConfig(host: Tree, options: NormalizedSchema) { - updateJson(host, 'tsconfig.base.json', (json) => { + updateJson(host, getRootTsConfigPathInTree(host), (json) => { const c = json.compilerOptions; c.paths = c.paths || {}; delete c.paths[options.name]; @@ -264,6 +265,7 @@ function updateBaseTsConfig(host: Tree, options: NormalizedSchema) { } function createFiles(host: Tree, options: NormalizedSchema) { + const rootOffset = offsetFromRoot(options.projectRoot); generateFiles( host, joinPathFragments(__dirname, './files/lib'), @@ -272,7 +274,8 @@ function createFiles(host: Tree, options: NormalizedSchema) { ...options, ...names(options.name), tmpl: '', - offsetFromRoot: offsetFromRoot(options.projectRoot), + offsetFromRoot: rootOffset, + rootTsConfigPath: rootOffset + getRootTsConfigPathInTree(host), } ); diff --git a/packages/react/src/generators/redux/redux.ts b/packages/react/src/generators/redux/redux.ts index eaae388dfa1135..e6cbc419e0061a 100644 --- a/packages/react/src/generators/redux/redux.ts +++ b/packages/react/src/generators/redux/redux.ts @@ -20,6 +20,7 @@ import { toJS, Tree, } from '@nrwl/devkit'; +import { getRootTsConfigPathInTree } from '@nrwl/workspace/src/utilities/typescript'; export async function reduxGenerator(host: Tree, schema: Schema) { const options = normalizeOptions(host, schema); @@ -139,7 +140,7 @@ function normalizeOptions(host: Tree, options: Schema): NormalizedSchema { const project = projects.get(options.project); const { sourceRoot, projectType } = project; - const tsConfigJson = readJson(host, 'tsconfig.base.json'); + const tsConfigJson = readJson(host, getRootTsConfigPathInTree(host)); const tsPaths: { [module: string]: string[] } = tsConfigJson.compilerOptions ? tsConfigJson.compilerOptions.paths || {} : {}; diff --git a/packages/storybook/src/generators/configuration/configuration.spec.ts b/packages/storybook/src/generators/configuration/configuration.spec.ts index 822223df4a5a99..09ca31bd9ef3b2 100644 --- a/packages/storybook/src/generators/configuration/configuration.spec.ts +++ b/packages/storybook/src/generators/configuration/configuration.spec.ts @@ -44,6 +44,7 @@ describe('@nrwl/storybook:configuration', () => { tree, '.storybook/tsconfig.json' ); + expect(rootStorybookTsconfigJson.extends).toBe('../tsconfig.base.json'); expect(rootStorybookTsconfigJson.exclude).toEqual([ '../**/*.spec.js', '../**/*.test.js', @@ -81,6 +82,22 @@ describe('@nrwl/storybook:configuration', () => { ).toBeFalsy(); }); + it('should extend from root tsconfig.json when no tsconfig.base.json', async () => { + tree.rename('tsconfig.base.json', 'tsconfig.json'); + + await configurationGenerator(tree, { + name: 'test-ui-lib', + uiFramework: '@storybook/angular', + standaloneConfig: false, + }); + + const rootStorybookTsconfigJson = readJson( + tree, + '.storybook/tsconfig.json' + ); + expect(rootStorybookTsconfigJson.extends).toBe('../tsconfig.json'); + }); + it('should generate a webpackFinal into the main.js and reference a potential global webpackFinal definition', async () => { await configurationGenerator(tree, { name: 'test-ui-lib', diff --git a/packages/storybook/src/generators/configuration/configuration.ts b/packages/storybook/src/generators/configuration/configuration.ts index 82949005409600..27d4e134777bce 100644 --- a/packages/storybook/src/generators/configuration/configuration.ts +++ b/packages/storybook/src/generators/configuration/configuration.ts @@ -30,6 +30,7 @@ import { initGenerator } from '../init/init'; import { checkAndCleanWithSemver } from '@nrwl/workspace/src/utilities/version-utils'; import { gte } from 'semver'; import { findStorybookAndBuildTargets } from '../../executors/utils'; +import { getRootTsConfigPathInTree } from '@nrwl/workspace/src/utilities/typescript'; export async function configurationGenerator( tree: Tree, @@ -115,7 +116,9 @@ function createRootStorybookDir( __dirname, workspaceStorybookVersion === '6' ? './root-files' : './root-files-5' ); - generateFiles(tree, templatePath, '', {}); + generateFiles(tree, templatePath, '', { + rootTsConfigPath: getRootTsConfigPathInTree(tree), + }); if (js) { toJS(tree); @@ -160,6 +163,7 @@ function createProjectStorybookDir( tmpl: '', uiFramework, offsetFromRoot: offsetFromRoot(root), + rootTsConfigPath: getRootTsConfigPathInTree(tree), projectType: projectDirectory, useWebpack5: uiFramework === '@storybook/angular' || diff --git a/packages/storybook/src/generators/configuration/project-files-5/.storybook/tsconfig.json__tmpl__ b/packages/storybook/src/generators/configuration/project-files-5/.storybook/tsconfig.json__tmpl__ index 4b4497923f7557..034761f7e51086 100644 --- a/packages/storybook/src/generators/configuration/project-files-5/.storybook/tsconfig.json__tmpl__ +++ b/packages/storybook/src/generators/configuration/project-files-5/.storybook/tsconfig.json__tmpl__ @@ -1,5 +1,5 @@ { - "extends": "<%= offsetFromRoot %>../tsconfig.base.json", + "extends": "<%= offsetFromRoot %>../<%= rootTsConfigPath %>", "compilerOptions": { "emitDecoratorMetadata": true }, diff --git a/packages/storybook/src/generators/configuration/project-files-5/.storybook/webpack.config.js__tmpl__ b/packages/storybook/src/generators/configuration/project-files-5/.storybook/webpack.config.js__tmpl__ index d13c7cffceae5c..01e6265ee9627e 100644 --- a/packages/storybook/src/generators/configuration/project-files-5/.storybook/webpack.config.js__tmpl__ +++ b/packages/storybook/src/generators/configuration/project-files-5/.storybook/webpack.config.js__tmpl__ @@ -9,7 +9,7 @@ module.exports = async ({ config, mode }) => { config = await rootWebpackConfig({ config, mode }); const tsPaths = new TsconfigPathsPlugin({ - configFile: './tsconfig.base.json', + configFile: './<%= rootTsConfigPath %>', }); config.resolve.plugins diff --git a/packages/storybook/src/generators/configuration/root-files-5/.storybook/tsconfig.json b/packages/storybook/src/generators/configuration/root-files-5/.storybook/tsconfig.json index 7dd91521d33ee7..1e25d668b5a97c 100644 --- a/packages/storybook/src/generators/configuration/root-files-5/.storybook/tsconfig.json +++ b/packages/storybook/src/generators/configuration/root-files-5/.storybook/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../tsconfig.base.json", + "extends": "../<%= rootTsConfigPath %>", "exclude": [ "../**/*.spec.js", "../**/*.test.js", diff --git a/packages/storybook/src/generators/configuration/root-files/.storybook/tsconfig.json b/packages/storybook/src/generators/configuration/root-files/.storybook/tsconfig.json index 7dd91521d33ee7..1e25d668b5a97c 100644 --- a/packages/storybook/src/generators/configuration/root-files/.storybook/tsconfig.json +++ b/packages/storybook/src/generators/configuration/root-files/.storybook/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../tsconfig.base.json", + "extends": "../<%= rootTsConfigPath %>", "exclude": [ "../**/*.spec.js", "../**/*.test.js", diff --git a/packages/storybook/src/generators/migrate-defaults-5-to-6/migrate-defaults-5-to-6.ts b/packages/storybook/src/generators/migrate-defaults-5-to-6/migrate-defaults-5-to-6.ts index be650a146dabdb..f87f27404abd6f 100644 --- a/packages/storybook/src/generators/migrate-defaults-5-to-6/migrate-defaults-5-to-6.ts +++ b/packages/storybook/src/generators/migrate-defaults-5-to-6/migrate-defaults-5-to-6.ts @@ -16,6 +16,7 @@ import { StorybookMigrateDefault5to6Schema } from './schema'; import { storybookVersion } from '../../utils/versions'; import { join } from 'path'; import { checkAndCleanWithSemver } from '@nrwl/workspace/src/utilities/version-utils'; +import { getRootTsConfigPathInTree } from '@nrwl/workspace/src/utilities/typescript'; export function migrateDefaultsGenerator( tree: Tree, @@ -239,6 +240,7 @@ function migrateProjectLevelStorybookInstance( tmpl: '', uiFramework, offsetFromRoot: offsetFromRoot(root), + rootTsConfigPath: getRootTsConfigPathInTree(tree), projectType: projectDirectory, useWebpack5: uiFramework === '@storybook/angular', existsRootWebpackConfig: tree.exists('.storybook/webpack.config.js'), @@ -263,7 +265,7 @@ function migrateRootLevelStorybookInstance(tree: Tree, keepOld: boolean) { tree, join(__dirname, '../configuration/root-files/.storybook'), '.storybook', - {} + { rootTsConfigPath: getRootTsConfigPathInTree(tree) } ); } diff --git a/packages/web/src/generators/application/application.spec.ts b/packages/web/src/generators/application/application.spec.ts index e35ef4ce122b99..9834b4ee138109 100644 --- a/packages/web/src/generators/application/application.spec.ts +++ b/packages/web/src/generators/application/application.spec.ts @@ -59,6 +59,7 @@ describe('app', () => { expect(tree.exists('apps/my-app/src/app/app.element.css')).toBeTruthy(); const tsconfig = readJson(tree, 'apps/my-app/tsconfig.json'); + expect(tsconfig.extends).toBe('../../tsconfig.base.json'); expect(tsconfig.references).toEqual([ { path: './tsconfig.app.json', @@ -130,6 +131,18 @@ describe('app', () => { } `); }); + + it('should extend from root tsconfig.json when no tsconfig.base.json', async () => { + tree.rename('tsconfig.base.json', 'tsconfig.json'); + + await applicationGenerator(tree, { + name: 'myApp', + standaloneConfig: false, + }); + + const tsconfig = readJson(tree, 'apps/my-app/tsconfig.json'); + expect(tsconfig.extends).toBe('../../tsconfig.json'); + }); }); describe('nested', () => { @@ -210,6 +223,30 @@ describe('app', () => { ].forEach(hasJsonValue); }); + it('should extend from root tsconfig.base.json', async () => { + await applicationGenerator(tree, { + name: 'myApp', + directory: 'myDir', + standaloneConfig: false, + }); + + const tsconfig = readJson(tree, 'apps/my-dir/my-app/tsconfig.json'); + expect(tsconfig.extends).toBe('../../../tsconfig.base.json'); + }); + + it('should extend from root tsconfig.json when no tsconfig.base.json', async () => { + tree.rename('tsconfig.base.json', 'tsconfig.json'); + + await applicationGenerator(tree, { + name: 'myApp', + directory: 'myDir', + standaloneConfig: false, + }); + + const tsconfig = readJson(tree, 'apps/my-dir/my-app/tsconfig.json'); + expect(tsconfig.extends).toBe('../../../tsconfig.json'); + }); + it('should create Nx specific template', async () => { await applicationGenerator(tree, { name: 'myApp', diff --git a/packages/web/src/generators/application/application.ts b/packages/web/src/generators/application/application.ts index 4f89cb983cba33..ce51088979afb8 100644 --- a/packages/web/src/generators/application/application.ts +++ b/packages/web/src/generators/application/application.ts @@ -20,6 +20,7 @@ import { jestProjectGenerator } from '@nrwl/jest'; import { swcCoreVersion } from '@nrwl/js/src/utils/versions'; import { Linter, lintProjectGenerator } from '@nrwl/linter'; import { runTasksInSerial } from '@nrwl/workspace/src/utilities/run-tasks-in-serial'; +import { getRootTsConfigPathInTree } from '@nrwl/workspace/src/utilities/typescript'; import { join } from 'path'; @@ -38,11 +39,13 @@ interface NormalizedSchema extends Schema { } function createApplicationFiles(tree: Tree, options: NormalizedSchema) { + const rootOffset = offsetFromRoot(options.appProjectRoot); generateFiles(tree, join(__dirname, './files/app'), options.appProjectRoot, { ...options, ...names(options.name), tmpl: '', - offsetFromRoot: offsetFromRoot(options.appProjectRoot), + offsetFromRoot: rootOffset, + rootTsConfigPath: rootOffset + getRootTsConfigPathInTree(tree), }); if (options.unitTestRunner === 'none') { tree.delete(join(options.appProjectRoot, './src/app/app.element.spec.ts')); diff --git a/packages/web/src/generators/application/files/app/tsconfig.json b/packages/web/src/generators/application/files/app/tsconfig.json index 795ebf01cbe3c6..595598eddbf3e9 100644 --- a/packages/web/src/generators/application/files/app/tsconfig.json +++ b/packages/web/src/generators/application/files/app/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "<%= offsetFromRoot %>tsconfig.base.json", + "extends": "<%= rootTsConfigPath %>", "files": [], "include": [], "references": [ diff --git a/packages/workspace/src/command-line/format.ts b/packages/workspace/src/command-line/format.ts index 4295104bf29e90..e04fc94c371679 100644 --- a/packages/workspace/src/command-line/format.ts +++ b/packages/workspace/src/command-line/format.ts @@ -21,6 +21,10 @@ import { writeJsonFile, } from '@nrwl/devkit'; import { sortObjectByKeys } from '@nrwl/tao/src/utils/object-sort'; +import { + getRootTsConfigFileName, + getRootTsConfigPath, +} from '../utilities/typescript'; const PRETTIER_PATH = require.resolve('prettier/bin-prettier'); @@ -121,7 +125,9 @@ function addRootConfigFiles( if (workspaceJsonPath) { addToChunkIfNeeded(workspaceJsonPath); } - ['nx.json', 'tsconfig.base.json'].forEach(addToChunkIfNeeded); + ['nx.json', getRootTsConfigFileName()] + .filter(Boolean) + .forEach(addToChunkIfNeeded); if (chunk.length > 0) { chunkList.push(chunk); @@ -190,7 +196,7 @@ function sortWorkspaceJson(workspaceJsonPath: string) { function sortTsConfig() { try { - const tsconfigPath = path.join(appRootPath, 'tsconfig.base.json'); + const tsconfigPath = getRootTsConfigPath(); const tsconfig = readJsonFile(tsconfigPath); const sortedPaths = sortObjectByKeys(tsconfig.compilerOptions.paths); tsconfig.compilerOptions.paths = sortedPaths; diff --git a/packages/workspace/src/core/affected-project-graph/locators/tsconfig-json-changes.spec.ts b/packages/workspace/src/core/affected-project-graph/locators/tsconfig-json-changes.spec.ts index 79c710521cbd8f..a2f5db0b8c679f 100644 --- a/packages/workspace/src/core/affected-project-graph/locators/tsconfig-json-changes.spec.ts +++ b/packages/workspace/src/core/affected-project-graph/locators/tsconfig-json-changes.spec.ts @@ -2,6 +2,7 @@ import { WholeFileChange } from '../../file-utils'; import { jsonDiff } from '../../../utilities/json-diff'; import { getTouchedProjectsFromTsConfig } from './tsconfig-json-changes'; import { DependencyType, ProjectGraph } from '../../project-graph'; +import * as tsUtils from '../../../utilities/typescript'; describe('getTouchedProjectsFromTsConfig', () => { let graph: ProjectGraph; @@ -40,6 +41,13 @@ describe('getTouchedProjectsFromTsConfig', () => { ['tsconfig.json', 'tsconfig.base.json'].forEach((tsConfig) => { describe(`(${tsConfig})`, () => { + beforeEach(() => { + jest + .spyOn(tsUtils, 'getRootTsConfigFileName') + .mockReturnValue(tsConfig); + jest.clearAllMocks(); + }); + it(`should not return changes when ${tsConfig} is not touched`, () => { const result = getTouchedProjectsFromTsConfig( [ diff --git a/packages/workspace/src/core/affected-project-graph/locators/tsconfig-json-changes.ts b/packages/workspace/src/core/affected-project-graph/locators/tsconfig-json-changes.ts index 360e33a4f71670..967ec7a73b374f 100644 --- a/packages/workspace/src/core/affected-project-graph/locators/tsconfig-json-changes.ts +++ b/packages/workspace/src/core/affected-project-graph/locators/tsconfig-json-changes.ts @@ -4,15 +4,19 @@ import { isJsonChange, JsonChange, } from '../../../utilities/json-diff'; +import { getRootTsConfigFileName } from '../../../utilities/typescript'; import { TouchedProjectLocator } from '../affected-project-graph-models'; import { ProjectGraphProjectNode } from '../../project-graph'; export const getTouchedProjectsFromTsConfig: TouchedProjectLocator< WholeFileChange | JsonChange > = (touchedFiles, _a, _b, _c, graph): string[] => { + const rootTsConfig = getRootTsConfigFileName(); + if (!rootTsConfig) { + return []; + } const tsConfigJsonChanges = touchedFiles.find( - (change) => - change.file === 'tsconfig.json' || change.file === 'tsconfig.base.json' + (change) => change.file === rootTsConfig ); if (!tsConfigJsonChanges) { return []; diff --git a/packages/workspace/src/core/hasher/hasher.spec.ts b/packages/workspace/src/core/hasher/hasher.spec.ts index 49225a4ab3e2fa..de1efe2c16d586 100644 --- a/packages/workspace/src/core/hasher/hasher.spec.ts +++ b/packages/workspace/src/core/hasher/hasher.spec.ts @@ -6,14 +6,16 @@ jest.doMock('../../utils/app-root', () => { }); import fs = require('fs'); +import tsUtils = require('../../utilities/typescript'); import { DependencyType } from '@nrwl/devkit'; import { Hasher } from './hasher'; jest.mock('fs'); +jest.mock('../../utilities/typescript'); fs.existsSync = () => true; -fdescribe('Hasher', () => { +describe('Hasher', () => { const nxJson = { npmScope: 'nrwl', }; @@ -65,6 +67,8 @@ fdescribe('Hasher', () => { } return file; }; + + tsUtils.getRootTsConfigFileName = () => 'tsconfig.base.json'; }); it('should create project hash', async () => { diff --git a/packages/workspace/src/core/hasher/hasher.ts b/packages/workspace/src/core/hasher/hasher.ts index ddf6f4f61726b5..06682197d42e0e 100644 --- a/packages/workspace/src/core/hasher/hasher.ts +++ b/packages/workspace/src/core/hasher/hasher.ts @@ -11,6 +11,7 @@ import { existsSync } from 'fs'; import * as minimatch from 'minimatch'; import { join } from 'path'; import { performance } from 'perf_hooks'; +import { getRootTsConfigFileName } from '../../utilities/typescript'; import { appRootPath } from '../../utils/app-root'; import { workspaceFileName } from '../file-utils'; import { defaultHashing, HashingImpl } from './hashing-impl'; @@ -412,7 +413,7 @@ class ProjectHasher { private readTsConfig() { try { - const res = readJsonFile('tsconfig.base.json'); + const res = readJsonFile(getRootTsConfigFileName()); res.compilerOptions.paths ??= {}; return res; } catch { diff --git a/packages/workspace/src/core/project-graph/build-project-graph.ts b/packages/workspace/src/core/project-graph/build-project-graph.ts index de0c943c940bd5..2a3799304699c4 100644 --- a/packages/workspace/src/core/project-graph/build-project-graph.ts +++ b/packages/workspace/src/core/project-graph/build-project-graph.ts @@ -30,12 +30,12 @@ import { buildNpmPackageNodes, buildWorkspaceProjectNodes, } from './build-nodes'; -import { existsSync } from 'fs'; import * as os from 'os'; import { buildExplicitTypescriptAndPackageJsonDependencies } from './build-dependencies/build-explicit-typescript-and-package-json-dependencies'; import { loadNxPlugins } from '@nrwl/tao/src/shared/nx-plugin'; import { defaultFileHasher } from '../hasher/file-hasher'; import { createProjectFileMap } from '../file-map-utils'; +import { getRootTsConfigPath } from '../../utilities/typescript'; export async function buildProjectGraph() { const workspaceJson = readWorkspaceJson(); @@ -397,10 +397,8 @@ function updateProjectGraphWithPlugins( } function readRootTsConfig() { - for (const tsConfigName of ['tsconfig.base.json', 'tsconfig.json']) { - const tsConfigPath = join(appRootPath, tsConfigName); - if (existsSync(tsConfigPath)) { - return readJsonFile(tsConfigPath); - } + const tsConfigPath = getRootTsConfigPath(); + if (tsConfigPath) { + return readJsonFile(tsConfigPath); } } diff --git a/packages/workspace/src/core/target-project-locator.spec.ts b/packages/workspace/src/core/target-project-locator.spec.ts index 5e6a720ee90346..9d11babb1ff46a 100644 --- a/packages/workspace/src/core/target-project-locator.spec.ts +++ b/packages/workspace/src/core/target-project-locator.spec.ts @@ -352,6 +352,19 @@ describe('findTargetProjectWithImport', () => { expect(proj2deep).toEqual('proj2'); }); + it('should be able to resolve nested files using tsConfig paths when having a root tsconfig.json instead of tsconfig.base.json', () => { + fsJson['./tsconfig.json'] = fsJson['./tsconfig.base.json']; + delete fsJson['./tsconfig.base.json']; + + const proj2deep = targetProjectLocator.findProjectWithImport( + '@proj/my-second-proj/deep', + 'libs/proj1/index.ts', + ctx.workspace.npmScope + ); + + expect(proj2deep).toEqual('proj2'); + }); + it('should be able to resolve nested files using tsConfig paths that have similar names', () => { const proj = targetProjectLocator.findProjectWithImport( '@proj/proj123/deep', diff --git a/packages/workspace/src/core/target-project-locator.ts b/packages/workspace/src/core/target-project-locator.ts index dd1a29421bb22b..8d7ee90ee843cb 100644 --- a/packages/workspace/src/core/target-project-locator.ts +++ b/packages/workspace/src/core/target-project-locator.ts @@ -1,5 +1,7 @@ -import { resolveModuleByImport } from '../utilities/typescript'; -import { readFileIfExisting } from './file-utils'; +import { + getRootTsConfigFileName, + resolveModuleByImport, +} from '../utilities/typescript'; import { parseJson, ProjectGraphExternalNode, @@ -8,6 +10,7 @@ import { import { isRelativePath } from '../utilities/fileutils'; import { dirname, join, posix } from 'path'; import { appRootPath } from '@nrwl/tao/src/utils/app-root'; +import { readFileSync } from 'fs'; export class TargetProjectLocator { private projectRootMappings = createProjectRootMappings(this.nodes); @@ -164,22 +167,21 @@ export class TargetProjectLocator { } private getRootTsConfig() { - let path = 'tsconfig.base.json'; - let absolutePath = this.getAbsolutePath(path); - let content = readFileIfExisting(absolutePath); - if (!content) { - path = 'tsconfig.json'; - absolutePath = this.getAbsolutePath(path); - content = readFileIfExisting(absolutePath); - } - if (!content) { + const path = getRootTsConfigFileName(); + if (!path) { return { path: null, absolutePath: null, config: null, }; } - return { path, absolutePath, config: parseJson(content) }; + + const absolutePath = this.getAbsolutePath(path); + return { + absolutePath, + path, + config: parseJson(readFileSync(absolutePath, 'utf8')), + }; } private findMatchingProjectFiles(file: string) { diff --git a/packages/workspace/src/generators/init/files/root/tools/tsconfig.tools.json__tmpl__ b/packages/workspace/src/generators/init/files/root/tools/tsconfig.tools.json__tmpl__ index 99428e1473febd..58db6461e90582 100644 --- a/packages/workspace/src/generators/init/files/root/tools/tsconfig.tools.json__tmpl__ +++ b/packages/workspace/src/generators/init/files/root/tools/tsconfig.tools.json__tmpl__ @@ -1,5 +1,5 @@ { - "extends": "../tsconfig.base.json", + "extends": "../<%= rootTsConfigPath %>", "compilerOptions": { "outDir": "../dist/out-tsc/tools", "rootDir": ".", diff --git a/packages/workspace/src/generators/init/init.spec.ts b/packages/workspace/src/generators/init/init.spec.ts index 18675dc38d7c34..50fc60d0038aa6 100644 --- a/packages/workspace/src/generators/init/init.spec.ts +++ b/packages/workspace/src/generators/init/init.spec.ts @@ -585,6 +585,7 @@ describe('workspace', () => { beforeEach(() => { tree.write('/package.json', JSON.stringify({ devDependencies: {} })); tree.write('/angular.json', JSON.stringify({ projects: { myproj: {} } })); + tree.write('/tsconfig.json', '{"compilerOptions": {}}'); }); it('should update package.json', async () => { diff --git a/packages/workspace/src/generators/init/init.ts b/packages/workspace/src/generators/init/init.ts index f683805a22e1e5..521c509f3602f1 100755 --- a/packages/workspace/src/generators/init/init.ts +++ b/packages/workspace/src/generators/init/init.ts @@ -35,6 +35,7 @@ import { } from '../../utils/versions'; import { DEFAULT_NRWL_PRETTIER_CONFIG } from '../workspace/workspace'; import { Schema } from './schema'; +import { getRootTsConfigPathInTree } from '../../utilities/typescript'; function updatePackageJson(tree) { updateJson(tree, 'package.json', (packageJson) => { @@ -80,12 +81,6 @@ function updatePackageJson(tree) { }); } -function getRootTsConfigPath(host: Tree) { - return host.exists('tsconfig.base.json') - ? 'tsconfig.base.json' - : 'tsconfig.json'; -} - function convertPath(name: string, originalPath: string) { return `apps/${name}/${originalPath}`; } @@ -330,7 +325,7 @@ function updateTsConfig(host: Tree) { writeJson( host, 'tsconfig.base.json', - setUpCompilerOptions(readJson(host, getRootTsConfigPath(host))) + setUpCompilerOptions(readJson(host, getRootTsConfigPathInTree(host))) ); if (host.exists('tsconfig.json')) { host.delete('tsconfig.json'); @@ -340,7 +335,7 @@ function updateTsConfig(host: Tree) { function updateTsConfigsJson(host: Tree, options: Schema) { const app = readProjectConfiguration(host, options.name); const e2eProject = getE2eProject(host); - const tsConfigPath = getRootTsConfigPath(host); + const tsConfigPath = getRootTsConfigPathInTree(host); const appOffset = offsetFromRoot(app.root); updateJson(host, app.targets.build.options.tsConfig, (json) => { @@ -768,7 +763,7 @@ function createNxJson(host: Tree) { ); } const name = Object.keys(projects)[0]; - const tsConfigPath = getRootTsConfigPath(host); + const tsConfigPath = getRootTsConfigPathInTree(host); writeJson(host, 'nx.json', { npmScope: name, implicitDependencies: { @@ -824,6 +819,7 @@ function addFiles(host: Tree) { generateFiles(host, joinPathFragments(__dirname, './files/root'), '.', { tmpl: '', dot: '.', + rootTsConfigPath: getRootTsConfigPathInTree(host), }); if (!host.exists('.prettierignore')) { diff --git a/packages/workspace/src/generators/library/files/lib/tsconfig.json b/packages/workspace/src/generators/library/files/lib/tsconfig.json index 83a68fa354843a..8bf55e434eef11 100644 --- a/packages/workspace/src/generators/library/files/lib/tsconfig.json +++ b/packages/workspace/src/generators/library/files/lib/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "<%= offsetFromRoot %>tsconfig.base.json",<% if (js) { %> + "extends": "<%= rootTsConfigPath %>",<% if (js) { %> "compilerOptions": { "allowJs": true },<% } %> diff --git a/packages/workspace/src/generators/library/library.spec.ts b/packages/workspace/src/generators/library/library.spec.ts index 2d01f6cb7be811..603ead8ad16725 100644 --- a/packages/workspace/src/generators/library/library.spec.ts +++ b/packages/workspace/src/generators/library/library.spec.ts @@ -108,7 +108,7 @@ describe('lib', () => { }); }); - it('should update root tsconfig.json', async () => { + it('should update root tsconfig.base.json', async () => { await libraryGenerator(tree, { ...defaultOptions, name: 'myLib' }); const tsconfigJson = readJson(tree, '/tsconfig.base.json'); expect(tsconfigJson.compilerOptions.paths['@proj/my-lib']).toEqual([ @@ -116,6 +116,17 @@ describe('lib', () => { ]); }); + it('should update root tsconfig.json when no tsconfig.base.json', async () => { + tree.rename('tsconfig.base.json', 'tsconfig.json'); + + await libraryGenerator(tree, { ...defaultOptions, name: 'myLib' }); + + const tsconfigJson = readJson(tree, '/tsconfig.json'); + expect(tsconfigJson.compilerOptions.paths['@proj/my-lib']).toEqual([ + 'libs/my-lib/src/index.ts', + ]); + }); + it('should update root tsconfig.json (no existing path mappings)', async () => { updateJson(tree, 'tsconfig.base.json', (json) => { json.compilerOptions.paths = undefined; @@ -155,6 +166,15 @@ describe('lib', () => { `); }); + it('should extend from root tsconfig.json when no tsconfig.base.json', async () => { + tree.rename('tsconfig.base.json', 'tsconfig.json'); + + await libraryGenerator(tree, { ...defaultOptions, name: 'myLib' }); + + const tsconfigJson = readJson(tree, 'libs/my-lib/tsconfig.json'); + expect(tsconfigJson.extends).toBe('../../tsconfig.json'); + }); + it('should extend the local tsconfig.json with tsconfig.spec.json', async () => { await libraryGenerator(tree, { ...defaultOptions, name: 'myLib' }); const tsconfigJson = readJson(tree, 'libs/my-lib/tsconfig.spec.json'); @@ -301,7 +321,7 @@ describe('lib', () => { }); }); - it('should update tsconfig.json', async () => { + it('should update root tsconfig.base.json', async () => { await libraryGenerator(tree, { ...defaultOptions, name: 'myLib', @@ -316,6 +336,24 @@ describe('lib', () => { ).toBeUndefined(); }); + it('should update root tsconfig.json when no tsconfig.base.json', async () => { + tree.rename('tsconfig.base.json', 'tsconfig.json'); + + await libraryGenerator(tree, { + ...defaultOptions, + name: 'myLib', + directory: 'myDir', + }); + + const tsconfigJson = readJson(tree, '/tsconfig.json'); + expect(tsconfigJson.compilerOptions.paths['@proj/my-dir/my-lib']).toEqual( + ['libs/my-dir/my-lib/src/index.ts'] + ); + expect( + tsconfigJson.compilerOptions.paths['my-dir-my-lib/*'] + ).toBeUndefined(); + }); + it('should create a local tsconfig.json', async () => { await libraryGenerator(tree, { ...defaultOptions, @@ -324,6 +362,7 @@ describe('lib', () => { }); const tsconfigJson = readJson(tree, 'libs/my-dir/my-lib/tsconfig.json'); + expect(tsconfigJson.extends).toBe('../../../tsconfig.base.json'); expect(tsconfigJson.references).toEqual([ { path: './tsconfig.lib.json', @@ -333,6 +372,19 @@ describe('lib', () => { }, ]); }); + + it('should extend from root tsconfig.json when no tsconfig.base.json', async () => { + tree.rename('tsconfig.base.json', 'tsconfig.json'); + + await libraryGenerator(tree, { + ...defaultOptions, + name: 'myLib', + directory: 'myDir', + }); + + const tsconfigJson = readJson(tree, 'libs/my-dir/my-lib/tsconfig.json'); + expect(tsconfigJson.extends).toBe('../../../tsconfig.json'); + }); }); describe('--linter', () => { diff --git a/packages/workspace/src/generators/library/library.ts b/packages/workspace/src/generators/library/library.ts index e7c1167c2bfee0..a7927952f1f071 100644 --- a/packages/workspace/src/generators/library/library.ts +++ b/packages/workspace/src/generators/library/library.ts @@ -16,6 +16,7 @@ import { } from '@nrwl/devkit'; import { join } from 'path'; import { runTasksInSerial } from '../../utilities/run-tasks-in-serial'; +import { getRootTsConfigPathInTree } from '../../utilities/typescript'; import { nxVersion } from '../../utils/versions'; import { Schema } from './schema'; @@ -100,7 +101,7 @@ function updateTsConfig(tree: Tree, options: NormalizedSchema) { } function updateRootTsConfig(host: Tree, options: NormalizedSchema) { - updateJson(host, 'tsconfig.base.json', (json) => { + updateJson(host, getRootTsConfigPathInTree(host), (json) => { const c = json.compilerOptions; c.paths = c.paths || {}; delete c.paths[options.name]; @@ -126,6 +127,7 @@ function updateRootTsConfig(host: Tree, options: NormalizedSchema) { function createFiles(tree: Tree, options: NormalizedSchema) { const { className, name, propertyName } = names(options.name); + const rootOffset = offsetFromRoot(options.projectRoot); generateFiles(tree, join(__dirname, './files/lib'), options.projectRoot, { ...options, dot: '.', @@ -136,7 +138,8 @@ function createFiles(tree: Tree, options: NormalizedSchema) { cliCommand: 'nx', strict: undefined, tmpl: '', - offsetFromRoot: offsetFromRoot(options.projectRoot), + offsetFromRoot: rootOffset, + rootTsConfigPath: rootOffset + getRootTsConfigPathInTree(tree), hasUnitTestRunner: options.unitTestRunner !== 'none', }); diff --git a/packages/workspace/src/generators/move/lib/update-imports.spec.ts b/packages/workspace/src/generators/move/lib/update-imports.spec.ts index 0c83169f06f3d1..3dd5360fb8e00e 100644 --- a/packages/workspace/src/generators/move/lib/update-imports.spec.ts +++ b/packages/workspace/src/generators/move/lib/update-imports.spec.ts @@ -268,7 +268,7 @@ export MyExtendedClass extends MyClass {};` ); }); - it('should update project ref in the tsconfig file', async () => { + it('should update project ref in the root tsconfig.base.json', async () => { await libraryGenerator(tree, { name: 'my-source', standaloneConfig: false, @@ -283,6 +283,22 @@ export MyExtendedClass extends MyClass {};` }); }); + it('should update project ref in the root tsconfig.json when no tsconfig.base.json', async () => { + tree.rename('tsconfig.base.json', 'tsconfig.json'); + await libraryGenerator(tree, { + name: 'my-source', + standaloneConfig: false, + }); + const projectConfig = readProjectConfiguration(tree, 'my-source'); + + updateImports(tree, schema, projectConfig); + + const tsConfig = readJson(tree, '/tsconfig.json'); + expect(tsConfig.compilerOptions.paths).toEqual({ + '@proj/my-destination': ['libs/my-destination/src/index.ts'], + }); + }); + it('should only update the project ref paths in the tsconfig file when --updateImportPath=false', async () => { await libraryGenerator(tree, { name: 'my-source', diff --git a/packages/workspace/src/generators/move/lib/update-imports.ts b/packages/workspace/src/generators/move/lib/update-imports.ts index 0664c9cc5996b5..f9cdf7055fd5f5 100644 --- a/packages/workspace/src/generators/move/lib/update-imports.ts +++ b/packages/workspace/src/generators/move/lib/update-imports.ts @@ -10,6 +10,7 @@ import { writeJson, } from '@nrwl/devkit'; import * as ts from 'typescript'; +import { getRootTsConfigPathInTree } from '../../../utilities/typescript'; import { findNodes } from '../../../utilities/typescript/find-nodes'; import { NormalizedSchema } from '../schema'; import { normalizeSlashes } from './utils'; @@ -34,7 +35,7 @@ export function updateImports( // use the source root to find the from location // this attempts to account for libs that have been created with --importPath - const tsConfigPath = 'tsconfig.base.json'; + const tsConfigPath = getRootTsConfigPathInTree(tree); let tsConfig: any; let fromPath: string; if (tree.exists(tsConfigPath)) { diff --git a/packages/workspace/src/generators/remove/lib/update-tsconfig.spec.ts b/packages/workspace/src/generators/remove/lib/update-tsconfig.spec.ts index 31a0268377e9c2..6e75027d1206b8 100644 --- a/packages/workspace/src/generators/remove/lib/update-tsconfig.spec.ts +++ b/packages/workspace/src/generators/remove/lib/update-tsconfig.spec.ts @@ -18,7 +18,7 @@ describe('updateTsconfig', () => { }; }); - it('should delete project ref from the tsconfig', async () => { + it('should delete project ref from the root tsconfig.base.json', async () => { await libraryGenerator(tree, { name: 'my-lib', standaloneConfig: false, @@ -30,4 +30,18 @@ describe('updateTsconfig', () => { const tsConfig = readJson(tree, '/tsconfig.base.json'); expect(tsConfig.compilerOptions.paths).toEqual({}); }); + + it('should delete project ref from the root tsconfig.json when no tsconfig.base.json', async () => { + tree.rename('tsconfig.base.json', 'tsconfig.json'); + await libraryGenerator(tree, { + name: 'my-lib', + standaloneConfig: false, + }); + const project = readProjectConfiguration(tree, 'my-lib'); + + updateTsconfig(tree, schema, project); + + const tsConfig = readJson(tree, '/tsconfig.json'); + expect(tsConfig.compilerOptions.paths).toEqual({}); + }); }); diff --git a/packages/workspace/src/generators/remove/lib/update-tsconfig.ts b/packages/workspace/src/generators/remove/lib/update-tsconfig.ts index 0ef5cdcf2a8fb9..ce1a0c2608721b 100644 --- a/packages/workspace/src/generators/remove/lib/update-tsconfig.ts +++ b/packages/workspace/src/generators/remove/lib/update-tsconfig.ts @@ -4,6 +4,7 @@ import { Tree, updateJson, } from '@nrwl/devkit'; +import { getRootTsConfigPathInTree } from '../../../utilities/typescript'; import { Schema } from '../schema'; /** @@ -18,7 +19,7 @@ export function updateTsconfig( ) { const { appsDir, libsDir, npmScope } = getWorkspaceLayout(tree); - const tsConfigPath = 'tsconfig.base.json'; + const tsConfigPath = getRootTsConfigPathInTree(tree, false); if (tree.exists(tsConfigPath)) { updateJson(tree, tsConfigPath, (json) => { delete json.compilerOptions.paths[ diff --git a/packages/workspace/src/utilities/typescript.ts b/packages/workspace/src/utilities/typescript.ts index a6c08267f3bfd8..87049a88183d59 100644 --- a/packages/workspace/src/utilities/typescript.ts +++ b/packages/workspace/src/utilities/typescript.ts @@ -1,9 +1,10 @@ -import { dirname } from 'path'; -import type * as ts from 'typescript'; +import { Tree } from '@nrwl/devkit'; import { appRootPath } from '@nrwl/tao/src/utils/app-root'; - -export type { TypeScriptCompilationOptions } from './typescript/compilation'; +import { existsSync } from 'fs'; +import { dirname, join } from 'path'; +import type * as ts from 'typescript'; export { compileTypeScript } from './typescript/compilation'; +export type { TypeScriptCompilationOptions } from './typescript/compilation'; export { findNodes } from './typescript/find-nodes'; export { getSourceNodes } from './typescript/get-source-nodes'; @@ -91,3 +92,37 @@ function getCompilerHost(tsConfigPath: string) { ); return { options, host, moduleResolutionCache }; } + +export function getRootTsConfigPathInTree( + tree: Tree, + throwIfNotFound: boolean = true +): string { + for (const path of ['tsconfig.base.json', 'tsconfig.json']) { + if (tree.exists(path)) { + return path; + } + } + + if (throwIfNotFound) { + throw new Error( + 'Could not find root tsconfig file. Tried with "tsconfig.base.json" and "tsconfig.json".' + ); + } +} + +export function getRootTsConfigFileName(): string | undefined { + for (const tsConfigName of ['tsconfig.base.json', 'tsconfig.json']) { + const tsConfigPath = join(appRootPath, tsConfigName); + if (existsSync(tsConfigPath)) { + return tsConfigName; + } + } + + return undefined; +} + +export function getRootTsConfigPath(): string | undefined { + const tsConfigFileName = getRootTsConfigFileName(); + + return tsConfigFileName ? join(appRootPath, tsConfigFileName) : undefined; +}