Skip to content

Commit

Permalink
Merge pull request #33 from snyk/feat/add-tree-size-in-the-output
Browse files Browse the repository at this point in the history
feat: add tree size in the output
  • Loading branch information
Kirill89 authored Dec 18, 2018
2 parents fd173bc + 6e791e7 commit 32974f1
Show file tree
Hide file tree
Showing 16 changed files with 43 additions and 6 deletions.
1 change: 1 addition & 0 deletions lib/parsers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export interface PkgTree {
hasDevDependencies?: boolean;
cyclic?: boolean;
missingLockFileEntry?: boolean;
size?: number;
}

export enum DepType {
Expand Down
21 changes: 18 additions & 3 deletions lib/parsers/package-lock-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ export class PackageLockParser implements LockfileParser {
dependencies: {},
hasDevDependencies: !_.isEmpty(manifestFile.devDependencies),
name: manifestFile.name,
size: 1,
version: manifestFile.version || '',
};

Expand Down Expand Up @@ -104,10 +105,14 @@ export class PackageLockParser implements LockfileParser {
}

// transform depMap to a map of PkgTrees
const depTrees: {[depPath: string]: PkgTree} = await this.createDepTrees(depMap, depGraph);
const {depTrees, depTreesSizes} = await this.createDepTrees(depMap, depGraph);

// get trees for dependencies from manifest file
const topLevelDeps: Dep[] = getTopLevelDeps(manifestFile, includeDev);

// number of dependencies including root one
let treeSize = 1;

for (const dep of topLevelDeps) {
// if any of top level dependencies is a part of cycle
// it now has a different item in the map
Expand All @@ -116,9 +121,11 @@ export class PackageLockParser implements LockfileParser {
// if the top level dependency is dev, all children are dev
depTree.dependencies[dep.name] = dep.dev ?
this.setDevDepRec(_.cloneDeep(depTrees[depName])) : depTrees[depName];
treeSize += depTreesSizes[depName];
await setImmediatePromise();
} else if (/^file:/.test(dep.version)) {
depTree.dependencies[dep.name] = createPkgTreeFromDep(dep);
treeSize++;
} else {
// TODO: also check the package version
// for a stricter check
Expand All @@ -127,8 +134,11 @@ export class PackageLockParser implements LockfileParser {
}
depTree.dependencies[dep.name] = createPkgTreeFromDep(dep);
depTree.dependencies[dep.name].missingLockFileEntry = true;
treeSize++;
}
}

depTree.size = treeSize;
return depTree;
}

