piral-cli
Version:
The standard CLI for creating and building a Piral instance or a Pilet.
84 lines (67 loc) • 2.63 kB
text/typescript
import { exec } from 'child_process';
import { resolve } from 'path';
import { log } from './log';
import { isWindows } from './info';
import { MemoryStream } from './MemoryStream';
function resolveWinPath(specialFolder: string, subPath: string): string | undefined {
const basePath = process.env[specialFolder];
if (basePath) {
return resolve(basePath, subPath);
}
return undefined;
}
export function runScript(script: string, cwd = process.cwd(), output: NodeJS.WritableStream = process.stdout) {
const bin = resolve(cwd, './node_modules/.bin');
const sep = isWindows ? ';' : ':';
const env = Object.assign({}, process.env);
log('generalDebug_0003', `Running "${script}" in "${cwd}" ("${bin}").`);
if (isWindows) {
// on windows we sometimes may see a strange behavior,
// see https://github.com/smapiot/piral/issues/192
const newPaths = [
resolveWinPath('AppData', 'npm'),
resolveWinPath('ProgramFiles', 'nodejs'),
resolveWinPath('ProgramFiles(x86)', 'nodejs'),
...(env.Path || env.PATH || '').split(';'),
];
env.PATH = newPaths.filter((path) => path && path.length > 0).join(sep);
}
env.PATH = `${bin}${sep}${env.PATH}`;
return new Promise<void>((resolve, reject) => {
const error = new MemoryStream();
const opt = { end: false };
const cp = exec(script, {
cwd,
env,
});
cp.stdout.pipe(output, opt);
cp.stderr.pipe(error, opt);
cp.on('error', () => reject(new Error(error.value)));
cp.on('close', (code) => (code === 0 ? resolve() : reject(new Error(error.value))));
});
}
export function runCommand(exe: string, args: Array<string>, cwd: string, output?: NodeJS.WritableStream) {
const npmCommand = isWindows ? `${exe}.cmd` : exe;
const sanitizedArgs = sanitizeCmdArgs(args);
const cmd = [npmCommand, ...sanitizedArgs].join(' ');
log('generalDebug_0003', `Applying cmd "${cmd}" in directory "${cwd}".`);
return runScript(cmd, cwd, output);
}
function sanitizeCmdArgs(args: Array<string>) {
// Introduced for fixing https://github.com/smapiot/piral/issues/259.
// If an arg contains a whitespace, it can be incorrectly interpreted as two separate arguments.
// For the moment, it's fixed by simply wrapping each arg in OS specific quotation marks.
const quote = isWindows ? '"' : "'";
return args.map((arg) => {
let result = arg.trim();
if (/\s/.test(result)) {
if (!result.startsWith(quote)) {
result = `${quote}${result}`;
}
if (!result.endsWith(quote)) {
result = `${result}${quote}`;
}
}
return result;
});
}