Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
exec.js108 linesDownload Raw Back to commands
1const { resolve } = require('node:path')
2const libexec = require('libnpmexec')
3const BaseCommand = require('../base-cmd.js')
4
5class Exec extends BaseCommand {
6  static description = 'Run a command from a local or remote npm package'
7  static params = [
8    'package',
9    'call',
10    'workspace',
11    'workspaces',
12    'include-workspace-root',
13  ]
14
15  static name = 'exec'
16  static usage = [
17    '-- <pkg>[@<version>] [args...]',
18    '--package=<pkg>[@<version>] -- <cmd> [args...]',
19    '-c \'<cmd> [args...]\'',
20    '--package=foo -c \'<cmd> [args...]\'',
21  ]
22
23  static workspaces = true
24  static ignoreImplicitWorkspace = false
25  static isShellout = true
26
27  async exec (args) {
28    return this.callExec(args)
29  }
30
31  async execWorkspaces (args) {
32    await this.setWorkspaces()
33
34    for (const [name, path] of this.workspaces) {
35      const locationMsg =
36        `in workspace ${this.npm.chalk.green(name)} at location:\n${this.npm.chalk.dim(path)}`
37      await this.callExec(args, { name, locationMsg, runPath: path })
38    }
39  }
40
41  async callExec (args, { name, locationMsg, runPath } = {}) {
42    let localBin = this.npm.localBin
43    let pkgPath = this.npm.localPrefix
44
45    // This is where libnpmexec will actually run the scripts from
46    if (!runPath) {
47      runPath = process.cwd()
48    } else {
49      // We have to consider if the workspace has its own separate versions libnpmexec will walk up to localDir after looking here
50      localBin = resolve(this.npm.localDir, name, 'node_modules', '.bin')
51      // We also need to look for `bin` entries in the workspace package.json
52      // libnpmexec will NOT look in the project root for the bin entry
53      pkgPath = runPath
54    }
55
56    const call = this.npm.config.get('call')
57    let globalPath
58    const {
59      flatOptions,
60      globalBin,
61      globalDir,
62      chalk,
63    } = this.npm
64    const scriptShell = this.npm.config.get('script-shell') || undefined
65    const packages = this.npm.config.get('package')
66    const yes = this.npm.config.get('yes')
67    // --prefix sets both of these to the same thing, meaning the global prefix is invalid (i.e. no lib/node_modules).
68    // This is not a trivial thing to untangle and fix so we work around it here.
69    if (this.npm.localPrefix !== this.npm.globalPrefix) {
70      globalPath = resolve(globalDir, '..')
71    }
72
73    if (call && args.length) {
74      throw this.usageError()
75    }
76
77    return libexec({
78      ...flatOptions,
79      // we explicitly set packageLockOnly to false because if it's true when we try to install a missing package, we won't actually install it
80      packageLockOnly: false,
81      // what the user asked to run args[0] is run by default
82      args: [...args], // copy args so they don't get mutated
83      // specify a custom command to be run instead of args[0]
84      call,
85      chalk,
86      // where to look for bins globally, if a file matches call or args[0] it is called
87      globalBin,
88      // where to look for packages globally, if a package matches call or args[0] it is called
89      globalPath,
90      // where to look for bins locally, if a file matches call or args[0] it is called
91      localBin,
92      locationMsg,
93      // packages that need to be installed
94      packages,
95      // path where node_modules is
96      path: this.npm.localPrefix,
97      // where to look for package.json#bin entries first
98      pkgPath,
99      // cwd to run from
100      runPath,
101      scriptShell,
102      yes,
103    })
104  }
105}
106
107module.exports = Exec
108 
codekingpro/portable-devtools · Team Ai