packages/aws-cdk/lib/commands/init/os.ts (54 lines of code) (raw):

import * as child_process from 'child_process'; import * as chalk from 'chalk'; import { ToolkitError } from '../../../../@aws-cdk/toolkit-lib/lib/api'; import { debug } from '../../logging'; /** * OS helpers * * Shell function which both prints to stdout and collects the output into a * string. */ export async function shell(command: string[]): Promise<string> { const commandLine = renderCommandLine(command); debug(`Executing ${chalk.blue(commandLine)}`); const child = child_process.spawn(command[0], renderArguments(command.slice(1)), { // Need this for Windows where we want .cmd and .bat to be found as well. shell: true, stdio: ['ignore', 'pipe', 'inherit'], }); return new Promise<string>((resolve, reject) => { const stdout = new Array<any>(); // Both write to stdout and collect child.stdout.on('data', chunk => { process.stdout.write(chunk); stdout.push(chunk); }); child.once('error', reject); child.once('exit', code => { if (code === 0) { resolve(Buffer.from(stdout).toString('utf-8')); } else { reject(new ToolkitError(`${commandLine} exited with error code ${code}`)); } }); }); } function renderCommandLine(cmd: string[]) { return renderArguments(cmd).join(' '); } /** * Render the arguments to include escape characters for each platform. */ function renderArguments(cmd: string[]) { if (process.platform !== 'win32') { return doRender(cmd, hasAnyChars(' ', '\\', '!', '"', "'", '&', '$'), posixEscape); } else { return doRender(cmd, hasAnyChars(' ', '"', '&', '^', '%'), windowsEscape); } } /** * Render a UNIX command line */ function doRender(cmd: string[], needsEscaping: (x: string) => boolean, doEscape: (x: string) => string): string[] { return cmd.map(x => needsEscaping(x) ? doEscape(x) : x); } /** * Return a predicate that checks if a string has any of the indicated chars in it */ function hasAnyChars(...chars: string[]): (x: string) => boolean { return (str: string) => { return chars.some(c => str.indexOf(c) !== -1); }; } /** * Escape a shell argument for POSIX shells * * Wrapping in single quotes and escaping single quotes inside will do it for us. */ function posixEscape(x: string) { // Turn ' -> '"'"' x = x.replace(/'/g, "'\"'\"'"); return `'${x}'`; } /** * Escape a shell argument for cmd.exe * * This is how to do it right, but I'm not following everything: * * https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/ */ function windowsEscape(x: string): string { // First surround by double quotes, ignore the part about backslashes x = `"${x}"`; // Now escape all special characters const shellMeta = new Set<string>(['"', '&', '^', '%']); return x.split('').map(c => shellMeta.has(x) ? '^' + c : c).join(''); }