NTUOSC Build System

20160603 Elantris
Note: https://hackmd.io/s/N1ERJm5Ux

tags: 2016 ntuosc javascript

Setup

Install latest npm, bower, pnpm, gulp in global

sudo npm install -g npm bower pnpm gulp

Find a place to clone the repository and install dependencies

git clone git@github.com:Elantris/ntuosc-course-20160603.git
cd ntuosc-course-20160603
pnpm install

Front-end Tools

Pug

HTML Template Engine

Language Reference

index.pug

doctype html
html(lang="en")
    head
        meta(charset="UTF-8")
        title NTUOSC Course 20160603
    body
        .container
            h1 Color Thief
        #color-thief

Use cli command to compile .pug file

$ npm install pug-cli -g
$ pug [options] [dir|file ...]

Pug's CLI GitHub

Sass

CSS Extension Language

Official Site

main.scss

body {
    background: #eee;
}

.center {
    text-align: center;
}

.row {
    margin-bottom: 64px;
}

Use cli command to compile .scss file

$ gem install sass
$ sass main.scss > main.css

React

JavaScript Framework

Official Site

main.jsx

var React = require('react');
var ReactDOM = require('react-dom');

var colorThief = new ColorThief();

var ColorBlock = React.createClass({
    render: function() {
        return (
            <div>Color Block</div>
        );
    }
});

var ColorThiefContainer = React.createClass({
    getInitialState: function() {
        return {};
    },
    handleFile: function() {},
    handleLoad: function() {},
    render: function() {
        return (
            <div>Color Thief</div>
        );
    }
});

ReactDOM.render(<ColorThiefContainer />, document.getElementById('color-thief'));

Use cli command to compile .jsx file

$ npm install --save react react-dom babelify babel-preset-react
$ browserify -t [ babelify ] main.js -o bundle.js

Gulp

Build System
Official Site

File Structure

.
โ”œโ”€โ”€ bower.json
โ”œโ”€โ”€ bower_components
โ”œโ”€โ”€ gulpfile.js
โ”œโ”€โ”€ index.html
โ”œโ”€โ”€ node_modules
โ”œโ”€โ”€ package.json
โ””โ”€โ”€ src
    โ”œโ”€โ”€ scripts
    โ”‚   โ””โ”€โ”€ main.jsx
    โ”œโ”€โ”€ styles
    โ”‚   โ””โ”€โ”€ main.scss
    โ””โ”€โ”€ views
        โ””โ”€โ”€ index.pug

Include dependencies

gulpfile.js

var fs = require('fs');
var gulp = require('gulp'); // https://github.com/gulpjs/gulp
var moment = require('moment'); // https://github.com/moment/moment/
var clc = require('cli-color'); // https://github.com/medikoo/cli-color
var rename = require('gulp-rename'); // https://github.com/hparra/gulp-rename
var sourcemaps = require('gulp-sourcemaps'); // https://github.com/floridoo/gulp-sourcemaps
var gulpif = require('gulp-if'); // https://github.com/robrich/gulp-if

var production = (process.env.NODE_ENV === 'production');

Colorful Log

function colorLog(color, message) {
    console.log('[%s] %s',
        clc.blackBright(moment().format('HH:mm:ss')),
        clc[color](message)
    );
}

function errorLog(err) {
    colorLog('redBright', err.stack);
}

Task: Concat Libraries

var cssmin = require('gulp-cssmin'); // https://github.com/chilijung/gulp-cssmin
var uglify = require('gulp-uglify'); // https://github.com/terinjokes/gulp-uglify
var concat = require('gulp-concat'); // https://github.com/contra/gulp-concat

gulp.task('move', function() {
    gulp.src([
            './bower_components/skeleton/css/normalize.css',
            './bower_components/skeleton/css/skeleton.css',
        ])
        .pipe(concat('lib.min.css'))
        .pipe(cssmin())
        .pipe(gulp.dest('./public/css/'));

    gulp.src([
            './node_modules/jquery/dist/jquery.min.js',
            './node_modules/color-thief/js/color-thief.js',
        ])
        .pipe(concat('lib.min.js'))
        .pipe(uglify({ mangle: false }))
        .pipe(gulp.dest('./public/js/'));
});

gulp.src: select files

Task: Compile Pug

var pug = require('gulp-pug'); // https://github.com/jamen/gulp-pug

gulp.task('pug', function() {
    gulp.src('./src/views/*.pug')
        .pipe(pug())
        .pipe(gulp.dest('./'));
});

gulp.task('pug:watch', ['pug'], function() {
    gulp.watch('./src/views/**/*.pug', ['pug', reload]);
});

gulp.watch: watch files changed

Task: Compile Sass

'use strict';

var sass = require('gulp-sass'); // https://github.com/dlmanning/gulp-sass

gulp.task('sass', function() {
    gulp.src('./src/styles/*.scss')
        .pipe(gulpif(production, sourcemaps.init()))
        .pipe(sass({ outputStyle: 'compressed' }).on('error', sass.logError))
        .pipe(gulpif(production, sourcemaps.write()))
        .pipe(rename({ suffix: '.min' }))
        .pipe(gulp.dest('./public/css/'));
});

gulp.task('sass:watch', ['sass'], function() {
    gulp.watch('./src/styles/**/*.scss', ['sass', reload]);
});

Task: Compile JSX

var browserify = require('browserify'); // https://github.com/substack/node-browserify
var watchify = require('watchify'); // https://github.com/substack/watchify
var source = require('vinyl-source-stream'); // https://github.com/hughsk/vinyl-source-stream
var buffer = require('vinyl-buffer'); // https://github.com/hughsk/vinyl-buffer
var reactify = require('reactify'); // https://github.com/andreypopp/reactify

var b = browserify('./src/scripts/main.jsx', {
    cache: {},
    packageCache: {},
    debug: !production,
    transform: [reactify]
});

gulp.task('react', function() {
    bundle();
});

gulp.task('react:watch', ['react'], function() {
    b.plugin(watchify);
    b.on('log', function(msg) {
        colorLog('yellowBright', msg);
        reload();
    });
    b.on('update', bundle);
});

function bundle() {
    b.bundle()
        .on('error', errorLog)
        .pipe(source('main.min.js'))
        .pipe(buffer())
        .pipe(gulpif(production, uglify()))
        .on('error', errorLog)
        .pipe(gulp.dest('./public/js/'));
}

Task: BrowserSync

var browserSync = require('browser-sync').create(); // https://github.com/Browsersync/browser-sync
var reload = browserSync.reload;

gulp.task('browser-sync', function() {
    browserSync.init({
        server: {
            baseDir: "./"
        }
    });
});

gulp.task('watch', ['pug:watch', 'sass:watch', 'react:watch', 'browser-sync']);

Task: Default

gulp.task('default', ['move', 'pug', 'sass', 'react']);

Run scripts

Run default task and specific task

gulp
gulp react

Inside the package.json

{
    "scripts": {
        "start": "pnpm install && bower install && NODE_ENV=production gulp",
        "test": "NODE_ENV=production gulp watch",
        "build:all": "NODE_ENV=production gulp"
    }
}

Run npm script

npm start
npm test
npm run build:all