Created
September 29, 2023 17:57
-
-
Save btaillon-coveo/c356eb606f991a9a0b9adcb8e85e625e to your computer and use it in GitHub Desktop.
compare nx runs
This file contains hidden or 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
import { resolve, parse } from "node:path"; | |
import { readFile, writeFile } from "node:fs/promises"; | |
/** | |
* @typedef File | |
* @property {'file'} type | |
*/ | |
/** | |
* @typedef Directory | |
* @property {'directory'} type | |
* @property {{ [name: string]: Graph }} contents | |
*/ | |
/** | |
* @typedef {File | Directory} Graph | |
*/ | |
/** | |
* @typedef DiffAdded | |
* @property {'added'} type | |
* @property {string[]} added | |
*/ | |
/** | |
* @typedef DiffRemoved | |
* @property {'removed'} type | |
* @property {string[]} removed | |
*/ | |
/** | |
* @typedef DiffDirectoryContents | |
* @property {'directory-contents'} type | |
* @property {{ [name: string]: Differences }} contents | |
*/ | |
/** | |
* @typedef DiffTypeChanged | |
* @property {'type-changed'} type | |
* @property {Graph['type']} oldType | |
* @property {Graph['type']} newType | |
*/ | |
/** | |
* @typedef {(DiffAdded | DiffRemoved | DiffTypeChanged | DiffDirectoryContents)[]} Differences | |
*/ | |
/** | |
* @param {Directory} root | |
* @param {string} directoryPath | |
* @returns {Directory} | |
*/ | |
function getDirectory(root, directoryPath) { | |
const { dir: parentDirectoryPath } = parse(directoryPath); | |
const parentDirectory = parentDirectoryPath.trim() | |
? getDirectory(root, parentDirectoryPath) | |
: root; | |
/** | |
* @type {Graph} | |
*/ | |
const currentDirectory = parentDirectory.contents[directoryPath]; | |
if (!currentDirectory) { | |
return (parentDirectory.contents[directoryPath] = { | |
type: "directory", | |
contents: {}, | |
}); | |
} | |
if (currentDirectory.type !== "directory") { | |
throw "expected directory"; | |
} | |
return currentDirectory; | |
} | |
/** | |
* @param {string[]} filePaths | |
* @returns {Directory} | |
*/ | |
function buildGraph(filePaths) { | |
/** | |
* @type {Directory} | |
*/ | |
const root = { type: "directory", contents: {} }; | |
for (const filePath of filePaths) { | |
const { dir: directoryPath } = parse(filePath); | |
getDirectory(root, directoryPath).contents[filePath] = { type: "file" }; | |
} | |
return root; | |
} | |
/** | |
* @param {Graph} graphA | |
* @param {Graph} graphB | |
* @returns {Promise<Differences>} | |
*/ | |
async function compareGraphs(graphA, graphB) { | |
/** | |
* @type {Differences} | |
*/ | |
const differences = []; | |
if (graphA.type !== graphB.type) { | |
differences.push({ | |
type: "type-changed", | |
oldType: graphA.type, | |
newType: graphB.type, | |
}); | |
return differences; | |
} | |
if (graphA.type !== "directory") { | |
return differences; | |
} | |
/** | |
* @type {DiffAdded['added']} | |
*/ | |
const added = []; | |
/** | |
* @type {DiffRemoved['removed']} | |
*/ | |
const removed = []; | |
/** | |
* @type {DiffDirectoryContents['contents']} | |
*/ | |
const contentsWithDifferences = {}; | |
for (const childName of Array.from( | |
new Set([...Object.keys(graphA.contents), ...Object.keys(graphB.contents)]) | |
)) { | |
if (!(childName in graphA.contents)) { | |
added.push(childName); | |
continue; | |
} | |
if (!(childName in graphB.contents)) { | |
removed.push(childName); | |
continue; | |
} | |
const childDifferences = await compareGraphs( | |
graphA.contents[childName], | |
graphB.contents[childName] | |
); | |
if (childDifferences.length) { | |
contentsWithDifferences[childName] = childDifferences; | |
} | |
} | |
if (added.length) { | |
differences.push({ type: "added", added }); | |
} | |
if (removed.length) { | |
differences.push({ type: "removed", removed }); | |
} | |
if (Object.keys(contentsWithDifferences).length) { | |
differences.push({ | |
type: "directory-contents", | |
contents: contentsWithDifferences, | |
}); | |
} | |
return differences; | |
} | |
/** | |
* @param {string} snapshotJsonPath | |
* @returns {Promise<Record<string, Directory>>} | |
*/ | |
async function buildGraphsForFile(snapshotJsonPath) { | |
const contentsBuffer = await readFile(snapshotJsonPath); | |
/** | |
* @type {Record<string, string[]>} | |
*/ | |
const projects = JSON.parse(contentsBuffer.toString()); | |
/** | |
* @type {Record<string, Directory>} | |
*/ | |
const newMap = {}; | |
for (const [projectName, projectFileNames] of Object.entries(projects)) { | |
newMap[projectName] = buildGraph(projectFileNames); | |
} | |
return newMap; | |
} | |
/** | |
* | |
* @param {string} snapshotJsonPathA | |
* @param {string} snapshotJsonPathB | |
* @param {string} outputPath | |
*/ | |
async function compareFiles(snapshotJsonPathA, snapshotJsonPathB, outputPath) { | |
console.log("Building graph A"); | |
const graphsA = await buildGraphsForFile(snapshotJsonPathA); | |
console.log("Building graph B"); | |
const graphsB = await buildGraphsForFile(snapshotJsonPathB); | |
const comparison = {}; | |
for (const key of Array.from( | |
new Set([...Object.keys(graphsA), ...Object.keys(graphsB)]) | |
)) { | |
console.log("Comparing", key); | |
comparison[key] = await compareGraphs(graphsA[key], graphsB[key]); | |
} | |
console.log("Writing to", outputPath); | |
await writeFile(outputPath, JSON.stringify(comparison, undefined, " ")); | |
console.log("Done!"); | |
} | |
compareFiles( | |
resolve("before.json"), | |
resolve("after.json"), | |
resolve("diff.json") | |
); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment