| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222 |
- const { output } = require('proc-log')
- const pkgJson = require('@npmcli/package-json')
- const BaseCommand = require('../base-cmd.js')
- const { getError } = require('../utils/error-message.js')
- const { outputError } = require('../utils/output-error.js')
- class RunScript extends BaseCommand {
- static description = 'Run arbitrary package scripts'
- static params = [
- 'workspace',
- 'workspaces',
- 'include-workspace-root',
- 'if-present',
- 'ignore-scripts',
- 'foreground-scripts',
- 'script-shell',
- ]
- static name = 'run'
- static usage = ['<command> [-- <args>]']
- static workspaces = true
- static ignoreImplicitWorkspace = false
- static isShellout = true
- static checkDevEngines = true
- static async completion (opts, npm) {
- const argv = opts.conf.argv.remain
- if (argv.length === 2) {
- const workspacePrefixes = npm.config.get('workspace', 'default')
- const localPrefix = workspacePrefixes.length
- ? workspacePrefixes[0]
- : npm.localPrefix
- const { content: { scripts = {} } } = await pkgJson.normalize(localPrefix)
- .catch(() => ({ content: {} }))
- if (opts.isFish) {
- return Object.keys(scripts).map(s => `${s}\t${scripts[s].slice(0, 30)}`)
- }
- return Object.keys(scripts)
- }
- }
- async exec (args) {
- if (args.length) {
- await this.#run(args, { path: this.npm.localPrefix })
- } else {
- await this.#list(this.npm.localPrefix)
- }
- }
- async execWorkspaces (args) {
- await this.setWorkspaces()
- const ws = [...this.workspaces.entries()]
- for (const [workspace, path] of ws) {
- const last = path === ws.at(-1)[1]
- if (!args.length) {
- const newline = await this.#list(path, { workspace })
- if (newline && !last) {
- output.standard()
- }
- continue
- }
- const pkg = await pkgJson.normalize(path).then(p => p.content)
- try {
- await this.#run(args, { path, pkg, workspace })
- } catch (e) {
- const err = getError(e, { npm: this.npm, command: null })
- outputError({
- ...err,
- error: [
- ['', `Lifecycle script \`${args[0]}\` failed with error:`],
- ...err.error,
- ['workspace', pkg._id || pkg.name],
- ['location', path],
- ],
- })
- process.exitCode = err.exitCode
- if (!last) {
- output.error('')
- }
- }
- }
- }
- async #run ([event, ...args], { path, pkg, workspace }) {
- const runScript = require('@npmcli/run-script')
- pkg ??= await pkgJson.normalize(path).then(p => p.content)
- const { scripts = {} } = pkg
- if (event === 'restart' && !scripts.restart) {
- scripts.restart = 'npm stop --if-present && npm start'
- } else if (event === 'env' && !scripts.env) {
- const { isWindowsShell } = require('../utils/is-windows.js')
- scripts.env = isWindowsShell ? 'SET' : 'env'
- }
- pkg.scripts = scripts
- if (
- !Object.prototype.hasOwnProperty.call(scripts, event) &&
- !(event === 'start' && (await runScript.isServerPackage(path)))
- ) {
- if (this.npm.config.get('if-present')) {
- return
- }
- const suggestions = require('../utils/did-you-mean.js')(pkg, event)
- const wsArg = workspace && path !== this.npm.localPrefix
- ? ` --workspace=${pkg._id || pkg.name}`
- : ''
- throw new Error([
- `Missing script: "${event}"${suggestions}`,
- '',
- 'To see a list of scripts, run:',
- ` npm run${wsArg}`,
- ].join('\n'))
- }
- // positional args only added to the main event, not pre/post
- const events = [[event, args]]
- if (!this.npm.config.get('ignore-scripts')) {
- if (scripts[`pre${event}`]) {
- events.unshift([`pre${event}`, []])
- }
- if (scripts[`post${event}`]) {
- events.push([`post${event}`, []])
- }
- }
- for (const [ev, evArgs] of events) {
- await runScript({
- args: evArgs,
- event: ev,
- nodeGyp: this.npm.config.get('node-gyp'),
- path,
- pkg,
- // || undefined is because runScript will be unhappy with the default null value
- scriptShell: this.npm.config.get('script-shell') || undefined,
- stdio: 'inherit',
- })
- }
- }
- async #list (path, { workspace } = {}) {
- const { scripts = {}, name, _id } = await pkgJson.normalize(path).then(p => p.content)
- const scriptEntries = Object.entries(scripts)
- if (this.npm.silent) {
- return
- }
- if (this.npm.config.get('json')) {
- output.buffer(workspace ? { [workspace]: scripts } : scripts)
- return
- }
- if (!scriptEntries.length) {
- return
- }
- if (this.npm.config.get('parseable')) {
- output.standard(scriptEntries
- .map((s) => (workspace ? [workspace, ...s] : s).join(':'))
- .join('\n')
- .trim())
- return
- }
- const cmdList = [
- 'prepare', 'prepublishOnly',
- 'prepack', 'postpack',
- 'dependencies',
- 'preinstall', 'install', 'postinstall',
- 'prepublish', 'publish', 'postpublish',
- 'prerestart', 'restart', 'postrestart',
- 'prestart', 'start', 'poststart',
- 'prestop', 'stop', 'poststop',
- 'pretest', 'test', 'posttest',
- 'preuninstall', 'uninstall', 'postuninstall',
- 'preversion', 'version', 'postversion',
- ]
- const [cmds, runScripts] = scriptEntries.reduce((acc, s) => {
- acc[cmdList.includes(s[0]) ? 0 : 1].push(s)
- return acc
- }, [[], []])
- const { reset, bold, cyan, dim, blue } = this.npm.chalk
- const pkgId = `in ${cyan(_id || name)}`
- const title = (t) => reset(bold(t))
- if (cmds.length) {
- output.standard(`${title('Lifecycle scripts')} included ${pkgId}:`)
- for (const [k, v] of cmds) {
- output.standard(` ${k}`)
- output.standard(` ${dim(v)}`)
- }
- }
- if (runScripts.length) {
- const via = `via \`${blue('npm run')}\`:`
- if (!cmds.length) {
- output.standard(`${title('Scripts')} available ${pkgId} ${via}`)
- } else {
- output.standard(`available ${via}`)
- }
- for (const [k, v] of runScripts) {
- output.standard(` ${k}`)
- output.standard(` ${dim(v)}`)
- }
- }
- // Return true to indicate that something was output for this path that should be separated from others
- return true
- }
- }
- module.exports = RunScript
|