更新
This commit is contained in:
+95
@@ -0,0 +1,95 @@
|
||||
const {spawn} = require('cross-spawn')
|
||||
const commandConvert = require('./command')
|
||||
const varValueConvert = require('./variable')
|
||||
|
||||
module.exports = crossEnv
|
||||
|
||||
const envSetterRegex = /(\w+)=('(.*)'|"(.*)"|(.*))/
|
||||
|
||||
function crossEnv(args, options = {}) {
|
||||
const [envSetters, command, commandArgs] = parseCommand(args)
|
||||
const env = getEnvVars(envSetters)
|
||||
if (command) {
|
||||
const proc = spawn(
|
||||
// run `path.normalize` for command(on windows)
|
||||
commandConvert(command, env, true),
|
||||
// by default normalize is `false`, so not run for cmd args
|
||||
commandArgs.map(arg => commandConvert(arg, env)),
|
||||
{
|
||||
stdio: 'inherit',
|
||||
shell: options.shell,
|
||||
env,
|
||||
},
|
||||
)
|
||||
process.on('SIGTERM', () => proc.kill('SIGTERM'))
|
||||
process.on('SIGINT', () => proc.kill('SIGINT'))
|
||||
process.on('SIGBREAK', () => proc.kill('SIGBREAK'))
|
||||
process.on('SIGHUP', () => proc.kill('SIGHUP'))
|
||||
proc.on('exit', (code, signal) => {
|
||||
let crossEnvExitCode = code
|
||||
// exit code could be null when OS kills the process(out of memory, etc) or due to node handling it
|
||||
// but if the signal is SIGINT the user exited the process so we want exit code 0
|
||||
if (crossEnvExitCode === null) {
|
||||
crossEnvExitCode = signal === 'SIGINT' ? 0 : 1
|
||||
}
|
||||
process.exit(crossEnvExitCode) //eslint-disable-line no-process-exit
|
||||
})
|
||||
return proc
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
function parseCommand(args) {
|
||||
const envSetters = {}
|
||||
let command = null
|
||||
let commandArgs = []
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
const match = envSetterRegex.exec(args[i])
|
||||
if (match) {
|
||||
let value
|
||||
|
||||
if (typeof match[3] !== 'undefined') {
|
||||
value = match[3]
|
||||
} else if (typeof match[4] === 'undefined') {
|
||||
value = match[5]
|
||||
} else {
|
||||
value = match[4]
|
||||
}
|
||||
|
||||
envSetters[match[1]] = value
|
||||
} else {
|
||||
// No more env setters, the rest of the line must be the command and args
|
||||
let cStart = []
|
||||
cStart = args
|
||||
.slice(i)
|
||||
// Regex:
|
||||
// match "\'" or "'"
|
||||
// or match "\" if followed by [$"\] (lookahead)
|
||||
.map(a => {
|
||||
const re = /\\\\|(\\)?'|([\\])(?=[$"\\])/g
|
||||
// Eliminate all matches except for "\'" => "'"
|
||||
return a.replace(re, m => {
|
||||
if (m === '\\\\') return '\\'
|
||||
if (m === "\\'") return "'"
|
||||
return ''
|
||||
})
|
||||
})
|
||||
command = cStart[0]
|
||||
commandArgs = cStart.slice(1)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return [envSetters, command, commandArgs]
|
||||
}
|
||||
|
||||
function getEnvVars(envSetters) {
|
||||
const envVars = {...process.env}
|
||||
if (process.env.APPDATA) {
|
||||
envVars.APPDATA = process.env.APPDATA
|
||||
}
|
||||
Object.keys(envSetters).forEach(varName => {
|
||||
envVars[varName] = varValueConvert(envSetters[varName], varName)
|
||||
})
|
||||
return envVars
|
||||
}
|
||||
Reference in New Issue
Block a user