Skip to content

Commit d68134c

Browse files
committed
feat: isTransitive(pkg)
1 parent 52c54c8 commit d68134c

File tree

3 files changed

+50
-0
lines changed

3 files changed

+50
-0
lines changed

src/core/dep-graph.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,14 @@ class DepGraphImpl implements types.DepGraphInternal {
158158
return count;
159159
}
160160

161+
public isTransitive(pkg: types.Pkg): boolean {
162+
const checking = new Set(this.getPkgNodeIds(pkg));
163+
for (const directDep of this.getNodeDepsNodeIds(this.rootNodeId)) {
164+
if (checking.has(directDep)) return false;
165+
}
166+
return true;
167+
}
168+
161169
public equals(
162170
other: types.DepGraph,
163171
{ compareRoot = true }: { compareRoot?: boolean } = {},

src/core/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ export interface DepGraph {
9494
getPkgNodes(pkg: Pkg): Node[];
9595
toJSON(): DepGraphData;
9696
pkgPathsToRoot(pkg: Pkg, opts?: { limit?: number }): PkgInfo[][];
97+
isTransitive(pkg: Pkg): boolean;
9798
directDepsLeadingTo(pkg: Pkg): PkgInfo[];
9899
countPathsToRoot(pkg: Pkg): number;
99100
equals(other: DepGraph, options?: { compareRoot?: boolean }): boolean;

test/core/is-transitive.test.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { createFromJSON, DepGraphData } from '../../src';
2+
import { loadFixture } from '../helpers';
3+
import * as depGraphLib from '../../src';
4+
5+
describe('isTransitive', () => {
6+
it('checks direct', () => {
7+
const graphJson: DepGraphData = loadFixture('simple-graph.json');
8+
9+
const depGraph = createFromJSON(graphJson);
10+
11+
expect(depGraph.isTransitive({ name: 'a', version: '1.0.0' })).toBe(false);
12+
expect(depGraph.isTransitive({ name: 'b', version: '1.0.0' })).toBe(false);
13+
expect(depGraph.isTransitive({ name: 'e', version: '5.0.0' })).toBe(true);
14+
expect(depGraph.isTransitive({ name: 'd', version: '0.0.1' })).toBe(true);
15+
expect(depGraph.isTransitive({ name: 'd', version: '0.0.2' })).toBe(true);
16+
expect(depGraph.isTransitive({ name: 'c', version: '1.0.0' })).toBe(true);
17+
});
18+
19+
it('assume non-transitive if any directly depends', () => {
20+
const builder = new depGraphLib.DepGraphBuilder(
21+
{ name: 'npm' },
22+
{ name: 'root', version: '1.2.3' },
23+
);
24+
const rootNodeId = 'root-node';
25+
26+
// root depends on 'a' and 'b', 'b' depends again on 'a'.
27+
builder.addPkgNode({ name: 'a', version: '1.0.0' }, 'a|1');
28+
builder.connectDep(rootNodeId, 'a|1');
29+
30+
builder.addPkgNode({ name: 'b', version: '1.0.0' }, 'b');
31+
builder.connectDep(rootNodeId, 'b');
32+
33+
builder.addPkgNode({ name: 'a', version: '1.0.0' }, 'a|2');
34+
builder.connectDep('b', 'a|2');
35+
36+
const depGraph = builder.build();
37+
38+
expect(depGraph.isTransitive({ name: 'a', version: '1.0.0' })).toBe(false);
39+
expect(depGraph.isTransitive({ name: 'b', version: '1.0.0' })).toBe(false);
40+
});
41+
});

0 commit comments

Comments
 (0)