Skip to content

Commit

Permalink
feat(bundling): add support only bundling in workspace libs, not npm …
Browse files Browse the repository at this point in the history
…packages (#12852)
  • Loading branch information
jaysoo committed Oct 31, 2022
1 parent 17993db commit 58b5196
Show file tree
Hide file tree
Showing 13 changed files with 260 additions and 37 deletions.
10 changes: 5 additions & 5 deletions docs/generated/packages/esbuild.json
Original file line number Diff line number Diff line change
Expand Up @@ -250,16 +250,16 @@
"description": "Skip type-checking via TypeScript. Skipping type-checking speeds up the build but type errors are not caught.",
"default": false
},
"updateBuildableProjectDepsInPackageJson": {
"thirdParty": {
"type": "boolean",
"description": "Update buildable project dependencies in `package.json`.",
"description": "Includes third-party packages in the bundle (i.e. npm packages).",
"default": true
},
"buildableProjectDepsInPackageJsonType": {
"dependenciesFieldType": {
"type": "string",
"description": "When `updateBuildableProjectDepsInPackageJson` is `true`, this adds dependencies to either `peerDependencies` or `dependencies`.",
"description": "When `bundleInternalProjectsOnly` is true, this option determines whether external packages should be in 'dependencies' or 'peerDependencies' field in the generated package.json file.",
"enum": ["dependencies", "peerDependencies"],
"default": "peerDependencies"
"default": "dependencies"
},
"esbuildOptions": {
"type": "object",
Expand Down
44 changes: 38 additions & 6 deletions e2e/esbuild/src/esbuild.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ import {
cleanupProject,
newProject,
readFile,
readJson,
runCLI,
runCommand,
uniq,
updateFile,
updateProjectConfig,
packageInstall,
} from '@nrwl/e2e/utils';

describe('EsBuild Plugin', () => {
Expand Down Expand Up @@ -65,24 +67,54 @@ describe('EsBuild Plugin', () => {
expect(runCommand(`node dist/libs/${myPkg}/index.js`)).toMatch(/Bye/);
}, 300_000);

it('should bundle in workspace libs', async () => {
it('should support bundling everything or only workspace libs', async () => {
packageInstall('rambda', undefined, '~7.3.0', 'prod');
packageInstall('lodash', undefined, '~4.14.0', 'prod');
const parentLib = uniq('parent-lib');
const childLib = uniq('child-lib');
runCLI(`generate @nrwl/js:lib ${parentLib} --bundler=esbuild`);
runCLI(`generate @nrwl/js:lib ${childLib} --buildable=false`);
updateFile(
`libs/${parentLib}/src/index.ts`,
`import '@${proj}/${childLib}';`
`
// @ts-ignore
import _ from 'lodash';
import { greet } from '@${proj}/${childLib}';
console.log(_.upperFirst('hello world'));
console.log(greet());
`
);
updateFile(
`libs/${childLib}/src/index.ts`,
`console.log('Hello from lib');\n`
`
import { always } from 'rambda';
export const greet = always('Hello from child lib');
`
);

// Bundle child lib and third-party packages
runCLI(`build ${parentLib}`);

expect(runCommand(`node dist/libs/${parentLib}/index.js`)).toMatch(
/Hello from lib/
);
expect(
readJson(`dist/libs/${parentLib}/package.json`).dependencies?.['dayjs']
).not.toBeDefined();
let runResult = runCommand(`node dist/libs/${parentLib}/index.js`);
expect(runResult).toMatch(/Hello world/);
expect(runResult).toMatch(/Hello from child lib/);

// Bundle only child lib
runCLI(`build ${parentLib} --third-party=false`);

expect(
readJson(`dist/libs/${parentLib}/package.json`).dependencies
).toEqual({
// Don't care about the versions, just that they exist
rambda: expect.any(String),
lodash: expect.any(String),
});
runResult = runCommand(`node dist/libs/${parentLib}/index.js`);
expect(runResult).toMatch(/Hello world/);
expect(runResult).toMatch(/Hello from child lib/);
}, 300_000);
});
24 changes: 16 additions & 8 deletions e2e/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,21 +244,25 @@ export function runCreatePlugin(
export function packageInstall(
pkg: string,
projName?: string,
version = getPublishedVersion()
version = getPublishedVersion(),
mode: 'dev' | 'prod' = 'dev'
) {
const cwd = projName ? `${e2eCwd}/${projName}` : tmpProjPath();
const pm = getPackageManagerCommand({ path: cwd });
const pkgsWithVersions = pkg
.split(' ')
.map((pgk) => `${pgk}@${version}`)
.join(' ');
const install = execSync(`${pm.addDev} ${pkgsWithVersions}`, {
cwd,
// stdio: [0, 1, 2],
stdio: ['pipe', 'pipe', 'pipe'],
env: process.env,
encoding: 'utf-8',
});
const install = execSync(
`${mode === 'dev' ? pm.addDev : pm.addProd} ${pkgsWithVersions}`,
{
cwd,
// stdio: [0, 1, 2],
stdio: ['pipe', 'pipe', 'pipe'],
env: process.env,
encoding: 'utf-8',
}
);
return install ? install.toString() : '';
}

Expand Down Expand Up @@ -853,6 +857,7 @@ export function getPackageManagerCommand({
runNx: string;
runNxSilent: string;
runUninstalledPackage: string;
addProd: string;
addDev: string;
list: string;
} {
Expand All @@ -868,6 +873,7 @@ export function getPackageManagerCommand({
runNx: `npx nx`,
runNxSilent: `npx nx`,
runUninstalledPackage: `npx --yes`,
addProd: `npm install --legacy-peer-deps`,
addDev: `npm install --legacy-peer-deps -D`,
list: 'npm ls --depth 10',
},
Expand All @@ -878,6 +884,7 @@ export function getPackageManagerCommand({
runNx: `yarn nx`,
runNxSilent: `yarn --silent nx`,
runUninstalledPackage: 'npx --yes',
addProd: `yarn add`,
addDev: `yarn add -D`,
list: 'npm ls --depth 10',
},
Expand All @@ -888,6 +895,7 @@ export function getPackageManagerCommand({
runNx: `pnpm exec nx`,
runNxSilent: `pnpm exec nx`,
runUninstalledPackage: 'pnpm dlx',
addProd: `pnpm add`,
addDev: `pnpm add -D`,
list: 'npm ls --depth 10',
},
Expand Down
56 changes: 46 additions & 10 deletions packages/esbuild/src/executors/esbuild/esbuild.impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { cacheDir, joinPathFragments, logger } from '@nrwl/devkit';
import {
copyAssets,
copyPackageJson,
CopyPackageJsonOptions,
printDiagnostics,
runTypeCheck as _runTypeCheck,
TypeCheckOptions,
Expand All @@ -16,8 +17,10 @@ import { EsBuildExecutorOptions } from './schema';
import { removeSync, writeJsonSync } from 'fs-extra';
import { createAsyncIterable } from '@nrwl/js/src/utils/create-async-iterable/create-async-iteratable';
import { buildEsbuildOptions } from './lib/build-esbuild-options';
import { getExtraDependencies } from './lib/get-extra-dependencies';
import { DependentBuildableProjectNode } from '@nrwl/workspace/src/utilities/buildable-libs-utils';

const CJS_FILE_EXTENSION = '.cjs';
const CJS_FILE_EXTENSION = '.cjs' as const;

const BUILD_WATCH_FAILED = `[ ${chalk.red(
'watch'
Expand All @@ -35,15 +38,48 @@ export async function* esbuildExecutor(

const assetsResult = await copyAssets(options, context);

const packageJsonResult = await copyPackageJson(
{
...options,
// TODO(jack): make types generate with esbuild
skipTypings: true,
outputFileExtensionForCjs: CJS_FILE_EXTENSION,
},
context
);
const externalDependencies: DependentBuildableProjectNode[] =
options.external.map((name) => {
const externalNode = context.projectGraph.externalNodes[`npm:${name}`];
if (!externalNode)
throw new Error(
`Cannot find external dependency ${name}. Check your package.json file.`
);
return {
name,
outputs: [],
node: externalNode,
};
});

if (!options.thirdParty) {
const thirdPartyDependencies = getExtraDependencies(
context.projectName,
context.projectGraph
);
for (const tpd of thirdPartyDependencies) {
options.external.push(tpd.node.data.packageName);
externalDependencies.push(tpd);
}
}

const cpjOptions: CopyPackageJsonOptions = {
...options,
// TODO(jack): make types generate with esbuild
skipTypings: true,
outputFileExtensionForCjs: CJS_FILE_EXTENSION,
excludeLibsInPackageJson: !options.thirdParty,
updateBuildableProjectDepsInPackageJson: externalDependencies.length > 0,
};

// If we're bundling third-party packages, then any extra deps from external should be the only deps in package.json
if (options.thirdParty && externalDependencies.length > 0) {
cpjOptions.overrideDependencies = externalDependencies;
} else {
cpjOptions.extraDependencies = externalDependencies;
}

const packageJsonResult = await copyPackageJson(cpjOptions, context);

if (options.watch) {
return yield* createAsyncIterable<{ success: boolean; outfile?: string }>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ describe('buildEsbuildOptions', () => {
assets: [],
outputFileName: 'index.js',
singleEntry: true,
external: [],
},
context
)
Expand All @@ -49,6 +50,7 @@ describe('buildEsbuildOptions', () => {
platform: 'browser',
outfile: 'dist/apps/myapp/index.js',
tsconfig: 'apps/myapp/tsconfig.app.json',
external: [],
outExtension: {
'.js': '.js',
},
Expand All @@ -69,6 +71,7 @@ describe('buildEsbuildOptions', () => {
assets: [],
outputFileName: 'index.js',
singleEntry: false,
external: [],
},
context
)
Expand All @@ -83,6 +86,7 @@ describe('buildEsbuildOptions', () => {
platform: 'browser',
outdir: 'dist/apps/myapp',
tsconfig: 'apps/myapp/tsconfig.app.json',
external: [],
outExtension: {
'.js': '.js',
},
Expand All @@ -102,6 +106,7 @@ describe('buildEsbuildOptions', () => {
assets: [],
outputFileName: 'index.js',
singleEntry: true,
external: [],
},
context
)
Expand All @@ -116,6 +121,7 @@ describe('buildEsbuildOptions', () => {
platform: 'browser',
outfile: 'dist/apps/myapp/index.cjs',
tsconfig: 'apps/myapp/tsconfig.app.json',
external: [],
outExtension: {
'.js': '.cjs',
},
Expand All @@ -135,6 +141,7 @@ describe('buildEsbuildOptions', () => {
assets: [],
outputFileName: 'index.js',
singleEntry: true,
external: [],
},
context
)
Expand All @@ -146,6 +153,7 @@ describe('buildEsbuildOptions', () => {
platform: 'node',
outfile: 'dist/apps/myapp/index.cjs',
tsconfig: 'apps/myapp/tsconfig.app.json',
external: [],
outExtension: {
'.js': '.cjs',
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { getExtraDependencies } from './get-extra-dependencies';

describe('getExtraDependencies', () => {
it('should include npm dependencies of child libs', () => {
const result = getExtraDependencies('parent', {
nodes: {
parent: {
type: 'app',
name: 'parent',
data: {},
},
child1: {
type: 'lib',
name: 'child1',
data: {},
},
child2: {
type: 'lib',
name: 'child2',
data: {},
},
},
externalNodes: {
'npm:react': {
type: 'npm',
name: 'npm:react',
data: { packageName: 'react', version: '18.0.0' },
},
'npm:axios': {
type: 'npm',
name: 'npm:axios',
data: { packageName: 'axios', version: '1.0.0' },
},
'npm:dayjs': {
type: 'npm',
name: 'npm:dayjs',
data: { packageName: 'dayjs', version: '1.11.0' },
},
},
dependencies: {
parent: [
{ source: 'parent', target: 'child1', type: 'static' },
{ source: 'parent', target: 'npm:react', type: 'static' },
],
child1: [
{ source: 'child1', target: 'child2', type: 'static' },
{ source: 'child1', target: 'npm:axios', type: 'static' },
],
child2: [{ source: 'child2', target: 'npm:dayjs', type: 'static' }],
},
});

expect(result).toEqual([
{
name: 'npm:react',
outputs: [],
node: {
type: 'npm',
name: 'npm:react',
data: { packageName: 'react', version: '18.0.0' },
},
},
{
name: 'npm:axios',
outputs: [],
node: {
type: 'npm',
name: 'npm:axios',
data: { packageName: 'axios', version: '1.0.0' },
},
},
{
name: 'npm:dayjs',
outputs: [],
node: {
type: 'npm',
name: 'npm:dayjs',
data: { packageName: 'dayjs', version: '1.11.0' },
},
},
]);
});
});

1 comment on commit 58b5196

@vercel
Copy link

@vercel vercel bot commented on 58b5196 Oct 31, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

nx-dev – ./

nx-five.vercel.app
nx-dev-git-master-nrwl.vercel.app
nx.dev
nx-dev-nrwl.vercel.app

Please sign in to comment.