daizong
Version:
`package.json` script runner for ES Modules
192 lines (190 loc) • 6.25 kB
JavaScript
import chalk from 'chalk';
import { inspect } from 'util';
import pMap from 'p-map';
import { readFile } from 'fs/promises';
import * as np from 'path';
import { fileURLToPath } from 'url';
import spawn from './spawn.js';
import { loadConfig } from './config.js';
import getTask from './getTask.js';
import { runBTCommands } from './btCmd.js';
import { parseArgs, Command } from './argsParser.js';
import { envPreset } from './envPreset.js';
import * as lib from './lib.js';
function log(s) {
console.log(s);
}
function logError(s) {
return log(chalk.red(s));
}
const cliArgs = process.argv.slice(2);
if (!cliArgs.length) {
logError('Missing task path.\nTry `daizong --help` for help.');
process.exit(1);
}
const cmd = parseArgs(cliArgs);
if (cmd.error) {
logError(cmd.error);
process.exit(1);
}
if (cmd.command === Command.help) {
log(`
Usage
$ daizong [options] <task-path> [task arguments]
Options
--config Explicitly specify the config file, \`--config config.js\`
--verbose Print verbose information during execution
--private Allow private tasks to be called from CLI
--version, -v Print version information
Examples
$ daizong --verbose test-browser --script-arg1 --script-arg2
`);
process.exit(0);
}
if (cmd.command === Command.version) {
const dirname = np.dirname(fileURLToPath(import.meta.url));
const pkg = JSON.parse(await readFile(np.join(dirname, '../package.json'), 'utf8'));
log(pkg.version);
process.exit(0);
}
const verboseLog = cmd.verbose
? (s) => log(`[DEBUG] ${s}`)
: null;
function processError(err) {
if (err instanceof Error) {
logError(err.message);
verboseLog?.(err.stack);
}
else {
logError(`${err}`);
}
process.exit(1);
}
async function runString(ctx, value, args, workingDir) {
if (value.startsWith('#')) {
return runTaskByName(ctx, value, workingDir);
}
const argsText = args ? ` ${chalk.cyan(lib.getArgsDisplayString(args))}` : '';
const workingDirText = workingDir ? ` [wd: ${workingDir}]` : '';
log(`>> ${chalk.yellow(value)}${argsText}${workingDirText}`);
return spawn(value, args ?? [], ctx.inheritedEnv, verboseLog, workingDir);
}
async function runTask(ctx, task, args, taskName, workingDir) {
const runValue = task.run;
if (task.workingDir) {
workingDir = task.workingDir;
}
if (runValue === undefined) {
throw new Error(`No \`run\` field found in task "${JSON.stringify(task)}"`);
}
const { settings } = ctx.config;
const { env: taskEnv, envGroups, before, after } = task;
let envGroupNames = [];
if (envGroups !== undefined) {
envGroupNames = typeof envGroups === 'string' ? [envGroups] : envGroups;
}
const groupEnv = {};
for (const groupName of envGroupNames) {
if (typeof groupName !== 'string') {
throw new Error(`Env group names must be strings, got ${JSON.stringify(groupName)}.`);
}
if (groupName) {
let vars = settings.envGroups[groupName];
if (!vars) {
const preset = envPreset(groupName);
if (preset) {
vars = preset;
}
if (!vars) {
throw new Error(`Env group "${groupName}" is not defined`);
}
}
Object.assign(groupEnv, vars);
}
}
const env = {
...settings.defaultEnv,
...ctx.inheritedEnv,
...groupEnv,
...taskEnv,
};
const hasBeforeOrAfter = !!(before || after);
if (before) {
log(`>> #${taskName} [before]`);
await runUnknown(ctx, before, null, undefined, workingDir);
}
if (hasBeforeOrAfter) {
log(`>> #${taskName} [run]`);
}
try {
await runUnknown({ ...ctx, inheritedEnv: env }, runValue, task, args, workingDir);
}
catch (err) {
if (!task.ignoreError) {
throw err;
}
}
if (after) {
log(`>> #${taskName} [after]`);
await runUnknown(ctx, after, null, undefined, workingDir);
}
}
async function runTaskByName(ctx, nameWithPrefix, workingDir) {
const name = nameWithPrefix.substring(1);
if (!name) {
throw new Error('"#" is not a valid task name');
}
const task = getTask(ctx.config, name, true);
log(`>> ${nameWithPrefix}`);
return runTask(ctx, task, undefined, name, workingDir);
}
async function runUnknown(ctx, value, task, args, workingDir) {
if (!value) {
throw new Error(`Empty run field ${JSON.stringify(value)}`);
}
if (typeof value === 'string') {
return runString(ctx, value, args, workingDir);
}
if (Array.isArray(value)) {
await pMap(value, (childVal) => runUnknown(ctx, childVal, null, undefined, workingDir), {
concurrency: task?.parallel ? undefined : 1,
stopOnError: task?.continueOnChildError !== undefined
? !task.continueOnChildError
: true,
});
return Promise.resolve();
}
if (typeof value === 'object') {
return runBTCommands(value, workingDir);
}
throw new Error(`Invalid run field ${JSON.stringify(value)}`);
}
(async () => {
try {
verboseLog?.(`CWD: ${process.cwd()}`);
verboseLog?.(`Loading config "${cmd.configFile}"`);
const config = await loadConfig(cmd.configFile);
const { settings } = config;
verboseLog?.(`Loaded config file at "${np.resolve(config.path)}"
${JSON.stringify(config)}
`);
if (settings.defaultEnv) {
log(`Loaded default environment variables: ${inspect(settings.defaultEnv, {
compact: false,
sorted: true,
})}`);
}
const task = getTask(config, cmd.taskName, cmd.private ?? false);
log(`>> #${cmd.taskName}`);
const ctx = {
config,
inheritedEnv: {},
};
await runTask(ctx, task, cmd.taskArgs.length ? cmd.taskArgs : undefined, cmd.taskName, undefined);
}
catch (err) {
processError(err);
}
})();
//# sourceMappingURL=main.js.map