exec.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. const libexec = require('libnpmexec')
  2. const BaseCommand = require('./base-command.js')
  3. const getLocationMsg = require('./exec/get-workspace-location-msg.js')
  4. // it's like this:
  5. //
  6. // npm x pkg@version <-- runs the bin named "pkg" or the only bin if only 1
  7. //
  8. // { name: 'pkg', bin: { pkg: 'pkg.js', foo: 'foo.js' }} <-- run pkg
  9. // { name: 'pkg', bin: { foo: 'foo.js' }} <-- run foo?
  10. //
  11. // npm x -p pkg@version -- foo
  12. //
  13. // npm x -p pkg@version -- foo --registry=/dev/null
  14. //
  15. // const pkg = npm.config.get('package') || getPackageFrom(args[0])
  16. // const cmd = getCommand(pkg, args[0])
  17. // --> npm x -c 'cmd ...args.slice(1)'
  18. //
  19. // we've resolved cmd and args, and escaped them properly, and installed the
  20. // relevant packages.
  21. //
  22. // Add the ${npx install prefix}/node_modules/.bin to PATH
  23. //
  24. // pkg = readPackageJson('./package.json')
  25. // pkg.scripts.___npx = ${the -c arg}
  26. // runScript({ pkg, event: 'npx', ... })
  27. // process.env.npm_lifecycle_event = 'npx'
  28. class Exec extends BaseCommand {
  29. /* istanbul ignore next - see test/lib/load-all-commands.js */
  30. static get description () {
  31. return 'Run a command from a local or remote npm package'
  32. }
  33. /* istanbul ignore next - see test/lib/load-all-commands.js */
  34. static get params () {
  35. return [
  36. 'package',
  37. 'call',
  38. 'workspace',
  39. 'workspaces',
  40. 'include-workspace-root',
  41. ]
  42. }
  43. /* istanbul ignore next - see test/lib/load-all-commands.js */
  44. static get name () {
  45. return 'exec'
  46. }
  47. /* istanbul ignore next - see test/lib/load-all-commands.js */
  48. static get usage () {
  49. return [
  50. '-- <pkg>[@<version>] [args...]',
  51. '--package=<pkg>[@<version>] -- <cmd> [args...]',
  52. '-c \'<cmd> [args...]\'',
  53. '--package=foo -c \'<cmd> [args...]\'',
  54. ]
  55. }
  56. exec (args, cb) {
  57. const path = this.npm.localPrefix
  58. const runPath = process.cwd()
  59. this._exec(args, { path, runPath }).then(() => cb()).catch(cb)
  60. }
  61. execWorkspaces (args, filters, cb) {
  62. this._execWorkspaces(args, filters).then(() => cb()).catch(cb)
  63. }
  64. // When commands go async and we can dump the boilerplate exec methods this
  65. // can be named correctly
  66. async _exec (_args, { locationMsg, path, runPath }) {
  67. const args = [..._args]
  68. const call = this.npm.config.get('call')
  69. const {
  70. flatOptions,
  71. localBin,
  72. log,
  73. globalBin,
  74. } = this.npm
  75. const output = (...outputArgs) => this.npm.output(...outputArgs)
  76. const scriptShell = this.npm.config.get('script-shell') || undefined
  77. const packages = this.npm.config.get('package')
  78. const yes = this.npm.config.get('yes')
  79. if (call && _args.length)
  80. throw this.usage
  81. return libexec({
  82. ...flatOptions,
  83. args,
  84. call,
  85. localBin,
  86. locationMsg,
  87. log,
  88. globalBin,
  89. output,
  90. packages,
  91. path,
  92. runPath,
  93. scriptShell,
  94. yes,
  95. })
  96. }
  97. async _execWorkspaces (args, filters) {
  98. await this.setWorkspaces(filters)
  99. const color = this.npm.color
  100. for (const path of this.workspacePaths) {
  101. const locationMsg = await getLocationMsg({ color, path })
  102. await this._exec(args, {
  103. locationMsg,
  104. path,
  105. runPath: path,
  106. })
  107. }
  108. }
  109. }
  110. module.exports = Exec