Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(bundling): add support for esbuild.config.js file #16092

Merged
merged 1 commit into from
Apr 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion docs/generated/packages/esbuild/executors/esbuild.json
Original file line number Diff line number Diff line change
Expand Up @@ -163,9 +163,14 @@
},
"esbuildOptions": {
"type": "object",
"description": "Additional options to pass to esbuild. See https://esbuild.github.io/api/.",
"description": "Additional options to pass to esbuild. See https://esbuild.github.io/api/. Cannot be used with 'esbuildConfig' option.",
"additionalProperties": true,
"x-priority": "important"
},
"esbuildConfig": {
"type": "string",
"description": "Path to a esbuild configuration file. See https://esbuild.github.io/api/. Cannot be used with 'esbuildOptions' option.",
"x-priority": "important"
}
},
"required": ["tsConfig", "main", "outputPath"],
Expand Down
25 changes: 21 additions & 4 deletions e2e/esbuild/src/esbuild.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@ import {
checkFilesExist,
cleanupProject,
newProject,
packageInstall,
readFile,
readJson,
rmDist,
runCLI,
runCommand,
runCommandUntil,
uniq,
updateFile,
updateProjectConfig,
packageInstall,
rmDist,
runCommandUntil,
waitUntil,
} from '@nrwl/e2e/utils';

Expand Down Expand Up @@ -229,5 +229,22 @@ describe('EsBuild Plugin', () => {

expect(runCommand(`node dist/libs/${myPkg}/main.js`)).toMatch(/main/);
expect(runCommand(`node dist/libs/${myPkg}/extra.js`)).toMatch(/extra/);
});
}, 120_000);

