cogs
Version:
The fast file transform pipeline.
137 lines (111 loc) • 3.35 kB
JavaScript
import { readFile } from 'node:fs/promises';
import { dirname } from 'node:path';
import { parseArgs, styleText } from 'node:util';
import formatSize from '#src/format-size.js';
import run from '#src/run.js';
const { console, process, URL } = globalThis;
['SIGTERM', 'SIGINT'].forEach(sig => process.once(sig, () => process.exit()));
const { pathname: thisPath } = new URL(import.meta.url);
const packagePath = `${dirname(thisPath)}/../package.json`;
const { version } = JSON.parse(await readFile(packagePath));
const options = /** @type {const} */ ({
'config-path': {
description: 'Load config from [path]',
type: 'string',
short: 'c',
default: 'cogs.js'
},
help: { description: 'Show usage details', type: 'boolean', short: 'h' },
slient: { description: 'Only output errors', type: 'boolean', short: 's' },
version: { description: 'Show version', type: 'boolean', short: 'v' },
'watch-paths': {
description: 'Rebuild if [path] changes, can be specified multiple times',
type: 'string',
short: 'w',
multiple: true
}
});
/** @param {string} [errorMessage] */
const showHelp = errorMessage => {
const maxKeyLength = Object.keys(options).reduce(
(max, key) => Math.max(key.length, max),
0
);
console[errorMessage ? 'error' : 'log'](
`${errorMessage ? `${errorMessage}\n\n` : ''}The fast file transform pipeline
cogs [options]
${Object.entries(options)
.map(
([key, { short, description }]) =>
`--${key.padEnd(maxKeyLength)} -${short} ${description}`
)
.join('\n')}`
);
process.exit(errorMessage ? 1 : 0);
};
let values;
try {
({ values } = parseArgs({ options, strict: true }));
} catch (er) {
throw showHelp(er.message);
}
if (values.help) throw showHelp();
if (values.version) {
console.log(version);
process.exit();
}
const { 'config-path': configPath, silent, 'watch-paths': watchPaths } = values;
let built;
let failed;
let start;
let totalSize;
let unchanged;
const log = silent ? () => {} : console.log.bind(console);
const onError = er => {
console.error(styleText('red', er.toString()));
if (!watchPaths) process.exit(1);
};
const onStart = () => {
log(styleText('gray', 'Building...'));
built = failed = totalSize = unchanged = 0;
start = Date.now();
};
const onResult = ({ didChange, error, size, sourcePath, targetPath }) => {
if (error) {
++failed;
return console.error(
styleText('red', `Failed to build ${sourcePath}\n${error}`)
);
}
totalSize += size;
if (!didChange) return ++unchanged;
++built;
log(
[
styleText('magenta', `${formatSize(size)}`),
styleText('green', sourcePath),
styleText('gray', '->'),
styleText('blue', targetPath)
].join(' ')
);
};
const onEnd = () => {
log(
[
styleText('magenta', `${formatSize(totalSize)}`),
styleText('green', `${built} built`),
styleText('blue', `${unchanged} unchanged`),
styleText('red', `${failed} failed`),
styleText('yellow', `${((Date.now() - start) / 1000).toFixed(1)}s`)
].join(styleText('gray', ' | '))
);
if (!watchPaths && failed) {
onError(new Error(`${failed} build${failed > 1 ? 's' : ''} failed`));
}
};
try {
await run({ configPath, onEnd, onError, onResult, onStart, watchPaths });
} catch (er) {
onError(er);
}