Skip to content

Instantly share code, notes, and snippets.

@drochgenius
Created November 23, 2020 16:22
Show Gist options
  • Save drochgenius/c0dfd033790c74bbc3e08c22d276677a to your computer and use it in GitHub Desktop.
Save drochgenius/c0dfd033790c74bbc3e08c22d276677a to your computer and use it in GitHub Desktop.
Use lerna graph to build packages in topological order
/* eslint-disable @typescript-eslint/no-var-requires */
/**
* this script will list packages in topological order (dependencies before dependents)
* and execute the given command in sequence
* it will substitute any {package} found on the command by the actual package of the current cursor.
* We no longer trust lerna to build in topological order.
*/
const {execSync} = require('child_process');
const colors = require('colors');
const rawCommand = process.argv[2];
try {
const buffer = execSync(`npx lerna list --graph -a`);
const unformattedGraph = JSON.parse(buffer.toString())
let graph = Object.keys(unformattedGraph).map((name) => ({
name,
dependencies: unformattedGraph[name].filter(dep => Object.keys(unformattedGraph).includes(dep))
}));
const pkgCount = graph.length;
const sorted = [];
/** in every iteration */
while(graph.length && sorted.length < pkgCount) {
/** we add the packages without dependencies to the final list */
const independentPackagesNames = graph.filter(pkg => !pkg.dependencies.length).map(pkg => pkg.name);
if (!independentPackagesNames.length) {
throw new Error(`in remaining graph a circular dependency is observed: ${JSON.stringify(graph, null, 2)}`);
}
sorted.push(...independentPackagesNames);
/** and remove them from the original graph */
graph = graph.filter(pkg => pkg.dependencies.length);
/** and these packages are removed as dependencies of the remaining packages */
graph.forEach(pkg => {
independentPackagesNames.forEach(independentPackageName => {
const index = pkg.dependencies.indexOf(independentPackageName);
if (index > -1) {
pkg.dependencies.splice(index, 1);
}
});
})
}
process.stdout.write(colors.green(`building ${sorted.length} packages sequentially in topological order`)+'\n');
for (const package of sorted) {
const command = rawCommand.replace('{package}', package);
console.info(`running ${command}`);
try {
execSync(command,
{stdio: [process.stdin, process.stdout, process.stderr]});
}catch(e) {
process.exit(1);
}
}
} catch (error) {
console.error(error);
process.exit(0)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment