|
| 1 | +const makeSpawnArgs = require('./make-spawn-args.js') |
| 2 | +const promiseSpawn = require('@npmcli/promise-spawn') |
| 3 | +const packageEnvs = require('./package-envs.js') |
| 4 | +const { isNodeGypPackage, defaultGypInstallScript } = require('@npmcli/node-gyp') |
| 5 | +const signalManager = require('./signal-manager.js') |
| 6 | +const isServerPackage = require('./is-server-package.js') |
| 7 | + |
| 8 | +// you wouldn't like me when I'm angry... |
| 9 | +const bruce = (id, event, cmd, args) => { |
| 10 | + let banner = id |
| 11 | + ? `\n> ${id} ${event}\n` |
| 12 | + : `\n> ${event}\n` |
| 13 | + banner += `> ${cmd.trim().replace(/\n/g, '\n> ')}` |
| 14 | + if (args.length) { |
| 15 | + banner += ` ${args.join(' ')}` |
| 16 | + } |
| 17 | + banner += '\n' |
| 18 | + return banner |
| 19 | +} |
| 20 | + |
| 21 | +const runScriptPkg = async options => { |
| 22 | + const { |
| 23 | + event, |
| 24 | + path, |
| 25 | + scriptShell, |
| 26 | + binPaths = false, |
| 27 | + env = {}, |
| 28 | + stdio = 'pipe', |
| 29 | + pkg, |
| 30 | + args = [], |
| 31 | + stdioString, |
| 32 | + // note: only used when stdio:inherit |
| 33 | + banner = true, |
| 34 | + // how long to wait for a process.kill signal |
| 35 | + // only exposed here so that we can make the test go a bit faster. |
| 36 | + signalTimeout = 500, |
| 37 | + } = options |
| 38 | + |
| 39 | + const { scripts = {}, gypfile } = pkg |
| 40 | + let cmd = null |
| 41 | + if (options.cmd) { |
| 42 | + cmd = options.cmd |
| 43 | + } else if (pkg.scripts && pkg.scripts[event]) { |
| 44 | + cmd = pkg.scripts[event] |
| 45 | + } else if ( |
| 46 | + // If there is no preinstall or install script, default to rebuilding node-gyp packages. |
| 47 | + event === 'install' && |
| 48 | + !scripts.install && |
| 49 | + !scripts.preinstall && |
| 50 | + gypfile !== false && |
| 51 | + await isNodeGypPackage(path) |
| 52 | + ) { |
| 53 | + cmd = defaultGypInstallScript |
| 54 | + } else if (event === 'start' && await isServerPackage(path)) { |
| 55 | + cmd = 'node server.js' |
| 56 | + } |
| 57 | + |
| 58 | + if (!cmd) { |
| 59 | + return { code: 0, signal: null } |
| 60 | + } |
| 61 | + |
| 62 | + if (stdio === 'inherit' && banner !== false) { |
| 63 | + // we're dumping to the parent's stdout, so print the banner |
| 64 | + console.log(bruce(pkg._id, event, cmd, args)) |
| 65 | + } |
| 66 | + |
| 67 | + const [spawnShell, spawnArgs, spawnOpts] = makeSpawnArgs({ |
| 68 | + event, |
| 69 | + path, |
| 70 | + scriptShell, |
| 71 | + binPaths, |
| 72 | + env: packageEnvs(env, pkg), |
| 73 | + stdio, |
| 74 | + cmd, |
| 75 | + args, |
| 76 | + stdioString, |
| 77 | + }) |
| 78 | + |
| 79 | + const p = promiseSpawn(spawnShell, spawnArgs, spawnOpts, { |
| 80 | + event, |
| 81 | + script: cmd, |
| 82 | + pkgid: pkg._id, |
| 83 | + path, |
| 84 | + }) |
| 85 | + |
| 86 | + if (stdio === 'inherit') { |
| 87 | + signalManager.add(p.process) |
| 88 | + } |
| 89 | + |
| 90 | + if (p.stdin) { |
| 91 | + p.stdin.end() |
| 92 | + } |
| 93 | + |
| 94 | + return p.catch(er => { |
| 95 | + const { signal } = er |
| 96 | + if (stdio === 'inherit' && signal) { |
| 97 | + process.kill(process.pid, signal) |
| 98 | + // just in case we don't die, reject after 500ms |
| 99 | + // this also keeps the node process open long enough to actually |
| 100 | + // get the signal, rather than terminating gracefully. |
| 101 | + return new Promise((res, rej) => setTimeout(() => rej(er), signalTimeout)) |
| 102 | + } else { |
| 103 | + throw er |
| 104 | + } |
| 105 | + }) |
| 106 | +} |
| 107 | + |
| 108 | +module.exports = runScriptPkg |
0 commit comments