Skip to content

Commit ac25561

Browse files
committed
Use @npmcli/run-script for npm run
1 parent 99848a7 commit ac25561

1 file changed

Lines changed: 109 additions & 128 deletions

File tree

lib/run-script.js

Lines changed: 109 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,21 @@
1-
module.exports = runScript
2-
3-
var lifecycle = require('./utils/lifecycle.js')
4-
var npm = require('./npm.js')
5-
var path = require('path')
6-
var readJson = require('read-package-json')
7-
var log = require('npmlog')
8-
var chain = require('slide').chain
9-
var usage = require('./utils/usage')
10-
var output = require('./utils/output.js')
11-
var didYouMean = require('./utils/did-you-mean')
12-
var isWindowsShell = require('./utils/is-windows-shell.js')
13-
14-
runScript.usage = usage(
1+
module.exports = runScriptCmd
2+
3+
const run = require('@npmcli/run-script')
4+
const npm = require('./npm.js')
5+
const readJson = require('read-package-json-fast')
6+
const { resolve, join } = require('path')
7+
const output = require('./utils/output.js')
8+
const log = require('npmlog')
9+
const usage = require('./utils/usage')
10+
const didYouMean = require('./utils/did-you-mean')
11+
const isWindowsShell = require('./utils/is-windows-shell.js')
12+
13+
runScriptCmd.usage = usage(
1514
'run-script',
1615
'npm run-script <command> [-- <args>...]'
1716
)
1817

19-
runScript.completion = function (opts, cb) {
18+
runScriptCmd.completion = function (opts, cb) {
2019
// see if there's already a package specified.
2120
var argv = opts.conf.argv.remain
2221

@@ -25,17 +24,16 @@ runScript.completion = function (opts, cb) {
2524
if (argv.length === 3) {
2625
// either specified a script locally, in which case, done,
2726
// or a package, in which case, complete against its scripts
28-
var json = path.join(npm.localPrefix, 'package.json')
27+
var json = join(npm.localPrefix, 'package.json')
2928
return readJson(json, function (er, d) {
3029
if (er && er.code !== 'ENOENT' && er.code !== 'ENOTDIR') return cb(er)
3130
if (er) d = {}
3231
var scripts = Object.keys(d.scripts || {})
33-
console.error('local scripts', scripts)
3432
if (scripts.indexOf(argv[2]) !== -1) return cb()
3533
// ok, try to find out which package it was, then
3634
var pref = npm.config.get('global') ? npm.config.get('prefix')
3735
: npm.localPrefix
38-
var pkgDir = path.resolve(pref, 'node_modules', argv[2], 'package.json')
36+
var pkgDir = resolve(pref, 'node_modules', argv[2], 'package.json')
3937
readJson(pkgDir, function (er, d) {
4038
if (er && er.code !== 'ENOENT' && er.code !== 'ENOTDIR') return cb(er)
4139
if (er) d = {}
@@ -45,28 +43,71 @@ runScript.completion = function (opts, cb) {
4543
})
4644
}
4745

48-
readJson(path.join(npm.localPrefix, 'package.json'), function (er, d) {
46+
readJson(join(npm.localPrefix, 'package.json'), function (er, d) {
4947
if (er && er.code !== 'ENOENT' && er.code !== 'ENOTDIR') return cb(er)
5048
d = d || {}
5149
cb(null, Object.keys(d.scripts || {}))
5250
})
5351
}
5452

55-
function runScript (args, cb) {
56-
if (!args.length) return list(cb)
53+
function runScriptCmd (args, cb) {
54+
const fn = args.length ? runScript : list
55+
fn(args).then(() => cb()).catch(cb)
56+
}
5757

58-
var pkgdir = npm.localPrefix
59-
var cmd = args.shift()
58+
const runScript = async (args) => {
59+
const path = npm.localPrefix
60+
const event = args.shift()
61+
const { scriptShell } = npm.flatOptions
6062

61-
readJson(path.resolve(pkgdir, 'package.json'), function (er, d) {
62-
if (er) return cb(er)
63-
run(d, pkgdir, cmd, args, cb)
64-
})
63+
const pkg = await readJson(`${path}/package.json`)
64+
const { _id, scripts = {} } = pkg
65+
66+
if (event === 'restart' && !scripts.restart) {
67+
scripts.restart = 'npm stop && npm start'
68+
} else if (event === 'env') {
69+
scripts.env = isWindowsShell ? 'SET' : 'env'
70+
}
71+
72+
if (!scripts[event]) {
73+
if (npm.config.get('if-present')) {
74+
return
75+
}
76+
const suggestions = didYouMean(event, Object.keys(scripts))
77+
throw new Error(`missing script: ${event}${
78+
suggestions ? `\n${suggestions}` : '' }`)
79+
}
80+
81+
// positional args only added to the main event, not pre/post
82+
const events = [[event, args]]
83+
if (scripts[`pre${event}`]) {
84+
events.unshift([`pre${event}`, []])
85+
}
86+
if (scripts[`post${event}`]) {
87+
events.push([`post${event}`, []])
88+
}
89+
90+
const opts = {
91+
path,
92+
args,
93+
scriptShell,
94+
stdio: log.level === 'silent' ? 'pipe' : 'inherit',
95+
pkg
96+
}
97+
98+
for (const [event, args] of events) {
99+
await run({
100+
...opts,
101+
event,
102+
args
103+
})
104+
}
65105
}
66106

67-
function list (cb) {
68-
var json = path.join(npm.localPrefix, 'package.json')
69-
var cmdList = [
107+
const list = async () => {
108+
const path = npm.localPrefix
109+
const { scripts, name } = await readJson(`${path}/package.json`)
110+
const cmdList = [
70111
'publish',
71112
'install',
72113
'uninstall',
@@ -75,111 +116,51 @@ function list (cb) {
75116
'start',
76117
'restart',
77118
'version'
78-
].reduce(function (l, p) {
79-
return l.concat(['pre' + p, p, 'post' + p])
80-
}, [])
81-
return readJson(json, function (er, d) {
82-
if (er && er.code !== 'ENOENT' && er.code !== 'ENOTDIR') return cb(er)
83-
if (er) d = {}
84-
var allScripts = Object.keys(d.scripts || {})
85-
var scripts = []
86-
var runScripts = []
87-
allScripts.forEach(function (script) {
88-
if (cmdList.indexOf(script) !== -1) scripts.push(script)
89-
else runScripts.push(script)
90-
})
91-
92-
if (log.level === 'silent') {
93-
return cb(null, allScripts)
94-
}
95-
96-
if (npm.config.get('json')) {
97-
output(JSON.stringify(d.scripts || {}, null, 2))
98-
return cb(null, allScripts)
99-
}
119+
].reduce((l, p) => l.concat(['pre' + p, p, 'post' + p]))
100120

101-
if (npm.config.get('parseable')) {
102-
allScripts.forEach(function (script) {
103-
output(script + ':' + d.scripts[script])
104-
})
105-
return cb(null, allScripts)
106-
}
107-
108-
var s = '\n '
109-
var prefix = ' '
110-
if (scripts.length) {
111-
output('Lifecycle scripts included in %s:', d.name)
112-
}
113-
scripts.forEach(function (script) {
114-
output(prefix + script + s + d.scripts[script])
115-
})
116-
if (!scripts.length && runScripts.length) {
117-
output('Scripts available in %s via `npm run-script`:', d.name)
118-
} else if (runScripts.length) {
119-
output('\navailable via `npm run-script`:')
120-
}
121-
runScripts.forEach(function (script) {
122-
output(prefix + script + s + d.scripts[script])
123-
})
124-
return cb(null, allScripts)
125-
})
126-
}
121+
if (!scripts) {
122+
return []
123+
}
127124

128-
function run (pkg, wd, cmd, args, cb) {
129-
if (!pkg.scripts) pkg.scripts = {}
130-
131-
var cmds
132-
if (cmd === 'restart' && !pkg.scripts.restart) {
133-
cmds = [
134-
'prestop', 'stop', 'poststop',
135-
'restart',
136-
'prestart', 'start', 'poststart'
137-
]
138-
} else {
139-
if (pkg.scripts[cmd] == null) {
140-
if (cmd === 'test') {
141-
pkg.scripts.test = 'echo \'Error: no test specified\''
142-
} else if (cmd === 'env') {
143-
if (isWindowsShell) {
144-
log.verbose('run-script using default platform env: SET (Windows)')
145-
pkg.scripts[cmd] = 'SET'
146-
} else {
147-
log.verbose('run-script using default platform env: env (Unix)')
148-
pkg.scripts[cmd] = 'env'
149-
}
150-
} else if (npm.config.get('if-present')) {
151-
return cb(null)
152-
} else {
153-
let suggestions = didYouMean(cmd, Object.keys(pkg.scripts))
154-
suggestions = suggestions ? '\n' + suggestions : ''
155-
return cb(new Error('missing script: ' + cmd + suggestions))
156-
}
157-
}
158-
cmds = [cmd]
125+
const allScripts = scripts ? Object.keys(scripts) : []
126+
if (log.level === 'silent') {
127+
return allScripts
159128
}
160129

161-
if (!cmd.match(/^(pre|post)/)) {
162-
cmds = ['pre' + cmd].concat(cmds).concat('post' + cmd)
130+
if (npm.flatOptions.json) {
131+
output(JSON.stringify(scripts, null, 2))
132+
return allScripts
163133
}
164134

165-
log.verbose('run-script', cmds)
166-
chain(cmds.map(function (c) {
167-
// pass cli arguments after -- to script.
168-
if (pkg.scripts[c] && c === cmd) {
169-
pkg.scripts[c] = pkg.scripts[c] + joinArgs(args)
135+
if (npm.flatOptions.parseable) {
136+
for (const [script, cmd] of Object.entries(scripts)) {
137+
output(`${script}:${cmd}`)
170138
}
139+
return allScripts
140+
}
171141

172-
// when running scripts explicitly, assume that they're trusted.
173-
return [lifecycle, pkg, c, wd, { unsafePerm: true }]
174-
}), cb)
175-
}
142+
const indent = '\n '
143+
const prefix = ' '
144+
const cmds = []
145+
const runScripts = []
146+
for (const script of allScripts) {
147+
const list = cmdList.includes(script) ? cmds : runScripts
148+
list.push(script)
149+
}
176150

177-
// join arguments after '--' and pass them to script,
178-
// handle special characters such as ', ", ' '.
179-
function joinArgs (args) {
180-
var joinedArgs = ''
181-
args.forEach(function (arg) {
182-
joinedArgs += ' "' + arg.replace(/"/g, '\\"') + '"'
183-
})
184-
return joinedArgs
151+
if (cmds.length) {
152+
output(`Lifecycle scripts included in ${name}:`)
153+
}
154+
for (const script of cmds) {
155+
output(prefix + script + indent + scripts[script])
156+
}
157+
if (!cmds.length && runScripts.length) {
158+
output(`Scripts available in ${name} via \`npm run-script\`:`)
159+
} else if (runScripts.length) {
160+
output(`\navailable via \`npm run-script\`:`)
161+
}
162+
for (const script of runScripts) {
163+
output(prefix + script + indent + scripts[script])
164+
}
165+
return allScripts
185166
}

0 commit comments

Comments
 (0)