Minimalistic building tool
Install globally (for a command line script):
npm install runjs -g
Install in your project (to use runjs api inside your runfile.js
):
npm install runjs --save-dev
If you want to use Babel, install it. RunJS will pickup your
babel-register
automatically.
npm install babel-core babel-preset-es2015 babel-register --save-dev
Configure Babel in your package.json
:
"babel": {
"presets": ["es2015"]
}
Create runfile.js
:
import {run} from 'runjs';
export const build = {
js () {
run('webpack -p --config config/webpack/prod.js --progress');
},
css () {
},
all () {
build.js()
build.css()
}
}
export function createcomponent (name) {
}
export function lint (path = '.', options = {}) {
options.fix ? run(`eslint ${path} --fix`) : run(`eslint ${path}`)
}
Run:
run createcomponent AppContainer
run build:js
run build:all
run lint --fix components/Button.js
Mechanism of RunJS is very simple. Tasks are run by just importing runfile.js
as a
normal node.js module. Then based on command line arguments a proper exported function
from runfile.js
is run.
We have Grunt, Gulp, npm scripts, Makefile. Why another building tool ?
Gulp or Grunt files seem overly complex for what they do and the plugin ecosystem adds a layer of complexity towards the simple command line tools underneath. The documentation is not always up to date and the plugin does not always use the latest version of the tool. After a while customizing the process even with simple things, reconfiguring it becomes time consuming.
Npm scripts are simple but they get out of hand pretty quickly if we need more complex process which make them quite hard to read.
Makefiles are simple, better for more complex processes
but they depend on bash scripting. Within runfile
you can use
command line calls as well as JavaScript code and npm
libraries which makes that approach much more flexible. Additionally
each task and command call is reported in the console.
Provided arguments in the command line are passed to the function:
export function sayHello (who) {
console.log(`Hello ${who}!`)
}
$ run sayHello world
Hello world!
You can also provide dash arguments like -a
or --test
. Order of them doesn't matter
after task name but they will be always passed as a last argument in a form of JSON object.
export function sayHello (who, options) {
console.log(`Hello ${who}!`)
console.log('Given options:', options)
}
$ run sayHello -a --test=something world
Hello world!
Given options: { a: true, test: 'something' }
For inside runfile.js
usage
import {run, generate} from 'runjs';
run given command as a child process and log the call in the output.
./node_modules/.bin/
is included into PATH so you can call installed scripts directly.
Options:
{
cwd: ..., // current working directory (String)
async: ... // run command asynchronously (true/false), false by default
stdio: ... // 'inherit' (default), 'pipe' or 'ignore'
env: ... // environment key-value pairs (Object)
timeout: ...
}
Examples:
To get an output from run
function we need to set stdio
option to pipe
otherwise
output
will be null
:
const output = run('ls -la', {stdio: 'pipe'})
run('http-server .', {async: true, stdio: 'pipe'}).then((output) => {
log(output)
}).catch((error) => {
throw error
})
For stdio: 'pipe'
outputs are returned but not forwarded to the parent process thus
not printed out to the terminal. For stdio: 'inherit'
(default) outputs are passed
to the terminal, but run
function will resolve (async) / return (sync)
null
.
generate a file specified by dst
path by given template file src
and context
object
file1.tmp.js
:
{
author: '<%= AUTHOR %>'
}
generate('file1.tmp.js', 'file1.js', {AUTHOR: 'Pawel'});
will generate file1.js
:
{
author: 'Pawel'
}
Gather information from the user.
import { ask } from 'runjs'
export function prompt () {
ask('Who are you?').then((name) => {
console.log(`Hello ${name}!`)
})
}
$ run prompt
Who are you? Pawel
Hello Pawel!
If you have Babel and babel-register
already installed, RunJS will pick up it
automatically and use it for you runfile.js
. If RunJS not finds babel-register
it will fallback to pure node.
RunJS performs better with npm>=3.0
when using with Babel. It is because new
version of npm
handles modules loading much more effective.
If you have very specific location for your babel-register
, you can define
a path to it through config in your package.json
(default path is
./node_modules/babel-register
):
"runjs": {
"babel-register": "./node_modules/some_package/node_modules/babel-register"
}
When runfile.js
gets large it is a good idea to extract some logic to external modules
and import them back to runfile.js
:
./tasks/css
:
export function compile () {
...
}
./tasks/lint
:
export function fix () {
...
}
./tasks/common
:
export function serve () {
...
}
runfile.js
:
import { run } from 'runjs'
import lint from './tasks/lint'
import css from './tasks/css'
import common from './tasks/common'
export default {
css, // equals to css: css
lint, // equals to lint: lint
...common,
clean: () => {
run('rm -rf node_modules')
},
deploy: {
'production': () => {
},
'staging': () => {
}
}
}
run css:compile
run lint:fix
run serve
run clean
run deploy:production
run staging:staging
You can notice a couple of approaches here but in general RunJS will treat object key as
a namespace. It is also possible to bump tasks directly without the namespace by using ES7
object spread operator as with common
tasks in the example above.
To display all available tasks from your runfile.js
type run
in your command line
without any arguments:
$ run
Requiring babel-register...
Processing runfile...
Available tasks:
echo
testapi
Add doc
property to your task to get additional description:
import { run } from 'runjs'
export function buildjs () {
}
buildjs.doc = 'Compile JavaScript files'
$ run
Requiring babel-register...
Processing runfile...
Available tasks:
buildjs - Compile JavaScript files