Skip to content

Commit 13d69ef

Browse files
AgentEndernx-cloud[bot]
authored andcommitted
fix(core): overwrite inferred script target when nx prop defines executor or command (#35227)
## Current Behavior When a `package.json` has both a script entry and an `nx.targets` entry for the same target name, and the `nx.targets` entry uses command shorthand (`command: "tsc"`) or an explicit executor, the two targets are merged together. This produces an invalid hybrid target that has both `executor: "nx:run-script"` (from the inferred script target) and the `command` property (from the nx prop), causing the node to fail to merge into the project graph. ## Expected Behavior When the `nx.targets` entry specifies how to run (via `executor` or `command`), it should completely overwrite the inferred script target instead of merging with it. Targets without `executor` or `command` (e.g., just adding `outputs` or `dependsOn`) should continue to merge as before. ## Related Issue(s) Fixes NXC-3923 --------- Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com> (cherry picked from commit 7edc7b1)
1 parent 2b3a819 commit 13d69ef

2 files changed

Lines changed: 46 additions & 4 deletions

File tree

packages/nx/src/utils/package-json.spec.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,16 @@ describe('installPackageToTmp', () => {
7676
});
7777

7878
describe('readTargetsFromPackageJson', () => {
79+
beforeEach(() => {
80+
jest
81+
.spyOn(pacakgeManager, 'getPackageManagerCommand')
82+
.mockReturnValue({ run: (script) => `npm run ${script}` } as any);
83+
});
84+
85+
afterEach(() => {
86+
jest.restoreAllMocks();
87+
});
88+
7989
const packageJson: PackageJson = {
8090
name: 'my-app',
8191
version: '0.0.0',
@@ -319,6 +329,33 @@ describe('readTargetsFromPackageJson', () => {
319329
`);
320330
});
321331

332+
it('should override script target when nx target uses command shorthand', () => {
333+
const result = readTargetsFromPackageJson(
334+
{
335+
name: 'my-other-app',
336+
version: '',
337+
scripts: {
338+
build: 'echo 1',
339+
},
340+
nx: {
341+
targets: {
342+
build: {
343+
command: 'echo 2',
344+
},
345+
},
346+
},
347+
},
348+
{},
349+
workspaceRoot,
350+
'/root'
351+
);
352+
expect(result.build).toMatchInlineSnapshot(`
353+
{
354+
"command": "echo 2",
355+
}
356+
`);
357+
});
358+
322359
it('should override script if provided in options', () => {
323360
const result = readTargetsFromPackageJson(
324361
{

packages/nx/src/utils/package-json.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -228,10 +228,15 @@ export function readTargetsFromPackageJson(
228228
res[script] = buildTargetFromScript(script, scripts, packageManagerCommand);
229229
}
230230
for (const targetName in nx?.targets) {
231-
res[targetName] = mergeTargetConfigurations(
232-
nx?.targets[targetName],
233-
res[targetName]
234-
);
231+
const nxTarget = nx.targets[targetName];
232+
// If the nx target specifies how to run (via executor or command shorthand),
233+
// it's incompatible with the inferred nx:run-script target from scripts,
234+
// so overwrite instead of merge.
235+
if (res[targetName] && (nxTarget.executor || nxTarget.command)) {
236+
res[targetName] = nxTarget;
237+
} else {
238+
res[targetName] = mergeTargetConfigurations(nxTarget, res[targetName]);
239+
}
235240
}
236241

237242
/**

0 commit comments

Comments
 (0)