run-script-pkg.js 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  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. // you wouldn't like me when I'm angry...
  8. const bruce = (id, event, cmd) =>
  9. `\n> ${id ? id + ' ' : ''}${event}\n> ${cmd.trim().replace(/\n/g, '\n> ')}\n`
  10. const runScriptPkg = async options => {
  11. const {
  12. event,
  13. path,
  14. scriptShell,
  15. env = {},
  16. stdio = 'pipe',
  17. pkg,
  18. args = [],
  19. stdioString = false,
  20. // note: only used when stdio:inherit
  21. banner = true,
  22. // how long to wait for a process.kill signal
  23. // only exposed here so that we can make the test go a bit faster.
  24. signalTimeout = 500,
  25. } = options
  26. const {scripts = {}, gypfile} = pkg
  27. let cmd = null
  28. if (options.cmd)
  29. cmd = options.cmd
  30. else if (pkg.scripts && pkg.scripts[event])
  31. cmd = pkg.scripts[event] + args.map(a => ` ${JSON.stringify(a)}`).join('')
  32. else if ( // If there is no preinstall or install script, default to rebuilding node-gyp packages.
  33. event === 'install' &&
  34. !scripts.install &&
  35. !scripts.preinstall &&
  36. gypfile !== false &&
  37. await isNodeGypPackage(path)
  38. )
  39. cmd = defaultGypInstallScript
  40. else if (event === 'start' && await isServerPackage(path))
  41. cmd = 'node server.js' + args.map(a => ` ${JSON.stringify(a)}`).join('')
  42. if (!cmd)
  43. return { code: 0, signal: null }
  44. if (stdio === 'inherit' && banner !== false) {
  45. // we're dumping to the parent's stdout, so print the banner
  46. console.log(bruce(pkg._id, event, cmd))
  47. }
  48. const p = promiseSpawn(...makeSpawnArgs({
  49. event,
  50. path,
  51. scriptShell,
  52. env: packageEnvs(env, pkg),
  53. stdio,
  54. cmd,
  55. stdioString,
  56. }), {
  57. event,
  58. script: cmd,
  59. pkgid: pkg._id,
  60. path,
  61. })
  62. if (stdio === 'inherit')
  63. signalManager.add(p.process)
  64. if (p.stdin)
  65. p.stdin.end()
  66. return p.catch(er => {
  67. const { signal } = er
  68. if (stdio === 'inherit' && signal) {
  69. process.kill(process.pid, signal)
  70. // just in case we don't die, reject after 500ms
  71. // this also keeps the node process open long enough to actually
  72. // get the signal, rather than terminating gracefully.
  73. return new Promise((res, rej) => setTimeout(() => rej(er), signalTimeout))
  74. } else
  75. throw er
  76. })
  77. }
  78. module.exports = runScriptPkg