$
This commit is contained in:
32
node_modules/spawn-wrap/lib/debug.js
generated
vendored
Normal file
32
node_modules/spawn-wrap/lib/debug.js
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
'use strict';
|
||||
|
||||
const util = require('util');
|
||||
const fs = require('fs')
|
||||
|
||||
/**
|
||||
* Boolean indicating if debug mode is enabled.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
const IS_DEBUG = process.env.SPAWN_WRAP_DEBUG === '1'
|
||||
|
||||
/**
|
||||
* If debug is enabled, write message to stderr.
|
||||
*
|
||||
* If debug is disabled, no message is written.
|
||||
*/
|
||||
function debug(...args) {
|
||||
if (!IS_DEBUG) {
|
||||
return;
|
||||
}
|
||||
|
||||
const prefix = `SW ${process.pid}: `
|
||||
const data = util.format(...args).trim()
|
||||
const message = data.split('\n').map(line => `${prefix}${line}\n`).join('')
|
||||
fs.writeSync(2, message)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
IS_DEBUG,
|
||||
debug,
|
||||
}
|
53
node_modules/spawn-wrap/lib/exe-type.js
generated
vendored
Normal file
53
node_modules/spawn-wrap/lib/exe-type.js
generated
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
'use strict';
|
||||
|
||||
const isWindows = require("is-windows")
|
||||
const path = require("path")
|
||||
|
||||
function isCmd(file) {
|
||||
const comspec = path.basename(process.env.comspec || '').replace(/\.exe$/i, '')
|
||||
return isWindows() && (file === comspec || /^cmd(?:\.exe)?$/i.test(file))
|
||||
}
|
||||
|
||||
function isNode(file) {
|
||||
const cmdname = path.basename(process.execPath).replace(/\.exe$/i, '')
|
||||
return file === 'node' || cmdname === file
|
||||
}
|
||||
|
||||
function isNpm(file) {
|
||||
// XXX is this even possible/necessary?
|
||||
// wouldn't npm just be detected as a node shebang?
|
||||
return file === 'npm' && !isWindows()
|
||||
}
|
||||
|
||||
function isSh(file) {
|
||||
return ['dash', 'sh', 'bash', 'zsh'].includes(file)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the basename of the executable.
|
||||
*
|
||||
* On Windows, strips the `.exe` extension (if any) and normalizes the name to
|
||||
* lowercase.
|
||||
*
|
||||
* @param exePath {string} Path of the executable as passed to spawned processes:
|
||||
* either command or a path to a file.
|
||||
* @return {string} Basename of the executable.
|
||||
*/
|
||||
function getExeBasename(exePath) {
|
||||
const baseName = path.basename(exePath);
|
||||
if (isWindows()) {
|
||||
// Stripping `.exe` seems to be enough for our usage. We may eventually
|
||||
// want to handle all executable extensions (such as `.bat` or `.cmd`).
|
||||
return baseName.replace(/\.exe$/i, "").toLowerCase();
|
||||
} else {
|
||||
return baseName;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
isCmd,
|
||||
isNode,
|
||||
isNpm,
|
||||
isSh,
|
||||
getExeBasename,
|
||||
}
|
5
node_modules/spawn-wrap/lib/homedir.js
generated
vendored
Normal file
5
node_modules/spawn-wrap/lib/homedir.js
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
'use strict'
|
||||
|
||||
const os = require('os')
|
||||
|
||||
module.exports = process.env.SPAWN_WRAP_SHIM_ROOT || os.homedir()
|
84
node_modules/spawn-wrap/lib/munge.js
generated
vendored
Normal file
84
node_modules/spawn-wrap/lib/munge.js
generated
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
'use strict';
|
||||
|
||||
const {isCmd, isNode, isNpm, isSh, getExeBasename} = require("./exe-type")
|
||||
const mungeCmd = require("./mungers/cmd")
|
||||
const mungeEnv = require("./mungers/env")
|
||||
const mungeNode = require("./mungers/node")
|
||||
const mungeNpm = require("./mungers/npm")
|
||||
const mungeSh = require("./mungers/sh")
|
||||
const mungeShebang = require("./mungers/shebang")
|
||||
|
||||
/**
|
||||
* @typedef {object} InternalSpawnOptions Options for the internal spawn functions
|
||||
* `childProcess.ChildProcess.prototype.spawn` and `process.binding('spawn_sync').spawn`.
|
||||
* These are the options mapped by the `munge` function to intercept spawned processes and
|
||||
* handle the wrapping logic.
|
||||
*
|
||||
* @property {string} file File to execute: either an absolute system-dependent path or a
|
||||
* command name.
|
||||
* @property {string[]} args Command line arguments passed to the spawn process, including argv0.
|
||||
* @property {string | undefined} cwd Optional path to the current working directory passed to the
|
||||
* spawned process. Default: `process.cwd()`
|
||||
* @property {boolean} windowsHide Boolean controlling if the process should be spawned as
|
||||
* hidden (no GUI) on Windows.
|
||||
* @property {boolean} windowsVerbatimArguments Boolean controlling if Node should preprocess
|
||||
* the CLI arguments on Windows.
|
||||
* @property {boolean} detached Boolean controlling if the child process should keep its parent
|
||||
* alive or not.
|
||||
* @property {string[]} envPairs Array of serialized environment variable key/value pairs. The
|
||||
* variables serialized as `key + "=" + value`.
|
||||
* @property {import("child_process").StdioOptions} stdio Stdio options, with the same semantics
|
||||
* as the `stdio` parameter from the public API.
|
||||
* @property {number | undefined} uid User id for the spawn process, same as the `uid` parameter
|
||||
* from the public API.
|
||||
* @property {number | undefined} gid Group id for the spawn process, same as the `gid` parameter
|
||||
* from the public API.
|
||||
*
|
||||
* @property {string | undefined} originalNode Custom property only used by `spawn-wrap`. It is
|
||||
* used to remember the original Node executable that was intended to be spawned by the user.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Returns updated internal spawn options to redirect the process through the shim and wrapper.
|
||||
*
|
||||
* This works on the options passed to `childProcess.ChildProcess.prototype.spawn` and
|
||||
* `process.binding('spawn_sync').spawn`.
|
||||
*
|
||||
* This function works by trying to identify the spawn process and map the options accordingly.
|
||||
* `spawn-wrap` recognizes most shells, Windows `cmd.exe`, Node and npm invocations; when spawn
|
||||
* either directly or through a script with a shebang line.
|
||||
* It also unconditionally updates the environment variables so bare `node` commands execute
|
||||
* the shim script instead of Node's binary.
|
||||
*
|
||||
* @param workingDir {string} Absolute system-dependent path to the directory containing the shim files.
|
||||
* @param options {InternalSpawnOptions} Original internal spawn options.
|
||||
* @return {InternalSpawnOptions} Updated internal spawn options.
|
||||
*/
|
||||
function munge(workingDir, options) {
|
||||
const basename = getExeBasename(options.file);
|
||||
|
||||
// XXX: dry this
|
||||
if (isSh(basename)) {
|
||||
options = mungeSh(workingDir, options)
|
||||
} else if (isCmd(basename)) {
|
||||
options = mungeCmd(workingDir, options)
|
||||
} else if (isNode(basename)) {
|
||||
options = mungeNode(workingDir, options)
|
||||
} else if (isNpm(basename)) {
|
||||
// XXX unnecessary? on non-windows, npm is just another shebang
|
||||
options = mungeNpm(workingDir, options)
|
||||
} else {
|
||||
options = mungeShebang(workingDir, options)
|
||||
}
|
||||
|
||||
// now the options are munged into shape.
|
||||
// whether we changed something or not, we still update the PATH
|
||||
// so that if a script somewhere calls `node foo`, it gets our
|
||||
// wrapper instead.
|
||||
|
||||
options = mungeEnv(workingDir, options)
|
||||
|
||||
return options
|
||||
}
|
||||
|
||||
module.exports = munge
|
59
node_modules/spawn-wrap/lib/mungers/cmd.js
generated
vendored
Normal file
59
node_modules/spawn-wrap/lib/mungers/cmd.js
generated
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
'use strict';
|
||||
|
||||
const path = require("path")
|
||||
const whichOrUndefined = require("../which-or-undefined")
|
||||
|
||||
/**
|
||||
* Intercepts Node and npm processes spawned through Windows' `cmd.exe`.
|
||||
*
|
||||
* @param workingDir {string} Absolute system-dependent path to the directory containing the shim files.
|
||||
* @param options {import("../munge").InternalSpawnOptions} Original internal spawn options.
|
||||
* @return {import("../munge").InternalSpawnOptions} Updated internal spawn options.
|
||||
*/
|
||||
function mungeCmd(workingDir, options) {
|
||||
const cmdi = options.args.indexOf('/c')
|
||||
if (cmdi === -1) {
|
||||
return {...options}
|
||||
}
|
||||
|
||||
const re = /^\s*("*)([^"]*?\bnode(?:\.exe|\.EXE)?)("*)( .*)?$/
|
||||
const npmre = /^\s*("*)([^"]*?\b(?:npm))("*)( |$)/
|
||||
|
||||
const command = options.args[cmdi + 1]
|
||||
if (command === undefined) {
|
||||
return {...options}
|
||||
}
|
||||
|
||||
let newArgs = [...options.args];
|
||||
// Remember the original Node command to use it in the shim
|
||||
let originalNode;
|
||||
|
||||
let m = command.match(re)
|
||||
let replace
|
||||
if (m) {
|
||||
originalNode = m[2]
|
||||
// TODO: Remove `replace`: seems unused
|
||||
replace = m[1] + path.join(workingDir, 'node.cmd') + m[3] + m[4]
|
||||
newArgs[cmdi + 1] = m[1] + m[2] + m[3] +
|
||||
' "' + path.join(workingDir, 'node') + '"' + m[4]
|
||||
} else {
|
||||
// XXX probably not a good idea to rewrite to the first npm in the
|
||||
// path if it's a full path to npm. And if it's not a full path to
|
||||
// npm, then the dirname will not work properly!
|
||||
m = command.match(npmre)
|
||||
if (m === null) {
|
||||
return {...options}
|
||||
}
|
||||
|
||||
let npmPath = whichOrUndefined('npm') || 'npm'
|
||||
npmPath = path.join(path.dirname(npmPath), 'node_modules', 'npm', 'bin', 'npm-cli.js')
|
||||
replace = m[1] + '"' + path.join(workingDir, 'node.cmd') + '"' +
|
||||
' "' + npmPath + '"' +
|
||||
m[3] + m[4]
|
||||
newArgs[cmdi + 1] = command.replace(npmre, replace)
|
||||
}
|
||||
|
||||
return {...options, args: newArgs, originalNode};
|
||||
}
|
||||
|
||||
module.exports = mungeCmd
|
49
node_modules/spawn-wrap/lib/mungers/env.js
generated
vendored
Normal file
49
node_modules/spawn-wrap/lib/mungers/env.js
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
'use strict';
|
||||
|
||||
const isWindows = require("is-windows")
|
||||
const path = require("path")
|
||||
const homedir = require("../homedir")
|
||||
|
||||
const pathRe = isWindows() ? /^PATH=/i : /^PATH=/;
|
||||
|
||||
/**
|
||||
* Updates the environment variables to intercept `node` commands and pass down options.
|
||||
*
|
||||
* @param workingDir {string} Absolute system-dependent path to the directory containing the shim files.
|
||||
* @param options {import("../munge").InternalSpawnOptions} Original internal spawn options.
|
||||
* @return {import("../munge").InternalSpawnOptions} Updated internal spawn options.
|
||||
*/
|
||||
function mungeEnv(workingDir, options) {
|
||||
let pathEnv
|
||||
|
||||
const envPairs = options.envPairs.map((ep) => {
|
||||
if (pathRe.test(ep)) {
|
||||
// `PATH` env var: prefix its value with `workingDir`
|
||||
// `5` corresponds to the length of `PATH=`
|
||||
pathEnv = ep.substr(5)
|
||||
const k = ep.substr(0, 5)
|
||||
return k + workingDir + path.delimiter + pathEnv
|
||||
} else {
|
||||
// Return as-is
|
||||
return ep;
|
||||
}
|
||||
});
|
||||
|
||||
if (pathEnv === undefined) {
|
||||
envPairs.push((isWindows() ? 'Path=' : 'PATH=') + workingDir)
|
||||
}
|
||||
if (options.originalNode) {
|
||||
const key = path.basename(workingDir).substr('.node-spawn-wrap-'.length)
|
||||
envPairs.push('SW_ORIG_' + key + '=' + options.originalNode)
|
||||
}
|
||||
|
||||
envPairs.push('SPAWN_WRAP_SHIM_ROOT=' + homedir)
|
||||
|
||||
if (process.env.SPAWN_WRAP_DEBUG === '1') {
|
||||
envPairs.push('SPAWN_WRAP_DEBUG=1')
|
||||
}
|
||||
|
||||
return {...options, envPairs};
|
||||
}
|
||||
|
||||
module.exports = mungeEnv
|
79
node_modules/spawn-wrap/lib/mungers/node.js
generated
vendored
Normal file
79
node_modules/spawn-wrap/lib/mungers/node.js
generated
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
'use strict';
|
||||
|
||||
const path = require('path')
|
||||
const {debug} = require("../debug")
|
||||
const {getExeBasename} = require("../exe-type")
|
||||
const whichOrUndefined = require("../which-or-undefined")
|
||||
|
||||
/**
|
||||
* Intercepts Node spawned processes.
|
||||
*
|
||||
* @param workingDir {string} Absolute system-dependent path to the directory containing the shim files.
|
||||
* @param options {import("../munge").InternalSpawnOptions} Original internal spawn options.
|
||||
* @return {import("../munge").InternalSpawnOptions} Updated internal spawn options.
|
||||
*/
|
||||
function mungeNode(workingDir, options) {
|
||||
// Remember the original Node command to use it in the shim
|
||||
const originalNode = options.file
|
||||
|
||||
const command = getExeBasename(options.file)
|
||||
// make sure it has a main script.
|
||||
// otherwise, just let it through.
|
||||
let a = 0
|
||||
let hasMain = false
|
||||
let mainIndex = 1
|
||||
for (a = 1; !hasMain && a < options.args.length; a++) {
|
||||
switch (options.args[a]) {
|
||||
case '-p':
|
||||
case '-i':
|
||||
case '--interactive':
|
||||
case '--eval':
|
||||
case '-e':
|
||||
case '-pe':
|
||||
hasMain = false
|
||||
a = options.args.length
|
||||
continue
|
||||
|
||||
case '-r':
|
||||
case '--require':
|
||||
a += 1
|
||||
continue
|
||||
|
||||
default:
|
||||
// TODO: Double-check this part
|
||||
if (options.args[a].startsWith('-')) {
|
||||
continue
|
||||
} else {
|
||||
hasMain = true
|
||||
mainIndex = a
|
||||
a = options.args.length
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const newArgs = [...options.args];
|
||||
let newFile = options.file;
|
||||
|
||||
if (hasMain) {
|
||||
const replace = path.join(workingDir, command)
|
||||
newArgs.splice(mainIndex, 0, replace)
|
||||
}
|
||||
|
||||
// If the file is just something like 'node' then that'll
|
||||
// resolve to our shim, and so to prevent double-shimming, we need
|
||||
// to resolve that here first.
|
||||
// This also handles the case where there's not a main file, like
|
||||
// `node -e 'program'`, where we want to avoid the shim entirely.
|
||||
if (options.file === command) {
|
||||
const realNode = whichOrUndefined(options.file) || process.execPath
|
||||
newArgs[0] = realNode
|
||||
newFile = realNode
|
||||
}
|
||||
|
||||
debug('mungeNode after', options.file, options.args)
|
||||
|
||||
return {...options, file: newFile, args: newArgs, originalNode};
|
||||
}
|
||||
|
||||
module.exports = mungeNode
|
32
node_modules/spawn-wrap/lib/mungers/npm.js
generated
vendored
Normal file
32
node_modules/spawn-wrap/lib/mungers/npm.js
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
'use strict';
|
||||
|
||||
const path = require("path")
|
||||
const {debug} = require("../debug")
|
||||
const whichOrUndefined = require("../which-or-undefined")
|
||||
|
||||
/**
|
||||
* Intercepts npm spawned processes.
|
||||
*
|
||||
* @param workingDir {string} Absolute system-dependent path to the directory containing the shim files.
|
||||
* @param options {import("../munge").InternalSpawnOptions} Original internal spawn options.
|
||||
* @return {import("../munge").InternalSpawnOptions} Updated internal spawn options.
|
||||
*/
|
||||
function mungeNpm(workingDir, options) {
|
||||
debug('munge npm')
|
||||
// XXX weird effects of replacing a specific npm with a global one
|
||||
const npmPath = whichOrUndefined('npm')
|
||||
|
||||
if (npmPath === undefined) {
|
||||
return {...options};
|
||||
}
|
||||
|
||||
const newArgs = [...options.args]
|
||||
|
||||
newArgs[0] = npmPath
|
||||
const file = path.join(workingDir, 'node')
|
||||
newArgs.unshift(file)
|
||||
|
||||
return {...options, file, args: newArgs}
|
||||
}
|
||||
|
||||
module.exports = mungeNpm
|
61
node_modules/spawn-wrap/lib/mungers/sh.js
generated
vendored
Normal file
61
node_modules/spawn-wrap/lib/mungers/sh.js
generated
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
'use strict';
|
||||
|
||||
const isWindows = require("is-windows")
|
||||
const path = require("path")
|
||||
const {debug} = require("../debug")
|
||||
const {isNode} = require("../exe-type")
|
||||
const whichOrUndefined = require("../which-or-undefined")
|
||||
|
||||
/**
|
||||
* Intercepts Node and npm processes spawned through a Linux shell.
|
||||
*
|
||||
* @param workingDir {string} Absolute system-dependent path to the directory containing the shim files.
|
||||
* @param options {import("../munge").InternalSpawnOptions} Original internal spawn options.
|
||||
* @return {import("../munge").InternalSpawnOptions} Updated internal spawn options.
|
||||
*/
|
||||
function mungeSh(workingDir, options) {
|
||||
const cmdi = options.args.indexOf('-c')
|
||||
if (cmdi === -1) {
|
||||
return {...options} // no -c argument
|
||||
}
|
||||
|
||||
let c = options.args[cmdi + 1]
|
||||
const re = /^\s*((?:[^\= ]*\=[^\=\s]*)*[\s]*)([^\s]+|"[^"]+"|'[^']+')( .*)?$/
|
||||
const match = c.match(re)
|
||||
if (match === null) {
|
||||
return {...options} // not a command invocation. weird but possible
|
||||
}
|
||||
|
||||
let command = match[2]
|
||||
// strip quotes off the command
|
||||
const quote = command.charAt(0)
|
||||
if ((quote === '"' || quote === '\'') && command.endsWith(quote)) {
|
||||
command = command.slice(1, -1)
|
||||
}
|
||||
const exe = path.basename(command)
|
||||
|
||||
let newArgs = [...options.args];
|
||||
// Remember the original Node command to use it in the shim
|
||||
let originalNode;
|
||||
const workingNode = path.join(workingDir, 'node')
|
||||
|
||||
if (isNode(exe)) {
|
||||
originalNode = command
|
||||
c = `${match[1]}${match[2]} "${workingNode}" ${match[3]}`
|
||||
newArgs[cmdi + 1] = c
|
||||
} else if (exe === 'npm' && !isWindows()) {
|
||||
// XXX this will exhibit weird behavior when using /path/to/npm,
|
||||
// if some other npm is first in the path.
|
||||
const npmPath = whichOrUndefined('npm')
|
||||
|
||||
if (npmPath) {
|
||||
c = c.replace(re, `$1 "${workingNode}" "${npmPath}" $3`)
|
||||
newArgs[cmdi + 1] = c
|
||||
debug('npm munge!', c)
|
||||
}
|
||||
}
|
||||
|
||||
return {...options, args: newArgs, originalNode};
|
||||
}
|
||||
|
||||
module.exports = mungeSh
|
43
node_modules/spawn-wrap/lib/mungers/shebang.js
generated
vendored
Normal file
43
node_modules/spawn-wrap/lib/mungers/shebang.js
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
'use strict';
|
||||
|
||||
const fs = require("fs")
|
||||
const path = require("path")
|
||||
const {isNode} = require("../exe-type")
|
||||
const whichOrUndefined = require("../which-or-undefined")
|
||||
|
||||
/**
|
||||
* Intercepts processes spawned through a script with a shebang line.
|
||||
*
|
||||
* @param workingDir {string} Absolute system-dependent path to the directory containing the shim files.
|
||||
* @param options {import("../munge").InternalSpawnOptions} Original internal spawn options.
|
||||
* @return {import("../munge").InternalSpawnOptions} Updated internal spawn options.
|
||||
*/
|
||||
function mungeShebang(workingDir, options) {
|
||||
const resolved = whichOrUndefined(options.file)
|
||||
if (resolved === undefined) {
|
||||
return {...options}
|
||||
}
|
||||
|
||||
const shebang = fs.readFileSync(resolved, 'utf8')
|
||||
const match = shebang.match(/^#!([^\r\n]+)/)
|
||||
if (!match) {
|
||||
return {...options} // not a shebang script, probably a binary
|
||||
}
|
||||
|
||||
const shebangbin = match[1].split(' ')[0]
|
||||
const maybeNode = path.basename(shebangbin)
|
||||
if (!isNode(maybeNode)) {
|
||||
return {...options} // not a node shebang, leave untouched
|
||||
}
|
||||
|
||||
const originalNode = shebangbin
|
||||
const file = shebangbin
|
||||
const args = [shebangbin, path.join(workingDir, maybeNode)]
|
||||
.concat(resolved)
|
||||
.concat(match[1].split(' ').slice(1))
|
||||
.concat(options.args.slice(1))
|
||||
|
||||
return {...options, file, args, originalNode};
|
||||
}
|
||||
|
||||
module.exports = mungeShebang
|
12
node_modules/spawn-wrap/lib/which-or-undefined.js
generated
vendored
Normal file
12
node_modules/spawn-wrap/lib/which-or-undefined.js
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
'use strict';
|
||||
|
||||
const which = require("which")
|
||||
|
||||
function whichOrUndefined(executable) {
|
||||
try {
|
||||
return which.sync(executable)
|
||||
} catch (error) {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = whichOrUndefined
|
Reference in New Issue
Block a user