it('should support external esbuild.config.js file', async () => {
const myPkg = uniq('my-pkg');
runCLI(`generate @nrwl/js:lib ${myPkg} --bundler=esbuild`);
updateFile(
`libs/${myPkg}/esbuild.config.js`,
`console.log('custom config loaded');\nmodule.exports = {};\n`
);
updateProjectConfig(myPkg, (json) => {
delete json.targets.build.options.esbuildOptions;
json.targets.build.options.esbuildConfig = `libs/${myPkg}/esbuild.config.js`;
return json;
});

const output = runCLI(`build ${myPkg}`);
expect(output).toContain('custom config loaded');
}, 120_000);
});
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ describe('buildEsbuildOptions', () => {
outputFileName: 'index.js',
singleEntry: true,
external: [],
userDefinedBuildOptions: {},
},
context
)
Expand Down Expand Up @@ -89,6 +90,7 @@ describe('buildEsbuildOptions', () => {
outputFileName: 'index.js',
singleEntry: false,
external: [],
userDefinedBuildOptions: {},
},
context
)
Expand Down Expand Up @@ -128,6 +130,7 @@ describe('buildEsbuildOptions', () => {
outputFileName: 'index.js',
singleEntry: true,
external: [],
userDefinedBuildOptions: {},
},
context
)
Expand Down Expand Up @@ -167,6 +170,7 @@ describe('buildEsbuildOptions', () => {
outputFileName: 'index.js',
singleEntry: true,
external: [],
userDefinedBuildOptions: {},
},
context
)
Expand Down Expand Up @@ -203,7 +207,7 @@ describe('buildEsbuildOptions', () => {
assets: [],
singleEntry: true,
external: [],
esbuildOptions: {
userDefinedBuildOptions: {
outExtension: {
'.js': '.mjs',
},
Expand Down Expand Up @@ -242,7 +246,7 @@ describe('buildEsbuildOptions', () => {
assets: [],
singleEntry: true,
external: [],
esbuildOptions: {
userDefinedBuildOptions: {
outExtension: {
'.js': '.js',
},
Expand Down Expand Up @@ -282,7 +286,7 @@ describe('buildEsbuildOptions', () => {
assets: [],
singleEntry: true,
external: [],
esbuildOptions: {
userDefinedBuildOptions: {
outExtension: {
'.js': '.cjs',
},
Expand Down Expand Up @@ -323,7 +327,7 @@ describe('buildEsbuildOptions', () => {
singleEntry: true,
outputFileName: 'index.js',
external: ['foo'],
esbuildOptions: {
userDefinedBuildOptions: {
external: ['bar'],
},
},
Expand Down Expand Up @@ -361,6 +365,7 @@ describe('buildEsbuildOptions', () => {
assets: [],
singleEntry: true,
external: ['foo'],
userDefinedBuildOptions: {},
},
context
)
Expand Down Expand Up @@ -398,6 +403,7 @@ describe('buildEsbuildOptions', () => {
singleEntry: true,
sourcemap: true,
external: [],
userDefinedBuildOptions: {},
},
context
)
Expand Down Expand Up @@ -434,6 +440,7 @@ describe('buildEsbuildOptions', () => {
assets: [],
singleEntry: true,
external: [],
userDefinedBuildOptions: {},
},
context
)
Expand Down Expand Up @@ -469,7 +476,7 @@ describe('buildEsbuildOptions', () => {
outputFileName: 'index.js',
assets: [],
singleEntry: true,
esbuildOptions: {
userDefinedBuildOptions: {
sourcemap: true,
},
external: [],
Expand Down Expand Up @@ -508,7 +515,7 @@ describe('buildEsbuildOptions', () => {
outputFileName: 'index.js',
assets: [],
singleEntry: true,
esbuildOptions: {
userDefinedBuildOptions: {
sourcemap: false,
},
sourcemap: true,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,15 @@
import * as esbuild from 'esbuild';
import * as path from 'path';
import { join, parse } from 'path';
import { existsSync, mkdirSync, writeFileSync } from 'fs';
import {
normalizePath,
ExecutorContext,
joinPathFragments,
normalizePath,
ProjectGraphProjectNode,
} from '@nrwl/devkit';
import { existsSync, mkdirSync, writeFileSync } from 'fs';

import { getClientEnvironment } from '../../../utils/environment-variables';
import {
EsBuildExecutorOptions,
NormalizedEsBuildExecutorOptions,
} from '../schema';
import { NormalizedEsBuildExecutorOptions } from '../schema';
import { getEntryPoints } from '../../../utils/get-entry-points';

const ESM_FILE_EXTENSION = '.js';
Expand All @@ -25,22 +21,27 @@ export function buildEsbuildOptions(
context: ExecutorContext
): esbuild.BuildOptions {
const outExtension = getOutExtension(format, options);

const esbuildOptions: esbuild.BuildOptions = {
...options.esbuildOptions,
...options.userDefinedBuildOptions,
entryNames:
options.outputHashing === 'all' ? '[dir]/[name].[hash]' : '[dir]/[name]',
bundle: options.bundle,
// Cannot use external with bundle option
external: options.bundle
? [...(options.esbuildOptions?.external ?? []), ...options.external]
? [
...(options.userDefinedBuildOptions?.external ?? []),
...options.external,
]
: undefined,
minify: options.minify,
platform: options.platform,
target: options.target,
metafile: options.metafile,
tsconfig: options.tsConfig,
sourcemap:
(options.sourcemap ?? options.esbuildOptions?.sourcemap) || false,
(options.sourcemap ?? options.userDefinedBuildOptions?.sourcemap) ||
false,
format,
outExtension: {
'.js': outExtension,
Expand Down Expand Up @@ -119,9 +120,9 @@ export function buildEsbuildOptions(

export function getOutExtension(
format: 'cjs' | 'esm',
options: EsBuildExecutorOptions
options: NormalizedEsBuildExecutorOptions
): '.cjs' | '.mjs' | '.js' {
const userDefinedExt = options.esbuildOptions?.outExtension?.['.js'];
const userDefinedExt = options.userDefinedBuildOptions?.outExtension?.['.js'];
// Allow users to change the output extensions from default CJS and ESM extensions.
// CJS -> .js
// ESM -> .mjs
Expand All @@ -136,15 +137,15 @@ export function getOutExtension(

export function getOutfile(
format: 'cjs' | 'esm',
options: EsBuildExecutorOptions,
options: NormalizedEsBuildExecutorOptions,
context: ExecutorContext
) {
const ext = getOutExtension(format, options);
const candidate = joinPathFragments(
context.target.options.outputPath,
options.outputFileName
);
const { dir, name } = parse(candidate);
const { dir, name } = path.parse(candidate);
return `${dir}/${name}${ext}`;
}

Expand Down Expand Up @@ -305,7 +306,7 @@ function getTsConfigCompilerPaths(context: ExecutorContext): {

function getRootTsConfigPath(context: ExecutorContext): string | null {
for (const tsConfigName of ['tsconfig.base.json', 'tsconfig.json']) {
const tsConfigPath = join(context.root, tsConfigName);
const tsConfigPath = path.join(context.root, tsConfigName);
if (existsSync(tsConfigPath)) {
return tsConfigPath;
}
Expand Down
28 changes: 25 additions & 3 deletions packages/esbuild/src/executors/esbuild/lib/normalize.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { parse } from 'path';
import * as fs from 'fs';
import * as path from 'path';
import {
EsBuildExecutorOptions,
NormalizedEsBuildExecutorOptions,
} from '../schema';
import { ExecutorContext, joinPathFragments, logger } from '@nrwl/devkit';
import chalk = require('chalk');
import * as esbuild from 'esbuild';

export function normalizeOptions(
options: EsBuildExecutorOptions,
Expand Down Expand Up @@ -36,6 +38,24 @@ export function normalizeOptions(
}

const thirdParty = !options.bundle ? false : options.thirdParty;

let userDefinedBuildOptions: esbuild.BuildOptions;
if (options.esbuildConfig) {
const userDefinedConfig = path.resolve(context.root, options.esbuildConfig);

if (options.esbuildOptions)
throw new Error(
`Cannot use both esbuildOptions and esbuildConfig options. Remove one of them and try again.`
);
if (!fs.existsSync(userDefinedConfig))
throw new Error(
`Path of esbuildConfig does not exist: ${userDefinedConfig}`
);
userDefinedBuildOptions = require(userDefinedConfig);
} else if (options.esbuildOptions) {
userDefinedBuildOptions = options.esbuildOptions;
}

if (options.additionalEntryPoints?.length > 0) {
const { outputFileName, ...rest } = options;
if (outputFileName) {
Expand All @@ -47,23 +67,25 @@ export function normalizeOptions(
...rest,
thirdParty,
assets,
userDefinedBuildOptions,
external: options.external ?? [],
singleEntry: false,
// Use the `main` file name as the output file name.
// This is needed for `@nrwl/js:node` to know the main file to execute.
// NOTE: The .js default extension may be replaced later in getOutfile() call.
outputFileName: `${parse(options.main).name}.js`,
outputFileName: `${path.parse(options.main).name}.js`,
};
} else {
return {
...options,
thirdParty,
assets,
userDefinedBuildOptions,
external: options.external ?? [],
singleEntry: true,
outputFileName:
// NOTE: The .js default extension may be replaced later in getOutfile() call.
options.outputFileName ?? `${parse(options.main).name}.js`,
options.outputFileName ?? `${path.parse(options.main).name}.js`,
};
}
}
5 changes: 4 additions & 1 deletion packages/esbuild/src/executors/esbuild/schema.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { AssetGlob } from '@nrwl/js/src/utils/assets/assets';
import * as esbuild from 'esbuild';

type Compiler = 'babel' | 'swc';

Expand All @@ -10,6 +11,7 @@ export interface EsBuildExecutorOptions {
deleteOutputPath?: boolean;
dependenciesFieldType?: boolean;
esbuildOptions?: Record<string, any>;
esbuildConfig?: string;
external?: string[];
format?: Array<'esm' | 'cjs'>;
generatePackageJson?: boolean;
Expand All @@ -29,7 +31,8 @@ export interface EsBuildExecutorOptions {
}

export interface NormalizedEsBuildExecutorOptions
extends EsBuildExecutorOptions {
extends Omit<EsBuildExecutorOptions, 'esbuildOptions' | 'esbuildConfig'> {
singleEntry: boolean;
external: string[];
userDefinedBuildOptions: esbuild.BuildOptions;
}
7 changes: 6 additions & 1 deletion packages/esbuild/src/executors/esbuild/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -138,9 +138,14 @@
},
"esbuildOptions": {
"type": "object",
"description": "Additional options to pass to esbuild. See https://esbuild.github.io/api/.",
"description": "Additional options to pass to esbuild. See https://esbuild.github.io/api/. Cannot be used with 'esbuildConfig' option.",
"additionalProperties": true,
"x-priority": "important"
},
"esbuildConfig": {
"type": "string",
"description": "Path to a esbuild configuration file. See https://esbuild.github.io/api/. Cannot be used with 'esbuildOptions' option.",
"x-priority": "important"
}
},
"required": ["tsConfig", "main", "outputPath"],
Expand Down