package-scripts (aka p-s)
All the benefits of npm scripts without the cost of a bloated package.json and limits of json
Quick Video Intro :tv:
Simplify and empower npm scripts with p-s 11:19
The problem
Even though npm scripts have a ton of advantages (learn more), it can grow into an
unmaintainable mess in your package.json
file. Part of the problem is we're configuring scripts in json
which has fundamental issues (like no comments).
This solution
Put all of your scripts in a file called package-scripts.js
and use p-s
in a single package.json
script:
package.json
{
"scripts": {
"start": "package-scripts"
}
}
package-scripts.js
module.exports = {
scripts: {
default: 'node index.js',
lint: 'eslint .',
test: {
default: 'ava',
watch: {
script: 'ava -w',
description: 'run in the amazingly intelligent AVA watch mode'
}
},
build: {
default: 'webpack',
prod: 'webpack -p',
},
validate: 'p-s --parallel lint,test,build',
},
}
Then you can run:
npm start # runs `node index.js`
npm start lint # runs `eslint .`
npm start test.watch # runs `ava -w`
npm start validate # runs the lint, test, and build scripts in parallel
But the fun doesn't end there! You can use a prefix:
npm start b # will run the build script
And these prefixes can go as deep as you like!
npm start b.p # will run the production build script
And if you want to speed things up even more, you can install npm-quick-run
and then:
nr s build.prod
Cool stuff right? And there's more on the roadmap.
Also check out the examples. You'll find some good stuff in there (including how to deal with windows
and other cross-platform issues).
Note: You don't have to use the start
script if you don't want. If you're writing a node application, you're
likely using this for starting your server. In that case, you can create a default
script which will be run when
p-s
is run without arguments (so effectively it'll work just the same). But if you'd prefer, you can use whatever you
wish. For example you could easily create a p-s
script and do: npm run p-s b
.
Installation
This module is distributed via npm which is bundled with node and should
be installed as one of your project's devDependencies
:
npm install --save-dev p-s
global installation
You can install this module globally also:
npm install --global p-s
From here you can use p-s
on the command line via one of the installed aliases: p-s
, package-scripts
, or nps
.
If you do this, you may also be interested in installing the shell autocompletion script. Do so by running:
nps completion <optionally-your-bash-profile-file>
The bash profile file defaults to ~/.bash_profile
for bash and ~/.zshrc
for zsh. Special thanks to the
omelette
package for making this so easy.
Getting started
If you're already using npm scripts, you can get up and going really quickly with the init
command:
./node_modules/.bin/p-s init
This will use your package.json
scripts
to generate a package-scripts.js
file and update your scripts
to
utilize the package-scripts
binary.
API
CLI
The CLI is fairly simple. It allows for a few options. The p-s
binary is available in your node_modules/.bin
directory when you install it locally in a project so you can use it in your npm
scripts. We also expose a
package-scripts
alias binary so you can use that as well if you'd like the script to be more clear.
$ p-s --help
Usage: p-s [options]
Options:
-h, --help output usage information
-V, --version output the version number
-s, --silent Silent p-s output
-p, --parallel <script-name1,script-name2> Scripts to run in parallel (comma seprated)
-c, --config <filepath> Config file to use (defaults to nearest package-scripts.js)
-l, --log-level <level> The log level to use (error, warn, info [default])
-r, --require <module> Module to preload
Available scripts (camel or kebab case accepted)
lint - Lint all files with eslint. Configuration is in package.json - eslint .
test - Run tests with AVA. See package.json for config - ava
test.watch - Run in the amazingly intelligent AVA watch mode - ava -w
build - The normal webpack UMD build for development - webpack
build.prod - The production webpack build - webpack -p
Commands
help
If you have a help
script, then your help
script will be run. Otherwise, this will output the help.
Note: you can do this with p-s --help
, but if you're using the start
script in your package.json
this allows you
to run npm start help
rather than npm start -- --help
init
As indicated above, this will migrate your npm scripts to package-scripts.
completion
Installs autocompletion functionality into your default bash or zsh configuration. You can override the default by
providing a specific file:
p-s completion ~/.bashrc
Note: you should probably only do this if you have the package installed globally. In that case you should probably also
normally use the nps
alias rather than p-s
because it's easier to type.
CLI options
-h, --help
Will print out the help you see above (the available scripts are colored 🌈 and come from the config specified/default
config).
-s, --silent
By default, p-s
will log out to the console before running the command. You can add -s
to your command to silence
this.
-p, --parallel
Run the given scripts in parallel. This enables handy workflows like this:
npm start -p lint,build,cover && npm start check-coverage && npm start report-coverage
-c, --config
Use a different config
npm start -c ./other/package-scripts.js lint
Normally, p-s
will look for a package-scripts.js
file and load that to get the scripts. Generally you'll want to
have this at the root of your project (next to the package.json
). But by specifying -c
or --config
, p-s
will
use that file instead.
-l, --log-level
Specify the log level to use
-r, --require
You can specify a module which will be loaded before the config file is loaded. This allows you to preload for example babel-register so you can use all babel presets you like.
args
You can pass additional arguments to the script(s) that are being spawned:
npm start lint --fix # --fix will be passed on to the lint script
scripts
If you don't use -p
(because you don't need parallelism) then you can simply provide the name of the script like so:
npm start cover
And you can run multiple scripts in series by providing a comma-separated list:
npm start cover,check-coverage
That's all for the CLI.
package-scripts.js
p-s
expects to your package-scripts.js
file to module.exports
an object with the following properties:
scripts
This can be an object or a function that returns an object. See the annotated example below for what this object can
look like (and different ways to run them):
module.exports = {
scripts: {
default: 'echo "This runs on `npm start`"',
simple: 'echo "this is easy"',
test: {
default: {
script: 'ava',
description: 'Run tests with ava',
},
otherStuff: {
script: 'echo "testing other things"',
description: 'this is a handy description',
},
},
'kebab-case': 'echo "kebab-case"',
series: 'p-s simple,test,kebabCase',
},
}
Remember, I find it considerably nicer to just use npm-quick-run
and then I can do:
nr s k # runs npm start kebab-case
options
This object is used to configure p-s
with the following options:
silent
Setting this to true
will prevent p-s
from outputting anything for your script (normally you'll get simple output
indicating the command that's being executed). This effectively sets the logLevel
to disable
.
logLevel
This sets the logLevel of p-s
.
ENV variables
LOG_LEVEL
By setting LOG_LEVEL
environment variable you can control the log level for p-s
Log level
Log levels available:
error
- errors onlywarn
- errors and warnings onlyinfo
- info, errors, and warnings (default)
FAQ
Why npm start
?
Just to be clear: You do not have to use the start
script. You can use whatever you like. But I recommend using
the start
. npm scripts are generally run with npm run <script-name>
. There are some exceptions to
this. For example:
npm run test
=== npm test
=== npm t
npm run start
=== npm start
So, while you could use a script called script
and run npm run script build
, I just think it reads more clearly to
just use the start
script and run npm start build
. It's also nice that it's fewer things to type. You could also use
the test
script and then type even less: npm t build
, but thats just... odd.
Note, often servers are configured to run npm start
by default to start the server.
To allow for this case, you can provide a default
script at the root of your scripts
which will be run when p-s
is run without any arguments. Effectively this will
allow you to have a script run when npm start
is executed.
Inspiration
This was inspired by a tweet by @sindresorhus.
Other Solutions
- scripty has a solution for this problem as well. The reason I didn't go with that though is you still need
a line for every script (one of the pains I'm trying to solve) and a each script requires its own file (one of the
benefits of npm scripts I wanted to keep).
In the wild
-
react-component-template uses p-s
to implement shareable npm scripts. See then how dependent react-swap can reuse them.
GOTCHAS:
- use
process.cwd()
as the base for all paths
-
Hypercubed/EventsSpeedTests uses p-s
to automate benchmark running and reporting in node and the browser. package-scripts.js
enables us to keep our scripts DRY. Combined with grunion allows benchmarks to be run, serially or concurrently, on glob patterns.
-
SmithersAssistant/Smithers is an electron based personal assistant. Smithers works on multiple platforms. Smithers uses p-s
to dynamically find the current platform and execute the dev environment. Now we don't have to manually update the package.json
scripts when you are on a different platform!
Contributors
Thanks goes to these people (emoji key):
This project follows the all-contributors specification.
Contributions of any kind welcome!
LICENSE
MIT