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
|
}
|