Current Behavior
When running an @nx/esbuild:esbuild build for a "singleEntry + bundle" Node app and overriding the output directory via the --output-path CLI flag (or a target configuration that isn't the one baked into project.json), the bundled entry file (e.g. main.js) is written to the static outputPath configured in the project's project.json target — NOT to the overridden path.
Everything else in the same build (copied assets, generated package.json/lockfile via generatePackageJson) correctly respects the overridden --output-path. Only the actual JS bundle file ends up in the wrong directory.
This is a silent failure: the build exits 0, "Successfully ran target", and the override directory looks fully populated (assets, fixtures, package.json) — just missing the one file (main.js) that makes the output runnable. We only noticed because a Docker image built from the "overridden" output directory failed at runtime with Cannot find module './main.js'.
Root cause (traced in v23.2.1):
node_modules/@nx/esbuild/dist/src/executors/esbuild/lib/build-esbuild-options.js, function getOutfile:
function getOutfile(format, options, context) {
const ext = getOutExtension(format, options, context);
const candidate = joinPathFragments(context.target.options.outputPath, options.outputFileName);
...
}
context.target.options.outputPath is the unmerged, static value from project.json for that target. It does not reflect a CLI --output-path override (or any override that isn't statically present in the target/configuration definition read from the project graph). Meanwhile copyAssets and copyPackageJson (called earlier in esbuild.impl.js) are passed the normalized options object, which does correctly include the override — hence the asset/package.json vs. bundle-file inconsistency.
Expected Behavior
When --output-path=<dir> (or any equivalent override) is passed on the CLI, ALL build outputs — including the bundled entry file computed via getOutfile — should be written to the overridden directory, consistent with where assets and the generated package.json are written.
At minimum, getOutfile should use the normalized options.outputPath (which reflects CLI/configuration overrides) instead of context.target.options.outputPath (the static project.json value).
GitHub Repo
No response
Steps to Reproduce
-
In an Nx workspace, create/use a Node app project with an @nx/esbuild:esbuild build target configured roughly like:
{
"targets": {
"build-container": {
"executor": "@nx/esbuild:esbuild",
"options": {
"platform": "node",
"outputPath": "dist-a",
"format": ["cjs"],
"main": "apps/my-app/src/main.ts",
"tsConfig": "apps/my-app/tsconfig.app.json",
"generatePackageJson": true
}
}
}
}
-
Run the target with the default (static) outputPath and confirm dist-a/main.js is produced:
npx nx build-container my-app --skip-nx-cache
-
Clean both directories, then run the SAME target but override the output path via CLI:
rm -rf dist-a dist-b
npx nx build-container my-app --skip-nx-cache --output-path=dist-b
-
Inspect the results:
dist-b/ contains assets and package.json (respects the override) but NOT main.js.
dist-a/main.js exists instead — the bundle silently landed in the static project.json path, not the overridden one.
Nx Report
Node : 24.20.0
OS : win32-x64
Native Target : x86_64-windows
npm : 11.19.0
daemon : Available
nx : 23.2.1
@nx/js : 23.2.1
@nx/eslint : 23.2.1
@nx/workspace : 23.2.1
@nx/angular : 23.2.1
@nx/jest : 23.2.1
@nx/devkit : 23.2.1
@nx/esbuild : 23.2.1
@nx/eslint-plugin : 23.2.1
@nx/node : 23.2.1
@nx/vite : 23.2.1
@nx/vitest : 23.2.1
@nx/web : 23.2.1
@nx/webpack : 23.2.1
@nx/docker : 23.2.1
typescript : 6.0.3
---------------------------------------
Registered Plugins:
@nx/webpack/plugin
@nx/eslint/plugin
@nx/vite/plugin
@nx/vitest
@nx/jest/plugin
Failure Logs
Package Manager Version
No response
Operating System
Additional Information
No response
Current Behavior
When running an
@nx/esbuild:esbuildbuild for a "singleEntry + bundle" Node app and overriding the output directory via the--output-pathCLI flag (or a target configuration that isn't the one baked intoproject.json), the bundled entry file (e.g.main.js) is written to the staticoutputPathconfigured in the project'sproject.jsontarget — NOT to the overridden path.Everything else in the same build (copied assets, generated
package.json/lockfile viageneratePackageJson) correctly respects the overridden--output-path. Only the actual JS bundle file ends up in the wrong directory.This is a silent failure: the build exits 0, "Successfully ran target", and the override directory looks fully populated (assets, fixtures, package.json) — just missing the one file (
main.js) that makes the output runnable. We only noticed because a Docker image built from the "overridden" output directory failed at runtime withCannot find module './main.js'.Root cause (traced in v23.2.1):
node_modules/@nx/esbuild/dist/src/executors/esbuild/lib/build-esbuild-options.js, functiongetOutfile:context.target.options.outputPathis the unmerged, static value fromproject.jsonfor that target. It does not reflect a CLI--output-pathoverride (or any override that isn't statically present in the target/configuration definition read from the project graph). MeanwhilecopyAssetsandcopyPackageJson(called earlier inesbuild.impl.js) are passed the normalizedoptionsobject, which does correctly include the override — hence the asset/package.json vs. bundle-file inconsistency.Expected Behavior
When
--output-path=<dir>(or any equivalent override) is passed on the CLI, ALL build outputs — including the bundled entry file computed viagetOutfile— should be written to the overridden directory, consistent with where assets and the generatedpackage.jsonare written.At minimum,
getOutfileshould use the normalizedoptions.outputPath(which reflects CLI/configuration overrides) instead ofcontext.target.options.outputPath(the static project.json value).GitHub Repo
No response
Steps to Reproduce
In an Nx workspace, create/use a Node app project with an
@nx/esbuild:esbuildbuild target configured roughly like:{
"targets": {
"build-container": {
"executor": "@nx/esbuild:esbuild",
"options": {
"platform": "node",
"outputPath": "dist-a",
"format": ["cjs"],
"main": "apps/my-app/src/main.ts",
"tsConfig": "apps/my-app/tsconfig.app.json",
"generatePackageJson": true
}
}
}
}
Run the target with the default (static) outputPath and confirm
dist-a/main.jsis produced:npx nx build-container my-app --skip-nx-cache
Clean both directories, then run the SAME target but override the output path via CLI:
rm -rf dist-a dist-b
npx nx build-container my-app --skip-nx-cache --output-path=dist-b
Inspect the results:
dist-b/contains assets andpackage.json(respects the override) but NOTmain.js.dist-a/main.jsexists instead — the bundle silently landed in the static project.json path, not the overridden one.Nx Report
Failure Logs
Package Manager Version
No response
Operating System
Additional Information
No response