flecks/packages/core/src/cli.js
2022-02-26 11:47:21 -06:00

110 lines
3.0 KiB
JavaScript
Executable File

import {fork} from 'child_process';
import {join, resolve, sep} from 'path';
import {Command} from 'commander';
import D from 'debug';
import Flecks from './server/flecks';
const {
FLECKS_ROOT = process.cwd(),
} = process.env;
const debug = D('@flecks/core/cli');
// Guarantee local node_modules path.
const defaultNodeModules = resolve(join(FLECKS_ROOT, 'node_modules'));
const nodePathSeparator = '/' === sep ? ':' : ';';
let updatedNodePath;
if (!process.env.NODE_PATH) {
updatedNodePath = defaultNodeModules;
}
else {
const parts = process.env.NODE_PATH.split(nodePathSeparator);
if (!parts.some((part) => resolve(part) === defaultNodeModules)) {
parts.push(defaultNodeModules);
updatedNodePath = parts.join(nodePathSeparator);
}
}
// Guarantee symlink preservation for linked flecks.
const updateSymlinkPreservation = !process.env.NODE_PRESERVE_SYMLINKS;
const environmentUpdates = (
updatedNodePath
|| updateSymlinkPreservation
)
? {
NODE_PATH: updatedNodePath,
NODE_PRESERVE_SYMLINKS: 1,
}
: undefined;
if (environmentUpdates) {
debug('updating environment, forking with %O...', environmentUpdates);
const forkOptions = {
env: {
...process.env,
...environmentUpdates,
DEBUG_COLORS: 'true',
},
stdio: 'inherit',
};
fork(__filename, process.argv.slice(2), forkOptions)
.on('exit', (code, signal) => {
process.exitCode = code;
process.exit(signal);
});
}
else {
// Asynchronous command process code forwarding.
const forwardProcessCode = (fn) => async (...args) => {
const child = await fn(...args);
if ('object' !== typeof child) {
debug('action returned code %d', child);
process.exitCode = child;
return;
}
const reject = (error) => {
// eslint-disable-next-line no-console
console.error(error);
process.exitCode = child.exitCode || 1;
};
child.on('error', reject);
child.on('exit', (code) => {
child.off('error', reject);
debug('action exited with code %d', code);
process.exitCode = code;
});
};
// Initialize Commander.
const program = new Command();
program.enablePositionalOptions();
// Bootstrap.
debug('bootstrapping flecks...');
const flecks = Flecks.bootstrap();
debug('bootstrapped');
// Register commands.
const commands = flecks.invokeReduce('@flecks/core/commands', undefined, undefined, program);
const keys = Object.keys(commands);
for (let i = 0; i < keys.length; ++i) {
const {
action,
args = [],
description,
name = keys[i],
options = [],
} = commands[keys[i]];
debug('adding command %s...', name);
const cmd = program.command(name);
cmd.description(description);
for (let i = 0; i < args.length; ++i) {
cmd.addArgument(args[i]);
}
for (let i = 0; i < options.length; ++i) {
cmd.option(...options[i]);
}
cmd.action(forwardProcessCode(action));
}
// Parse commandline.
program.parse(process.argv);
}