feat(bundling): remove esbuild 0.16.0 support (#16435)

This commit is contained in:
Jack Hsu 2023-04-20 10:14:52 -04:00 committed by GitHub
parent b0d4cba79f
commit bef152d94e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 94 additions and 255 deletions

View File

@ -178,41 +178,6 @@ describe('EsBuild Plugin', () => {
expect(runCommand(`node dist/libs/${myPkg}`)).toMatch(/Hello/);
}, 300_000);
it('should support new watch API in >= 0.17.0 and old watch API in < 0.17.0', async () => {
const myPkg = uniq('my-pkg');
runCLI(`generate @nrwl/js:lib ${myPkg} --bundler=esbuild`);
updateFile(`libs/${myPkg}/src/index.ts`, `console.log('new API');\n`);
let watchProcess = await runCommandUntil(
`build ${myPkg} --bundle=false --watch`,
(output) => {
return output.includes('build succeeded');
}
);
watchProcess.kill();
// Check that the build is correct
expect(runCommand(`node dist/libs/${myPkg}`)).toMatch(/new API/);
// Now install legacy esbuild and do a build watch
packageInstall('esbuild', undefined, '0.16.17');
rmDist();
watchProcess = await runCommandUntil(
`build ${myPkg} --bundle=false --watch`,
(output) => {
return output.includes('build succeeded');
}
);
watchProcess.kill();
// Check that the build is correct
expect(runCommand(`node dist/libs/${myPkg}`)).toMatch(/new API/);
}, 120_000);
it('should support additional entry points', () => {
const myPkg = uniq('my-pkg');
runCLI(`generate @nrwl/js:lib ${myPkg} --bundler=esbuild`);

View File

@ -22,6 +22,15 @@
"alwaysAddToPackageJson": false
}
}
},
"16.0.0": {
"version": "16.0.0-beta.5",
"packages": {
"esbuild": {
"version": "0.17.17",
"alwaysAddToPackageJson": false
}
}
}
}
}

View File

