flecks/packages/core/build/commands.js
2024-01-19 03:38:16 -06:00

182 lines
4.9 KiB
JavaScript

const {spawn} = require('child_process');
const {join, normalize} = require('path');
const {Argument, Option, program} = require('commander');
const {glob} = require('glob');
const rimraf = require('rimraf');
const D = require('./debug');
const addFleckToYml = require('./add-fleck-to-yml');
const {
FLECKS_CORE_ROOT = process.cwd(),
} = process.env;
const debug = D('@flecks/core/commands');
const debugSilly = debug.extend('silly');
const flecksRoot = normalize(FLECKS_CORE_ROOT);
exports.commands = (program, flecks) => {
const {packageManager} = flecks.get('@flecks/core');
const commands = {
add: {
args: [
new Argument('<fleck>', 'fleck'),
],
description: 'add a fleck to your application',
action: async (fleck) => {
const args = [];
if ('yarn' === packageManager) {
args.push('yarn', ['add', fleck]);
}
else {
args.push(packageManager, ['install', fleck]);
}
args.push({stdio: 'inherit'});
await module.exports.processCode(spawn(...args));
await addFleckToYml(fleck);
},
},
clean: {
description: 'remove node_modules, lock file, and build artifacts',
action: () => {
rimraf.sync(join(flecksRoot, 'dist'));
rimraf.sync(join(flecksRoot, 'node_modules'));
switch (packageManager) {
case 'yarn':
rimraf.sync(join(flecksRoot, 'yarn.lock'));
break;
case 'bun':
rimraf.sync(join(flecksRoot, 'bun.lockb'));
break;
case 'npm':
rimraf.sync(join(flecksRoot, 'package-lock.json'));
break;
default:
break;
}
},
},
};
const {targets} = flecks;
if (targets.length > 0) {
commands.build = {
args: [
new Argument('[target]', 'build target').choices(targets.map(([, target]) => target)),
],
options: [
['-d, --no-production', 'dev build'],
['-h, --hot', 'build with hot module reloading'],
['-w, --watch', 'watch for changes'],
],
description: 'build a target in your application',
action: async (target, opts) => {
const {
hot,
production,
watch,
} = opts;
debug('Building...', opts);
const webpackConfig = await flecks.resolveBuildConfig('fleckspack.config.js');
const cmd = [
'npx', 'webpack',
'--config', webpackConfig,
'--mode', (production && !hot) ? 'production' : 'development',
...((watch || hot) ? ['--watch'] : []),
];
return module.exports.spawnWith(
cmd,
{
env: {
FLECKS_CORE_IS_PRODUCTION: production,
...(target ? {FLECKS_CORE_BUILD_LIST: target} : {}),
...(hot ? {FLECKS_ENV__flecks_server__hot: 'true'} : {}),
},
},
);
},
};
}
commands.lint = {
description: 'run linter',
args: [],
action: async () => {
const promises = [];
const packages = await glob(join('packages', '*'));
if (0 === packages.length) {
packages.push('.');
}
await Promise.all(
packages
.map((pkg) => join(process.cwd(), pkg))
.map(async (cwd) => {
const cmd = [
'npx', 'eslint',
'--config', await flecks.resolveBuildConfig('eslint.config.js'),
'.',
];
promises.push(new Promise((resolve, reject) => {
const child = module.exports.spawnWith(
cmd,
{
cwd,
},
);
child.on('error', reject);
child.on('exit', (code) => {
child.off('error', reject);
resolve(code);
});
}));
}),
);
const promise = Promise.all(promises)
.then(
(codes) => (
codes.every((code) => 0 === parseInt(code, 10))
? 0
: codes.find((code) => code !== 0)
),
);
return {
off: () => {},
on: (type, fn) => {
if ('error' === type) {
promise.catch(fn);
}
else if ('exit' === type) {
promise.then(fn);
}
},
};
},
};
return commands;
};
exports.processCode = (child) => new Promise((resolve, reject) => {
child.on('error', reject);
child.on('exit', (code) => {
child.off('error', reject);
resolve(code);
});
});
exports.spawnWith = (cmd, opts = {}) => {
debug("spawning: '%s'", cmd.join(' '));
debugSilly('with options: %O', opts);
const child = spawn(cmd[0], cmd.slice(1), {
stdio: 'inherit',
...opts,
env: {
...process.env,
...opts.env,
},
});
return child;
};
exports.Argument = Argument;
exports.Option = Option;
exports.program = program;