piral-cli
Version:
The standard CLI for creating and building a Piral instance or a Pilet.
140 lines (128 loc) • 3.69 kB
text/typescript
import { resolve } from 'path';
import { fork, ChildProcess } from 'child_process';
import type { Bundler, BundleDetails, BaseBundleParameters } from '../types';
function getPath(name: string) {
return resolve(__dirname, '..', '..', 'lib', 'build', `run-${name}.js`);
}
type BundleListener = (args: any) => void;
function createBundler(cwd: string, ps: ChildProcess, args: any) {
let promise = Promise.resolve();
let started = false;
const listeners: Array<BundleListener> = [];
const bundle: BundleDetails = {
dir: cwd,
hash: '',
name: '',
};
const setPending = () => {
promise = new Promise((done) => {
const f = () => {
done();
bundler.off(f);
};
bundler.on(f);
});
};
const bundler = {
bundle,
start() {
if (!started) {
started = true;
ps.send({
type: 'bundle',
...args,
});
}
},
stop() {
return new Promise<void>((resolve) => {
ps.on('exit', resolve);
ps.kill();
});
},
on(cb: BundleListener) {
listeners.push(cb);
},
off(cb: BundleListener) {
listeners.splice(listeners.indexOf(cb), 1);
},
emit(args: any) {
[...listeners].forEach((cb) => cb(args));
},
ready() {
return promise;
},
setPending,
};
setPending();
return bundler;
}
export function callDynamic<T extends BaseBundleParameters>(name: string, path: string, args: T, exec?: string) {
const cwd = args.root;
return new Promise<Bundler>((resolve, reject) => {
const ps = fork(getPath(name), [], { cwd, stdio: 'pipe', env: process.env, execPath: exec });
const bundler = createBundler(cwd, ps, args);
const setup = {
type: 'init',
path,
};
const start = () => {
ps.send({
type: 'start',
...args,
});
};
ps.stderr && process.stderr && ps.stderr.pipe(process.stderr, { end: false });
ps.stdout && process.stdout && ps.stdout.pipe(process.stdout, { end: false });
ps.stdin && process.stdin && ps.stdin.pipe(process.stdin, { end: false });
ps.on('message', (msg: any) => {
switch (msg.type) {
case 'pending':
bundler.setPending();
break;
case 'update':
bundler.bundle.hash = msg.outHash;
bundler.bundle.name = msg.outName;
bundler.emit(msg.args);
break;
case 'done':
bundler.bundle.dir = msg.outDir;
return resolve(bundler);
case 'fail':
return reject(msg.error);
}
});
ps.send(setup, start);
});
}
export function callStatic<T extends BaseBundleParameters>(name: string, path: string, args: T, exec?: string) {
const cwd = args.root;
return new Promise<Bundler>((resolve, reject) => {
const ps = fork(getPath(name), [], { cwd, stdio: 'pipe', env: process.env, execPath: exec });
const bundler = createBundler(cwd, ps, args);
const setup = {
type: 'init',
path,
};
const start = () => {
ps.send({
type: 'start',
...args,
});
};
ps.stderr && process.stderr && ps.stderr.pipe(process.stderr, { end: false });
ps.stdout && process.stdout && ps.stdout.pipe(process.stdout, { end: false });
ps.stdin && process.stdin && ps.stdin.pipe(process.stdin, { end: false });
ps.on('message', (msg: any) => {
switch (msg.type) {
case 'done':
bundler.bundle.dir = msg.outDir;
bundler.bundle.name = msg.outFile;
return resolve(bundler);
case 'fail':
return reject(msg.error);
}
});
ps.send(setup, start);
});
}