Skip to content

Commit 4229697

Browse files
fix(js): avoid double-prefixing node executor output paths (#35050)
## Summary - avoid prepending `outputPath` twice when `@nx/js:node` derives the runnable file for `@nx/js:tsc` and `@nx/js:swc` - keep the existing source-relative behavior for normal `src/...` entries while preserving nested paths already inside the output directory - respect a configured `rootDir` on the build target so the node executor does not add an extra path segment that tsc/swc stripped from the output - reuse the canonical `normalizePath` and `getRelativeDirectoryToProjectRoot` utilities instead of re-declaring them - add focused coverage for the `dist/main.js`, source-entry, nested-output, and `rootDir` cases Fixes #35044 Fixes #33577 ## Validation - `corepack pnpm exec prettier --check packages/js/src/executors/node/node.impl.ts packages/js/src/executors/node/lib/output-file.ts packages/js/src/executors/node/lib/output-file.spec.ts` - `corepack pnpm exec eslint packages/js/src/executors/node/node.impl.ts packages/js/src/executors/node/lib/output-file.ts packages/js/src/executors/node/lib/output-file.spec.ts` - `pnpm nx test js --testPathPatterns=output-file` — all 4 cases pass --------- Co-authored-by: FrozenPandaz <[email protected]>
1 parent 331ebce commit 4229697

3 files changed

Lines changed: 92 additions & 13 deletions

File tree

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { getOutputFileName } from './output-file';
2+
3+
describe('getOutputFileName', () => {
4+
it('does not double-prefix outputPath when main is already inside the build output', () => {
5+
expect(
6+
getOutputFileName({
7+
buildTargetExecutor: '@nx/js:tsc',
8+
main: 'packages/failing-project/dist/main.js',
9+
outputPath: 'packages/failing-project/dist',
10+
rootDir: 'packages/failing-project',
11+
})
12+
).toBe('main.js');
13+
});
14+
15+
it('keeps project-root-relative subdirectories for source entries', () => {
16+
expect(
17+
getOutputFileName({
18+
buildTargetExecutor: '@nx/js:tsc',
19+
main: 'packages/failing-project/src/main.ts',
20+
outputPath: 'dist/packages/failing-project',
21+
rootDir: 'packages/failing-project',
22+
})
23+
).toBe('src/main.js');
24+
});
25+
26+
it('preserves nested subdirectories that already live under outputPath', () => {
27+
expect(
28+
getOutputFileName({
29+
buildTargetExecutor: '@nx/js:swc',
30+
main: 'packages/failing-project/dist/server/main.js',
31+
outputPath: 'packages/failing-project/dist',
32+
rootDir: 'packages/failing-project',
33+
})
34+
).toBe('server/main.js');
35+
});
36+
37+
it('resolves main relative to rootDir when tsc strips the rootDir prefix from the output', () => {
38+
expect(
39+
getOutputFileName({
40+
buildTargetExecutor: '@nx/js:tsc',
41+
main: 'apps/project_a/src/main.ts',
42+
outputPath: 'dist/apps/project_a',
43+
rootDir: 'apps/project_a/src',
44+
})
45+
).toBe('main.js');
46+
});
47+
});
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { dirname, parse } from 'path';
2+
import { joinPathFragments, normalizePath } from 'nx/src/utils/path';
3+
import { getRelativeDirectoryToProjectRoot } from '../../../utils/get-main-file-dir';
4+
5+
interface OutputFileNameOptions {
6+
buildTargetExecutor: string;
7+
main: string;
8+
outputPath: string;
9+
rootDir: string;
10+
}
11+
12+
export function getOutputFileName({
13+
buildTargetExecutor,
14+
main,
15+
outputPath,
16+
rootDir,
17+
}: OutputFileNameOptions): string {
18+
const fileName = `${parse(main).name}.js`;
19+
20+
if (
21+
buildTargetExecutor !== '@nx/js:tsc' &&
22+
buildTargetExecutor !== '@nx/js:swc'
23+
) {
24+
return fileName;
25+
}
26+
27+
const mainDirectory = normalizePath(dirname(main));
28+
const normalizedOutputPath = normalizePath(outputPath);
29+
const isMainInsideOutputPath =
30+
mainDirectory === normalizedOutputPath ||
31+
mainDirectory.startsWith(`${normalizedOutputPath}/`);
32+
const base = isMainInsideOutputPath ? normalizedOutputPath : rootDir;
33+
34+
return joinPathFragments(
35+
getRelativeDirectoryToProjectRoot(main, base),
36+
fileName
37+
);
38+
}

packages/js/src/executors/node/node.impl.ts

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ import { killTree } from './lib/kill-tree';
2222
import { LineAwareWriter } from './lib/line-aware-writer';
2323
import { createCoalescingDebounce } from './lib/coalescing-debounce';
2424
import { fileExists } from 'nx/src/utils/fileutils';
25-
import { getRelativeDirectoryToProjectRoot } from '../../utils/get-main-file-dir';
2625
import { interpolate } from 'nx/src/tasks-runner/utils';
2726
import { detectModuleFormat } from './lib/detect-module-format';
27+
import { getOutputFileName } from './lib/output-file';
2828

2929
interface ActiveTask {
3030
id: string;
@@ -478,18 +478,12 @@ function getFileToRun(
478478
let outputFileName = buildOptions.outputFileName;
479479

480480
if (!outputFileName) {
481-
const fileName = `${path.parse(buildOptions.main).name}.js`;
482-
if (
483-
buildTargetExecutor === '@nx/js:tsc' ||
484-
buildTargetExecutor === '@nx/js:swc'
485-
) {
486-
outputFileName = path.join(
487-
getRelativeDirectoryToProjectRoot(buildOptions.main, project.data.root),
488-
fileName
489-
);
490-
} else {
491-
outputFileName = fileName;
492-
}
481+
outputFileName = getOutputFileName({
482+
buildTargetExecutor,
483+
main: buildOptions.main,
484+
outputPath: buildOptions.outputPath,
485+
rootDir: buildOptions.rootDir ?? project.data.root,
486+
});
493487
}
494488

495489
return join(context.root, buildOptions.outputPath, outputFileName);

0 commit comments

Comments
 (0)