Skip to content

Instantly share code, notes, and snippets.

@craigphicks
Created January 6, 2022 02:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save craigphicks/c556ab4e2ce505337fa5be6f383d656b to your computer and use it in GitHub Desktop.
Save craigphicks/c556ab4e2ce505337fa5be6f383d656b to your computer and use it in GitHub Desktop.
Workaround for lerna run bug (not calling in topological order).
Lerna issue [ Lerna does not run scripts according to topological order #2731 ](https://github.com/lerna/lerna/issues/2731)
reports the bug, but issue was closed although not resolved.
Strangely, I didn't **notice** any problem until adding new scopes in the project.
But now I have the same bug.
I threw up something quick to get builds working again, and posted it here.
It's a near replacement but uses regexp instead of globbing.
import cp from 'child_process';
type LernaListEntry = {
name: string; // [@scope/]packagename
version: string;
private: boolean;
location: string; // absolute path
};
function getLernaListEntriesTopo(): LernaListEntry[] {
const lintext = cp.execSync('lerna la --ndjson --toposort').toString();
const linarr = lintext.split(/\r\n?|\n/).filter((s) => s);
return linarr.map((item) => JSON.parse(item) as LernaListEntry);
}
async function executeOne(name: string, otherArgs: string[]): Promise<number> {
return await new Promise((resolve, reject) => {
const prog = 'lerna';
const args = ['run', '--scope', name, ...otherArgs];
console.log(`DEBUG: `, prog, ...args);
const child = cp.spawn(prog, args, {stdio: 'inherit'});
child.on('error', (err) => {
reject(err);
});
child.on('close', (code, signal) => {
if (signal)
reject(new Error(`${name} ${args} terminated with signal ${signal}`));
resolve(code ?? 0);
});
});
}
function help(): void {
const text = `
lenra-serial run [--scope XXX [--scope XXX ... ][ --ignore XXX] ...] <command>
where XXX are escaped js strings as constructor args for RegExp(XXX)
`;
console.error(text);
process.exit(1);
}
const args = process.argv.slice(2);
if (args.length < 2) help();
if (args[0] !== 'run') {
console.error('first arg must be "run"');
help();
}
const includeRe: RegExp[] = [];
const excludeRe: RegExp[] = [];
const otherArgs: string[] = [];
for (let i = 1; i < args.length; i++) {
if (args[i] === '--scope') {
if (i === args.length) help();
includeRe.push(RegExp(args[++i]));
} else if (args[i] === '--ignore') {
if (i === args.length) help();
excludeRe.push(RegExp(args[++i]));
} else {
otherArgs.push(args[i]);
}
}
const lentarr = getLernaListEntriesTopo();
(async (): Promise<number> => {
for (let lent of lentarr) {
if (
includeRe.some((re) => re.test(lent.name)) &&
!excludeRe.some((re) => re.test(lent.name))
) {
const rcode = await executeOne(lent.name, otherArgs);
if (rcode) return rcode;
}
}
return 0;
})()
.then((rcode) => (process.exitCode = rcode))
.catch((e) => {
console.error(e.message);
process.exitCode = 127;
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment