
🏃 runner
A better way to organize npm scripts, with argument hoisting, env variable management and complex task chaining.

Why this?
If you're like us, you've broken up with task runners like Grunt and Gulp. Using plain node scripts helps us steer clear of plugin bottlenecks, but calling those scripts and chaining them together into complex tasks -- especially with CLI options -- can be a bit ugly to do in package.json.
runner gives us a more natural way to map out how our scripts are called and clearly define the arguments and environment they share. It makes our NPM scripts easier to read, keeps our individual task CLIs clean and helps us write better, friendlier task chains.
What's it do?
runner uses a tasks key in your package.json (or a dedicated config file) to help orchestrate your NPM scripts. You can chain scripts into complex tasks and compose arguments passed down through your task's commands.
Quickstart
yarn global add @reuters-graphics/runner
... or ...
npm install -g @reuters-graphics/runner
- Configure tasks either directly in
package.json or in a .tasksrc.js file in the root of your project.
{
"tasks": {
"build": { ... }
}
}
- Call a task from the root of your project using runner's CLI.
$ runner build
Writing tasks
At its simplest, each key is a task you can call with runner.
module.exports = {
tasks: {
cat: {
run: [
['echo', ['meow']],
],
},
},
}
Call the task with the CLI:
$ runner cat
Commands can include positional and named arguments.
module.exports = {
tasks: {
dev: {
run: [
['webpack', ['index.js'], { config: './config.js', env: 'prod' }],
],
},
},
}
$ runner dev
You can refer arguments passed to the task in each command using a special notation, $.
module.exports = {
tasks: {
dev: {
run: [
['webpack', ['$1'], { config: './config.js', env: '$env' }],
],
},
},
}
$ runner dev index.js --env prod
Add environment variables to the scope of your task.
module.exports = {
tasks: {
build: {
run: [
['webpack', ['$1'], { config: './config.js', env: 'prod' }],
],
env: {
NODE_ENV: 'production',
},
},
},
}
Call scripts and other tasks to create complex task chains.
module.exports = {
scripts: {
'img': 'npx ./bin/imgResizer.js',
'aws': 'aws s3 sync ./dist/',
},
tasks: {
build: {
run: [
['img', { sizes: '$img' }],
['webpack', ['$1'], { config: './config.js', env: 'prod' }],
],
},
publish: {
run: [
['build', ['$2']],
['aws', ['$1']],
],
},
},
}
$ runner publish s3://stagingBucket index.js --img 600 --img 1200
Writing inputs
If you're writing your tasks config in .tasksrc.js file, you can also define functions in an inputs key that can change or supply additional positional and named arguments to your task chains.
module.exports = {
tasks: {
build: { },
publish: {
run: [
'ask:locale',
['build', { locale: '$locale' }],
],
},
},
inputs: {
'ask:locale': async({ args, kwargs }) => {
const prompts = require('prompts');
const { locale } = await prompts({
type: 'select',
name: 'locale',
message: 'Which locale do you want to publish?',
choices: [
{ title: 'English', value: 'en' },
{ title: 'Spanish', value: 'es' },
{ title: 'German', value: 'de' },
],
});
kwargs.locale = locale;
},
},
}
Help tips
You can write tips to tell a user what commands are available and descriptions for what they do in a help key in your .tasksrc.js file.
module.exports = {
tasks: {
build: { },
publish: { },
'img:resize': { },
},
help: {
publish: 'Build and publish your project.',
'img:resize': 'Create responsive image sets for srcset attrs.',
},
}
Now when a user runs $ runner without a task, they'll see your tips.
Testing
$ yarn build && yarn test