@ -91,236 +91,103 @@ export async function* esbuildExecutor(
packageJsonResult = await copyPackageJson(cpjOptions, context);
}
if ('context' in esbuild) {
// 0.17.0+ adds esbuild.context and context.watch()
if (options.watch) {
return yield* createAsyncIterable<{ success: boolean; outfile?: string }>(
async ({ next, done }) => {
let hasTypeErrors = false;
const disposeFns = await Promise.all(
options.format.map(async (format, idx) => {
const esbuildOptions = buildEsbuildOptions(
format,
options,
context
);
const ctx = await esbuild.context({
...esbuildOptions,
plugins: [
// Only emit info on one of the watch processes.
idx === 0
? {
name: 'nx-watch-plugin',
setup(build: esbuild.PluginBuild) {
build.onEnd(async (result: esbuild.BuildResult) => {
if (!options.skipTypeCheck) {
const { errors } = await runTypeCheck(
options,
context
);
hasTypeErrors = errors.length > 0;
}
const success =
result.errors.length === 0 && !hasTypeErrors;
if (!success) {
logger.info(BUILD_WATCH_FAILED);
} else {
logger.info(BUILD_WATCH_SUCCEEDED);
}
next({
success,
// Need to call getOutfile directly in the case of bundle=false and outfile is not set for esbuild.
outfile: join(
context.root,
getOutfile(format, options, context)
),
});
});
},
}
: null,
].filter(Boolean),
});
await ctx.watch();
return () => ctx.dispose();
})
);
registerCleanupCallback(() => {
assetsResult?.stop();
packageJsonResult?.stop();
disposeFns.forEach((fn) => fn());
done(); // return from async iterable
});
}
);
} else {
// Run type-checks first and bail if they don't pass.
if (!options.skipTypeCheck) {
const { errors } = await runTypeCheck(options, context);
if (errors.length > 0) {
yield { success: false };
return;
}
}
// Emit a build event for each file format.
for (let i = 0; i < options.format.length; i++) {
const format = options.format[i];
const esbuildOptions = buildEsbuildOptions(format, options, context);
const buildResult = await esbuild.build(esbuildOptions);
if (options.metafile) {
const filename =
options.format.length === 1
? 'meta.json'
: `meta.${options.format[i]}.json`;
writeJsonSync(
joinPathFragments(options.outputPath, filename),
buildResult.metafile
);
}
yield {
success: buildResult.errors.length === 0,
// Need to call getOutfile directly in the case of bundle=false and outfile is not set for esbuild.
// This field is needed for `@nx/js:node` executor to work.
outfile: join(context.root, getOutfile(format, options, context)),
};
}
}
} else {
// TODO(jack): Remove in Nx 16
// < 0.17.0 takes watch as an argument to build()
if (options.watch) {
return yield* createAsyncIterable<{ success: boolean; outfile?: string }>(
async ({ next, done }) => {
let hasTypeErrors = false;
const results = await Promise.all(
options.format.map(async (format, idx) => {
const esbuildOptions = buildEsbuildOptions(
format,
options,
context
);
const watch =
if (options.watch) {
return yield* createAsyncIterable<{ success: boolean; outfile?: string }>(
async ({ next, done }) => {
let hasTypeErrors = false;
const disposeFns = await Promise.all(
options.format.map(async (format, idx) => {
const esbuildOptions = buildEsbuildOptions(
format,
options,
context
);
const ctx = await esbuild.context({
...esbuildOptions,
plugins: [
// Only emit info on one of the watch processes.
idx === 0
? {
onRebuild: async (
error: esbuild.BuildFailure,
result: esbuild.BuildResult
) => {
if (!options.skipTypeCheck) {
const { errors } = await runTypeCheck(
options,
context
);
hasTypeErrors = errors.length > 0;
}
const success = !error && !hasTypeErrors;
name: 'nx-watch-plugin',
setup(build: esbuild.PluginBuild) {
build.onEnd(async (result: esbuild.BuildResult) => {
if (!options.skipTypeCheck) {
const { errors } = await runTypeCheck(
options,
context
);
hasTypeErrors = errors.length > 0;
}
const success =
result.errors.length === 0 && !hasTypeErrors;
if (!success) {
logger.info(BUILD_WATCH_FAILED);
} else {
logger.info(BUILD_WATCH_SUCCEEDED);
}
if (!success) {
logger.info(BUILD_WATCH_FAILED);
} else {
logger.info(BUILD_WATCH_SUCCEEDED);
}
next({
success,
// Need to call getOutfile directly in the case of bundle=false and outfile is not set for esbuild.
// This field is needed for `@nx/js:node` executor to work.
outfile: join(
context.root,
getOutfile(format, options, context)
),
next({
success,
// Need to call getOutfile directly in the case of bundle=false and outfile is not set for esbuild.
outfile: join(
context.root,
getOutfile(format, options, context)
),
});
});
},
}
: true;
try {
const result = await esbuild.build({
...esbuildOptions,
watch,
});
: null,
].filter(Boolean),
});
next({
success: true,
// Need to call getOutfile directly in the case of bundle=false and outfile is not set for esbuild.
outfile: join(
context.root,
getOutfile(format, options, context)
),
});
await ctx.watch();
return () => ctx.dispose();
})
);
return result;
} catch {
next({ success: false });
}
})
);
registerCleanupCallback(() => {
assetsResult?.stop();
packageJsonResult?.stop();
disposeFns.forEach((fn) => fn());
done(); // return from async iterable
});
}
);
} else {
// Run type-checks first and bail if they don't pass.
if (!options.skipTypeCheck) {
const { errors } = await runTypeCheck(options, context);
if (errors.length > 0) {
yield { success: false };
return;
}
}
registerCleanupCallback(() => {
assetsResult?.stop();
packageJsonResult?.stop();
results.forEach((r) =>
// result.stop() is no in esbuild 0.17.0+ but it exists in earlier versions
r?.['stop']?.()
);
done();
});
// Emit a build event for each file format.
for (let i = 0; i < options.format.length; i++) {
const format = options.format[i];
const esbuildOptions = buildEsbuildOptions(format, options, context);
const buildResult = await esbuild.build(esbuildOptions);
if (!options.skipTypeCheck) {
const { errors } = await runTypeCheck(options, context);
hasTypeErrors = errors.length > 0;
}
const success =
results.every((r) => r.errors?.length === 0) && !hasTypeErrors;
if (!success) {
logger.info(BUILD_WATCH_FAILED);
} else {
logger.info(BUILD_WATCH_SUCCEEDED);
}
}
);
} else {
// Run type-checks first and bail if they don't pass.
if (!options.skipTypeCheck) {
const { errors } = await runTypeCheck(options, context);
if (errors.length > 0) {
yield { success: false };
return;
}
if (options.metafile) {
const filename =
options.format.length === 1
? 'meta.json'
: `meta.${options.format[i]}.json`;
writeJsonSync(
joinPathFragments(options.outputPath, filename),
buildResult.metafile
);
}
// Emit a build event for each file format.
for (let i = 0; i < options.format.length; i++) {
const format = options.format[i];
const esbuildOptions = buildEsbuildOptions(format, options, context);
const buildResult = await (esbuild as EsBuild).build(esbuildOptions);
if (options.metafile) {
const filename =
options.format.length === 1
? 'meta.json'
: `meta.${options.format[i]}.json`;
writeJsonSync(
joinPathFragments(options.outputPath, filename),
buildResult.metafile
);
}
yield {
success: buildResult.errors.length === 0,
// Need to call getOutfile directly in the case of bundle=false and outfile is not set for esbuild.
outfile: join(context.root, getOutfile(format, options, context)),
};
}
yield {
success: buildResult.errors.length === 0,
// Need to call getOutfile directly in the case of bundle=false and outfile is not set for esbuild.
// This field is needed for `@nx/js:node` executor to work.
outfile: join(context.root, getOutfile(format, options, context)),
};
}
}
}

View File

@ -1,6 +1,6 @@
export const nxVersion = require('../../package.json').version;
export const esbuildVersion = '^0.17.5';
export const esbuildVersion = '^0.17.17';
export const prettierVersion = '^2.6.2';
export const swcCliVersion = '~0.1.62';
export const swcCoreVersion = '~1.3.51';

View File

@ -30,7 +30,6 @@ import { join } from 'path';
import { initGenerator } from '../init/init';
import {
esbuildVersion,
expressTypingsVersion,
expressVersion,
fastifyAutoloadVersion,
@ -46,6 +45,7 @@ import { setupDockerGenerator } from '../setup-docker/setup-docker';
import { Schema } from './schema';
import { mapLintPattern } from '@nx/linter/src/generators/lint-project/lint-project';
import { esbuildVersion } from '@nx/js/src/utils/versions';
export interface NormalizedSchema extends Schema {
appProjectRoot: string;

View File

@ -4,8 +4,6 @@ export const tslibVersion = '^2.3.0';
export const typesNodeVersion = '~18.7.1';
export const esbuildVersion = '^0.17.5';
export const expressVersion = '~4.18.1';
export const expressTypingsVersion = '~4.17.13';