Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
run.js223 linesDownload Raw Back to commands
1const { output } = require('proc-log')
2const pkgJson = require('@npmcli/package-json')
3const BaseCommand = require('../base-cmd.js')
4const { getError } = require('../utils/error-message.js')
5const { outputError } = require('../utils/output-error.js')
6
7class RunScript extends BaseCommand {
8  static description = 'Run arbitrary package scripts'
9  static params = [
10    'workspace',
11    'workspaces',
12    'include-workspace-root',
13    'if-present',
14    'ignore-scripts',
15    'foreground-scripts',
16    'script-shell',
17  ]
18
19  static name = 'run'
20  static usage = ['<command> [-- <args>]']
21  static workspaces = true
22  static ignoreImplicitWorkspace = false
23  static isShellout = true
24  static checkDevEngines = true
25
26  static async completion (opts, npm) {
27    const argv = opts.conf.argv.remain
28    if (argv.length === 2) {
29      const workspacePrefixes = npm.config.get('workspace', 'default')
30      const localPrefix = workspacePrefixes.length
31        ? workspacePrefixes[0]
32        : npm.localPrefix
33      const { content: { scripts = {} } } = await pkgJson.normalize(localPrefix)
34        .catch(() => ({ content: {} }))
35      if (opts.isFish) {
36        return Object.keys(scripts).map(s => `${s}\t${scripts[s].slice(0, 30)}`)
37      }
38      return Object.keys(scripts)
39    }
40  }
41
42  async exec (args) {
43    if (args.length) {
44      await this.#run(args, { path: this.npm.localPrefix })
45    } else {
46      await this.#list(this.npm.localPrefix)
47    }
48  }
49
50  async execWorkspaces (args) {
51    await this.setWorkspaces()
52
53    const ws = [...this.workspaces.entries()]
54    for (const [workspace, path] of ws) {
55      const last = path === ws.at(-1)[1]
56
57      if (!args.length) {
58        const newline = await this.#list(path, { workspace })
59        if (newline && !last) {
60          output.standard()
61        }
62        continue
63      }
64
65      const pkg = await pkgJson.normalize(path).then(p => p.content)
66      try {
67        await this.#run(args, { path, pkg, workspace })
68      } catch (e) {
69        const err = getError(e, { npm: this.npm, command: null })
70        outputError({
71          ...err,
72          error: [
73            ['', `Lifecycle script \`${args[0]}\` failed with error:`],
74            ...err.error,
75            ['workspace', pkg._id || pkg.name],
76            ['location', path],
77          ],
78        })
79        process.exitCode = err.exitCode
80        if (!last) {
81          output.error('')
82        }
83      }
84    }
85  }
86
87  async #run ([event, ...args], { path, pkg, workspace }) {
88    const runScript = require('@npmcli/run-script')
89
90    pkg ??= await pkgJson.normalize(path).then(p => p.content)
91
92    const { scripts = {} } = pkg
93
94    if (event === 'restart' && !scripts.restart) {
95      scripts.restart = 'npm stop --if-present && npm start'
96    } else if (event === 'env' && !scripts.env) {
97      const { isWindowsShell } = require('../utils/is-windows.js')
98      scripts.env = isWindowsShell ? 'SET' : 'env'
99    }
100
101    pkg.scripts = scripts
102
103    if (
104      !Object.prototype.hasOwnProperty.call(scripts, event) &&
105      !(event === 'start' && (await runScript.isServerPackage(path)))
106    ) {
107      if (this.npm.config.get('if-present')) {
108        return
109      }
110
111      const suggestions = require('../utils/did-you-mean.js')(pkg, event)
112      const wsArg = workspace && path !== this.npm.localPrefix
113        ? ` --workspace=${pkg._id || pkg.name}`
114        : ''
115      throw new Error([
116        `Missing script: "${event}"${suggestions}`,
117        '',
118        'To see a list of scripts, run:',
119        `  npm run${wsArg}`,
120      ].join('\n'))
121    }
122
123    // positional args only added to the main event, not pre/post
124    const events = [[event, args]]
125    if (!this.npm.config.get('ignore-scripts')) {
126      if (scripts[`pre${event}`]) {
127        events.unshift([`pre${event}`, []])
128      }
129
130      if (scripts[`post${event}`]) {
131        events.push([`post${event}`, []])
132      }
133    }
134
135    for (const [ev, evArgs] of events) {
136      await runScript({
137        args: evArgs,
138        event: ev,
139        nodeGyp: this.npm.config.get('node-gyp'),
140        path,
141        pkg,
142        // || undefined is because runScript will be unhappy with the default null value
143        scriptShell: this.npm.config.get('script-shell') || undefined,
144        stdio: 'inherit',
145      })
146    }
147  }
148
149  async #list (path, { workspace } = {}) {
150    const { scripts = {}, name, _id } = await pkgJson.normalize(path).then(p => p.content)
151    const scriptEntries = Object.entries(scripts)
152
153    if (this.npm.silent) {
154      return
155    }
156
157    if (this.npm.config.get('json')) {
158      output.buffer(workspace ? { [workspace]: scripts } : scripts)
159      return
160    }
161
162    if (!scriptEntries.length) {
163      return
164    }
165
166    if (this.npm.config.get('parseable')) {
167      output.standard(scriptEntries
168        .map((s) => (workspace ? [workspace, ...s] : s).join(':'))
169        .join('\n')
170        .trim())
171      return
172    }
173
174    const cmdList = [
175      'prepare', 'prepublishOnly',
176      'prepack', 'postpack',
177      'dependencies',
178      'preinstall', 'install', 'postinstall',
179      'prepublish', 'publish', 'postpublish',
180      'prerestart', 'restart', 'postrestart',
181      'prestart', 'start', 'poststart',
182      'prestop', 'stop', 'poststop',
183      'pretest', 'test', 'posttest',
184      'preuninstall', 'uninstall', 'postuninstall',
185      'preversion', 'version', 'postversion',
186    ]
187    const [cmds, runScripts] = scriptEntries.reduce((acc, s) => {
188      acc[cmdList.includes(s[0]) ? 0 : 1].push(s)
189      return acc
190    }, [[], []])
191
192    const { reset, bold, cyan, dim, blue } = this.npm.chalk
193    const pkgId = `in ${cyan(_id || name)}`
194    const title = (t) => reset(bold(t))
195
196    if (cmds.length) {
197      output.standard(`${title('Lifecycle scripts')} included ${pkgId}:`)
198      for (const [k, v] of cmds) {
199        output.standard(`  ${k}`)
200        output.standard(`    ${dim(v)}`)
201      }
202    }
203
204    if (runScripts.length) {
205      const via = `via \`${blue('npm run')}\`:`
206      if (!cmds.length) {
207        output.standard(`${title('Scripts')} available ${pkgId} ${via}`)
208      } else {
209        output.standard(`available ${via}`)
210      }
211      for (const [k, v] of runScripts) {
212        output.standard(`  ${k}`)
213        output.standard(`    ${dim(v)}`)
214      }
215    }
216
217    // Return true to indicate that something was output for this path that should be separated from others
218    return true
219  }
220}
221
222module.exports = RunScript
223 
codekingpro/portable-devtools · Team Ai