Expand Down Expand Up @@ -280,14 +290,16 @@ export class PackageLockParser implements LockfileParser {

// Algorithm is based on dynamic programming technique and tries to build
// "more simple" trees and compose them into bigger ones.
private async createDepTrees(depMap: DepMap, depGraph): Promise<{[depPath: string]: PkgTree}> {
private async createDepTrees(depMap: DepMap, depGraph):
Promise<{depTrees: {[depPath: string]: PkgTree}, depTreesSizes: {[depPath: string]: number}}> {

// Graph has to be acyclic
if (!graphlib.alg.isAcyclic(depGraph)) {
throw new Error('Cycles were not removed from graph.');
}

const depTrees: {[depPath: string]: PkgTree} = {};
const depTreesSizes: {[depPath: string]: number} = {};
// topological sort guarantees that when we create a pkg-tree for a dep,
// all it's sub-trees were already created. This also implies that leaf
// packages will be processed first as they have no sub-trees.
Expand All @@ -296,12 +308,14 @@ export class PackageLockParser implements LockfileParser {
while (depOrder.length) {
const depKey = depOrder.shift() as string;
const dep = depMap[depKey];
let treeSize = 1;

// direction is from the dependency to the package requiring it, so we are
// looking for predecessors
for (const subDepPath of depGraph.predecessors(depKey)) {
const subDep = depTrees[subDepPath];
dep.dependencies[subDep.name] = subDep;
treeSize += depTreesSizes[subDepPath];
}
const pkgTree: PkgTree = {
depType: dep.depType,
Expand All @@ -316,12 +330,13 @@ export class PackageLockParser implements LockfileParser {
pkgTree.hasDevDependencies = dep.hasDevDependencies;
}
depTrees[depKey] = pkgTree;
depTreesSizes[depKey] = treeSize;
// Since this code doesn't handle any I/O or network, we need to force
// event loop to tick while being used in server for request processing
await setImmediatePromise();
}

return depTrees;
return {depTrees, depTreesSizes};
}

private flattenLockfile(lockfile: PackageLock): DepMap {
Expand Down
8 changes: 8 additions & 0 deletions lib/parsers/yarn-lock-parse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export class YarnLockParser implements LockfileParser {

private yarnLockfileParser;
private eventLoop: EventLoopSpinner;
private treeSize: number;

constructor() {
// @yarnpkg/lockfile doesn't work with Node.js < 6 and crashes just after
Expand All @@ -49,6 +50,9 @@ export class YarnLockParser implements LockfileParser {
// processed in ~150ms. Idea is to let those average requests through in one
// tick and split only bigger ones.
this.eventLoop = new EventLoopSpinner(200);

// Number of dependencies including root one.
this.treeSize = 1;
}

public parseLockFile(lockFileContents: string): YarnLock {
Expand Down Expand Up @@ -76,6 +80,7 @@ export class YarnLockParser implements LockfileParser {
dependencies: {},
hasDevDependencies: !_.isEmpty(manifestFile.devDependencies),
name: manifestFile.name,
size: 1,
version: manifestFile.version || '',
};

Expand All @@ -93,8 +98,10 @@ export class YarnLockParser implements LockfileParser {
depTree.dependencies[dep.name] = await this.buildSubTreeRecursiveFromYarnLock(
dep, yarnLock, [], strict);
}
this.treeSize++;
}

depTree.size = this.treeSize;
return depTree;
}

Expand Down Expand Up @@ -138,6 +145,7 @@ export class YarnLockParser implements LockfileParser {
};
depSubTree.dependencies[name] = await this.buildSubTreeRecursiveFromYarnLock(
newDep, lockFile, [...depPath]);
this.treeSize++;
}
}

Expand Down
1 change: 1 addition & 0 deletions test/lib/fixtures/dev-deps-only/expected-tree-empty.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"name": "pkg-dev-deps-only",
"size": 1,
"version": "0.0.1",
"hasDevDependencies": true,
"dependencies": {}
Expand Down
1 change: 1 addition & 0 deletions test/lib/fixtures/dev-deps-only/expected-tree.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"name": "pkg-dev-deps-only",
"size": 3,
"version": "0.0.1",
"hasDevDependencies": true,
"dependencies": {
Expand Down
1 change: 1 addition & 0 deletions test/lib/fixtures/external-tarball/expected-tree.json
Original file line number Diff line number Diff line change
Expand Up @@ -98,5 +98,6 @@
},
"hasDevDependencies": false,
"name": "external-tarball",
"size": 16,
"version": ""
}
1 change: 1 addition & 0 deletions test/lib/fixtures/file-as-version/expected-tree.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,6 @@
},
"hasDevDependencies": false,
"name": "pkg-dev-deps-only",
"size": 4,
"version": "0.0.1"
}
1 change: 1 addition & 0 deletions test/lib/fixtures/git-ssh-url-deps/expected-tree.json
Original file line number Diff line number Diff line change
Expand Up @@ -98,5 +98,6 @@
},
"hasDevDependencies": false,
"name": "git-ssh-url-deps",
"size": 16,
"version": ""
}
3 changes: 2 additions & 1 deletion test/lib/fixtures/goof/dep-tree-no-dev-deps-yarn.json
Original file line number Diff line number Diff line change
Expand Up @@ -5854,5 +5854,6 @@
},
"hasDevDependencies": true,
"name": "goof",
"version": "0.0.3"
"version": "0.0.3",
"size": 914
}
1 change: 1 addition & 0 deletions test/lib/fixtures/goof/dep-tree-no-dev-deps.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"name": "goof",
"version": "0.0.3",
"size": 910,
"hasDevDependencies": true,
"dependencies": {
"adm-zip": {
Expand Down
3 changes: 2 additions & 1 deletion test/lib/fixtures/goof/dep-tree-with-dev-deps-yarn.json
Original file line number Diff line number Diff line change
Expand Up @@ -11450,5 +11450,6 @@
},
"hasDevDependencies": true,
"name": "goof",
"version": "0.0.3"
"version": "0.0.3",
"size": 1795
}
3 changes: 2 additions & 1 deletion test/lib/fixtures/goof/dep-tree-with-dev-deps.json
Original file line number Diff line number Diff line change
Expand Up @@ -11425,5 +11425,6 @@
},
"hasDevDependencies": true,
"name": "goof",
"version": "0.0.3"
"version": "0.0.3",
"size": 1791
}
1 change: 1 addition & 0 deletions test/lib/fixtures/missing-deps/expected-tree.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"name": "pkg-missing-deps",
"size": 1,
"version": "1.0.0",
"hasDevDependencies": false,
"dependencies": {}
Expand Down
1 change: 1 addition & 0 deletions test/lib/fixtures/out-of-sync-tree/expected-tree.json
Original file line number Diff line number Diff line change
Expand Up @@ -188,5 +188,6 @@
},
"hasDevDependencies": true,
"name": "out-of-sync-small",
"size": 30,
"version": "1.0.0"
}
1 change: 1 addition & 0 deletions test/lib/fixtures/out-of-sync/expected-tree.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,6 @@
},
"hasDevDependencies": true,
"name": "trucolor",
"size": 6,
"version": "0.7.1"
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"name": "goof",
"size": 17,
"version": "0.0.3",
"hasDevDependencies": true,
"dependencies": {
Expand Down

0 comments on commit 32974f1

Please sign in to comment.