Comparing via Google Trends:
List of task-runners:
- NPM (Node Package Manager)
- Grunt
- Gulp
- WebPack
- Browserify
- Cake
- Broccoli
- Brunch
- Mimosa
- Pint
- Fly
- Gobble
- Plumber
- Assetgraph
- Yeoman
└─ app/
├── assets/
│ ├── scripts/
│ │ ├── main.js
│ │ ├── page-index.js
│ │ └── page-login.js
│ │ └── page-help.jsx
│ ├── styles/
│ │ ├── main.scss
│ │ ├── mixins.scss
│ │ ├── const.scss
│ │ ├── page-index.scss
│ │ └── page-login.scss
│ └── images/
│ ├── icons-social/
│ │ ├── instagram.png
│ │ ├── instagram.png
│ │ └── youtube.png
│ └── logout.png
└── build/
└── index.html
The above folder-structure is created & located on cursor-education/sample-frontend-app repo, so you can clone & use it:
$ git clone https://github.com/cursor-education/sample-frontend-app.git
$ npm install npm@latest -g
$ npm init --yes
$ npm -v
3.10.5
{
"name": "sample-frontend-app",
"scripts": {
"test": "echo \"hello\" && exit 0",
"build": "echo \"build\""
}
}
You can additionally read about exit-status and list of exit-codes.
List of reserved script actions - https://docs.npmjs.com/misc/scripts.
npm install
will runpreinstall
,install
,postinstall
actions one-by-onenpm test
will runpretest
,test
,posttest
actions one-by-one
Usage:
$ npm run-script test
$ npm run test
$ npm test # only for reserved script names
To compile SASS into CSS (using node-sass):
$ npm i node-sass
$ node_modules/.bin/node-sass --version
node-sass 3.8.0 (Wrapper) [JavaScript]
libsass 3.3.6 (Sass Compiler) [C/C++]
$ node_modules/.bin/node-sass assets/styles/page-index.scss # to build specific file & output to console
$ node_modules/.bin/node-sass assets/styles/page-login.scss -o build/ # to build specific file & save output into file
$ node_modules/.bin/node-sass assets/styles/ -o build/ # to build all files in folder & save outputs into files
$ node_modules/.bin/node-sass assets/styles/ -o build/ --output-style compressed
$ find assets/styles/ -name "page-*.scss" -type f
$ find assets/styles/ -name "page-*.scss" -type f -exec cat {} | node_modules/.bin/node-sass > output \;
# "scripts": {
# "build:css": "node_modules/.bin/node-sass assets/styles/ -o build/"
# }
$ npm run build:css
To cleanup the build directory, lets add the separate task:
$ rm -rf build/*.{js,css,png}
# "scripts": {
# "cleanup": "rm -rf build/*.{js,css,png}"
# }
$ npm run cleanup
Lets add compiling of js-es6 into es5 using Babel CLI
$ npm i babel-cli
$ node_modules/.bin/babel --version
6.11.4 (babel-core 6.11.4)
# http://babeljs.io/docs/usage/cli/
$ node_modules/.bin/babel assets/scripts/page-help.jsx
$ node_modules/.bin/babel assets/scripts/ --out-dir build/
$ node_modules/.bin/babel assets/scripts/ --out-dir build/ --extensions ".jsx"
# "scripts": {
# "compile-es6": "node_modules/.bin/babel assets/scripts/ --out-dir build/"
# }
$ npm run compile-es6
The images remaining, so we need to add task to create a sprites from sources-images. The css-sprite will be used.
$ npm i css-sprite
$ node_modules/.bin/css-sprite build/ assets/images/icons/*.png
$ node_modules/.bin/css-sprite build/ assets/images/icons/*.png --base64
# src="sprite.css", class="item icon-facebook"
The script with name "build:images" will be the same as above scripts.
Now, lets group above tasks (cleanup, css & js compiling, sprites) into one task "build"
# "scripts": {
# "build:js": "npm run compile-es6",
# "build": "npm run cleanup && npm run build:css && npm run build:js && npm run build:images"
# }
$ npm run build
The tasks can be combined into similar, like build:css
, build:js
, build:images
, etc.
On the same way you can add any pre/post processors (that have command line tool), for instance:
- autoprefix or postcss to automatically add vendor prefixes
- eslint for "linting", to keep a standard format on your js code
- uglify to uglify your static
- imagemin-cli to compress images
- sprite & others to generate sprites
- browser-sync to automaticall refresh page in browser on any change appears
- onchange or nodemon to watch & run actions on changes
"scripts": {
"cleanup": "rm -rf dest/*.*",
"jade": "node node_modules/.bin/jade src/views/ --pretty --out dest/",
"less": "node node_modules/.bin/less-cli src/styles/ && mv src/styles/*.css dest/",
"build": "npm run cleanup && npm run jade && npm run less",
"server": "node node_modules/.bin/http-server dest/",
"watch": "npm run build && node node_modules/.bin/onchange 'src/**/*.*' -- npm run build"
},
- released at 2011
- is a system for running a sequence of commands/tasks (is Task Runner, not build tool)
- is the first “second generation” task runner
- each task is is a plug-in that represents cli tool
- over 1200 plugins, and you need to read the doc of each of them
- plugin take an file(s) on input, do some transormation, and save into output
- each plugin has configuration located on
Gruntfile.js
- configurations are over js objects (configuration over code)
- the "wrapper" function on
Gruntfile.js
ismodule.exports = function(grunt) { ... }
$ npm install grunt-cli grunt
$ node_modules/.bin/grunt --version
grunt-cli v1.2.0
grunt v1.0.1
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
uglify: { all: { /* ... */ } }
});
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.registerTask('default', ['uglify']);
}
Lets add jshint
task:
$ npm i grunt-contrib-jshint
$ npm init --yes
$ touch Gruntfile.js
module.exports = function(grunt) {
// project configuration
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
jshint: {
all: ['Gruntfile.js', 'assets/scripts/**/*.js']
}
});
grunt.loadNpmTasks('grunt-contrib-jshint');
// default task
grunt.registerTask('default', ['jshint']);
};
$ node_modules/.bin/grunt
Other plugins:
- grunt-sass or grunt-contrib-sass to compile SASS to CSS
- grunt-babel to use ES6 syntax
- grunt-babel-cli Grunt CLI wrapper for writing Gruntfiles in es6 syntax
- grunt-contrib-uglify to minify files with UglifyJS
- grunt-contrib-watch to run tasks whenever watched files change
- grunt-contrib-concat to join files into one
- grunt-clean or grunt-remove to cleanup the generated files
- released at 2013
- configuration located on
gulpfile.js
- using the streams (example >1000 sass \w autoprefixer)
- no temporary files
- stream instances are basically Unix pipes
- Unix pipes (and so nodejs’ streams) improve performance on I/O operations
- you can plug the output of one stream to the input of another
- Grunt’s way of doing things is more disk heavy resulting in lower performance than Gulp
- based on pipes (use a series of connected tubes instead of a big truck)
- single responsibility (one action) for each plugin ("task" means sequence of actions)
- more than 2500 plugins
- less config, more code
$ npm i gulp
$ node_modules/.bin/gulp --version
[00:46:32] CLI version 3.9.1
[00:46:32] Local version 3.9.1
gulps.task(name, fn)
define a task by passing its name and a function.gulp.run(tasks…)
runs all tasksgulp.watch(glob, fn)
runs a function when a file that matches the glob changesgulp.src(glob)
this returns a readable stream which can then be piped to other streams (plugins)gulp.dest(folder)
returns a writable stream. Objects piped into this are saved to the file system.
Compile sass to css using grunt-sass.
$ npm i grunt-sass
var gulp = require('gulp')
, sass = require('gulp-sass');
gulp.task('sass', function () {
return gulp.src('./assets/styles/**/*.scss')
.pipe(sass().on('error', sass.logError))
.pipe(gulp.dest('./build'));
});
gulp.task('default', ['sass']);
Adding sass:watch task.
$ node_modules/.bin/gulp sass:watch
gulp.task('sass:watch', function () {
gulp.watch('./assets/styles/**/*.scss', ['sass']);
});
Adding gulp-sourcemaps
var sourcemaps = require('gulp-sourcemaps');
gulp.task('sass', function () {
return gulp.src('./assets/styles/**/*.scss')
.pipe(sourcemaps.init())
.pipe(sass({ outputStyle: 'compressed' }).on('error', sass.logError))
.pipe(sourcemaps.write())
.pipe(gulp.dest('./build'));
});
gulp.task('sass:watch', function () {
gulp.watch('./assets/styles/**/*.scss', ['sass']);
});
Adding autoprefixer Interactive demo.
var autoprefixer = require('gulp-autoprefixer');
.pipe(autoprefixer({
browsers: ['last 5 versions'],
cascade: false
}))
Adding simple static server to serve static
$ npm i npm install connect@2.X.X
var connect = require('connect');
gulp.task('server', function() {
connect()
.use(require('connect-livereload')())
.use(connect.static('./build'))
.listen('9000');
console.log('Server listening on http://localhost:9000');
});
http://localhost:9000/index.html
<script type="text/javascript" src="/base.css"></script>
Adding livereload
$ npm i connect-livereload tiny-lr gulp-livereload
tiny-lr
Adding linting & minification for js.
- supports a module system (AMD, CommonJS, ES6)
- supports Babel, ReactJS, CommonJS, among others
$ npm i webpack
$ node_modules/.bin/webpack --version
webpack 1.13.1
$ node_modules/.bin/webpack ./app.js bundle.js
var webpack = require('webpack');
module.exports = {
entry: './entry.js',
output: {
path: __dirname,
filename: 'bundle.js'
},
module: {
loaders: [
{
test: /\.css$/,
loaders: ['style', 'css']
}
]
},
plugins: [
new webpack.optimize.UglifyJsPlugin()
]
};
webpack --watch
npm i webpack-dev-server
webpack-dev-server
- loaders
$ npm install browserify
$ node_modules/.bin/browserify --version
13.1.0
$ npm i underscore
$ node_modules/.bin/browserify assets/scripts/test-browserify.js -o build/test-browserify.js
$ echo 'console.log("Hello world.")' > hello.js
$ node hello.js
Hello world.
$ node_modules/.bin/browserify hello.js -o hello-bundle.js
$ cat hello-bundle.js
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
console.log("Hello world.")},{}]},{},[1]);
$ node hello-bundle.js
Hello world.
Btw, you can beautify your js here.
exports.beep = function (n) { return n * 1000 };
exports.boop = 555;
var beep = require('./beep.js').beep;
var boop = require('./boop.js').boop;
console.log(beep(5), boop);
- is shipped with Coffeescript and is not supported without it
- almost everything is described through code logic (opposite of Grunt)
- the recipes is on
Cakefile
- no plugin system
- npm/cake
$ npm install -g coffee-script cacke
$ cake -- version
- released at 2013
- tasks based on code logic and plugin system for helpers
- "will figure out by itself which files to watch, and only rebuild those that need rebuilding"
- caching system built on top of its core abstraction of file trees
- comparison with other build systems
$ npm install -g broccoli
$ brocoli --version
- acts more as framework by providing you with project template
- about 382 plugins
$ npm i brunch
$ node_modules/.bin/brunch --version
2.8.2
$ node_modules/.bin/brunch new testAppBrunch
$ cd testAppBrunch/ && node_modules/.bin/brunch build
- the
new
action
$ npm i mimosa
$ node_modules/.bin/mimosa --version
2.3.32
$ node_modules/.bin/mimosa new mySampleApp
$ tree -d mySampleApp
mySampleApp
├── assets
│ ├── javascripts
│ │ ├── app
│ │ │ └── template
│ │ └── vendor
│ └── stylesheets
├── node_modules
│ ├── mimosa-ractive
│ │ └── src
│ │ └── client
│ ├── mimosa-sass
│ │ └── src
│ ├── mimosa-typescript
│ │ └── src
│ ├── ractive
│ ├── typescript
│ │ └── bin
│ │ └── resources ...
│ └── typescript-api
│ └── bower_components
│ └── dt-node
└── views
$ node_modules/.bin/mimosa skel:list # list of skeletons
$ node_modules/.bin/mimosa skel:new angular-node myAngularSampleApp
$ node_modules/.bin/mimosa skel:list
$ npm install pint
$ node_modules/pint/bin/pint --version
0.2.1
'use strict';
module.exports = {
jobs: [
require('./build/test.js'),
]
};
- based in co-routines, generators and promises
- cascading tasks
$ npm install fly fly-esnext
$ node_modules/.bin/fly --version
fly, 1.3.1
const paths = {
scripts: ['src/**/*.js', '!src/ignore/**/*.js']
}
export default async function () {
await this.watch(paths.scripts, 'build')
}
export async function build() {
await this.source(paths.scripts)
.eslint({
rules: {'no-extra-semi': 0}
})
await this.source(paths.scripts)
.babel({
presets: ['es2015', 'stage-0']
})
.concat('app.js')
.target('dist')
}
- operates with
nodes
(source, transformation, merging, etc), that reprensents contents of an input folder - about 89 plugins
$ npm install -g gobble gobble-cli
$ node_modules/.bin/gobble --version
gobble-cli version 0.7.0
$ npm i gobble-sass
node = gobble('foo')
represents foo/ directorynode = gobble([node1, node2, etc])
merging the input nodesnode2 = node1.transform(transformer, options)
applyingtransformer
tonode1
node.include(patterns)
filter files with match topattern
var gobble = require('gobble');
module.exports = gobble([
gobble('build/'),
gobble('assets/styles').transform('sass', {
src: 'page-index.scss',
dest: 'page-index.css'
})
])
and navigate your browser to http://localhost:4567.
$ node_modules/.bin/gobble # to build & server via built-in server
$ node_modules/.bin/gobble build dest/ # to build & save outputs into dest/ folder
- about 121 plugins
$ npm install plumber plumber-cli
$ node_modules/.bin/plumber --version
plumber-cli v0.4.5
plumber v0.4.8
$ npm i plumber-libsass
var all = require('plumber-all')
, glob = require('plumber-glob')
, sass = require('plumber-libsass')
, write = require('plumber-write');
module.exports = function(pipelines) {
var sources = glob.within('assets');
var writeToDist = write('buildx');
// compile stylesheets
pipelines['compile:css'] = [
all(
sources('styles/page-index.css')
),
sass()
writeToDist
];
}
- scaffold (to automatically build the folder-structure)
- set of generators, more than 4000+
$ npm install yo
$ node_modules/.bin/yo --version
1.8.4
$ node_modules/.bin/yo
Sample of material-ui
genetator
$ npm i generator-material-react
$ node_modules/.bin/yo material-react
$ cd app && start
Sample of angular
genetator
$ npm i generator-angular
$ node_modules/.bin/yo angular
$ node_modules/.bin/yo angular --help # to list generators
$ npm i # to install generated nodejs dependencies
$ node_modules/.bin/bower install # to install generated bower dependencies
$ node_modules/.bin/grunt serve
$ node_modules/.bin/yo angular:route about
$ node_modules/.bin/yo angular:route contact