run-script.js 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. module.exports = runScript
  2. var lifecycle = require('./utils/lifecycle.js')
  3. var npm = require('./npm.js')
  4. var path = require('path')
  5. var readJson = require('read-package-json')
  6. var log = require('npmlog')
  7. var chain = require('slide').chain
  8. var usage = require('./utils/usage')
  9. var output = require('./utils/output.js')
  10. var didYouMean = require('./utils/did-you-mean')
  11. var isWindowsShell = require('./utils/is-windows-shell.js')
  12. runScript.usage = usage(
  13. 'run-script',
  14. 'npm run-script <command> [-- <args>...]'
  15. )
  16. runScript.completion = function (opts, cb) {
  17. // see if there's already a package specified.
  18. var argv = opts.conf.argv.remain
  19. if (argv.length >= 4) return cb()
  20. if (argv.length === 3) {
  21. // either specified a script locally, in which case, done,
  22. // or a package, in which case, complete against its scripts
  23. var json = path.join(npm.localPrefix, 'package.json')
  24. return readJson(json, function (er, d) {
  25. if (er && er.code !== 'ENOENT' && er.code !== 'ENOTDIR') return cb(er)
  26. if (er) d = {}
  27. var scripts = Object.keys(d.scripts || {})
  28. console.error('local scripts', scripts)
  29. if (scripts.indexOf(argv[2]) !== -1) return cb()
  30. // ok, try to find out which package it was, then
  31. var pref = npm.config.get('global') ? npm.config.get('prefix')
  32. : npm.localPrefix
  33. var pkgDir = path.resolve(pref, 'node_modules', argv[2], 'package.json')
  34. readJson(pkgDir, function (er, d) {
  35. if (er && er.code !== 'ENOENT' && er.code !== 'ENOTDIR') return cb(er)
  36. if (er) d = {}
  37. var scripts = Object.keys(d.scripts || {})
  38. return cb(null, scripts)
  39. })
  40. })
  41. }
  42. readJson(path.join(npm.localPrefix, 'package.json'), function (er, d) {
  43. if (er && er.code !== 'ENOENT' && er.code !== 'ENOTDIR') return cb(er)
  44. d = d || {}
  45. cb(null, Object.keys(d.scripts || {}))
  46. })
  47. }
  48. function runScript (args, cb) {
  49. if (!args.length) return list(cb)
  50. var pkgdir = npm.localPrefix
  51. var cmd = args.shift()
  52. readJson(path.resolve(pkgdir, 'package.json'), function (er, d) {
  53. if (er) return cb(er)
  54. run(d, pkgdir, cmd, args, cb)
  55. })
  56. }
  57. function list (cb) {
  58. var json = path.join(npm.localPrefix, 'package.json')
  59. var cmdList = [
  60. 'publish',
  61. 'install',
  62. 'uninstall',
  63. 'test',
  64. 'stop',
  65. 'start',
  66. 'restart',
  67. 'version'
  68. ].reduce(function (l, p) {
  69. return l.concat(['pre' + p, p, 'post' + p])
  70. }, [])
  71. return readJson(json, function (er, d) {
  72. if (er && er.code !== 'ENOENT' && er.code !== 'ENOTDIR') return cb(er)
  73. if (er) d = {}
  74. var allScripts = Object.keys(d.scripts || {})
  75. var scripts = []
  76. var runScripts = []
  77. allScripts.forEach(function (script) {
  78. if (cmdList.indexOf(script) !== -1) scripts.push(script)
  79. else runScripts.push(script)
  80. })
  81. if (log.level === 'silent') {
  82. return cb(null, allScripts)
  83. }
  84. if (npm.config.get('json')) {
  85. output(JSON.stringify(d.scripts || {}, null, 2))
  86. return cb(null, allScripts)
  87. }
  88. if (npm.config.get('parseable')) {
  89. allScripts.forEach(function (script) {
  90. output(script + ':' + d.scripts[script])
  91. })
  92. return cb(null, allScripts)
  93. }
  94. var s = '\n '
  95. var prefix = ' '
  96. if (scripts.length) {
  97. output('Lifecycle scripts included in %s:', d.name)
  98. }
  99. scripts.forEach(function (script) {
  100. output(prefix + script + s + d.scripts[script])
  101. })
  102. if (!scripts.length && runScripts.length) {
  103. output('Scripts available in %s via `npm run-script`:', d.name)
  104. } else if (runScripts.length) {
  105. output('\navailable via `npm run-script`:')
  106. }
  107. runScripts.forEach(function (script) {
  108. output(prefix + script + s + d.scripts[script])
  109. })
  110. return cb(null, allScripts)
  111. })
  112. }
  113. function run (pkg, wd, cmd, args, cb) {
  114. if (!pkg.scripts) pkg.scripts = {}
  115. var cmds
  116. if (cmd === 'restart' && !pkg.scripts.restart) {
  117. cmds = [
  118. 'prestop', 'stop', 'poststop',
  119. 'restart',
  120. 'prestart', 'start', 'poststart'
  121. ]
  122. } else {
  123. if (pkg.scripts[cmd] == null) {
  124. if (cmd === 'test') {
  125. pkg.scripts.test = 'echo \'Error: no test specified\''
  126. } else if (cmd === 'env') {
  127. if (isWindowsShell) {
  128. log.verbose('run-script using default platform env: SET (Windows)')
  129. pkg.scripts[cmd] = 'SET'
  130. } else {
  131. log.verbose('run-script using default platform env: env (Unix)')
  132. pkg.scripts[cmd] = 'env'
  133. }
  134. } else if (npm.config.get('if-present')) {
  135. return cb(null)
  136. } else {
  137. let suggestions = didYouMean(cmd, Object.keys(pkg.scripts))
  138. suggestions = suggestions ? '\n' + suggestions : ''
  139. return cb(new Error('missing script: ' + cmd + suggestions))
  140. }
  141. }
  142. cmds = [cmd]
  143. }
  144. if (!cmd.match(/^(pre|post)/)) {
  145. cmds = ['pre' + cmd].concat(cmds).concat('post' + cmd)
  146. }
  147. log.verbose('run-script', cmds)
  148. chain(cmds.map(function (c) {
  149. // pass cli arguments after -- to script.
  150. if (pkg.scripts[c] && c === cmd) {
  151. pkg.scripts[c] = pkg.scripts[c] + joinArgs(args)
  152. }
  153. // when running scripts explicitly, assume that they're trusted.
  154. return [lifecycle, pkg, c, wd, { unsafePerm: true }]
  155. }), cb)
  156. }
  157. // join arguments after '--' and pass them to script,
  158. // handle special characters such as ', ", ' '.
  159. function joinArgs (args) {
  160. var joinedArgs = ''
  161. args.forEach(function (arg) {
  162. joinedArgs += ' "' + arg.replace(/"/g, '\\"') + '"'
  163. })
  164. return joinedArgs
  165. }