ff-stream-web

git clone git://archive.git.mtrnord.blog/MTRNord/ff-stream-web.git
Log | Files | Refs | README | LICENSE

commit ed852c283ec9f7ba36a14dfb7aea0eb48cbf1364
parent f3efa29f261f8ca8614d02b9dfa7edacebdc9c40
Author: Konstantin Tarkus <hello@tarkus.me>
Date:   Tue, 20 Oct 2015 16:58:44 +0300

Update build automation scripts

Diffstat:
MCHANGELOG.md | 2++
MREADME.md | 8++++----
Mpackage.json | 15++++++++-------
Mtools/build.js | 14++++++++------
Mtools/bundle.js | 39+++++++++++++++++++++------------------
Mtools/clean.js | 9+++++----
Mtools/copy.js | 19+++++++++++--------
Mtools/deploy.js | 14++++++++------
Dtools/lib/copy.js | 14--------------
Dtools/lib/task.js | 21---------------------
Atools/run.js | 29+++++++++++++++++++++++++++++
Mtools/serve.js | 68++++++++++++++++++++++++++++++++++++++++----------------------------
Mtools/start.js | 12+++++++-----
Mtools/webpack.config.js | 4++--
14 files changed, 145 insertions(+), 123 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md @@ -4,6 +4,8 @@ All notable changes to this project will be documented in this file. ### [Unreleased][unreleased] +- Update build automation scripts to use plain functions +- Add support of `--release` and `--verbose` flags to build scripts - Add `CHANGELOG.md` file with a list of notable changes to this project ### [v0.4.1] - 2015-10-04 diff --git a/README.md b/README.md @@ -78,17 +78,17 @@ $ npm start # Compile and launch ### How to Build ```shell -$ npm run build # or, `npm run build -- release` +$ npm run build # or, `npm run build -- --release` ``` By default, it builds in *debug* mode. If you need to build in release -mode, just add a `-- release` flag. This will optimize the output bundle for +mode, just add a `-- --release` flag. This will optimize the output bundle for production. ### How to Run ```shell -$ npm start # or, `npm start -- release` +$ npm start # or, `npm start -- --release` ``` This will start a light-weight development server with "live reload" and @@ -97,7 +97,7 @@ synchronized browsing across multiple devices and browsers. ### How to Deploy ```shell -$ npm run deploy # or, `npm run deploy -- production` +$ npm run deploy # or, `npm run deploy -- --production` ``` For more information see `tools/deploy.js`. diff --git a/package.json b/package.json @@ -6,6 +6,7 @@ }, "dependencies": { "babel-core": "5.8.25", + "bluebird": "2.10.2", "classnames": "2.2.0", "eventemitter3": "1.1.1", "express": "4.13.3", @@ -74,12 +75,12 @@ "csslint": "csscomb src/components --lint --verbose", "csscomb": "csscomb src/components --verbose", "test": "eslint src && jest", - "clean": "babel-node --eval \"require('./tools/clean')().catch(err => console.error(err.stack))\"", - "copy": "babel-node --eval \"require('./tools/copy')().catch(err => console.error(err.stack))\"", - "bundle": "babel-node --eval \"require('./tools/bundle')().catch(err => console.error(err.stack))\"", - "build": "babel-node --eval \"require('./tools/build')().catch(err => console.error(err.stack))\"", - "deploy": "babel-node --eval \"require('./tools/deploy')().catch(err => console.error(err.stack))\"", - "serve": "babel-node --eval \"require('./tools/serve')().catch(err => console.error(err.stack))\"", - "start": "babel-node --eval \"require('./tools/start')().catch(err => console.error(err.stack))\"" + "clean": "babel-node tools/run clean", + "copy": "babel-node tools/run copy", + "bundle": "babel-node tools/run bundle", + "build": "babel-node tools/run build", + "deploy": "babel-node tools/run deploy", + "serve": "babel-node --ignore=build/server.js --ignore=node_modules/ tools/run serve", + "start": "babel-node --ignore=node_modules tools/run start" } } diff --git a/tools/build.js b/tools/build.js @@ -7,15 +7,17 @@ * LICENSE.txt file in the root directory of this source tree. */ -import task from './lib/task'; +import run from './run'; /** * Compiles the project from source files into a distributable * format and copies it to the output (build) folder. */ -export default task('build', async () => { - await require('./clean')(); - await require('./copy')(); - await require('./bundle')(); -}); +async function build() { + await run(require('./clean')); + await run(require('./copy')); + await run(require('./bundle')); +} + +export default build; diff --git a/tools/bundle.js b/tools/bundle.js @@ -8,32 +8,35 @@ */ import webpack from 'webpack'; -import task from './lib/task'; import webpackConfig from './webpack.config'; /** * Bundles JavaScript, CSS and images into one or more packages * ready to be used in a browser. */ -export default task('bundle', async () => new Promise((resolve, reject) => { - const bundler = webpack(webpackConfig); - let bundlerRunCount = 0; +function bundle() { + return new Promise((resolve, reject) => { + const bundler = webpack(webpackConfig); + let bundlerRunCount = 0; - function bundle(err, stats) { - if (err) { - return reject(err); - } + function onComplete(err, stats) { + if (err) { + return reject(err); + } + + console.log(stats.toString(webpackConfig[0].stats)); - console.log(stats.toString(webpackConfig[0].stats)); + if (++bundlerRunCount === (global.WATCH ? webpackConfig.length : 1)) { + return resolve(); + } + } - if (++bundlerRunCount === (global.WATCH ? webpackConfig.length : 1)) { - return resolve(); + if (global.WATCH) { + bundler.watch(200, onComplete); + } else { + bundler.run(onComplete); } - } + }); +} - if (global.WATCH) { - bundler.watch(200, bundle); - } else { - bundler.run(bundle); - } -})); +export default bundle; diff --git a/tools/clean.js b/tools/clean.js @@ -8,13 +8,14 @@ */ import del from 'del'; -import task from './lib/task'; import fs from './lib/fs'; /** * Cleans up the output (build) directory. */ -export default task('clean', async () => { - await del(['.tmp', 'build/*', '!build/.git'], {dot: true}); +async function clean() { + await del(['.tmp', 'build/*', '!build/.git'], { dot: true }); await fs.makeDir('build/public'); -}); +} + +export default clean; diff --git a/tools/copy.js b/tools/copy.js @@ -9,19 +9,20 @@ import path from 'path'; import replace from 'replace'; -import task from './lib/task'; -import copy from './lib/copy'; +import Promise from 'bluebird'; import watch from './lib/watch'; /** * Copies static files such as robots.txt, favicon.ico to the * output (build) folder. */ -export default task('copy', async () => { +async function copy() { + const ncp = Promise.promisify(require('ncp')); + await Promise.all([ - copy('src/public', 'build/public'), - copy('src/content', 'build/content'), - copy('package.json', 'build/package.json'), + ncp('src/public', 'build/public'), + ncp('src/content', 'build/content'), + ncp('package.json', 'build/package.json'), ]); replace({ @@ -36,7 +37,9 @@ export default task('copy', async () => { const watcher = await watch('src/content/**/*.*'); watcher.on('changed', async (file) => { const relPath = file.substr(path.join(__dirname, '../src/content/').length); - await copy(`src/content/${relPath}`, `build/content/${relPath}`); + await ncp(`src/content/${relPath}`, `build/content/${relPath}`); }); } -}); +} + +export default copy; diff --git a/tools/deploy.js b/tools/deploy.js @@ -8,7 +8,7 @@ */ import GitRepo from 'git-repository'; -import task from './lib/task'; +import run from './run'; import fetch from './lib/fetch'; // TODO: Update deployment URL @@ -23,9 +23,9 @@ const getRemote = (slot) => ({ * Deploy the contents of the `/build` folder to a remote * server via Git. Example: `npm run deploy -- production` */ -export default task('deploy', async () => { +async function deploy() { // By default deploy to the staging deployment slot - const remote = getRemote(process.argv.includes('production') ? null : 'staging'); + const remote = getRemote(process.argv.includes('--production') ? null : 'staging'); // Initialize a new Git repository inside the `/build` folder // if it doesn't exist yet @@ -41,8 +41,8 @@ export default task('deploy', async () => { // Build the project in RELEASE mode which // generates optimized and minimized bundles - process.argv.push('release'); - await require('./build')(); + process.argv.push('--release'); + await run(require('./build')); // Push the contents of the build folder to the remote server via Git await repo.add('--all .'); @@ -52,4 +52,6 @@ export default task('deploy', async () => { // Check if the site was successfully deployed const response = await fetch(remote.website); console.log(`${remote.website} -> ${response.statusCode}`); -}); +} + +export default deploy; diff --git a/tools/lib/copy.js b/tools/lib/copy.js @@ -1,14 +0,0 @@ -/** - * React Starter Kit (http://www.reactstarterkit.com/) - * - * Copyright © 2014-2015 Kriasoft, LLC. All rights reserved. - * - * This source code is licensed under the MIT license found in the - * LICENSE.txt file in the root directory of this source tree. - */ - -import ncp from 'ncp'; - -export default (source, dest) => new Promise((resolve, reject) => { - ncp(source, dest, err => err ? reject(err) : resolve()); -}); diff --git a/tools/lib/task.js b/tools/lib/task.js @@ -1,21 +0,0 @@ -/** - * React Starter Kit (http://www.reactstarterkit.com/) - * - * Copyright © 2014-2015 Kriasoft, LLC. All rights reserved. - * - * This source code is licensed under the MIT license found in the - * LICENSE.txt file in the root directory of this source tree. - */ - -function format(time) { - return time.toTimeString().replace(/.*(\d{2}:\d{2}:\d{2}).*/, '$1'); -} - -export default (name, fn) => async () => { - const start = new Date(); - console.log(`[${format(start)}] Starting '${name}'...`); - await fn(); - const end = new Date(); - const time = end.getTime() - start.getTime(); - console.log(`[${format(end)}] Finished '${name}' after ${time} ms`); -}; diff --git a/tools/run.js b/tools/run.js @@ -0,0 +1,29 @@ +/** + * React Starter Kit (http://www.reactstarterkit.com/) + * + * Copyright © 2014-2015 Kriasoft, LLC. All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE.txt file in the root directory of this source tree. + */ + +function format(time) { + return time.toTimeString().replace(/.*(\d{2}:\d{2}:\d{2}).*/, '$1'); +} + +async function run(fn, options) { + const start = new Date(); + console.log(`[${format(start)}] Starting '${fn.name}'...`); + await fn(options); + const end = new Date(); + const time = end.getTime() - start.getTime(); + console.log(`[${format(end)}] Finished '${fn.name}' after ${time} ms`); +} + +if (process.mainModule.children.length === 0 && process.argv.length > 2) { + delete require.cache[__filename]; + const module = process.argv[2]; + run(require('./' + module + '.js')).catch(err => console.error(err.stack)); +} + +export default run; diff --git a/tools/serve.js b/tools/serve.js @@ -9,37 +9,49 @@ import path from 'path'; import cp from 'child_process'; -import task from './lib/task'; import watch from './lib/watch'; /** * Launches Node.js/Express web server in a separate (forked) process. */ -export default task('serve', () => new Promise((resolve, reject) => { - function start() { - const server = cp.fork(path.join(__dirname, '../build/server.js'), { - env: Object.assign({ NODE_ENV: 'development' }, process.env), - silent: false, - }); - - server.once('message', message => { - if (message.match(/^online$/)) { - resolve(); - } - }); - server.once('error', err => reject(err)); - process.on('exit', () => server.kill('SIGTERM')); - return server; - } - - let server = start(); - - if (global.WATCH) { - watch('build/server.js').then(watcher => { - watcher.on('changed', () => { - server.kill('SIGTERM'); - server = start(); +function serve() { + return new Promise((resolve, reject) => { + function start() { + const server = cp.spawn('node', [path.join(__dirname, '../build/server.js')], { + env: Object.assign({ NODE_ENV: 'development' }, process.env), + silent: false, }); - }); - } -})); + + server.stdout.on('data', data => { + const message = data.toString(); + if (message.match(' running ')) { + console.log(message.trim()); + resolve(); + } else { + console.log(message); + } + }); + + server.stderr.once('data', data => { + reject(data.toString()); + }); + + server.on('error', err => reject(err)); + process.on('exit', () => server.kill('SIGTERM')); + return server; + } + + let server = start(); + + if (global.WATCH) { + watch('build/server.js').then(watcher => { + watcher.on('changed', () => { + server.kill('SIGTERM'); + server = start(); + }); + }); + } + }); +} + +export default serve; diff --git a/tools/start.js b/tools/start.js @@ -11,7 +11,7 @@ import browserSync from 'browser-sync'; import webpack from 'webpack'; import webpackDevMiddleware from 'webpack-dev-middleware'; import webpackHotMiddleware from 'webpack-hot-middleware'; -import task from './lib/task'; +import run from './run'; global.WATCH = true; const webpackConfig = require('./webpack.config')[0]; // Client-side bundle configuration @@ -21,9 +21,9 @@ const bundler = webpack(webpackConfig); * Launches a development web server with "live reload" functionality - * synchronizing URLs, interactions and code changes across multiple devices. */ -export default task('start', async () => { - await require('./build')(); - await require('./serve')(); +async function start() { + await run(require('./build')); + await run(require('./serve')); browserSync({ proxy: { @@ -57,4 +57,6 @@ export default task('start', async () => { 'build/templates/**/*.*', ], }); -}); +} + +export default start; diff --git a/tools/webpack.config.js b/tools/webpack.config.js @@ -11,8 +11,8 @@ import path from 'path'; import webpack from 'webpack'; import merge from 'lodash.merge'; -const DEBUG = !process.argv.includes('release'); -const VERBOSE = process.argv.includes('verbose'); +const DEBUG = !process.argv.includes('--release'); +const VERBOSE = process.argv.includes('--verbose'); const WATCH = global.WATCH === undefined ? false : global.WATCH; const AUTOPREFIXER_BROWSERS = [ 'Android 2.3',