import Fault from 'jsc-classes/fault'; import { objectAssign } from 'jsc-utils/object'; import path from 'path'; import shelljs, { ExecOptions, ShellString } from 'shelljs'; import { isFile, isFolder } from './file-system'; import { mergeProcessEnvPath } from './process'; /**************************************************************** * 重写 which,使之可以在 node 项目下自动寻找 node_modules/.bin 下的命令 ****************************************************************/ const shellWhich = shelljs.which; const which = (command: string, cwd = process.cwd()): string | null => { const exits = shellWhich(command); if (exits) return exits.toString(); const localBinFile = path.join(cwd, 'node_modules/.bin', command); return isFile(localBinFile) ? localBinFile : null; }; /**************************************************************** * 重写 exec,使之可以在 node 项目下自动寻找 node_modules/.bin 下的命令 ****************************************************************/ const shellExec = shelljs.exec; interface _ShellExecOptions { // 默认 197 errorExitCode: number; cwd: string; } // 排除了 fatal 选项,强制为 true // 排除了 async 选项,强制为 false export type ShellExecOptions = _ShellExecOptions & Omit & { env: NodeJS.ProcessEnv; }; export interface ShellExecFault { command: string; code: number; exitCode: number; stdout: string; stderr: string; options: ShellExecOptions; message: string; } export type ShellExecString = ShellString & { command: string; options: ShellExecOptions; }; const exec = (command: string, options?: Partial): ShellExecString => { const defaults: ShellExecOptions = { errorExitCode: 197, cwd: process.cwd(), env: {} }; const finalOptions = objectAssign(defaults, options, { async: false, fatal: true }); const { cwd, env } = finalOptions; const npmBinDir = path.join(cwd, 'node_modules/.bin'); finalOptions.env = mergeProcessEnvPath(npmBinDir, env); shelljs.config.fatal = false; const result = shellExec(command, finalOptions); const finalResult = Object.assign(result, { command, options: finalOptions }); const { code, stdout, stderr } = result; if (code === 0) return finalResult; throw Fault.create({ command, code, stdout, stderr, exitCode: code, options: finalOptions, message: stderr.trim() || `[exit ${code}] ${command}` }); }; const shell = { ...shelljs, which, exec }; export default shell;