Frontend Build Tools
with NPM Scripts
Divya Sasidharan
Web Developer @KnightLab
Building story telling tools and apps for journalists and newsrooms
Storytime!
Legacy Build system
“fablib” is a small collection of helper functions for the fabric deployment and system administration tool.
You can download a model copy of a typical font CSS file from our CDN. You may want to put it through a CSS formatter before you start to edit it. If you are familiar with LESS, you may prefer to work from our LESS files on GitHub.
Mission:
Implement Build system
Confession:
I hate build tools
/src
/js/first.js
/js/second.js
/scss/first.scss
/scss/second.scss
/css/main.css
/assets/image.jpg
/assets/logo.jpg
/templates/partial.hbs
/templates/layout.hbs
Compile to CSS
Copy Assets
Compile to HTML
Compile JS
/dist
/assets/image.jpg
/assets/logo.jpg
/templates/index.html
/templates/index.hbs
/js/main.js
Compile + Transpile JS (ES2015)
Compile Templates (Jade/Handlebars/Mustache)
CSS Preprocessers (Sass/LESS)
Style transforms (Modernizr)
File minification
Source Maps
CommonJS/AMD compatibility
Handle Dependencies
Supports testing functionality (regression + integration)
Local Server that supports live reload
Specific Requirements:
Build system == Task Runner
Dependence on plugins and plugin maintenance
Unnecessary layer of abstraction
Tools are opinionated
Fragmented and inadequate documentation
Debugging build tools is pain
Problems with build tools
*ahem* Gulp and Grunt *ahem*
#goals
A build tool that just works
™
(Enter, pad left)
Why NPM Scripts?
We already use Node + NPM
CLI 💖
grunt-sass
grunt-browserify
grunt-webpack
grunt-contrib-jasmine
grunt-contrib-watch
etc.
node-sass
browserify
webpack
jasmine
live-reload
etc.
{
"scripts": {
"test": "jasmine spec/*.js"
}
}
package.json
npm run test
npm run
$Path Config
{
scripts: {
"test": "./node_modules/.bin/jasmine spec/*.js"
}
}
{
scripts: {
"test": "jasmine spec/*.js"
}
}
vs.
🎉🎉 🎉
😭😭 😭
package.json
package.json
Pre + post Hooks
{
scripts: {
"test": "jasmine spec/*.js",
"pretest": "npm run lint",
"lint": "jslint spec/*.js"
}
}
npm run pretest
npm run test
npm run posttest
package.json
&&: Running Multiple Tasks
{
"scripts": {
"compile": "npm run templates && npm run sass && npm run copy",
"templates": "node tasks/compile-hbs.js",
"sass": "node-sass -o dist/css --output-style compact src/scss",
"copy": "npm src/assets dist/assets"
}
}
package.json
| : Streaming Tasks
{
devDependencies: {
"cssmin": "^0.4.3"
},
scripts: {
"prod:css": "node-sass -o dist/css --output-style compact src/scss >
dist/css/base.css | cssmin > dist/css/base.min.css"
}
}
src/scss/*.scss => dist/css/base.css => dist/css/base.min.css
package.json
Decompose Tasks
var cssmin = require('cssmin'),
fs = require('fs'),
css = fs.readFileSync('dist/css/base.css')
minify = css.min(css);
fs.outputFileSync('dist/css/main.min.css', min, 'utf8');
tasks/minify-css.js
{
scripts: {
"sass:min": "node tasks/minify-css.js"
}
}
package.json
npm run sass:min
Write Less code
var source = require('vinyl-source-stream')
var streamify = require('gulp-streamify')
var browserify = require('browserify')
var uglify = require('gulp-uglify')
var gulpify = require('gulpify')
var rename = require('gulp-rename')
var gulp = require('gulp')
// using gulpify:
gulp.task('gulpify', function() {
gulp.src('index.js')
.pipe(gulpify())
.pipe(uglify())
.pipe(rename('bundle.js'))
.pipe(gulp.dest('./'))
})
// using vinyl-source-stream:
gulp.task('browserify', function() {
var bundleStream = browserify('./index.js')
.bundle()
bundleStream
.pipe(source('index.js'))
.pipe(streamify(uglify()))
.pipe(rename('bundle.js'))
.pipe(gulp.dest('./'))
})
{
"devDependencies": {
"browserify": "^13.1.0",
"uglifyjs": "^2.4.10"
},
"scripts": {
"browserify": "browserify index.js |
uglifyjs > bundle.js"
}
}
gulpfile.js
package.json
But.....
what about Cross platform compatibility?
Solution 1: ShellJS
Portable unix shell commands for Node.js
loadMethods = (function() {
var shell = require('shelljs'),
lodash = require('lodash-cli'),
methodString = "",
// add lodash methods used here //
methods = [ 'assign',
'forEach'
],
outputFile = './src/js/lib/lodash.js';
methods.forEach(function(method) {
methodString += "," + method;
})
lodashCommand = 'lodash -d -o ' + outputFile + ' include=' + methodString
shell.exec(lodashCommand, function() {
console.log("Compiling lodash methods...")
});
})();
tasks/loaderdash.js
Solution 2: Npm Packages
rm-rf
cp -r
&
mkdir
rim-raf
ncp
parallelshell
mkdirp
/bash
/node
Solution 3: &&, &, <, >, |
&& : Chaining tasks
& : Run Parallel Tasks
< : STdIn
> : Stdout
| : Pipe
Less Dependencies = Less Maintenance = Dev Happiness
Thank you.
@shortdiv
https://github.com/NUKnightLab/frontend-buildkit
Frontend Build Tools with NPM Scripts
By shortdiv
Frontend Build Tools with NPM Scripts
- 694