diff --git a/e2e/workspace-integrations/src/workspace.test.ts b/e2e/workspace-integrations/src/workspace.test.ts index 104ca4a0d81f8..c2a93f402fa77 100644 --- a/e2e/workspace-integrations/src/workspace.test.ts +++ b/e2e/workspace-integrations/src/workspace.test.ts @@ -402,12 +402,19 @@ describe('affected:*', () => { expect(affectedLibs).not.toContain(mylib2); const implicitlyAffectedLibs = runCLI( - 'affected:libs --files="tsconfig.json"' + 'affected:libs --files="tsconfig.base.json"' ); expect(implicitlyAffectedLibs).toContain(mypublishablelib); expect(implicitlyAffectedLibs).toContain(mylib); expect(implicitlyAffectedLibs).toContain(mylib2); + const noAffectedLibsNonExistentFile = runCLI( + 'affected:libs --files="tsconfig.json"' + ); + expect(noAffectedLibsNonExistentFile).not.toContain(mypublishablelib); + expect(noAffectedLibsNonExistentFile).not.toContain(mylib); + expect(noAffectedLibsNonExistentFile).not.toContain(mylib2); + const noAffectedLibs = runCLI('affected:libs --files="README.md"'); expect(noAffectedLibs).not.toContain(mypublishablelib); expect(noAffectedLibs).not.toContain(mylib); diff --git a/packages/angular/src/generators/application/application.spec.ts b/packages/angular/src/generators/application/application.spec.ts index 36a9a6f164605..b6852588fc102 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 795ebf01cbe3c..595598eddbf3e 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 a4b64f2836095..ac5153d6a86cb 100644 --- a/packages/angular/src/generators/application/lib/create-files.ts +++ b/packages/angular/src/generators/application/lib/create-files.ts @@ -1,8 +1,8 @@ import type { Tree } from '@nrwl/devkit'; +import { generateFiles, joinPathFragments } from '@nrwl/devkit'; +import { getRelativePathToRootTsConfig } from '@nrwl/workspace/src/utilities/typescript'; import type { NormalizedSchema } from './normalized-schema'; -import { generateFiles, joinPathFragments, offsetFromRoot } from '@nrwl/devkit'; - export function createFiles( host: Tree, options: NormalizedSchema, @@ -16,7 +16,10 @@ export function createFiles( options.appProjectRoot, { ...options, - offsetFromRoot: offsetFromRoot(options.appProjectRoot), + rootTsConfigPath: getRelativePathToRootTsConfig( + host, + options.appProjectRoot + ), 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 90a1df3996bab..386344c22fc33 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 { getRelativePathToRootTsConfig } from '@nrwl/workspace/src/utilities/typescript'; import type { NormalizedSchema } from './normalized-schema'; export function updateE2eProject(tree: Tree, options: NormalizedSchema) { @@ -60,7 +61,7 @@ export function updateE2eProject(tree: Tree, options: NormalizedSchema) { updateJson(tree, `${options.e2eProjectRoot}/tsconfig.json`, (json) => { return { ...json, - extends: `${offsetFromRoot(options.e2eProjectRoot)}tsconfig.base.json`, + extends: getRelativePathToRootTsConfig(tree, options.e2eProjectRoot), }; }); } 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 e54c0ec1cfe5a..2168c9405f602 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 4cd7c6693dc39..5f1d4a18200df 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 d4a0841f5b0ed..6feda9591dc03 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 7c4ec2f870c97..acc4856cd82ad 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 { getRelativePathToRootTsConfig } 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,10 @@ function createFiles(host: Tree, options: NormalizedSchema) { options.projectRoot, { ...options, - offsetFromRoot: offsetFromRoot(options.projectRoot), + rootTsConfigPath: getRelativePathToRootTsConfig( + host, + options.projectRoot + ), tpl: '', } ); diff --git a/packages/angular/src/generators/library/lib/update-tsconfig.ts b/packages/angular/src/generators/library/lib/update-tsconfig.ts index 10ee0b03b6b35..a3a5d5039a17c 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 cec6c69fecdc3..fa70df8956e08 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 fe8a6cc9c1e78..cc3f73bc1a9c8 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 e0e4b2b5a3eda..b9e4ff1490c2e 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 { getRelativePathToRootTsConfig } from '@nrwl/workspace/src/utilities/typescript'; +import type { Schema } from '../schema'; const SHARED_SINGLETON_LIBRARIES = [ '@angular/core', @@ -30,6 +31,7 @@ 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.` ); } + generateFiles( host, joinPathFragments(__dirname, '../files/webpack'), @@ -42,6 +44,7 @@ export function generateWebpackConfig( sourceRoot: appRoot, sharedLibraries: SHARED_SINGLETON_LIBRARIES, offsetFromRoot: offsetFromRoot(appRoot), + rootTsConfigPath: getRelativePathToRootTsConfig(host, appRoot), } ); } 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 0f9810681ba43..2563987c3a4d9 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 941d6d2ab509f..21d7bce675e7a 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,16 @@ import type { Tree } from '@nrwl/devkit'; import { - getWorkspaceLayout, joinPathFragments, - offsetFromRoot, readProjectConfiguration, updateJson, } from '@nrwl/devkit'; +import { getRelativePathToRootTsConfig } 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 = getRelativePathToRootTsConfig(tree, root); 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 829762ba3709a..3742de6fdf01a 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 1ae61257a8ffc..783a8cf17cc52 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 0f025e115165e..27c9e0fbabf8c 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 { getRelativePathToRootTsConfig } from '@nrwl/workspace/src/utilities/typescript'; import { join } from 'path'; // app @@ -39,6 +40,7 @@ function createFiles(tree: Tree, options: CypressProjectSchema) { project: options.project || 'Project', ext: options.js ? 'js' : 'ts', offsetFromRoot: offsetFromRoot(options.projectRoot), + rootTsConfigPath: getRelativePathToRootTsConfig(tree, options.projectRoot), }); 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 ab92eb639b4f9..5af7bb44f8560 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 a98b83920ef96..f8b07b9525e21 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 c31c52e04ce1a..eea95f339bebc 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 c625f987cbf02..f0583f3beb8e2 100644 --- a/packages/detox/src/generators/application/lib/create-files.ts +++ b/packages/detox/src/generators/application/lib/create-files.ts @@ -1,4 +1,5 @@ import { generateFiles, offsetFromRoot, toJS, Tree } from '@nrwl/devkit'; +import { getRelativePathToRootTsConfig } from '@nrwl/workspace/src/utilities/typescript'; import { join } from 'path'; import { NormalizedSchema } from './normalize-options'; @@ -6,6 +7,7 @@ export function createFiles(host: Tree, options: NormalizedSchema) { generateFiles(host, join(__dirname, '../files/app'), options.projectRoot, { ...options, offsetFromRoot: offsetFromRoot(options.projectRoot), + rootTsConfigPath: getRelativePathToRootTsConfig(host, options.projectRoot), }); if (options.js) { toJS(host); diff --git a/packages/devkit/src/generators/format-files.ts b/packages/devkit/src/generators/format-files.ts index b9c87581590f2..5808e42f34a88 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 | null { + for (const path of ['tsconfig.base.json', 'tsconfig.json']) { + if (tree.exists(path)) { + return path; + } + } + + return null; +} diff --git a/packages/js/src/generators/library/files/lib/tsconfig.json__tmpl__ b/packages/js/src/generators/library/files/lib/tsconfig.json__tmpl__ index f29aa94d03b51..da6f51affaaa6 100644 --- a/packages/js/src/generators/library/files/lib/tsconfig.json__tmpl__ +++ b/packages/js/src/generators/library/files/lib/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 6c660950b2664..bcda94e582795 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 f7ec2e6f6d814..aa9c88c3abc4c 100644 --- a/packages/js/src/generators/library/library.ts +++ b/packages/js/src/generators/library/library.ts @@ -16,6 +16,10 @@ import { import { jestProjectGenerator } from '@nrwl/jest'; import { Linter, lintProjectGenerator } from '@nrwl/linter'; import { runTasksInSerial } from '@nrwl/workspace/src/utilities/run-tasks-in-serial'; +import { + getRelativePathToRootTsConfig, + getRootTsConfigPathInTree, +} from '@nrwl/workspace/src/utilities/typescript'; import { join } from 'path'; import { LibraryGeneratorSchema } from '../../utils/schema'; import { addSwcConfig } from '../../utils/swc/add-swc-config'; @@ -172,6 +176,7 @@ function createFiles(tree: Tree, options: NormalizedSchema, filesDir: string) { strict: undefined, tmpl: '', offsetFromRoot: offsetFromRoot(options.projectRoot), + rootTsConfigPath: getRelativePathToRootTsConfig(tree, options.projectRoot), buildable: options.buildable === true, hasUnitTestRunner: options.unitTestRunner !== 'none', }); @@ -318,7 +323,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 10bb5c8c3d42d..709828769d96e 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 026d6dc757d03..164206b967674 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 7fe2ac9913bb3..023a93857559b 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 { getRelativePathToRootTsConfig } from '@nrwl/workspace/src/utilities/typescript'; import { join } from 'path'; import { workspaceLintPluginDir } from '../../utils/workspace-lint-rules'; @@ -36,6 +37,7 @@ export async function lintWorkspaceRulesProjectGenerator(tree: Tree) { generateFiles(tree, join(__dirname, 'files'), workspaceLintPluginDir, { tmpl: '', offsetFromRoot: offsetFromRoot(WORKSPACE_PLUGIN_DIR), + rootTsConfigPath: getRelativePathToRootTsConfig(tree, WORKSPACE_PLUGIN_DIR), }); /** diff --git a/packages/next/src/generators/application/application.spec.ts b/packages/next/src/generators/application/application.spec.ts index 3f1379fd1f7ab..66cf6e23c3aee 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 1d4d8b2e69b20..1237169a451c5 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 6e1eaecd81aa6..6b369bc4a6767 100644 --- a/packages/next/src/generators/application/lib/create-application-files.ts +++ b/packages/next/src/generators/application/lib/create-application-files.ts @@ -4,14 +4,18 @@ import { createAppJsx, createStyleRules, } from './create-application-files.helpers'; -import { generateFiles, names, offsetFromRoot, toJS, Tree } from '@nrwl/devkit'; +import { generateFiles, names, toJS, Tree } from '@nrwl/devkit'; +import { getRelativePathToRootTsConfig } 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: getRelativePathToRootTsConfig( + host, + options.appProjectRoot + ), 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 6a10fa798f4d2..e28876e43c86b 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 97259b213eb27..f64cfdb486f70 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 f5ad92e0eabe3..2ba46d101c419 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 { getRelativePathToRootTsConfig } from '@nrwl/workspace/src/utilities/typescript'; export interface NormalizedSchema extends Schema { appProjectRoot: string; @@ -113,6 +114,10 @@ function addAppFiles(tree: Tree, options: NormalizedSchema) { name: options.name, root: options.appProjectRoot, offset: offsetFromRoot(options.appProjectRoot), + rootTsConfigPath: getRelativePathToRootTsConfig( + tree, + options.appProjectRoot + ), }); 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 a6db50db59d2d..595598eddbf3e 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 558f82dadfb89..ffd2484d273d6 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 5f02498b414e0..ba2ccc861c5ff 100644 --- a/packages/nx-plugin/src/generators/e2e-project/e2e.ts +++ b/packages/nx-plugin/src/generators/e2e-project/e2e.ts @@ -7,12 +7,12 @@ import { generateFiles, addProjectConfiguration, updateProjectConfiguration, - offsetFromRoot, } from '@nrwl/devkit'; import type { Tree } from '@nrwl/devkit'; import type { Schema } from './schema'; import * as path from 'path'; import { jestProjectGenerator } from '@nrwl/jest'; +import { getRelativePathToRootTsConfig } from '@nrwl/workspace/src/utilities/typescript'; interface NormalizedSchema extends Schema { projectRoot: string; @@ -51,7 +51,7 @@ function addFiles(host: Tree, options: NormalizedSchema) { generateFiles(host, path.join(__dirname, './files'), options.projectRoot, { ...options, tmpl: '', - offsetFromRoot: offsetFromRoot(options.projectRoot), + rootTsConfigPath: getRelativePathToRootTsConfig(host, options.projectRoot), }); } 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 c31c52e04ce1a..eea95f339bebc 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 6485fe26e7db8..149246bbde961 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 6038dc3da8349..bed1e573009c6 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 22a1fe04f3eea..b3a1f79c754ec 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,4 +1,5 @@ import { generateFiles, offsetFromRoot, toJS, Tree } from '@nrwl/devkit'; +import { getRelativePathToRootTsConfig } from '@nrwl/workspace/src/utilities/typescript'; import { join } from 'path'; import { NormalizedSchema } from './normalize-options'; @@ -6,6 +7,10 @@ export function createApplicationFiles(host: Tree, options: NormalizedSchema) { generateFiles(host, join(__dirname, '../files/app'), options.appProjectRoot, { ...options, offsetFromRoot: offsetFromRoot(options.appProjectRoot), + rootTsConfigPath: getRelativePathToRootTsConfig( + host, + options.appProjectRoot + ), }); 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 514efa55d7104..d9affba903da0 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 67f96a2c27175..985673fe9d681 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 4715cdde7f640..70e067896ff4a 100644 --- a/packages/react-native/src/generators/library/library.ts +++ b/packages/react-native/src/generators/library/library.ts @@ -19,6 +19,10 @@ import { addLinting } from '../../utils/add-linting'; import { addJest } from '../../utils/add-jest'; import { NormalizedSchema, normalizeOptions } from './lib/normalize-options'; import { Schema } from './schema'; +import { + getRelativePathToRootTsConfig, + getRootTsConfigPathInTree, +} from '@nrwl/workspace/src/utilities/typescript'; export async function reactNativeLibraryGenerator( host: Tree, @@ -130,7 +134,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]; @@ -164,6 +168,10 @@ function createFiles(host: Tree, options: NormalizedSchema) { ...names(options.name), tmpl: '', offsetFromRoot: offsetFromRoot(options.projectRoot), + rootTsConfigPath: getRelativePathToRootTsConfig( + host, + options.projectRoot + ), } ); diff --git a/packages/react/src/generators/application/application.spec.ts b/packages/react/src/generators/application/application.spec.ts index 5661587aee45b..d03f3d4f2279e 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 877486407b8df..fc23421ea4caf 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 303bd263aae51..9e2276f04c2f5 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 { getRelativePathToRootTsConfig } from '@nrwl/workspace/src/utilities/typescript'; function updateTsConfig(host: Tree, options: NormalizedSchema) { updateJson( @@ -51,6 +52,10 @@ export function createApplicationFiles(host: Tree, options: NormalizedSchema) { ...options, tmpl: '', offsetFromRoot: offsetFromRoot(options.appProjectRoot), + rootTsConfigPath: getRelativePathToRootTsConfig( + host, + options.appProjectRoot + ), }; 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 1e7db70dbfadf..c5c5460225c53 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 0367541397aa2..096f89c6ff0e3 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 1be7064bab489..4a468803ec5ed 100644 --- a/packages/react/src/generators/library/library.ts +++ b/packages/react/src/generators/library/library.ts @@ -20,6 +20,10 @@ 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 { + getRelativePathToRootTsConfig, + getRootTsConfigPathInTree, +} from '@nrwl/workspace/src/utilities/typescript'; import * as ts from 'typescript'; import { assertValidStyle } from '../../utils/assertion'; import { @@ -239,7 +243,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]; @@ -273,6 +277,10 @@ function createFiles(host: Tree, options: NormalizedSchema) { ...names(options.name), tmpl: '', offsetFromRoot: offsetFromRoot(options.projectRoot), + rootTsConfigPath: getRelativePathToRootTsConfig( + host, + options.projectRoot + ), } ); diff --git a/packages/react/src/generators/redux/redux.ts b/packages/react/src/generators/redux/redux.ts index eaae388dfa113..e6cbc419e0061 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 822223df4a5a9..09ca31bd9ef3b 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 8294900540960..27d4e134777bc 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 4b4497923f755..034761f7e5108 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 d13c7cffceae5..01e6265ee9627 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 7dd91521d33ee..1e25d668b5a97 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 7dd91521d33ee..1e25d668b5a97 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 be650a146dabd..f87f27404abd6 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 e35ef4ce122b9..9834b4ee13810 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 4f89cb983cba3..f97d9f3062252 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 { getRelativePathToRootTsConfig } from '@nrwl/workspace/src/utilities/typescript'; import { join } from 'path'; @@ -43,6 +44,10 @@ function createApplicationFiles(tree: Tree, options: NormalizedSchema) { ...names(options.name), tmpl: '', offsetFromRoot: offsetFromRoot(options.appProjectRoot), + rootTsConfigPath: getRelativePathToRootTsConfig( + tree, + options.appProjectRoot + ), }); 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 795ebf01cbe3c..595598eddbf3e 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 4295104bf29e9..e04fc94c37167 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 79c710521cbd8..a2f5db0b8c679 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 360e33a4f7167..967ec7a73b374 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 49225a4ab3e2f..de1efe2c16d58 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 ddf6f4f61726b..06682197d42e0 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 de0c943c940bd..2a3799304699c 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 5e6a720ee9034..9d11babb1ff46 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 dd1a29421bb22..8d7ee90ee843c 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 99428e1473feb..58db6461e9058 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 18675dc38d7c3..50fc60d0038aa 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 f683805a22e1e..521c509f3602f 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 83a68fa354843..8bf55e434eef1 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 2d01f6cb7be81..603ead8ad1672 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 e7c1167c2bfee..a9e3b235d20e6 100644 --- a/packages/workspace/src/generators/library/library.ts +++ b/packages/workspace/src/generators/library/library.ts @@ -16,6 +16,10 @@ import { } from '@nrwl/devkit'; import { join } from 'path'; import { runTasksInSerial } from '../../utilities/run-tasks-in-serial'; +import { + getRelativePathToRootTsConfig, + getRootTsConfigPathInTree, +} from '../../utilities/typescript'; import { nxVersion } from '../../utils/versions'; import { Schema } from './schema'; @@ -100,7 +104,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 +130,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 +141,8 @@ function createFiles(tree: Tree, options: NormalizedSchema) { cliCommand: 'nx', strict: undefined, tmpl: '', - offsetFromRoot: offsetFromRoot(options.projectRoot), + offsetFromRoot: rootOffset, + rootTsConfigPath: getRelativePathToRootTsConfig(tree, options.projectRoot), 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 0c83169f06f3d..3dd5360fb8e00 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 0664c9cc5996b..f9cdf7055fd5f 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 31a0268377e9c..6e75027d1206b 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 0ef5cdcf2a8fb..96f16c5aabf63 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); 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 a6c08267f3bfd..f3218cd9b502e 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 { offsetFromRoot, 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): string | null { + for (const path of ['tsconfig.base.json', 'tsconfig.json']) { + if (tree.exists(path)) { + return path; + } + } + + return 'tsconfig.base.json'; +} + +export function getRelativePathToRootTsConfig( + tree: Tree, + targetPath: string +): string { + return offsetFromRoot(targetPath) + getRootTsConfigPathInTree(tree); +} + +export function getRootTsConfigFileName(): string | null { + for (const tsConfigName of ['tsconfig.base.json', 'tsconfig.json']) { + const tsConfigPath = join(appRootPath, tsConfigName); + if (existsSync(tsConfigPath)) { + return tsConfigName; + } + } + + return null; +} + +export function getRootTsConfigPath(): string | null { + const tsConfigFileName = getRootTsConfigFileName(); + + return tsConfigFileName ? join(appRootPath, tsConfigFileName) : null; +}