1 module.exports = runScript
3 var lifecycle = require("./utils/lifecycle.js")
4 , npm = require("./npm.js")
5 , path = require("path")
6 , readJson = require("read-package-json")
7 , log = require("npmlog")
8 , chain = require("slide").chain
10 runScript.usage = "npm run-script <command> [-- <args>]"
12 runScript.completion = function (opts, cb) {
14 // see if there's already a package specified.
15 var argv = opts.conf.argv.remain
17 if (argv.length >= 4) return cb()
19 if (argv.length === 3) {
20 // either specified a script locally, in which case, done,
21 // or a package, in which case, complete against its scripts
22 var json = path.join(npm.localPrefix, "package.json")
23 return readJson(json, function (er, d) {
24 if (er && er.code !== "ENOENT" && er.code !== "ENOTDIR") return cb(er)
26 var scripts = Object.keys(d.scripts || {})
27 console.error("local scripts", scripts)
28 if (scripts.indexOf(argv[2]) !== -1) return cb()
29 // ok, try to find out which package it was, then
30 var pref = npm.config.get("global") ? npm.config.get("prefix")
32 var pkgDir = path.resolve( pref, "node_modules"
33 , argv[2], "package.json" )
34 readJson(pkgDir, function (er, d) {
35 if (er && er.code !== "ENOENT" && er.code !== "ENOTDIR") return cb(er)
37 var scripts = Object.keys(d.scripts || {})
38 return cb(null, scripts)
43 readJson(path.join(npm.localPrefix, "package.json"), function (er, d) {
44 if (er && er.code !== "ENOENT" && er.code !== "ENOTDIR") return cb(er)
46 cb(null, Object.keys(d.scripts || {}))
50 function runScript (args, cb) {
51 if (!args.length) return list(cb)
53 var pkgdir = npm.localPrefix
56 readJson(path.resolve(pkgdir, "package.json"), function (er, d) {
58 run(d, pkgdir, cmd, args, cb)
63 var json = path.join(npm.localPrefix, "package.json")
64 var cmdList = [ "publish", "install", "uninstall"
65 , "test", "stop", "start", "restart", "version"
66 ].reduce(function (l, p) {
67 return l.concat(["pre" + p, p, "post" + p])
69 return readJson(json, function(er, d) {
70 if (er && er.code !== "ENOENT" && er.code !== "ENOTDIR") return cb(er)
72 var allScripts = Object.keys(d.scripts || {})
75 allScripts.forEach(function (script) {
76 if (cmdList.indexOf(script) !== -1) scripts.push(script)
77 else runScripts.push(script)
80 if (log.level === "silent") {
81 return cb(null, allScripts)
84 if (npm.config.get("json")) {
85 console.log(JSON.stringify(d.scripts || {}, null, 2))
86 return cb(null, allScripts)
89 if (npm.config.get("parseable")) {
90 allScripts.forEach(function(script) {
91 console.log(script + ":" + d.scripts[script])
93 return cb(null, allScripts)
99 console.log("Lifecycle scripts included in %s:", d.name)
101 scripts.forEach(function(script) {
102 console.log(prefix + script + s + d.scripts[script])
104 if (!scripts.length && runScripts.length) {
105 console.log("Scripts available in %s via `npm run-script`:", d.name)
107 else if (runScripts.length) {
108 console.log("\navailable via `npm run-script`:")
110 runScripts.forEach(function(script) {
111 console.log(prefix + script + s + d.scripts[script])
113 return cb(null, allScripts)
117 function run (pkg, wd, cmd, args, cb) {
118 if (!pkg.scripts) pkg.scripts = {}
121 if (cmd === "restart" && !pkg.scripts.restart) {
123 "prestop", "stop", "poststop",
125 "prestart", "start", "poststart"
128 if (!pkg.scripts[cmd]) {
129 if (cmd === "test") {
130 pkg.scripts.test = "echo \"Error: no test specified\""
131 } else if (cmd === "env") {
132 if (process.platform === "win32") {
133 log.verbose("run-script using default platform env: SET (Windows)")
134 pkg.scripts[cmd] = "SET"
136 log.verbose("run-script using default platform env: env (Unix)")
137 pkg.scripts[cmd] = "env"
139 } else if (npm.config.get("if-present")) {
142 return cb(new Error("missing script: " + cmd))
148 if (!cmd.match(/^(pre|post)/)) {
149 cmds = ["pre"+cmd].concat(cmds).concat("post"+cmd)
152 log.verbose("run-script", cmds)
153 chain(cmds.map(function (c) {
154 // pass cli arguments after -- to script.
155 if (pkg.scripts[c] && c === cmd) {
156 pkg.scripts[c] = pkg.scripts[c] + joinArgs(args)
159 // when running scripts explicitly, assume that they're trusted.
160 return [lifecycle, pkg, c, wd, true]
164 // join arguments after '--' and pass them to script,
165 // handle special characters such as ', ", ' '.
166 function joinArgs (args) {
168 args.forEach(function(arg) {
169 joinedArgs += ' "' + arg.replace(/"/g, '\\"') + '"'