ff-stream-web

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

commit ed1414567d5a34990873f5d9202b724dabe2ce09
parent c882f383dc0f1f9316e4a45499abe9fd33ab02e0
Author: Konstantin Tarkus <hello@tarkus.me>
Date:   Thu,  6 Aug 2015 23:05:27 +0300

Replace Gulp with custom build scripts

Diffstat:
M.gitignore | 1+
MREADME.md | 31+++++++++++++++++++------------
Dgulpfile.babel.js | 186-------------------------------------------------------------------------------
Mpackage.json | 39++++++++++++++++++++-------------------
Mpreprocessor.js | 7++++---
Atools/build.js | 20++++++++++++++++++++
Atools/bundle.js | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
Atools/clean.js | 25+++++++++++++++++++++++++
Atools/copy.js | 53+++++++++++++++++++++++++++++++++++++++++++++++++++++
Atools/lib/fs.js | 33+++++++++++++++++++++++++++++++++
Atools/serve.js | 28++++++++++++++++++++++++++++
Atools/start.js | 61+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mwebpack.config.js | 104+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
13 files changed, 375 insertions(+), 265 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -3,4 +3,5 @@ build node_modules +ncp-debug.log npm-debug.log diff --git a/README.md b/README.md @@ -48,7 +48,7 @@ Join [#react-starter-kit](https://gitter.im/kriasoft/react-starter-kit) chatroom │ ├── /utils/ # Utility classes and functions │ ├── /app.js # Client-side startup script │ └── /server.js # Server-side startup script -│── gulpfile.babel.js # Configuration file for automated builds +├── /tools/ # Build automation scripts and utilities │── package.json # The list of 3rd party libraries and utilities │── preprocessor.js # ES6 transpiler settings for Jest └── webpack.config.js # Webpack configuration for bundling and optimization @@ -63,37 +63,38 @@ Just [clone](github-windows://openRepo/https://github.com/kriasoft/react-starter $ git clone -o react-starter-kit -b master --single-branch \ https://github.com/kriasoft/react-starter-kit.git MyApp $ cd MyApp -$ npm install -g gulp # Install Gulp task runner globally $ npm install # Install Node.js components listed in ./package.json +$ npm start # Compile and launch ``` ### How to Build ```shell -$ gulp build # or, `gulp 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, add -`--release` flag. This will minimize your JavaScript; you will also see some warnings from -[uglify](https://github.com/mishoo/UglifyJS) where it removes unused code from your release. +By default, it builds in a *debug* mode. If you need to build in a release +mode, just add `--release` flag. This will optimize the output bundle for +production deployment; you will also see some warnings from +[uglify](https://github.com/mishoo/UglifyJS) where it removes unused code from +your release. ### How to Run ```shell -$ gulp # or, `gulp --release` +$ npm start # or, `npm start --release` ``` -This will start a lightweight development server with LiveReload and +This will start a lightweight development server with "live reload" and synchronized browsing across multiple devices and browsers. ### How to Deploy ```shell -$ gulp build --release # Builds the project in release mode -$ gulp deploy # or, `gulp deploy --production` +$ npm run deploy # or, `npm run deploy --production` ``` -For more information see `deploy` task in `gulpfile.babel.js`. +For more information see `tools/deploy.js`. ### How to Update @@ -150,4 +151,10 @@ the file is. Name the test by appending `-test.js` to the js file. ### License -The MIT License © Konstantin Tarkus ([@koistya](https://twitter.com/koistya)), [Kriasoft](http://www.kriasoft.com) +Copyright © 2014-2015 Kriasoft, LLC. This source code is licensed under the MIT +license found in the [LICENSE.txt](https://github.com/kriasoft/react-starter-kit/blob/master/LICENSE.txt) +file. The documentation to the project is licensed under the +[CC BY-SA 4.0](http://creativecommons.org/licenses/by-sa/4.0/) license. + +--- +Made with ♥ by Konstantin Tarkus ([@koistya](https://twitter.com/koistya)) and [contributors](https://github.com/kriasoft/react-starter-kit/graphs/contributors) diff --git a/gulpfile.babel.js b/gulpfile.babel.js @@ -1,186 +0,0 @@ -/* - * React.js Starter Kit - * Copyright (c) Konstantin Tarkus (@koistya), KriaSoft LLC - * - * This source code is licensed under the MIT license found in the - * LICENSE.txt file in the root directory of this source tree. - */ - -import path from 'path'; -import cp from 'child_process'; -import gulp from 'gulp'; -import gulpLoadPlugins from 'gulp-load-plugins'; -import del from 'del'; -import mkdirp from 'mkdirp'; -import runSequence from 'run-sequence'; -import webpack from 'webpack'; -import minimist from 'minimist'; - -const $ = gulpLoadPlugins(); -const argv = minimist(process.argv.slice(2)); -const src = Object.create(null); - -let watch = false; -let browserSync; - -// The default task -gulp.task('default', ['sync']); - -// Clean output directory -gulp.task('clean', cb => { - del(['.tmp', 'build/*', '!build/.git'], {dot: true}, () => { - mkdirp('build/public', cb); - }); -}); - -// Static files -gulp.task('assets', () => { - src.assets = 'src/public/**'; - return gulp.src(src.assets) - .pipe($.changed('build/public')) - .pipe(gulp.dest('build/public')) - .pipe($.size({title: 'assets'})); -}); - -// Resource files -gulp.task('resources', () => { - src.resources = [ - 'package.json', - 'src/content*/**', - 'src/templates*/**' - ]; - return gulp.src(src.resources) - .pipe($.changed('build')) - .pipe(gulp.dest('build')) - .pipe($.size({title: 'resources'})); -}); - -// Bundle -gulp.task('bundle', cb => { - const config = require('./webpack.config.js'); - const bundler = webpack(config); - const verbose = !!argv.verbose; - let bundlerRunCount = 0; - - function bundle(err, stats) { - if (err) { - throw new $.util.PluginError('webpack', err); - } - - console.log(stats.toString({ - colors: $.util.colors.supportsColor, - hash: verbose, - version: verbose, - timings: verbose, - chunks: verbose, - chunkModules: verbose, - cached: verbose, - cachedAssets: verbose - })); - - if (++bundlerRunCount === (watch ? config.length : 1)) { - return cb(); - } - } - - if (watch) { - bundler.watch(200, bundle); - } else { - bundler.run(bundle); - } -}); - -// Build the app from source code -gulp.task('build', ['clean'], cb => { - runSequence(['assets', 'resources'], ['bundle'], cb); -}); - -// Build and start watching for modifications -gulp.task('build:watch', cb => { - watch = true; - runSequence('build', () => { - gulp.watch(src.assets, ['assets']); - gulp.watch(src.resources, ['resources']); - cb(); - }); -}); - -// Launch a Node.js/Express server -gulp.task('serve', ['build:watch'], cb => { - src.server = [ - 'build/server.js', - 'build/content/**/*', - 'build/templates/**/*' - ]; - let started = false; - let server = (function startup() { - const child = cp.fork('build/server.js', { - env: Object.assign({NODE_ENV: 'development'}, process.env) - }); - child.once('message', message => { - if (message.match(/^online$/)) { - if (browserSync) { - browserSync.reload(); - } - if (!started) { - started = true; - gulp.watch(src.server, function() { - $.util.log('Restarting development server.'); - server.kill('SIGTERM'); - server = startup(); - }); - cb(); - } - } - }); - return child; - })(); - - process.on('exit', () => server.kill('SIGTERM')); -}); - -// Launch BrowserSync development server -gulp.task('sync', ['serve'], cb => { - browserSync = require('browser-sync'); - - browserSync({ - logPrefix: 'RSK', - notify: false, - // Run as an https by setting 'https: true' - // Note: this uses an unsigned certificate which on first access - // will present a certificate warning in the browser. - https: false, - // Informs browser-sync to proxy our Express app which would run - // at the following location - proxy: 'localhost:5000' - }, cb); - - process.on('exit', () => browserSync.exit()); - - gulp.watch(['build/**/*.*'].concat( - src.server.map(file => '!' + file) - ), file => { - browserSync.reload(path.relative(__dirname, file.path)); - }); -}); - -// Deploy via Git -gulp.task('deploy', cb => { - const push = require('git-push'); - const remote = argv.production ? - 'https://github.com/{user}/{repo}.git' : - 'https://github.com/{user}/{repo}-test.git'; - push('./build', remote, cb); -}); - -// Run PageSpeed Insights -gulp.task('pagespeed', cb => { - const pagespeed = require('psi'); - // Update the below URL to the public URL of your site - pagespeed('example.com', { - strategy: 'mobile' - // By default we use the PageSpeed Insights free (no API key) tier. - // Use a Google Developer API key if you have one: http://goo.gl/RkN0vE - // key: 'YOUR_API_KEY' - }, cb); -}); diff --git a/package.json b/package.json @@ -10,7 +10,7 @@ "eventemitter3": "1.1.1", "express": "4.13.3", "fastclick": "1.0.6", - "fbjs": "0.1.0-alpha.6", + "fbjs": "0.1.0-alpha.7", "flux": "2.0.3", "front-matter": "1.0.0", "jade": "1.11.0", @@ -31,28 +31,26 @@ "csscomb": "^3.1.8", "cssnext": "^1.8.3", "del": "^1.2.0", - "eslint": "^1.0.0", - "eslint-loader": "^0.14.2", - "eslint-plugin-react": "^3.2.0", + "eslint": "^1.1.0", + "eslint-loader": "^1.0.0", + "eslint-plugin-react": "^3.2.2", "git-push": "^0.1.1", - "gulp": "^3.9.0", - "gulp-changed": "^1.3.0", - "gulp-if": "^1.2.5", - "gulp-load-plugins": "^1.0.0-rc.1", - "gulp-rename": "^1.2.2", - "gulp-size": "^1.2.3", - "gulp-util": "^3.0.6", + "glob": "^5.0.14", "jest-cli": "^0.4.18", - "minimist": "^1.1.2", + "minimist": "^1.1.3", "mkdirp": "^0.5.1", + "ncp": "^2.0.0", "postcss": "^4.1.16", "postcss-loader": "^0.5.1", "postcss-nested": "^0.3.2", "psi": "^1.0.6", + "react-hot-loader": "^1.2.8", "run-sequence": "^1.1.2", "style-loader": "^0.12.3", "url-loader": "^0.5.6", - "webpack": "^1.11.0" + "webpack": "^1.11.0", + "webpack-dev-middleware": "^1.2.0", + "webpack-hot-middleware": "^1.2.0" }, "jest": { "rootDir": "./src", @@ -62,12 +60,15 @@ ] }, "scripts": { - "start": "node server.js", - "lint": "eslint src gulpfile.babel.js webpack.config.js && csscomb src/components --lint", - "comb": "csscomb src/components --verbose", + "lint": "eslint src tools", + "csslint": "csscomb src/components --lint --verbose", + "csscomb": "csscomb src/components --verbose", "test": "eslint src && jest", - "build": "gulp build", - "serve": "gulp serve", - "sync": "gulp sync" + "clean": "babel-node --eval \"require('./tools/clean')().catch(err => console.log(err.stack))\"", + "copy": "babel-node --eval \"require('./tools/copy')().catch(err => console.log(err.stack))\"", + "bundle": "babel-node --eval \"require('./tools/bundle')().catch(err => console.log(err.stack))\"", + "build": "babel-node --eval \"require('./tools/build')().catch(err => console.log(err.stack))\"", + "serve": "babel-node --eval \"require('./tools/serve')().catch(err => console.log(err.stack))\"", + "start": "babel-node --eval \"require('./tools/start')().catch(err => console.log(err.stack))\"" } } diff --git a/preprocessor.js b/preprocessor.js @@ -1,6 +1,7 @@ -/* - * React.js Starter Kit - * Copyright (c) Konstantin Tarkus (@koistya), KriaSoft LLC +/** + * 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. diff --git a/tools/build.js b/tools/build.js @@ -0,0 +1,20 @@ +/** + * 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. + */ + +/** + * Compiles the project from source files into a distributable + * format and copies it to the output (build) folder. + */ +export default async () => { + console.log('build'); + await require('./clean')(); + await require('./copy')(); + await require('./bundle')(); +}; + diff --git a/tools/bundle.js b/tools/bundle.js @@ -0,0 +1,52 @@ +/** + * 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 webpack from 'webpack'; +import config from '../webpack.config'; +import minimist from 'minimist'; + +const argv = minimist(process.argv.slice(2)); + +/** + * Bundles JavaScript, CSS and images into one or more packages + * ready to be used in a browser. + */ +export default async () => new Promise((resolve, reject) => { + console.log('bundle'); + const bundler = webpack(config); + const verbose = !!argv.verbose; + let bundlerRunCount = 0; + + function bundle(err, stats) { + if (err) { + return reject(err); + } + + console.log(stats.toString({ + colors: true, + hash: verbose, + version: verbose, + timings: verbose, + chunks: verbose, + chunkModules: verbose, + cached: verbose, + cachedAssets: verbose + })); + + if (++bundlerRunCount === (global.watch ? config.length : 1)) { + return resolve(); + } + } + + if (global.watch) { + bundler.watch(200, bundle); + } else { + bundler.run(bundle); + } +}); diff --git a/tools/clean.js b/tools/clean.js @@ -0,0 +1,25 @@ +/** + * 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 del from 'del'; +import fs from './lib/fs'; + +/** + * Cleans up the output (build) directory. + */ +export default () => new Promise((resolve, reject) => { + console.log('clean'); + del(['.tmp', 'build/*', '!build/.git'], {dot: true}, err => { + if (err) { + reject(err); + } else { + fs.makeDir('build/public').then(resolve, reject); + } + }); +}); diff --git a/tools/copy.js b/tools/copy.js @@ -0,0 +1,53 @@ +/** + * 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 as copy } from 'ncp'; + +/** + * Copies static files such as robots.txt, favicon.ico to the + * output (build) folder. + */ +export default () => { + console.log('copy'); + return Promise.all([ + + // Static files + new Promise((resolve, reject) => { + copy('src/public', 'build/public', err => { + if (err) { + reject(err); + } else { + resolve(); + } + }) + }), + + // Files with content (e.g. *.md files) + new Promise((resolve, reject) => { + copy('src/content', 'build/content', err => { + if (err) { + reject(err); + } else { + resolve(); + } + }) + }), + + // Website and email templates + new Promise((resolve, reject) => { + copy('src/templates', 'build/templates', err => { + if (err) { + reject(err); + } else { + resolve(); + } + }) + }) + ]); +}; diff --git a/tools/lib/fs.js b/tools/lib/fs.js @@ -0,0 +1,33 @@ +/** + * 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 fs from 'fs'; +import mkdirp from 'mkdirp'; + +const writeFile = (filename, contents) => new Promise((resolve, reject) => { + fs.writeFile(filename, contents, 'utf8', err => { + if (err) { + reject(err); + } else { + resolve(); + } + }); +}); + +const makeDir = name => new Promise((resolve, reject) => { + mkdirp(name, err => { + if (err) { + reject(err); + } else { + resolve(); + } + }); +}); + +export default { writeFile, makeDir }; diff --git a/tools/serve.js b/tools/serve.js @@ -0,0 +1,28 @@ +/** + * 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 path from 'path'; +import cp from 'child_process'; + +/** + * Launches Node.js/Express web server in a separate (forked) process. + */ +export default () => new Promise((resolve, reject) => { + console.log('serve'); + const server = cp.fork(path.join(__dirname, '../build/server.js'), { + env: Object.assign({NODE_ENV: 'development'}, process.env) + }); + server.once('message', message => { + if (message.match(/^online$/)) { + resolve(); + } + }); + server.once('error', err => reject(error)); + process.on('exit', () => server.kill('SIGTERM')); +}); diff --git a/tools/start.js b/tools/start.js @@ -0,0 +1,61 @@ +/** + * 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 browserSync from 'browser-sync'; +import webpack from 'webpack'; +import webpackDevMiddleware from 'webpack-dev-middleware'; +import webpackHotMiddleware from 'webpack-hot-middleware'; + +global.WATCH = true; +const config = require('../webpack.config')[0]; // Client-side bundle config +const bundler = webpack(config); + +/** + * Launches a development web server with "live reload" functionality - + * synchronizing URLs, interactions and code changes across multiple devices. + */ +export default async () => { + + await require('./build')(); + await require('./serve')(); + + browserSync({ + proxy: { + + target: 'localhost:5000', + + middleware: [ + webpackDevMiddleware(bundler, { + // IMPORTANT: dev middleware can't access config, so we should + // provide publicPath by ourselves + publicPath: config.output.publicPath, + + // pretty colored output + stats: config.stats, + + hot: true, + historyApiFallback: true + + // for other settings see + // http://webpack.github.io/docs/webpack-dev-middleware.html + }), + + // bundler should be the same as above + webpackHotMiddleware(bundler) + ] + }, + + // no need to watch '*.js' here, webpack will take care of it for us, + // including full page reloads if HMR won't work + files: [ + 'build/public/**/*.css', + 'build/public/**/*.html' + ] + }); +}; diff --git a/webpack.config.js b/webpack.config.js @@ -1,6 +1,7 @@ -/* - * React.js Starter Kit - * Copyright (c) Konstantin Tarkus (@koistya), KriaSoft LLC +/** + * 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. @@ -13,6 +14,8 @@ import minimist from 'minimist'; const argv = minimist(process.argv.slice(2)); const DEBUG = !argv.release; +const WATCH = global.WATCH === undefined ? false : global.WATCH; +const VERBOSE = global.VERBOSE === undefined ? false : global.VERBOSE; const STYLE_LOADER = 'style-loader/useable'; const CSS_LOADER = DEBUG ? 'css-loader' : 'css-loader?minimize'; const AUTOPREFIXER_BROWSERS = [ @@ -37,7 +40,7 @@ const GLOBALS = { const config = { output: { - publicPath: './', + publicPath: '/', sourcePrefix: ' ' }, @@ -46,7 +49,14 @@ const config = { stats: { colors: true, - reasons: DEBUG + reasons: DEBUG, + hash: VERBOSE, + version: VERBOSE, + timings: VERBOSE, + chunks: VERBOSE, + chunkModules: VERBOSE, + cached: VERBOSE, + cachedAssets: VERBOSE }, plugins: [ @@ -58,36 +68,29 @@ const config = { }, module: { - loaders: [ - { - test: /\.css$/, - loader: `${STYLE_LOADER}!${CSS_LOADER}!postcss-loader` - }, - { - test: /\.gif/, - loader: 'url-loader?limit=10000&mimetype=image/gif' - }, - { - test: /\.jpg/, - loader: 'url-loader?limit=10000&mimetype=image/jpg' - }, - { - test: /\.png/, - loader: 'url-loader?limit=10000&mimetype=image/png' - }, - { - test: /\.svg/, - loader: 'url-loader?limit=10000&mimetype=image/svg+xml' - }, - { - test: /\.jsx?$/, - include: [ - path.resolve(__dirname, 'node_modules/react-routing/src'), - path.resolve(__dirname, 'src') - ], - loader: 'babel-loader' - } - ] + loaders: [{ + test: /\.txt/, + loader: 'file?name=[path][name].[ext]' + }, { + test: /\.gif/, + loader: 'url-loader?limit=10000&mimetype=image/gif' + }, { + test: /\.jpg/, + loader: 'url-loader?limit=10000&mimetype=image/jpg' + }, { + test: /\.png/, + loader: 'url-loader?limit=10000&mimetype=image/png' + }, { + test: /\.svg/, + loader: 'url-loader?limit=10000&mimetype=image/svg+xml' + }, { + test: /\.jsx?$/, + include: [ + path.resolve(__dirname, 'node_modules/react-routing/src'), + path.resolve(__dirname, 'src') + ], + loaders: [...(WATCH ? ['react-hot'] : []), 'babel-loader'] + }] }, postcss: [ @@ -102,9 +105,13 @@ const config = { // ----------------------------------------------------------------------------- const appConfig = merge({}, config, { - entry: './src/app.js', + entry: [...(WATCH ? [ + 'webpack/hot/dev-server', + 'webpack-hot-middleware/client'] : []), + './src/app.js' + ], output: { - path: './build/public', + path: path.join(__dirname, 'build', 'public'), filename: 'app.js' }, devtool: DEBUG ? 'source-map' : false, @@ -114,8 +121,17 @@ const appConfig = merge({}, config, { new webpack.optimize.DedupePlugin(), new webpack.optimize.UglifyJsPlugin(), new webpack.optimize.AggressiveMergingPlugin() - ]) - ) + ]).concat(WATCH ? [ + new webpack.HotModuleReplacementPlugin(), + new webpack.NoErrorsPlugin() + ] : []) + ), + module: { + loaders: [...config.module.loaders, { + test: /\.css$/, + loader: `${STYLE_LOADER}!${CSS_LOADER}!postcss-loader` + }] + } }); // @@ -154,12 +170,10 @@ const serverConfig = merge({}, config, { { raw: true, entryOnly: false }) ), module: { - loaders: config.module.loaders.map(function(loader) { - // Remove style-loader - return merge(loader, { - loader: loader.loader = loader.loader.replace(STYLE_LOADER + '!', '') - }); - }) + loaders: [...config.module.loaders, { + test: /\.css$/, + loader: `${CSS_LOADER}!postcss-loader` + }] } });