diff --git a/e2e/js/src/js.test.ts b/e2e/js/src/js.test.ts index 51af7b938263a..4d5106b956511 100644 --- a/e2e/js/src/js.test.ts +++ b/e2e/js/src/js.test.ts @@ -5,7 +5,6 @@ import { readJson, runCLI, runCLIAsync, - runCommand, runCommandUntil, uniq, updateFile, @@ -14,8 +13,13 @@ import { } from '../../utils'; describe('js e2e', () => { + let scope: string; + + beforeEach(() => { + scope = newProject(); + }); + it('should create libs with npm scripts', () => { - const scope = newProject(); const npmScriptsLib = uniq('npmscriptslib'); runCLI(`generate @nrwl/js:lib ${npmScriptsLib} --config=npm-scripts`); const libPackageJson = readJson(`libs/${npmScriptsLib}/package.json`); @@ -31,7 +35,6 @@ describe('js e2e', () => { }, 120000); it('should create libs with js executors (--compiler=tsc)', async () => { - const scope = newProject(); const lib = uniq('lib'); runCLI(`generate @nrwl/js:lib ${lib} --buildable --compiler=tsc`); const libPackageJson = readJson(`libs/${lib}/package.json`); @@ -123,10 +126,35 @@ describe('js e2e', () => { const output = runCLI(`build ${parentLib}`); expect(output).toContain('1 task(s) it depends on'); expect(output).toContain('Done compiling TypeScript files'); + + updateJson(`libs/${lib}/tsconfig.json`, (json) => { + json.compilerOptions = { ...json.compilerOptions, importHelpers: true }; + return json; + }); + + runCLI(`build ${lib}`); + + const rootPackageJson = readJson(`package.json`); + + expect(readJson(`dist/libs/${lib}/package.json`)).toHaveProperty( + 'peerDependencies.tslib', + rootPackageJson.dependencies.tslib ?? + rootPackageJson.devDependencies.tslib + ); + + updateJson(`libs/${lib}/tsconfig.json`, (json) => { + json.compilerOptions = { ...json.compilerOptions, importHelpers: false }; + return json; + }); + + runCLI(`build ${lib}`); + + expect(readJson(`dist/libs/${lib}/package.json`)).not.toHaveProperty( + 'peerDependencies.tslib' + ); }, 120000); it('should create libs with js executors (--compiler=swc)', async () => { - const scope = newProject(); const lib = uniq('lib'); runCLI(`generate @nrwl/js:lib ${lib} --buildable --compiler=swc`); const libPackageJson = readJson(`libs/${lib}/package.json`); diff --git a/packages/js/src/executors/tsc/tsc.impl.ts b/packages/js/src/executors/tsc/tsc.impl.ts index 2a02159d267a5..d61994472c8b6 100644 --- a/packages/js/src/executors/tsc/tsc.impl.ts +++ b/packages/js/src/executors/tsc/tsc.impl.ts @@ -7,6 +7,7 @@ import { join, resolve } from 'path'; import { checkDependencies } from '../../utils/check-dependencies'; import { CopyAssetsHandler } from '../../utils/copy-assets-handler'; import { ExecutorOptions, NormalizedExecutorOptions } from '../../utils/schema'; +import { addTslibDependencyIfNeeded } from '../../utils/tslib-dependency'; import { compileTypeScriptFiles } from '../../utils/typescript/compile-typescript-files'; import { updatePackageJson } from '../../utils/update-package-json'; import { watchForSingleFileChanges } from '../../utils/watch-for-single-file-changes'; @@ -60,6 +61,8 @@ export async function* tscExecutor( options.tsConfig = tmpTsConfig; } + addTslibDependencyIfNeeded(options, context, dependencies); + const assetHandler = new CopyAssetsHandler({ projectDir: projectRoot, rootDir: context.root, diff --git a/packages/js/src/utils/tslib-dependency.ts b/packages/js/src/utils/tslib-dependency.ts new file mode 100644 index 0000000000000..a61c2b31aafcc --- /dev/null +++ b/packages/js/src/utils/tslib-dependency.ts @@ -0,0 +1,47 @@ +import { ExecutorContext, getPackageManagerCommand } from '@nrwl/devkit'; +import { readCachedProjectGraph } from '@nrwl/workspace/src/core/project-graph'; +import { DependentBuildableProjectNode } from '@nrwl/workspace/src/utilities/buildable-libs-utils'; +import { readTsConfig } from '@nrwl/workspace/src/utilities/typescript'; +import { NormalizedExecutorOptions } from './schema'; + +const tslibNodeName = 'npm:tslib'; + +function shouldAddTslibDependency( + tsConfig: string, + dependencies: DependentBuildableProjectNode[] +): boolean { + if (dependencies.some((dep) => dep.name === tslibNodeName)) { + return false; + } + + const config = readTsConfig(tsConfig); + return !!config.options.importHelpers; +} + +export function addTslibDependencyIfNeeded( + options: NormalizedExecutorOptions, + context: ExecutorContext, + dependencies: DependentBuildableProjectNode[] +): void { + if (!shouldAddTslibDependency(options.tsConfig, dependencies)) { + return; + } + + const depGraph = readCachedProjectGraph(); + const tslibNode = depGraph.externalNodes[tslibNodeName]; + + if (!tslibNode) { + const pmc = getPackageManagerCommand(); + throw new Error( + `"importHelpers" is enabled for ${context.targetName} but tslib is not installed. Use "${pmc.add} tslib" to install it.` + ); + } + + const tslibDependency: DependentBuildableProjectNode = { + name: tslibNodeName, + outputs: [], + node: tslibNode, + }; + + dependencies.push(tslibDependency); +} diff --git a/packages/workspace/src/utilities/typescript.ts b/packages/workspace/src/utilities/typescript.ts index ea170c21091df..2a217ce1ccd82 100644 --- a/packages/workspace/src/utilities/typescript.ts +++ b/packages/workspace/src/utilities/typescript.ts @@ -10,7 +10,7 @@ export { getSourceNodes } from './typescript/get-source-nodes'; const normalizedAppRoot = appRootPath.replace(/\\/g, '/'); -let tsModule: any; +let tsModule: typeof import('typescript'); export function readTsConfig(tsConfigPath: string) { if (!tsModule) { @@ -31,18 +31,21 @@ function readTsConfigOptions(tsConfigPath: string) { if (!tsModule) { tsModule = require('typescript'); } + const readResult = tsModule.readConfigFile( tsConfigPath, tsModule.sys.readFile ); + // we don't need to scan the files, we only care about options - const host = { + const host: Partial = { readDirectory: () => [], fileExists: tsModule.sys.fileExists, }; + return tsModule.parseJsonConfigFileContent( readResult.config, - host, + host as ts.ParseConfigHost, dirname(tsConfigPath) ).options; }