commit ed852c283ec9f7ba36a14dfb7aea0eb48cbf1364
parent f3efa29f261f8ca8614d02b9dfa7edacebdc9c40
Author: Konstantin Tarkus <hello@tarkus.me>
Date: Tue, 20 Oct 2015 16:58:44 +0300
Update build automation scripts
Diffstat:
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',