UNPKG

@nx/esbuild

Version:

The Nx Plugin for esbuild contains executors and generators that support building applications using esbuild

199 lines (198 loc) • 9.73 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.esbuildExecutor = esbuildExecutor; const pc = require("picocolors"); const devkit_1 = require("@nx/devkit"); const js_1 = require("@nx/js"); const esbuild = require("esbuild"); const normalize_1 = require("./lib/normalize"); const async_iterable_1 = require("@nx/devkit/src/utils/async-iterable"); const build_esbuild_options_1 = require("./lib/build-esbuild-options"); const get_extra_dependencies_1 = require("./lib/get-extra-dependencies"); const node_fs_1 = require("node:fs"); const path_1 = require("path"); const BUILD_WATCH_FAILED = `[ ${pc.red('watch')} ] build finished with errors (see above), watching for changes...`; const BUILD_WATCH_SUCCEEDED = `[ ${pc.green('watch')} ] build succeeded, watching for changes...`; async function* esbuildExecutor(_options, context) { process.env.NODE_ENV ??= context.configurationName ?? 'production'; const options = (0, normalize_1.normalizeOptions)(_options, context); if (options.deleteOutputPath) (0, node_fs_1.rmSync)(options.outputPath, { recursive: true, force: true }); const assetsResult = await (0, js_1.copyAssets)(options, context); const externalDependencies = options.external.reduce((acc, name) => { const externalNode = context.projectGraph.externalNodes[`npm:${name}`]; if (externalNode) { acc.push({ name, outputs: [], node: externalNode, }); } return acc; }, []); if (!options.thirdParty) { const thirdPartyDependencies = (0, get_extra_dependencies_1.getExtraDependencies)(context.projectName, context.projectGraph); for (const tpd of thirdPartyDependencies) { options.external.push(tpd.node.data.packageName); externalDependencies.push(tpd); } } let packageJsonResult; if (options.generatePackageJson) { if (context.projectGraph.nodes[context.projectName].type !== 'app') { devkit_1.logger.warn((0, devkit_1.stripIndents) `The project ${context.projectName} is using the 'generatePackageJson' option which is deprecated for library projects. It should only be used for applications. For libraries, configure the project to use the '@nx/dependency-checks' ESLint rule instead (https://nx.dev/nx-api/eslint-plugin/documents/dependency-checks).`); } const cpjOptions = { ...options, format: options.format, // TODO(jack): make types generate with esbuild skipTypings: true, generateLockfile: true, outputFileExtensionForCjs: (0, build_esbuild_options_1.getOutExtension)('cjs', options, context), outputFileExtensionForEsm: (0, build_esbuild_options_1.getOutExtension)('esm', options, context), excludeLibsInPackageJson: !options.thirdParty, // TODO(jack): Remove the need to pass updateBuildableProjectDepsInPackageJson option when overrideDependencies or extraDependencies are passed. // Add this back to fix a regression. // See: https://github.com/nrwl/nx/issues/19773 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; } packageJsonResult = await (0, js_1.copyPackageJson)(cpjOptions, context); } if (options.watch) { return yield* (0, async_iterable_1.createAsyncIterable)(async ({ next, done }) => { let hasTypeErrors = false; const disposeFns = await Promise.all(options.format.map(async (format, idx) => { const esbuildOptions = (0, build_esbuild_options_1.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) { build.onEnd(async (result) => { if (!options.skipTypeCheck || options.isTsSolutionSetup) { const { errors } = await runTypeCheck(options, context); hasTypeErrors = errors.length > 0; } const success = result.errors.length === 0 && !hasTypeErrors; if (!success) { devkit_1.logger.info(BUILD_WATCH_FAILED); } else { devkit_1.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: (0, path_1.join)(context.root, (0, build_esbuild_options_1.getOutfile)(format, options, context)), }); }); }, } : null, ...(esbuildOptions?.plugins || []), ].filter(Boolean), }); await ctx.watch(); return async () => ctx.dispose(); })); registerCleanupCallback(() => { if (typeof assetsResult?.stop === 'function') assetsResult.stop(); if (typeof packageJsonResult?.stop === 'function') { packageJsonResult.stop(); } disposeFns.forEach(async (fn) => { await fn(); }); done(); // return from async iterable }); }); } else { // Run type-checks first and bail if they don't pass. if (!options.skipTypeCheck || options.isTsSolutionSetup) { 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 = (0, build_esbuild_options_1.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`; (0, devkit_1.writeJsonFile)((0, devkit_1.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: (0, path_1.join)(context.root, (0, build_esbuild_options_1.getOutfile)(format, options, context)), }; } } } function getTypeCheckOptions(options, context) { const { watch, tsConfig, outputPath } = options; const projectRoot = context.projectGraph.nodes[context.projectName].data.root; const typeCheckOptions = { ...(options.declaration ? { mode: 'emitDeclarationOnly', outDir: outputPath, } : { mode: 'noEmit', }), tsConfigPath: (0, path_1.relative)(process.cwd(), (0, path_1.join)(context.root, tsConfig)), workspaceRoot: context.root, rootDir: options.declarationRootDir ?? context.root, projectRoot, }; if (watch) { typeCheckOptions.incremental = true; typeCheckOptions.cacheDir = devkit_1.cacheDir; } if (options.isTsSolutionSetup && options.skipTypeCheck) { typeCheckOptions.ignoreDiagnostics = true; } return typeCheckOptions; } async function runTypeCheck(options, context) { const { errors, warnings } = await (0, js_1.runTypeCheck)(getTypeCheckOptions(options, context)); const hasErrors = errors.length > 0; const hasWarnings = warnings.length > 0; if (hasErrors || hasWarnings) { await (0, js_1.printDiagnostics)(errors, warnings); } return { errors, warnings }; } function registerCleanupCallback(callback) { const wrapped = () => { callback(); process.off('SIGINT', wrapped); process.off('SIGTERM', wrapped); process.off('exit', wrapped); }; process.on('SIGINT', wrapped); process.on('SIGTERM', wrapped); process.on('exit', wrapped); } exports.default = esbuildExecutor;