Skip to content

Commit

Permalink
feat(js): infer tslib as dependency when using importHelpers
Browse files Browse the repository at this point in the history
When using "importHelpers" and building with "tsc", the built bundle expects to be supplied the
"tslib" package.

Currently, it's up to developers to add "tslib" as a dependency to those
libraries' package.json files.

This PR makes the "tsc" execute infer that "importHelpers" is used,
and subsequently add "tslib" as a dependency to the generated package.json

ISSUES CLOSED: #9343
  • Loading branch information
gioragutt committed Mar 16, 2022
1 parent 1b7a3b2 commit 2119fff
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 7 deletions.
28 changes: 23 additions & 5 deletions e2e/js/src/js.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
readJson,
runCLI,
runCLIAsync,
runCommand,
runCommandUntil,
uniq,
updateFile,
Expand All @@ -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`);
Expand All @@ -30,8 +34,7 @@ describe('js e2e', () => {
});
}, 120000);

it('should create libs with js executors (--compiler=tsc)', async () => {
const scope = newProject();
fit('should create libs with js executors (--compiler=tsc)', async () => {
const lib = uniq('lib');
runCLI(`generate @nrwl/js:lib ${lib} --buildable --compiler=tsc`);
const libPackageJson = readJson(`libs/${lib}/package.json`);
Expand Down Expand Up @@ -123,10 +126,25 @@ describe('js e2e', () => {
const output = runCLI(`build ${parentLib}`);
expect(output).toContain('1 task(s) it depends on');
expect(output).toContain('Done compiling TypeScript files');

const libWithImportHelpers = uniq('lib-with-import-helpers');
runCLI(
`generate @nrwl/js:lib ${libWithImportHelpers} --buildable --compiler=tsc`
);

updateJson(`libs/${libWithImportHelpers}/tsconfig.json`, (json) => {
json.compilerOptions = { ...json.compilerOptions, importHelpers: true };
return json;
});

runCLI(`build ${libWithImportHelpers}`);

expect(
readJson(`dist/libs/${libWithImportHelpers}/package.json`)
).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`);
Expand Down
3 changes: 3 additions & 0 deletions packages/js/src/executors/tsc/tsc.impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -60,6 +61,8 @@ export async function* tscExecutor(
options.tsConfig = tmpTsConfig;
}

addTslibDependencyIfNeeded(options, context, dependencies);

const assetHandler = new CopyAssetsHandler({
projectDir: projectRoot,
rootDir: context.root,
Expand Down
47 changes: 47 additions & 0 deletions packages/js/src/utils/tslib-dependency.ts
Original file line number Diff line number Diff line change
@@ -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} tslint" to install it.`
);
}

const tslibDependency: DependentBuildableProjectNode = {
name: tslibNodeName,
outputs: [],
node: tslibNode,
};

dependencies.push(tslibDependency);
}
4 changes: 2 additions & 2 deletions packages/workspace/src/utilities/typescript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -36,7 +36,7 @@ function readTsConfigOptions(tsConfigPath: string) {
tsModule.sys.readFile
);
// we don't need to scan the files, we only care about options
const host = {
const host: any = {
readDirectory: () => [],
fileExists: tsModule.sys.fileExists,
};
Expand Down

0 comments on commit 2119fff

Please sign in to comment.