madrun
Version:
CLI tool to run multiple npm-scripts in a madly comfortable way
207 lines (151 loc) • 4.49 kB
JavaScript
import {pathToFileURL} from 'node:url';
import keys from 'all-object-keys';
import {jessy} from 'jessy';
import wildcard from './wildcard.js';
import check from './check.js';
import supported from '../supported.json' with {
type: 'json',
};
const maybeArray = (a) => isArray(a) ? a : [a];
const isStr = (a) => typeof a === 'string';
const {isArray} = Array;
async function getFindUp() {
if (globalThis.MADRUN_FINDUP)
return globalThis.MADRUN_FINDUP;
const {findUpSync} = await import('find-up');
return findUpSync;
}
async function getScripts() {
const findUp = await getFindUp();
const path = findUp(supported);
if (!path)
throw Error('.madrun.js is missing!');
return (await import(pathToFileURL(path))).default;
}
export const cutEnv = async (names, opts = '', env, scripts) => {
names = maybeArray(names);
scripts = scripts || await getScripts();
const isParallel = false;
const commands = await runNames(names, opts, scripts, {
env,
cutEnv: true,
isParallel,
});
return joinByType(commands, {
isParallel,
});
};
export const run = async (name, opts = '', env, scripts) => {
scripts = scripts || await getScripts();
const problems = check(scripts);
if (problems)
return problems;
return await series(name, opts, env, scripts);
};
export async function series(names, opts = '', env, scripts) {
names = maybeArray(names);
scripts = scripts || await getScripts();
const isParallel = false;
const commands = await runNames(names, opts, scripts, {
env,
isParallel,
});
return joinByType(commands, {
isParallel,
});
}
export const parallel = async (names, opts = '', env, scripts) => {
scripts = scripts || await getScripts();
names = maybeArray(names);
const isParallel = true;
const commands = await runNames(names, opts, scripts, {
env,
isParallel,
});
return joinByType(commands, {
isParallel,
});
};
function joinByType(commands, {isParallel}) {
const type = isParallel ? ' & ' : ' && ';
return commands.join(type);
}
async function runNames(names, opts, scripts, {env, isParallel, cutEnv}) {
const cmds = [];
for (const name of names) {
const cmd = await parse(name, {
scripts,
opts,
env,
isParallel,
cutEnv,
});
cmds.push(cmd);
}
return cmds;
}
const addOpts = (name, opts) => {
if (!opts)
return name;
return `${name} ${opts}`;
};
const addEnv = (cmd, env) => {
const entries = env && Object.entries(env);
if (!env || !entries.length)
return cmd;
const line = [];
for (const [key, value] of entries) {
line.push(`${key}=${value}`);
}
const envLine = line.join(' ');
return `${envLine} ${cmd}`;
};
const parse = async (name, {scripts, opts, env, isParallel, cutEnv}) => {
const result = [];
const all = keys(scripts);
for (const key of all) {
if (!wildcard(`^${name}$`).test(key))
continue;
const fn = jessy(key, scripts);
const {line, lineEnv} = await runScriptFn(fn);
const cmd = addOpts(line, opts);
const cmdEnv = addEnv(cmd, {
...cutEnv ? {} : lineEnv,
...env,
});
const processed = await addPrePost(cmdEnv, key, scripts);
result.push(processed);
}
if (!result.length)
throw Error(`one of scripts not found: ${name}`);
return joinByType(result, {
isParallel,
});
};
async function runScriptFn(fn) {
const result = await fn();
if (!isArray(result))
return {
line: result,
lineEnv: {},
};
const [a, b] = result;
if (isStr(a))
return {
line: a,
lineEnv: b,
};
return {
line: b,
lineEnv: a,
};
}
async function addPrePost(cmd, key, scripts) {
const pre = jessy(`pre${key}`, scripts);
const post = jessy(`post${key}`, scripts);
if (pre)
cmd = `${await pre()} && ${cmd}`;
if (post)
cmd = `${cmd} && ${await post()}`;
return cmd;
}