-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add new functionality to create a graph from unchanged packages
This adds a function that creates a subgraph of only new and changed package nodes in a graph.
- Loading branch information
1 parent
eb98b7a
commit a0d5ba7
Showing
50 changed files
with
2,899 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
import { DepGraph, DepGraphInternal, NodeInfo, PkgInfo } from './types'; | ||
import { DepGraphImpl } from './dep-graph'; | ||
import { DepGraphBuilder } from './builder'; | ||
import { eventLoopSpinner } from 'event-loop-spinner'; | ||
|
||
type NodeId = string; | ||
|
||
/** | ||
* Creates an induced subgraph of {@param graphB} with only packages | ||
* that are not present in {@param graphA} or have a different version. | ||
* | ||
* @param graphA | ||
* @param graphB | ||
*/ | ||
export async function createChangedPackagesGraph( | ||
graphA: DepGraph, | ||
graphB: DepGraph, | ||
): Promise<DepGraph> { | ||
const depGraph = graphB as DepGraphInternal; | ||
|
||
const graphAPackageIds = new Set( | ||
graphA.getDepPkgs().map(DepGraphImpl.getPkgId), | ||
); | ||
|
||
const addedOrUpdatedPackages: PkgInfo[] = depGraph | ||
.getDepPkgs() | ||
.filter((pkg) => !graphAPackageIds.has(DepGraphImpl.getPkgId(pkg))); | ||
|
||
const depGraphBuilder = new DepGraphBuilder( | ||
depGraph.pkgManager, | ||
depGraph.rootPkg, | ||
); | ||
|
||
const parentQueue: [parentId: NodeId, nodeId: NodeId][] = []; | ||
for (const changedPackage of addedOrUpdatedPackages) { | ||
for (const changedNodeId of depGraph.getPkgNodeIds(changedPackage)) { | ||
//we add all nodes with new and changed packages to the new graph. | ||
//a newly added node will also have its dependencies added here, since they are "new". | ||
depGraphBuilder.addPkgNode( | ||
depGraph.getNodePkg(changedNodeId), | ||
changedNodeId, | ||
getNodeInfo(depGraph, changedNodeId), | ||
); | ||
|
||
//Push all direct parents of the changed nodes to a queue to later build up a path to root from that node | ||
for (const parentId of depGraph.getNodeParentsNodeIds(changedNodeId)) { | ||
parentQueue.push([parentId, changedNodeId]); | ||
|
||
if (eventLoopSpinner.isStarving()) { | ||
await eventLoopSpinner.spin(); | ||
} | ||
} | ||
} | ||
} | ||
|
||
//add direct and transitive parents for the changed nodes | ||
const visited = new Set([depGraph.rootNodeId]); | ||
|
||
while (parentQueue.length > 0) { | ||
const [nodeId, dependencyNodeId] = parentQueue.pop()!; | ||
if (visited.has(nodeId)) { | ||
//ensure we link parents even if visited through another path | ||
depGraphBuilder.connectDep(nodeId, dependencyNodeId); | ||
continue; | ||
} | ||
|
||
visited.add(nodeId); | ||
|
||
depGraphBuilder.addPkgNode( | ||
depGraph.getNodePkg(nodeId), | ||
nodeId, | ||
getNodeInfo(depGraph, nodeId), | ||
); | ||
depGraphBuilder.connectDep(nodeId, dependencyNodeId); | ||
|
||
for (const parentId of depGraph.getNodeParentsNodeIds(nodeId)) { | ||
parentQueue.push([parentId, nodeId]); | ||
|
||
if (eventLoopSpinner.isStarving()) { | ||
await eventLoopSpinner.spin(); | ||
} | ||
} | ||
} | ||
|
||
return depGraphBuilder.build(); | ||
} | ||
|
||
function getNodeInfo( | ||
depGraph: DepGraphInternal, | ||
nodeId: string, | ||
): NodeInfo | undefined { | ||
const nodeInfo: NodeInfo = depGraph.getNode(nodeId); | ||
if (!nodeInfo || Object.keys(nodeInfo).length === 0) { | ||
return undefined; | ||
} | ||
return nodeInfo; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
import * as depGraphLib from '../../src'; | ||
import * as helpers from '../helpers'; | ||
import { createChangedPackagesGraph } from '../../src'; | ||
|
||
describe('filter-unchanged-packages', () => { | ||
it.each` | ||
fixture | ||
${'equals/simple.json'} | ||
${'cyclic-complex-dep-graph.json'} | ||
${'goof-graph.json'} | ||
`( | ||
'result and $fixture are equals for empty initial graph', | ||
async ({ fixture }) => { | ||
const graphB = depGraphLib.createFromJSON(helpers.loadFixture(fixture)); | ||
|
||
const graphA = new depGraphLib.DepGraphBuilder( | ||
graphB.pkgManager, | ||
graphB.rootPkg, | ||
).build(); | ||
|
||
const result = await createChangedPackagesGraph(graphA, graphB); | ||
expect(graphB.equals(result)).toBe(true); | ||
}, | ||
); | ||
|
||
it.each` | ||
fixtureA | fixtureB | expected | ||
${'changed-packages-graph/graph.json'} | ${'changed-packages-graph/graph-direct-dep-added.json'} | ${'changed-packages-graph/graph-direct-dep-added-expected.json'} | ||
${'changed-packages-graph/graph.json'} | ${'changed-packages-graph/graph-direct-dep-changed-cycle.json'} | ${'changed-packages-graph/graph-direct-dep-changed-cycle-expected.json'} | ||
${'changed-packages-graph/graph.json'} | ${'changed-packages-graph/graph-direct-dep-changed.json'} | ${'changed-packages-graph/graph-direct-dep-changed-expected.json'} | ||
${'changed-packages-graph/graph.json'} | ${'changed-packages-graph/graph-direct-dep-removed.json'} | ${'changed-packages-graph/graph-direct-dep-removed-expected.json'} | ||
${'changed-packages-graph/graph.json'} | ${'changed-packages-graph/graph-direct-dep-with-exiting-transitive-dep-added.json'} | ${'changed-packages-graph/graph-direct-dep-with-exiting-transitive-dep-added-expected.json'} | ||
${'changed-packages-graph/graph.json'} | ${'changed-packages-graph/graph-root-and-direct-dep-changed.json'} | ${'changed-packages-graph/graph-root-and-direct-dep-changed-expected.json'} | ||
${'changed-packages-graph/graph.json'} | ${'changed-packages-graph/graph-root-changed-expected.json'} | ${'changed-packages-graph/graph-root-changed-expected.json'} | ||
${'changed-packages-graph/graph.json'} | ${'changed-packages-graph/graph-transitive-dep-as-direct-dep.json'} | ${'changed-packages-graph/graph-transitive-dep-as-direct-dep-expected.json'} | ||
${'changed-packages-graph/graph.json'} | ${'changed-packages-graph/graph-transitive-dep-changed-cycle.json'} | ${'changed-packages-graph/graph-transitive-dep-changed-cycle-expected.json'} | ||
${'changed-packages-graph/graph.json'} | ${'changed-packages-graph/graph-transitive-dep-changed.json'} | ${'changed-packages-graph/graph-transitive-dep-changed-expected.json'} | ||
${'changed-packages-graph/graph.json'} | ${'changed-packages-graph/graph-transitive-dep-removed.json'} | ${'changed-packages-graph/graph-transitive-dep-removed-expected.json'} | ||
`( | ||
'result is $expected for $fixtureA and $fixtureB', | ||
async ({ fixtureA, fixtureB, expected }) => { | ||
const graphA = depGraphLib.createFromJSON(helpers.loadFixture(fixtureA)); | ||
|
||
const graphB = depGraphLib.createFromJSON(helpers.loadFixture(fixtureB)); | ||
|
||
const expectedResult = depGraphLib.createFromJSON( | ||
helpers.loadFixture(expected), | ||
); | ||
|
||
const result = await createChangedPackagesGraph(graphA, graphB); | ||
expect(expectedResult.equals(result, { compareRoot: true })).toBe(true); | ||
}, | ||
); | ||
}); |
57 changes: 57 additions & 0 deletions
57
test/fixtures/changed-packages-graph/graph-direct-dep-added-expected.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
{ | ||
"schemaVersion": "1.3.0", | ||
"pkgManager": { | ||
"name": "pip" | ||
}, | ||
"pkgs": [ | ||
{ | ||
"id": "a@1", | ||
"info": { | ||
"name": "a", | ||
"version": "1" | ||
} | ||
}, | ||
{ | ||
"id": "[email protected]", | ||
"info": { | ||
"name": "l", | ||
"version": "0.1" | ||
} | ||
}, | ||
{ | ||
"id": "[email protected]", | ||
"info": { | ||
"name": "m", | ||
"version": "1.2" | ||
} | ||
} | ||
], | ||
"graph": { | ||
"rootNodeId": "root-node", | ||
"nodes": [ | ||
{ | ||
"nodeId": "12", | ||
"pkgId": "[email protected]", | ||
"deps": [ | ||
{ | ||
"nodeId": "13" | ||
} | ||
] | ||
}, | ||
{ | ||
"nodeId": "13", | ||
"pkgId": "[email protected]", | ||
"deps": [] | ||
}, | ||
{ | ||
"nodeId": "root-node", | ||
"pkgId": "a@1", | ||
"deps": [ | ||
{ | ||
"nodeId": "12" | ||
} | ||
] | ||
} | ||
] | ||
} | ||
} |
Binary file added
BIN
+19.2 KB
test/fixtures/changed-packages-graph/graph-direct-dep-added-expected.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Oops, something went wrong.