feat(bundling): add for esbuild to enable/disable package.json generation (#15777)
This commit is contained in:
parent
f10ecc1c8e
commit
7ebca5107e
@ -147,6 +147,11 @@
|
||||
"default": false,
|
||||
"x-priority": "internal"
|
||||
},
|
||||
"generatePackageJson": {
|
||||
"type": "boolean",
|
||||
"description": "Generates a `package.json` and pruned lock file with the project's `node_module` dependencies populated for installing in a container. If a `package.json` exists in the project's directory, it will be reused with dependencies populated.",
|
||||
"default": false
|
||||
},
|
||||
"thirdParty": {
|
||||
"type": "boolean",
|
||||
"description": "Includes third-party packages in the bundle (i.e. npm packages).",
|
||||
|
||||
@ -34,6 +34,17 @@ describe('EsBuild Plugin', () => {
|
||||
updateFile(`libs/${myPkg}/assets/a.md`, 'file a');
|
||||
updateFile(`libs/${myPkg}/assets/b.md`, 'file b');
|
||||
|
||||
// Copy package.json as asset rather than generate with Nx-detected fields.
|
||||
runCLI(`build ${myPkg} --generatePackageJson=false`);
|
||||
const packageJson = readJson(`libs/${myPkg}/package.json`);
|
||||
// This is the file that is generated by lib generator (no deps, no main, etc.).
|
||||
expect(packageJson).toEqual({
|
||||
name: `@proj/${myPkg}`,
|
||||
version: '0.0.1',
|
||||
type: 'commonjs',
|
||||
});
|
||||
|
||||
// Build normally with package.json generation.
|
||||
runCLI(`build ${myPkg}`);
|
||||
|
||||
expect(runCommand(`node dist/libs/${myPkg}/index.js`)).toMatch(/Hello/);
|
||||
@ -41,6 +52,9 @@ describe('EsBuild Plugin', () => {
|
||||
checkFilesExist(`dist/libs/${myPkg}/package.json`);
|
||||
expect(runCommand(`node dist/libs/${myPkg}`)).toMatch(/Hello/);
|
||||
|
||||
expect(runCommand(`node dist/libs/${myPkg}/index.js`)).toMatch(/Hello/);
|
||||
// main field should be set correctly in package.json
|
||||
|
||||
expect(readFile(`dist/libs/${myPkg}/assets/a.md`)).toMatch(/file a/);
|
||||
expect(readFile(`dist/libs/${myPkg}/assets/b.md`)).toMatch(/file b/);
|
||||
|
||||
|
||||
@ -1,5 +1,12 @@
|
||||
{
|
||||
"generators": {},
|
||||
"generators": {
|
||||
"set-generate-package-json": {
|
||||
"cli": "nx",
|
||||
"version": "15.8.7-beta.0",
|
||||
"description": "Set generatePackageJson to true to maintain existing behavior of generating package.json in output path.",
|
||||
"factory": "./src/migrations/update-15-8-7/set-generate-package-json"
|
||||
}
|
||||
},
|
||||
"packageJsonUpdates": {
|
||||
"15.7.0": {
|
||||
"version": "15.7.0-beta.0",
|
||||
|
||||
@ -41,7 +41,7 @@ export async function* esbuildExecutor(
|
||||
_options: EsBuildExecutorOptions,
|
||||
context: ExecutorContext
|
||||
) {
|
||||
const options = normalizeOptions(_options);
|
||||
const options = normalizeOptions(_options, context);
|
||||
if (options.deleteOutputPath) removeSync(options.outputPath);
|
||||
|
||||
const assetsResult = await copyAssets(options, context);
|
||||
@ -70,6 +70,8 @@ export async function* esbuildExecutor(
|
||||
}
|
||||
}
|
||||
|
||||
let packageJsonResult;
|
||||
if (options.generatePackageJson) {
|
||||
const cpjOptions: CopyPackageJsonOptions = {
|
||||
...options,
|
||||
// TODO(jack): make types generate with esbuild
|
||||
@ -86,7 +88,8 @@ export async function* esbuildExecutor(
|
||||
cpjOptions.extraDependencies = externalDependencies;
|
||||
}
|
||||
|
||||
const packageJsonResult = await copyPackageJson(cpjOptions, context);
|
||||
packageJsonResult = await copyPackageJson(cpjOptions, context);
|
||||
}
|
||||
|
||||
if ('context' in esbuild) {
|
||||
// 0.17.0+ adds esbuild.context and context.watch()
|
||||
|
||||
@ -45,7 +45,6 @@ describe('buildEsbuildOptions', () => {
|
||||
main: 'apps/myapp/src/index.ts',
|
||||
outputPath: 'dist/apps/myapp',
|
||||
tsConfig: 'apps/myapp/tsconfig.app.json',
|
||||
project: 'apps/myapp/package.json',
|
||||
assets: [],
|
||||
outputFileName: 'index.js',
|
||||
singleEntry: true,
|
||||
@ -82,7 +81,6 @@ describe('buildEsbuildOptions', () => {
|
||||
additionalEntryPoints: ['apps/myapp/src/extra-entry.ts'],
|
||||
outputPath: 'dist/apps/myapp',
|
||||
tsConfig: 'apps/myapp/tsconfig.app.json',
|
||||
project: 'apps/myapp/package.json',
|
||||
assets: [],
|
||||
outputFileName: 'index.js',
|
||||
singleEntry: false,
|
||||
@ -118,7 +116,6 @@ describe('buildEsbuildOptions', () => {
|
||||
main: 'apps/myapp/src/index.ts',
|
||||
outputPath: 'dist/apps/myapp',
|
||||
tsConfig: 'apps/myapp/tsconfig.app.json',
|
||||
project: 'apps/myapp/package.json',
|
||||
assets: [],
|
||||
outputFileName: 'index.js',
|
||||
singleEntry: true,
|
||||
@ -154,7 +151,6 @@ describe('buildEsbuildOptions', () => {
|
||||
main: 'apps/myapp/src/index.ts',
|
||||
outputPath: 'dist/apps/myapp',
|
||||
tsConfig: 'apps/myapp/tsconfig.app.json',
|
||||
project: 'apps/myapp/package.json',
|
||||
assets: [],
|
||||
outputFileName: 'index.js',
|
||||
singleEntry: true,
|
||||
@ -187,7 +183,6 @@ describe('buildEsbuildOptions', () => {
|
||||
main: 'apps/myapp/src/index.ts',
|
||||
outputPath: 'dist/apps/myapp',
|
||||
tsConfig: 'apps/myapp/tsconfig.app.json',
|
||||
project: 'apps/myapp/package.json',
|
||||
outputFileName: 'index.js',
|
||||
assets: [],
|
||||
singleEntry: true,
|
||||
@ -223,7 +218,6 @@ describe('buildEsbuildOptions', () => {
|
||||
main: 'apps/myapp/src/index.ts',
|
||||
outputPath: 'dist/apps/myapp',
|
||||
tsConfig: 'apps/myapp/tsconfig.app.json',
|
||||
project: 'apps/myapp/package.json',
|
||||
outputFileName: 'index.js',
|
||||
assets: [],
|
||||
singleEntry: true,
|
||||
@ -260,7 +254,6 @@ describe('buildEsbuildOptions', () => {
|
||||
main: 'apps/myapp/src/index.ts',
|
||||
outputPath: 'dist/apps/myapp',
|
||||
tsConfig: 'apps/myapp/tsconfig.app.json',
|
||||
project: 'apps/myapp/package.json',
|
||||
outputFileName: 'index.js',
|
||||
assets: [],
|
||||
singleEntry: true,
|
||||
@ -298,10 +291,9 @@ describe('buildEsbuildOptions', () => {
|
||||
main: 'apps/myapp/src/index.ts',
|
||||
outputPath: 'dist/apps/myapp',
|
||||
tsConfig: 'apps/myapp/tsconfig.app.json',
|
||||
project: 'apps/myapp/package.json',
|
||||
outputFileName: 'index.js',
|
||||
assets: [],
|
||||
singleEntry: true,
|
||||
outputFileName: 'index.js',
|
||||
external: ['foo'],
|
||||
esbuildOptions: {
|
||||
external: ['bar'],
|
||||
@ -334,8 +326,6 @@ describe('buildEsbuildOptions', () => {
|
||||
main: 'apps/myapp/src/index.ts',
|
||||
outputPath: 'dist/apps/myapp',
|
||||
tsConfig: 'apps/myapp/tsconfig.app.json',
|
||||
project: 'apps/myapp/package.json',
|
||||
outputFileName: 'index.js',
|
||||
assets: [],
|
||||
singleEntry: true,
|
||||
external: ['foo'],
|
||||
@ -367,7 +357,40 @@ describe('buildEsbuildOptions', () => {
|
||||
main: 'apps/myapp/src/index.ts',
|
||||
outputPath: 'dist/apps/myapp',
|
||||
tsConfig: 'apps/myapp/tsconfig.app.json',
|
||||
project: 'apps/myapp/package.json',
|
||||
outputFileName: 'index.js',
|
||||
assets: [],
|
||||
singleEntry: true,
|
||||
sourcemap: true,
|
||||
external: [],
|
||||
},
|
||||
context
|
||||
)
|
||||
).toEqual({
|
||||
bundle: false,
|
||||
entryNames: '[dir]/[name]',
|
||||
entryPoints: ['apps/myapp/src/index.ts'],
|
||||
format: 'esm',
|
||||
platform: 'node',
|
||||
outdir: 'dist/apps/myapp',
|
||||
tsconfig: 'apps/myapp/tsconfig.app.json',
|
||||
external: undefined,
|
||||
sourcemap: true,
|
||||
outExtension: {
|
||||
'.js': '.js',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should set sourcemap', () => {
|
||||
expect(
|
||||
buildEsbuildOptions(
|
||||
'esm',
|
||||
{
|
||||
bundle: false,
|
||||
platform: 'node',
|
||||
main: 'apps/myapp/src/index.ts',
|
||||
outputPath: 'dist/apps/myapp',
|
||||
tsConfig: 'apps/myapp/tsconfig.app.json',
|
||||
outputFileName: 'index.js',
|
||||
assets: [],
|
||||
singleEntry: true,
|
||||
|
||||
@ -1,21 +1,45 @@
|
||||
import { normalizeOptions } from './normalize';
|
||||
import { ExecutorContext } from '@nrwl/devkit';
|
||||
|
||||
describe('normalizeOptions', () => {
|
||||
const context: ExecutorContext = {
|
||||
root: '/',
|
||||
cwd: '/',
|
||||
isVerbose: false,
|
||||
projectName: 'myapp',
|
||||
projectGraph: {
|
||||
nodes: {
|
||||
myapp: {
|
||||
type: 'app',
|
||||
name: 'myapp',
|
||||
data: {
|
||||
root: 'apps/myapp',
|
||||
files: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
dependencies: {},
|
||||
},
|
||||
};
|
||||
|
||||
it('should handle single entry point options', () => {
|
||||
expect(
|
||||
normalizeOptions({
|
||||
normalizeOptions(
|
||||
{
|
||||
main: 'apps/myapp/src/index.ts',
|
||||
outputPath: 'dist/apps/myapp',
|
||||
tsConfig: 'apps/myapp/tsconfig.app.json',
|
||||
project: 'apps/myapp/package.json',
|
||||
generatePackageJson: true,
|
||||
assets: [],
|
||||
})
|
||||
},
|
||||
context
|
||||
)
|
||||
).toEqual({
|
||||
main: 'apps/myapp/src/index.ts',
|
||||
outputPath: 'dist/apps/myapp',
|
||||
tsConfig: 'apps/myapp/tsconfig.app.json',
|
||||
project: 'apps/myapp/package.json',
|
||||
assets: [],
|
||||
generatePackageJson: true,
|
||||
outputFileName: 'index.js',
|
||||
singleEntry: true,
|
||||
external: [],
|
||||
@ -24,20 +48,23 @@ describe('normalizeOptions', () => {
|
||||
|
||||
it('should handle multiple entry point options', () => {
|
||||
expect(
|
||||
normalizeOptions({
|
||||
normalizeOptions(
|
||||
{
|
||||
main: 'apps/myapp/src/index.ts',
|
||||
outputPath: 'dist/apps/myapp',
|
||||
tsConfig: 'apps/myapp/tsconfig.app.json',
|
||||
project: 'apps/myapp/package.json',
|
||||
assets: [],
|
||||
generatePackageJson: true,
|
||||
additionalEntryPoints: ['apps/myapp/src/extra-entry.ts'],
|
||||
})
|
||||
},
|
||||
context
|
||||
)
|
||||
).toEqual({
|
||||
main: 'apps/myapp/src/index.ts',
|
||||
outputPath: 'dist/apps/myapp',
|
||||
tsConfig: 'apps/myapp/tsconfig.app.json',
|
||||
project: 'apps/myapp/package.json',
|
||||
assets: [],
|
||||
generatePackageJson: true,
|
||||
outputFileName: 'index.js',
|
||||
additionalEntryPoints: ['apps/myapp/src/extra-entry.ts'],
|
||||
singleEntry: false,
|
||||
@ -47,20 +74,23 @@ describe('normalizeOptions', () => {
|
||||
|
||||
it('should support custom output file name', () => {
|
||||
expect(
|
||||
normalizeOptions({
|
||||
normalizeOptions(
|
||||
{
|
||||
main: 'apps/myapp/src/index.ts',
|
||||
outputPath: 'dist/apps/myapp',
|
||||
tsConfig: 'apps/myapp/tsconfig.app.json',
|
||||
project: 'apps/myapp/package.json',
|
||||
assets: [],
|
||||
generatePackageJson: true,
|
||||
outputFileName: 'test.js',
|
||||
})
|
||||
},
|
||||
context
|
||||
)
|
||||
).toEqual({
|
||||
main: 'apps/myapp/src/index.ts',
|
||||
outputPath: 'dist/apps/myapp',
|
||||
tsConfig: 'apps/myapp/tsconfig.app.json',
|
||||
project: 'apps/myapp/package.json',
|
||||
assets: [],
|
||||
generatePackageJson: true,
|
||||
outputFileName: 'test.js',
|
||||
singleEntry: true,
|
||||
external: [],
|
||||
@ -69,15 +99,42 @@ describe('normalizeOptions', () => {
|
||||
|
||||
it('should validate against multiple entry points + outputFileName', () => {
|
||||
expect(() =>
|
||||
normalizeOptions({
|
||||
normalizeOptions(
|
||||
{
|
||||
main: 'apps/myapp/src/index.ts',
|
||||
outputPath: 'dist/apps/myapp',
|
||||
tsConfig: 'apps/myapp/tsconfig.app.json',
|
||||
project: 'apps/myapp/package.json',
|
||||
assets: [],
|
||||
generatePackageJson: true,
|
||||
additionalEntryPoints: ['apps/myapp/src/extra-entry.ts'],
|
||||
outputFileName: 'test.js',
|
||||
})
|
||||
},
|
||||
context
|
||||
)
|
||||
).toThrow(/Cannot use/);
|
||||
});
|
||||
|
||||
it('should add package.json to assets array if generatePackageJson is false', () => {
|
||||
expect(
|
||||
normalizeOptions(
|
||||
{
|
||||
main: 'apps/myapp/src/index.ts',
|
||||
outputPath: 'dist/apps/myapp',
|
||||
tsConfig: 'apps/myapp/tsconfig.app.json',
|
||||
generatePackageJson: false,
|
||||
assets: [],
|
||||
},
|
||||
context
|
||||
)
|
||||
).toEqual({
|
||||
main: 'apps/myapp/src/index.ts',
|
||||
outputPath: 'dist/apps/myapp',
|
||||
tsConfig: 'apps/myapp/tsconfig.app.json',
|
||||
assets: ['apps/myapp/package.json'],
|
||||
generatePackageJson: false,
|
||||
outputFileName: 'index.js',
|
||||
singleEntry: true,
|
||||
external: [],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -3,10 +3,23 @@ import {
|
||||
EsBuildExecutorOptions,
|
||||
NormalizedEsBuildExecutorOptions,
|
||||
} from '../schema';
|
||||
import { ExecutorContext, joinPathFragments } from '@nrwl/devkit';
|
||||
|
||||
export function normalizeOptions(
|
||||
options: EsBuildExecutorOptions
|
||||
options: EsBuildExecutorOptions,
|
||||
context: ExecutorContext
|
||||
): NormalizedEsBuildExecutorOptions {
|
||||
// If we're not generating package.json file, then copy it as-is as an asset.
|
||||
const assets = options.generatePackageJson
|
||||
? options.assets
|
||||
: [
|
||||
...options.assets,
|
||||
joinPathFragments(
|
||||
context.projectGraph.nodes[context.projectName].data.root,
|
||||
'package.json'
|
||||
),
|
||||
];
|
||||
|
||||
if (options.additionalEntryPoints?.length > 0) {
|
||||
const { outputFileName, ...rest } = options;
|
||||
if (outputFileName) {
|
||||
@ -16,6 +29,7 @@ export function normalizeOptions(
|
||||
}
|
||||
return {
|
||||
...rest,
|
||||
assets,
|
||||
external: options.external ?? [],
|
||||
singleEntry: false,
|
||||
// Use the `main` file name as the output file name.
|
||||
@ -26,6 +40,7 @@ export function normalizeOptions(
|
||||
} else {
|
||||
return {
|
||||
...options,
|
||||
assets,
|
||||
external: options.external ?? [],
|
||||
singleEntry: true,
|
||||
outputFileName:
|
||||
|
||||
@ -4,7 +4,7 @@ type Compiler = 'babel' | 'swc';
|
||||
|
||||
export interface EsBuildExecutorOptions {
|
||||
additionalEntryPoints?: string[];
|
||||
assets: AssetGlob[];
|
||||
assets: (AssetGlob | string)[];
|
||||
buildableProjectDepsInPackageJsonType?: 'dependencies' | 'peerDependencies';
|
||||
bundle?: boolean;
|
||||
deleteOutputPath?: boolean;
|
||||
@ -20,7 +20,6 @@ export interface EsBuildExecutorOptions {
|
||||
outputHashing?: 'none' | 'all';
|
||||
outputPath: string;
|
||||
platform?: 'node' | 'browser' | 'neutral';
|
||||
project: string;
|
||||
sourcemap?: boolean | 'linked' | 'inline' | 'external' | 'both';
|
||||
skipTypeCheck?: boolean;
|
||||
target?: string;
|
||||
|
||||
@ -122,6 +122,11 @@
|
||||
"default": false,
|
||||
"x-priority": "internal"
|
||||
},
|
||||
"generatePackageJson": {
|
||||
"type": "boolean",
|
||||
"description": "Generates a `package.json` and pruned lock file with the project's `node_module` dependencies populated for installing in a container. If a `package.json` exists in the project's directory, it will be reused with dependencies populated.",
|
||||
"default": false
|
||||
},
|
||||
"thirdParty": {
|
||||
"type": "boolean",
|
||||
"description": "Includes third-party packages in the bundle (i.e. npm packages).",
|
||||
|
||||
@ -36,7 +36,6 @@ describe('esbuildProjectGenerator', () => {
|
||||
main: `libs/mypkg/src/${main}`,
|
||||
outputFileName: 'main.js',
|
||||
outputPath: 'dist/libs/mypkg',
|
||||
project: 'libs/mypkg/package.json',
|
||||
tsConfig: `libs/mypkg/${tsConfig}`,
|
||||
});
|
||||
});
|
||||
|
||||
@ -55,7 +55,6 @@ function addBuildTarget(tree: Tree, options: EsBuildProjectSchema) {
|
||||
outputPath: joinPathFragments('dist', project.root),
|
||||
outputFileName: 'main.js',
|
||||
tsConfig,
|
||||
project: `${project.root}/package.json`,
|
||||
assets: [],
|
||||
platform: options.platform,
|
||||
};
|
||||
|
||||
@ -0,0 +1,73 @@
|
||||
import {
|
||||
addProjectConfiguration,
|
||||
readProjectConfiguration,
|
||||
Tree,
|
||||
} from '@nrwl/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
|
||||
import update from './set-generate-package-json';
|
||||
|
||||
describe('Migration: Set generatePackageJson', () => {
|
||||
let tree: Tree;
|
||||
|
||||
beforeEach(() => {
|
||||
tree = createTreeWithEmptyWorkspace();
|
||||
});
|
||||
|
||||
it('should keep existing generatePackageJson option if it exists', async () => {
|
||||
addProjectConfiguration(tree, 'myapp', {
|
||||
root: 'myapp',
|
||||
targets: {
|
||||
build: {
|
||||
executor: '@nrwl/esbuild:esbuild',
|
||||
options: {
|
||||
generatePackageJson: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await update(tree);
|
||||
|
||||
const config = readProjectConfiguration(tree, 'myapp');
|
||||
|
||||
expect(config.targets.build.options).toEqual({
|
||||
generatePackageJson: false,
|
||||
});
|
||||
});
|
||||
|
||||
it('should set generatePackageJson to true for esbuild targets', async () => {
|
||||
addProjectConfiguration(tree, 'myapp', {
|
||||
root: 'myapp',
|
||||
targets: {
|
||||
build: {
|
||||
executor: '@nrwl/esbuild:esbuild',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await update(tree);
|
||||
|
||||
const config = readProjectConfiguration(tree, 'myapp');
|
||||
|
||||
expect(config.targets.build.options).toEqual({
|
||||
generatePackageJson: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('should ignore targets not using esbuild', async () => {
|
||||
addProjectConfiguration(tree, 'myapp', {
|
||||
root: 'myapp',
|
||||
targets: {
|
||||
build: {
|
||||
executor: '@nrwl/webpack:webpack',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await update(tree);
|
||||
|
||||
const config = readProjectConfiguration(tree, 'myapp');
|
||||
|
||||
expect(config.targets.build.options).toBeUndefined();
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,32 @@
|
||||
import type { Tree } from '@nrwl/devkit';
|
||||
import {
|
||||
formatFiles,
|
||||
getProjects,
|
||||
updateProjectConfiguration,
|
||||
} from '@nrwl/devkit';
|
||||
|
||||
export default async function update(tree: Tree): Promise<void> {
|
||||
const projects = getProjects(tree);
|
||||
|
||||
projects.forEach((projectConfig, projectName) => {
|
||||
let shouldUpdate = false;
|
||||
|
||||
Object.entries(projectConfig.targets).forEach(
|
||||
([targetName, targetConfig]) => {
|
||||
if (targetConfig.executor === '@nrwl/esbuild:esbuild') {
|
||||
shouldUpdate = true;
|
||||
|
||||
projectConfig.targets[targetName].options ??= {};
|
||||
projectConfig.targets[targetName].options.generatePackageJson ??=
|
||||
true;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
if (shouldUpdate) {
|
||||
updateProjectConfiguration(tree, projectName, projectConfig);
|
||||
}
|
||||
});
|
||||
|
||||
await formatFiles(tree);
|
||||
}
|
||||
@ -163,6 +163,10 @@ function addProject(
|
||||
},
|
||||
};
|
||||
|
||||
if (options.bundler === 'esbuild') {
|
||||
projectConfiguration.targets.build.options.generatePackageJson = true;
|
||||
}
|
||||
|
||||
if (options.bundler === 'rollup') {
|
||||
projectConfiguration.targets.build.options.project = `${options.projectRoot}/package.json`;
|
||||
projectConfiguration.targets.build.options.compiler = 'swc';
|
||||
|
||||
@ -110,6 +110,7 @@ function getEsBuildConfig(
|
||||
),
|
||||
tsConfig: joinPathFragments(options.appProjectRoot, 'tsconfig.app.json'),
|
||||
assets: [joinPathFragments(project.sourceRoot, 'assets')],
|
||||
generatePackageJson: true,
|
||||
esbuildOptions: {
|
||||
sourcemap: true,
|
||||
// Generate CJS files as .js so imports can be './foo' rather than './foo.cjs'.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user