Skip to content

Commit

Permalink
fix(core): recursive resolve deps on create command graph (#22989)
Browse files Browse the repository at this point in the history
## introductory
<img width="620" alt="image"
src="https://github.com/nrwl/nx/assets/21343944/1ef5e2a2-0f65-4fde-aa7d-b52152af59a7">

## Current Behavior

```shell
nx exec --projects=my-node-app -- pwd
TypeError: graph.dependencies[id] is not iterable
    at _findCycle (/Users/anatolijfedorov/prjcts/tmp/nx-nest-workspace/node_modules/nx/src/tasks-runner/task-graph-utils.js:8:39)
    at _findCycle (/Users/anatolijfedorov/prjcts/tmp/nx-nest-workspace/node_modules/nx/src/tasks-runner/task-graph-utils.js:11:23)
    at findCycle (/Users/anatolijfedorov/prjcts/tmp/nx-nest-workspace/node_modules/nx/src/tasks-runner/task-graph-utils.js:23:23)
    at createCommandGraph (/Users/anatolijfedorov/prjcts/tmp/nx-nest-workspace/node_modules/nx/src/commands-runner/create-command-graph.js:25:52)
    at getCommandProjects (/Users/anatolijfedorov/prjcts/tmp/nx-nest-workspace/node_modules/nx/src/commands-runner/get-command-projects.js:7:72)
    at runScriptAsNxTarget (/Users/anatolijfedorov/prjcts/tmp/nx-nest-workspace/node_modules/nx/src/command-line/exec/exec.js:60:73)
    at Object.nxExecCommand (/Users/anatolijfedorov/prjcts/tmp/nx-nest-workspace/node_modules/nx/src/command-line/exec/exec.js:44:16)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async Object.handler (/Users/anatolijfedorov/prjcts/tmp/nx-nest-workspace/node_modules/nx/src/command-line/exec/command-object.js:11:13)

```

## Expected Behavior

```shell
nx exec --projects=my-node-app -- pwd
/tmp/nx-nest-workspace/my-node-lib-2
/tmp/nx-nest-workspace/my-node-lib
/tmp/nx-nest-workspace/apps/my-node-app
```

Fixes #22994
Added function that resolves dependencies recursively

Co-authored-by: afedorov <[email protected]>
(cherry picked from commit e15479b)
  • Loading branch information
Naymi authored and FrozenPandaz committed Jul 5, 2024
1 parent c8c4295 commit 49da9b1
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 15 deletions.
97 changes: 97 additions & 0 deletions packages/nx/src/commands-runner/create-command-graph.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { source } from '@angular-devkit/schematics';
import { createCommandGraph } from './create-command-graph';
import { CommandGraph } from './command-graph';

export { createCommandGraph } from './create-command-graph';

describe('createCommandGraph', () => {
it('should create command graph with empty dependencies', () => {
const projectGraph = {
dependencies: {},
nodes: {},
};
const projectNames = [];
const nxArgs = {};
const result = createCommandGraph(projectGraph, projectNames, nxArgs);
expect(result).toEqual({ dependencies: {}, roots: [] });
});

it('should create command graph with dependencies', () => {
const projectGraph = {
dependencies: {
dep1: [{ target: 'dep2', type: 'static', source: 'dep1' }],
dep2: [],
},
nodes: {
dep1: {
type: 'lib' as 'lib',
name: 'dep1',
data: {
files: [],
root: 'dep1',
},
},
dep2: {
type: 'lib' as 'lib',
name: 'dep2',
data: {
files: [],
root: 'dep2',
},
},
},
};
const projectNames = ['dep1'];
const nxArgs = {};
const result = createCommandGraph(projectGraph, projectNames, nxArgs);
expect(result).toEqual({
dependencies: { dep1: ['dep2'], dep2: [] },
roots: ['dep2'],
});
});

it('should create command graph with nested dependencies', () => {
const projectGraph = {
dependencies: {
dep1: [{ target: 'dep2', type: 'static', source: 'dep1' }],
dep2: [],
dep3: [{ target: 'dep1', type: 'static', source: 'dep3' }],
},
nodes: {
dep1: {
type: 'lib' as 'lib',
name: 'dep1',
data: {
files: [],
root: 'dep1',
},
},
dep2: {
type: 'lib' as 'lib',
name: 'dep2',
data: {
files: [],
root: 'dep2',
},
},
dep3: {
type: 'lib' as 'lib',
name: 'dep3',
data: {
files: [],
root: 'dep3',
},
},
},
};
const result = createCommandGraph(
projectGraph,
['dep1', 'dep2', 'dep3'],
{}
);
expect(result).toEqual({
dependencies: { dep1: ['dep2'], dep2: [], dep3: ['dep1'] },
roots: ['dep2'],
});
});
});
52 changes: 41 additions & 11 deletions packages/nx/src/commands-runner/create-command-graph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,54 @@ import { NxArgs } from '../utils/command-line-utils';
import { output } from '../utils/output';
import { CommandGraph } from './command-graph';

/**
* Make structure { lib: [dep], dep: [dep1], dep1: [] } from projectName lib and projectGraph
* @param projectGraph
* @param projectName
* @param resolved reference to an object that will contain resolved dependencies
* @returns
*/
const recursiveResolveDeps = (
projectGraph: ProjectGraph,
projectName: string,
resolved: Record<string, string[]>
) => {
if (projectGraph.dependencies[projectName].length === 0) {
// no deps - no resolve
resolved[projectName] = [];
return;
}
// if already resolved - just skip
if (resolved[projectName]) {
return resolved[projectName];
}

// deps string list
const projectDeps = [
...new Set(
projectGraph.dependencies[projectName]
.map((projectDep) => projectDep.target)
.filter((projectDep) => projectGraph.nodes[projectDep])
).values(),
];

// define
resolved[projectName] = projectDeps;
if (projectDeps.length > 0) {
for (const dep of projectDeps) {
recursiveResolveDeps(projectGraph, dep, resolved);
}
}
};

export function createCommandGraph(
projectGraph: ProjectGraph,
projectNames: string[],
nxArgs: NxArgs
): CommandGraph {
const dependencies: Record<string, string[]> = {};
for (const projectName of projectNames) {
if (projectGraph.dependencies[projectName].length >= 1) {
dependencies[projectName] = [
...new Set(
projectGraph.dependencies[projectName]
.map((projectDep) => projectDep.target)
.filter((projectDep) => projectGraph.nodes[projectDep])
).values(),
];
} else {
dependencies[projectName] = [];
}
recursiveResolveDeps(projectGraph, projectName, dependencies);
}
const roots = Object.keys(dependencies).filter(
(d) => dependencies[d].length === 0
Expand Down
8 changes: 4 additions & 4 deletions packages/nx/src/tasks-runner/task-graph-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,16 @@ function _findCycle(
return null;
}

export function findCycle(taskGraph: {
export function findCycle(graph: {
dependencies: Record<string, string[]>;
}): string[] | null {
const visited = {};
for (const t of Object.keys(taskGraph.dependencies)) {
for (const t of Object.keys(graph.dependencies)) {
visited[t] = false;
}

for (const t of Object.keys(taskGraph.dependencies)) {
const cycle = _findCycle(taskGraph, t, visited, [t]);
for (const t of Object.keys(graph.dependencies)) {
const cycle = _findCycle(graph, t, visited, [t]);
if (cycle) return cycle;
}

Expand Down

0 comments on commit 49da9b1

Please sign in to comment.