Skip to content

Commit 9849ced

Browse files
committed
fix atomized detection to use nonAtomizedTarget metadata
1 parent f859ffb commit 9849ced

File tree

3 files changed

+88
-64
lines changed

3 files changed

+88
-64
lines changed

libs/nx-mcp/nx-mcp-server/src/lib/tools/nx-workspace.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -472,9 +472,8 @@ export function registerNxWorkspaceTools(
472472

473473
if (targets && typeof targets === 'object') {
474474
// Detect atomized targets
475-
const targetGroups = project.data.metadata?.targetGroups ?? {};
476475
const { atomizedTargetsMap, targetsToExclude } =
477-
detectAtomizedTargets(targetGroups);
476+
detectAtomizedTargets(targets);
478477

479478
// Create compressed descriptions for visible targets only
480479
const targetDescriptions = Object.entries(targets)

libs/shared/llm-context/src/lib/project-graph.spec.ts

Lines changed: 68 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -94,18 +94,13 @@ describe('project-graph', () => {
9494
root: 'libs/test-project',
9595
targets: {
9696
'e2e-ci': {},
97-
'e2e-ci--src/test1.cy.ts': {},
98-
'e2e-ci--src/test2.cy.ts': {},
99-
build: {},
100-
},
101-
metadata: {
102-
targetGroups: {
103-
'e2e-ci': [
104-
'e2e-ci',
105-
'e2e-ci--src/test1.cy.ts',
106-
'e2e-ci--src/test2.cy.ts',
107-
],
97+
'e2e-ci--src/test1.cy.ts': {
98+
metadata: { nonAtomizedTarget: 'e2e-ci' },
10899
},
100+
'e2e-ci--src/test2.cy.ts': {
101+
metadata: { nonAtomizedTarget: 'e2e-ci' },
102+
},
103+
build: {},
109104
},
110105
},
111106
},
@@ -134,26 +129,20 @@ describe('project-graph', () => {
134129
root: 'libs/test-project',
135130
targets: {
136131
'e2e-ci': {},
137-
'e2e-ci--src/test1.cy.ts': {},
138-
'e2e-ci--src/test2.cy.ts': {},
132+
'e2e-ci--src/test1.cy.ts': {
133+
metadata: { nonAtomizedTarget: 'e2e-ci' },
134+
},
135+
'e2e-ci--src/test2.cy.ts': {
136+
metadata: { nonAtomizedTarget: 'e2e-ci' },
137+
},
139138
'test-ci': {},
140-
'test-ci--src/test1.cy.ts': {},
141-
'test-ci--src/test2.cy.ts': {},
142-
build: {},
143-
},
144-
metadata: {
145-
targetGroups: {
146-
'e2e-ci': [
147-
'e2e-ci',
148-
'e2e-ci--src/test1.cy.ts',
149-
'e2e-ci--src/test2.cy.ts',
150-
],
151-
'test-ci': [
152-
'test-ci',
153-
'test-ci--src/test1.cy.ts',
154-
'test-ci--src/test2.cy.ts',
155-
],
139+
'test-ci--src/test1.cy.ts': {
140+
metadata: { nonAtomizedTarget: 'test-ci' },
141+
},
142+
'test-ci--src/test2.cy.ts': {
143+
metadata: { nonAtomizedTarget: 'test-ci' },
156144
},
145+
build: {},
157146
},
158147
},
159148
},
@@ -388,16 +377,14 @@ describe('project-graph', () => {
388377

389378
describe('detectAtomizedTargets', () => {
390379
it('should detect atomized targets correctly', () => {
391-
const targetGroups = {
392-
'test-ci': [
393-
'test-ci',
394-
'test-ci--Test1',
395-
'test-ci--Test2',
396-
'test-ci--Test3',
397-
],
380+
const targets = {
381+
'test-ci': {},
382+
'test-ci--Test1': { metadata: { nonAtomizedTarget: 'test-ci' } },
383+
'test-ci--Test2': { metadata: { nonAtomizedTarget: 'test-ci' } },
384+
'test-ci--Test3': { metadata: { nonAtomizedTarget: 'test-ci' } },
398385
};
399386

400-
const result = detectAtomizedTargets(targetGroups);
387+
const result = detectAtomizedTargets(targets);
401388

402389
expect(result.rootTargets).toEqual(new Set(['test-ci']));
403390
expect(result.atomizedTargetsMap.get('test-ci')).toEqual([
@@ -413,12 +400,16 @@ describe('project-graph', () => {
413400
});
414401

415402
it('should handle multiple root targets', () => {
416-
const targetGroups = {
417-
'test-ci': ['test-ci', 'test-ci--Test1', 'test-ci--Test2'],
418-
'e2e-ci': ['e2e-ci', 'e2e-ci--Test1', 'e2e-ci--Test2'],
403+
const targets = {
404+
'test-ci': {},
405+
'test-ci--Test1': { metadata: { nonAtomizedTarget: 'test-ci' } },
406+
'test-ci--Test2': { metadata: { nonAtomizedTarget: 'test-ci' } },
407+
'e2e-ci': {},
408+
'e2e-ci--Test1': { metadata: { nonAtomizedTarget: 'e2e-ci' } },
409+
'e2e-ci--Test2': { metadata: { nonAtomizedTarget: 'e2e-ci' } },
419410
};
420411

421-
const result = detectAtomizedTargets(targetGroups);
412+
const result = detectAtomizedTargets(targets);
422413

423414
expect(result.rootTargets).toEqual(new Set(['test-ci', 'e2e-ci']));
424415
expect(result.atomizedTargetsMap.get('test-ci')).toEqual([
@@ -437,25 +428,55 @@ describe('project-graph', () => {
437428
]);
438429
});
439430

440-
it('should handle empty targetGroups', () => {
431+
it('should handle empty targets', () => {
441432
const result = detectAtomizedTargets({});
442433

443434
expect(result.rootTargets).toEqual(new Set());
444435
expect(result.atomizedTargetsMap.size).toBe(0);
445436
expect(result.targetsToExclude).toEqual([]);
446437
});
447438

448-
it('should handle groups without atomized targets', () => {
449-
const targetGroups = {
450-
build: ['build'],
451-
test: ['test'],
439+
it('should handle targets without atomized metadata', () => {
440+
const targets = {
441+
build: {},
442+
test: {},
443+
deploy: { metadata: { description: 'Deploy the app' } },
452444
};
453445

454-
const result = detectAtomizedTargets(targetGroups);
446+
const result = detectAtomizedTargets(targets);
455447

456448
expect(result.rootTargets).toEqual(new Set());
457449
expect(result.atomizedTargetsMap.size).toBe(0);
458450
expect(result.targetsToExclude).toEqual([]);
459451
});
452+
453+
it('should not exclude nx:run-script targets in target groups', () => {
454+
// This test verifies the fix for the issue where nx:run-script targets
455+
// like 'deploy' and 'custom-hello' were incorrectly filtered out
456+
const targets = {
457+
'e2e-ci': {},
458+
'e2e-ci--src/test1.cy.ts': {
459+
metadata: { nonAtomizedTarget: 'e2e-ci' },
460+
},
461+
'e2e-ci--src/test2.cy.ts': {
462+
metadata: { nonAtomizedTarget: 'e2e-ci' },
463+
},
464+
deploy: {
465+
metadata: { description: 'Deploy script from package.json' },
466+
},
467+
'custom-hello': {},
468+
};
469+
470+
const result = detectAtomizedTargets(targets);
471+
472+
expect(result.rootTargets).toEqual(new Set(['e2e-ci']));
473+
expect(result.targetsToExclude).toEqual([
474+
'e2e-ci--src/test1.cy.ts',
475+
'e2e-ci--src/test2.cy.ts',
476+
]);
477+
// deploy and custom-hello should NOT be excluded
478+
expect(result.targetsToExclude).not.toContain('deploy');
479+
expect(result.targetsToExclude).not.toContain('custom-hello');
480+
});
460481
});
461482
});

libs/shared/llm-context/src/lib/project-graph.ts

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,16 @@ export type ProjectGraphOptimizations = {
1111
};
1212

1313
/**
14-
* Detects atomized targets from targetGroups metadata.
15-
* Atomized targets follow the pattern: rootTarget--identifier
14+
* Detects atomized targets using target metadata.
15+
* A target is atomized if it has a `nonAtomizedTarget` property in its metadata,
16+
* which points to the root target name.
1617
*
17-
* @param targetGroups - The targetGroups metadata from project data
18+
* @param targets - The targets object from project data
1819
* @returns Object with rootTargets set, atomizedTargetsMap, and targetsToExclude array
1920
*/
20-
export function detectAtomizedTargets(targetGroups: Record<string, string[]>): {
21+
export function detectAtomizedTargets(
22+
targets: Record<string, { metadata?: { nonAtomizedTarget?: string } }>,
23+
): {
2124
rootTargets: Set<string>;
2225
atomizedTargetsMap: Map<string, string[]>;
2326
targetsToExclude: string[];
@@ -26,17 +29,19 @@ export function detectAtomizedTargets(targetGroups: Record<string, string[]>): {
2629
const atomizedTargetsMap = new Map<string, string[]>();
2730
const targetsToExclude: string[] = [];
2831

29-
for (const groupName in targetGroups) {
30-
const targets = targetGroups[groupName];
32+
for (const targetName in targets) {
33+
const target = targets[targetName];
34+
const nonAtomizedTarget = target.metadata?.nonAtomizedTarget;
3135

32-
// The group name is the root target name (from metadata)
33-
const rootTarget = groupName;
34-
const atomizedTargets = targets.filter((t) => t !== rootTarget);
36+
if (nonAtomizedTarget) {
37+
// This target is atomized - it has a nonAtomizedTarget pointing to its root
38+
rootTargets.add(nonAtomizedTarget);
39+
targetsToExclude.push(targetName);
3540

36-
if (atomizedTargets.length > 0) {
37-
rootTargets.add(rootTarget);
38-
atomizedTargetsMap.set(rootTarget, atomizedTargets);
39-
targetsToExclude.push(...atomizedTargets);
41+
// Build the map of root targets to their atomized targets
42+
const existing = atomizedTargetsMap.get(nonAtomizedTarget) ?? [];
43+
existing.push(targetName);
44+
atomizedTargetsMap.set(nonAtomizedTarget, existing);
4045
}
4146
}
4247

@@ -96,8 +101,7 @@ function getRobotReadableProjectGraph(
96101
}
97102

98103
// targets
99-
const targetGroups = node.data.metadata?.targetGroups ?? {};
100-
const { targetsToExclude } = detectAtomizedTargets(targetGroups);
104+
const { targetsToExclude } = detectAtomizedTargets(node.data.targets ?? {});
101105

102106
const targets = Object.keys(node.data.targets ?? {}).filter(
103107
(target) =>

0 commit comments

Comments
 (0)