cli: add simple dependency graph utility for lockfiles

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2022-12-13 23:21:53 +01:00
parent 18543ed94b
commit 99898258cd
2 changed files with 217 additions and 0 deletions
@@ -586,4 +586,201 @@ d@^1:
});
});
});
describe('createSimplifiedDependencyGraph', () => {
it('for modern lockfile', () => {
expect(
Lockfile.parse(
`${MODERN_HEADER}
"@backstage/app-defaults@workspace:^, @backstage/app-defaults@workspace:packages/app-defaults":
version: 0.0.0-use.local
resolution: "@backstage/app-defaults@workspace:packages/app-defaults"
dependencies:
"@backstage/cli": "workspace:^"
"@backstage/core-app-api": "workspace:^"
"@backstage/core-components": "workspace:^"
"@backstage/core-plugin-api": "workspace:^"
"@backstage/plugin-permission-react": "workspace:^"
"@backstage/test-utils": "workspace:^"
"@backstage/theme": "workspace:^"
"@material-ui/core": ^4.12.2
"@material-ui/icons": ^4.9.1
"@testing-library/jest-dom": ^5.10.1
"@testing-library/react": ^12.1.3
"@types/node": ^16.11.26
"@types/react": ^16.13.1 || ^17.0.0
peerDependencies:
react: ^16.13.1 || ^17.0.0
react-dom: ^16.13.1 || ^17.0.0
react-router-dom: 6.0.0-beta.0 || ^6.3.0
languageName: unknown
linkType: soft
"@backstage/backend-app-api@workspace:^, @backstage/backend-app-api@workspace:packages/backend-app-api":
version: 0.0.0-use.local
resolution: "@backstage/backend-app-api@workspace:packages/backend-app-api"
dependencies:
"@backstage/backend-common": "workspace:^"
"@backstage/backend-plugin-api": "workspace:^"
"@backstage/backend-tasks": "workspace:^"
"@backstage/cli": "workspace:^"
"@backstage/errors": "workspace:^"
"@backstage/plugin-permission-node": "workspace:^"
express: ^4.17.1
express-promise-router: ^4.1.0
winston: ^3.2.1
languageName: unknown
linkType: soft
`,
).createSimplifiedDependencyGraph(),
).toEqual(
new Map([
[
'@backstage/app-defaults',
new Set([
'@backstage/cli',
'@backstage/core-app-api',
'@backstage/core-components',
'@backstage/core-plugin-api',
'@backstage/plugin-permission-react',
'@backstage/test-utils',
'@backstage/theme',
'@material-ui/core',
'@material-ui/icons',
'@testing-library/jest-dom',
'@testing-library/react',
'@types/node',
'@types/react',
'react',
'react-dom',
'react-router-dom',
]),
],
[
'@backstage/backend-app-api',
new Set([
'@backstage/backend-common',
'@backstage/backend-plugin-api',
'@backstage/backend-tasks',
'@backstage/cli',
'@backstage/errors',
'@backstage/plugin-permission-node',
'express',
'express-promise-router',
'winston',
]),
],
]),
);
});
it('for simple lockfile without dependencies', () => {
expect(
Lockfile.parse(
`${MODERN_HEADER}
"a@npm:^1":
version: "1.0.1"
"b@npm:3":
version: "3.0.1"
"b@npm:2.0.x":
version: "2.0.1"
checksum: sha512-abc2
`,
).createSimplifiedDependencyGraph(),
).toEqual(
new Map([
['a', new Set()],
['b', new Set()],
]),
);
});
it('for lockfile with dependencies', () => {
expect(
Lockfile.parse(
`${MODERN_HEADER}
"a@npm:^1":
version: "1.0.1"
dependencies:
b: "^2"
"b@npm:3":
version: "3.0.1"
checksum: sha512-abc1
"b@npm:2.0.x":
version: "2.0.1"
checksum: sha512-abc2
dependencies:
c: "^1"
"b@npm:^2":
version: "2.0.0"
checksum: sha512-abc3
peerDependencies:
d: "^1"
"c@npm:^1":
version: "1.0.1"
"d@npm:^1":
version: "1.0.2"
`,
).createSimplifiedDependencyGraph(),
).toEqual(
new Map([
['a', new Set(['b'])],
['b', new Set(['c', 'd'])],
['c', new Set()],
['d', new Set()],
]),
);
});
it('for legacy lockfile', () => {
expect(
Lockfile.parse(
`${LEGACY_HEADER}
a@^1:
version "1.0.1"
dependencies:
b "^2"
b@3:
version "3.0.1"
integrity sha512-abc1
b@2.0.x:
version "2.0.1"
integrity sha512-abc2
dependencies:
c "^1"
b@^2:
version "2.0.0"
integrity sha512-abc3
dependencies:
d "^1"
c@^1:
version "1.0.1"
integrity x
d@^1:
version "1.0.1"
integrity x
`,
).createSimplifiedDependencyGraph(),
).toEqual(
new Map([
['a', new Set(['b'])],
['b', new Set(['c', 'd'])],
['c', new Set()],
['d', new Set()],
]),
);
});
});
});
@@ -29,6 +29,7 @@ type LockfileData = {
integrity?: string /* old */;
checksum?: string /* new */;
dependencies?: { [name: string]: string };
peerDependencies?: { [name: string]: string };
};
};
@@ -339,6 +340,25 @@ export class Lockfile {
}
}
createSimplifiedDependencyGraph(): Map<string, Set<string>> {
const graph = new Map<string, Set<string>>();
for (const [name, entries] of this.packages) {
const dependencies = new Set(
entries.flatMap(e => {
const data = this.data[e.dataKey];
return [
...Object.keys(data?.dependencies ?? {}),
...Object.keys(data?.peerDependencies ?? {}),
];
}),
);
graph.set(name, dependencies);
}
return graph;
}
/**
* Diff with another lockfile, returning entries that have been
* added, changed, and removed compared to the other lockfile.