A simple, enhanced replacement for `npm run-script <command> [-- <args>...]`
npm install run-simplerun – Simple development task automationrun is a simple, enhanced replacement for npm run-script with zero third-party dependencies. If you already use npm run,
you can use run immediately. But run can do a bit more as well.
```
npm install -g run-simple
``
run [--version] [-h | --help] [-q | --quiet | -s | --silent]
`js
const Runner = require('run-simple')
const runner = new Runner()
runner.on('error', (error) => {
console.error(error)
process.exit(1)
})
runner.on('exit', (code) => {
process.exit(code)
})
runner.on('list', (scripts) => {
console.log(${runner.appName}: ${runner.moduleRoot} scripts:) ${name}:\n ${scripts[name]}
Object.keys(scripts).forEach((name) => console.log())
process.exit()
})
runner.on('run', (spec) => {
console.log()
console.log(> ${spec.name}${spec.version ? @${spec.version} : ''} ${spec.wd})> ${spec.script}
console.log()
console.log()
})
runner.run()
`
?Obviously, npm is a fine piece of software. And npm run-script (AKA npm
run) is one of the simplest development automation toolsgulp
available. I love it, and as a result, I have never
wanted to waste time learning more featureful alternatives like or grunt.
But… it could be better. One thing that npm run doesn’t support well isnpm run
signal handling. When executes scripts,'SIGINT'
handling signals like within the scripts themselves is unreliable atnpm
best, which means a script that should clean up before exiting simply can’t. The
implications go beyond signal handling – scripts behave differently when npm run
runs them than they do when they run by themselves. just isn’t
satisfied to run something and then get out out of the way.
Which may explain why npm run is verbose to a fault.npm has a lot more on its mind than just running your scripts so by default--silent
you’re going to see more output than you really need. Sure, you can pass it or redirect output to /dev/null, but other people (your teammatesnpm
and mine, for example) will need to do the same or they’ll get a bunch of
disclaimer boilerplate when what they really want to see is what the script
itself did.
Finally, package.json is great for project configuration, but it’s a pretty
poor place to write scripts. There’s no way to comment or document your scripts.
And being forced to write everything on a single line either hinders readability
or forces artificial factoring of script logic.
?Although run is quieter and will cede more control to scripts, it is designednpm run
to work as much like as possible. In fact, you can easily use it as ifnpm run
it was nothing more than an alias for ; it’ll happily find and executepackage.json
all the scripts you have already defined in .
But if you create a scripts.js file, run will look there for tasks as well.run
Here’s what the project’s own scripts.js looks like:
`js
const path = require('path')
const rollup = require('rollup')
const reimportFrom = require('./scripts/reimport-from')
const varsPathname = path.resolve('./scripts/vars.js')
const {
binPathname,
distPathname,
mainPathname
} = require(varsPathname)
module.exports = {
// ---------------------------------------------------------------------------
// Dist
predist: mkdir -p ${distPathname},chmod 755 ${binPathname}
dist: 'rollup -c',
postdist: ,
// ---------------------------------------------------------------------------
// Publish
prepublish: 'run test',
publish: 'npm publish',
// ---------------------------------------------------------------------------
// Test
pretest: 'NODE_ENV=test run -q dist',
test: "mocha -s 400 test/init.js './test/.test.js' './test//.test.js'",
watchtest() {
process.env.NODE_ENV = 'test'
return Promise.all([
reimportFrom(varsPathname),
reimportFrom('./rollup.config.js')
]).then(([, config]) => {
const {spawn, spawnSync} = require('child_process')
const {watch} = require('chokidar')
const {distPathname, mainPathname} = require(varsPathname)
const testPathname = path.resolve('test')
const wdRegExp = new RegExp(^${process.cwd()}/)
const rollupWatcher = rollup.watch(config)
const runTest = () => {
spawnSync('sh', ['-c', this.test], {stdio: 'inherit'})
}
const debounceTimeoutMilliseconds = 250
let debounceTimeoutId
let ready = false
const watchRollupOnce = (event) => {
if (!ready && event.code === 'END') {
ready = true
rollupWatcher.removeListener('event', watchRollupOnce)
runTest()
const testWatcher = watch(testPathname)
const watchTestOnce = () => {
testWatcher.on('all', (type, pathname) => {
clearTimeout(debounceTimeoutId)
debounceTimeoutId = setTimeout(runTest, debounceTimeoutMilliseconds)
})
}
testWatcher.once('ready', watchTestOnce)
}
}
rollupWatcher.on('event', watchRollupOnce)
rollupWatcher.on('event', (event) => {
switch (event.code) {
case 'BUNDLE_END':
const input = event.input
const output = event.output
.map((x) => x.replace(wdRegExp, ''))
.join(\n${new Array(input.length).join(' ')})
console.log(input, '→', output)
break
case 'ERROR':
console.error(event.error)
break
case 'FATAL':
throw event.error
}
})
})
}
// ---------------------------------------------------------------------------
}
`
The first thing you’ll see is that run supports imports. Now you can share
modules between the code you build and the code that builds it.
Then there are comments and whitespace, which make automation tasks easier to
write, read, and maintain.
Finally, the watchtest script is actually a plain-old JavaScript function.--
Just like other scripts, functions will be passed whatever arguments you define
after the in run .
Imagine that.
#### npm_package_*
Because npm_package_* variables are useful to shell scripts that depend onpackage.json
values stored in , run will define most of them exactly as npm
does. However, there are a couple of exceptions.
Unlike, npm run, run will _not_ attempt to normalize package.json keys orrun
values. For example, won’t produce the following environment variablespackage.json
unless they are explicitly included in :
* npm_package_bugs_urlnpm_package_homepage
* npm_package_readmeFilename
*
Similarly, run does not transform values like npm_package_repository_url.
For a complete description of how npm run transforms package.json, see:
npm/normalize-package-data.
#### npm_config_*
In the interest of simplicity in both implementation and behavior, run doesnpm
not read ’s configuration nor does it define npm_config_* variables.
#### Other npm_*
run defines a few miscellaneous npm environment variables as well:
* npm_execpath (as run_execpath – npm isn’t running)npm_lifecycle_event
* npm_lifecycle_script
* npm_node_execpath
*
run` was inspired in part by “An alternative to npm
scripts” by James Forbes.