UNPKG

5.54 kBJavaScriptView Raw
1module.exports = runScript
2
3var lifecycle = require('./utils/lifecycle.js')
4var npm = require('./npm.js')
5var path = require('path')
6var readJson = require('read-package-json')
7var log = require('npmlog')
8var chain = require('slide').chain
9var usage = require('./utils/usage')
10var output = require('./utils/output.js')
11var didYouMean = require('./utils/did-you-mean')
12var isWindowsShell = require('./utils/is-windows-shell.js')
13
14runScript.usage = usage(
15 'run-script',
16 'npm run-script <command> [-- <args>...]'
17)
18
19runScript.completion = function (opts, cb) {
20 // see if there's already a package specified.
21 var argv = opts.conf.argv.remain
22
23 if (argv.length >= 4) return cb()
24
25 if (argv.length === 3) {
26 // either specified a script locally, in which case, done,
27 // or a package, in which case, complete against its scripts
28 var json = path.join(npm.localPrefix, 'package.json')
29 return readJson(json, function (er, d) {
30 if (er && er.code !== 'ENOENT' && er.code !== 'ENOTDIR') return cb(er)
31 if (er) d = {}
32 var scripts = Object.keys(d.scripts || {})
33 console.error('local scripts', scripts)
34 if (scripts.indexOf(argv[2]) !== -1) return cb()
35 // ok, try to find out which package it was, then
36 var pref = npm.config.get('global') ? npm.config.get('prefix')
37 : npm.localPrefix
38 var pkgDir = path.resolve(pref, 'node_modules', argv[2], 'package.json')
39 readJson(pkgDir, function (er, d) {
40 if (er && er.code !== 'ENOENT' && er.code !== 'ENOTDIR') return cb(er)
41 if (er) d = {}
42 var scripts = Object.keys(d.scripts || {})
43 return cb(null, scripts)
44 })
45 })
46 }
47
48 readJson(path.join(npm.localPrefix, 'package.json'), function (er, d) {
49 if (er && er.code !== 'ENOENT' && er.code !== 'ENOTDIR') return cb(er)
50 d = d || {}
51 cb(null, Object.keys(d.scripts || {}))
52 })
53}
54
55function runScript (args, cb) {
56 if (!args.length) return list(cb)
57
58 var pkgdir = npm.localPrefix
59 var cmd = args.shift()
60
61 readJson(path.resolve(pkgdir, 'package.json'), function (er, d) {
62 if (er) return cb(er)
63 run(d, pkgdir, cmd, args, cb)
64 })
65}
66
67function list (cb) {
68 var json = path.join(npm.localPrefix, 'package.json')
69 var cmdList = [
70 'publish',
71 'install',
72 'uninstall',
73 'test',
74 'stop',
75 'start',
76 'restart',
77 '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 }
100
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}
127
128function 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]
159 }
160
161 if (!cmd.match(/^(pre|post)/)) {
162 cmds = ['pre' + cmd].concat(cmds).concat('post' + cmd)
163 }
164
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)
170 }
171
172 // when running scripts explicitly, assume that they're trusted.
173 return [lifecycle, pkg, c, wd, { unsafePerm: true }]
174 }), cb)
175}
176
177// join arguments after '--' and pass them to script,
178// handle special characters such as ', ", ' '.
179function joinArgs (args) {
180 var joinedArgs = ''
181 args.forEach(function (arg) {
182 joinedArgs += ' "' + arg.replace(/"/g, '\\"') + '"'
183 })
184 return joinedArgs
185}