commit aac5e419725b1fb9c6bad5110b7f014efc5907ae
parent 52abd70a5d8ebb43fa30922767d32405806ce483
Author: Konstantin Tarkus <hello@tarkus.me>
Date: Fri, 29 Aug 2014 13:10:25 +0400
Refactor gulpfile.js; add BrowserSync, autoprefixer, csscomb, gh-pages, imagemin plug-ins
Diffstat:
4 files changed, 159 insertions(+), 137 deletions(-)
diff --git a/README.md b/README.md
@@ -1,19 +1,25 @@
# Facebook React Starter Kit
-> **Web Application Front-end Starter Kit** built with [Facebook React](https://github.com/facebook/react).
+> This project template is a skeleton for a typical web application or
+> single-page application (SPA) based on Facebook React. You can use it
+> to quickly bootstrap your web application projects. It contains only
+> client-side components and development tools and is recommended to be
+> paired with a server-side project similar to [ASP.NET Server Template]
+> (https://github.com/kriasoft/AspNet-Server-Template).
-**Source**: [https://github.com/kriasoft/React-Seed](https://github.com/kriasoft/React-Seed)
+**Source**: [https://github.com/kriasoft/react-seed](https://github.com/kriasoft/React-Seed)
-#### Runtime Components:
+#### Runtime Components
* [React](https://facebook.github.io/react/) - A JavaScript library for building user interfaces, developed by Facebook
* [Bootstrap](http://getbootstrap.com/) - CSS framework for developing responsive, mobile first interfaces
* [jQuery](http://jquery.com/) - a JavaScript library designed to simplify the client-side scripting of HTML
-#### Development Tools:
+#### Development Tools
- * [Webpack](http://webpack.github.io/) - Compiles front-end source code into modules / bundles
+ * [BrowserSync](http://www.browsersync.io/) - Lightweight HTTP server for development
* [Gulp](http://gulpjs.com/) - JavaScript streaming build system and task automation
+ * [Webpack](http://webpack.github.io/) - Compiles front-end source code into modules / bundles
* [Karma](http://karma-runner.github.io/) - JavaScript unit-test runner (coming)
* [Protractor](https://github.com/angular/protractor) - End-to-end test framework (coming)
@@ -21,7 +27,6 @@
```
.
-├── /bower_components/ # 3rd party client-side libraries
├── /build/ # The folder for compiled output
├── /docs/ # Documentation files
├── /node_modules/ # Node.js-based dev tools and utilities
@@ -46,7 +51,8 @@
To get started you can simply clone the repo and install the dependencies:
```shell
-> git clone https://github.com/kriasoft/React-Seed MyApp && cd MyApp
+> git clone -o base https://github.com/kriasoft/React-Seed MyApp
+> cd MyApp
> npm install -g gulp # Install Gulp task runner globally
> npm install # Install Node.js components listed in ./package.json
```
@@ -54,7 +60,7 @@ To get started you can simply clone the repo and install the dependencies:
To compile the application and start a dev server just run:
```shell
-> gulp --watch # or, `gulp --watch --release`
+> gulp # or, `gulp --release`
```
To build the project, without starting a web server run:
@@ -63,18 +69,24 @@ To build the project, without starting a web server run:
> gulp build # or, `gulp build --release`
```
-Now browse to the app at [http://localhost:3000/](http://localhost:3000/)
+To deploy the application, run:
+
+```shell
+> gulp deploy # or, `gulp deploy --production`
+```
-### Authors
+### Contributors
- * [Konstantin Tarkus](https://angel.co/koistya) ([@koistya](https://twitter.com/koistya)), KriaSoft LLC
+Konstantin Tarkus — [@koistya](https://twitter.com/koistya)
### Support
-Need help or willing to contribute? Contact me via email: [hello@tarkus.me](mailto:hello@tarkus.me) or Skype: koistya
+Have questions, feedback or need help? Contact [support@kriasoft.com](mailto:support@kriasoft.com)
+or schedule a mentoring session on [codementor.io/koistya](https://www.codementor.io/koistya).
### Copyright
- * Source code is licensed under the MIT License. See [LICENSE.txt](./LICENSE.txt) file in the project root.
- * Documentation to the project is licensed under the [CC BY 4.0](http://creativecommons.org/licenses/by/4.0/) license.
- * React logo image is a trademark of Facebook, Inc.
+Source code is licensed under the MIT License. See [LICENSE.txt](./LICENSE.txt)
+file in the project root. Documentation to the project is licensed under the
+[CC BY 4.0](http://creativecommons.org/licenses/by/4.0/) license. React logo
+image is a trademark of Facebook, Inc.
diff --git a/config/webpack.config.js b/config/webpack.config.js
@@ -4,13 +4,13 @@
*/
/*
- * Webpack Configuration
- * http://webpack.github.io/docs/configuration.html
+ * Webpack configuration. For more information visit
+ * http://webpack.github.io/docs/configuration
*/
var webpack = require('webpack');
-module.exports = function (isDebug) {
+module.exports = function (release) {
return {
output: {
publicPatch: './build/',
@@ -18,23 +18,23 @@ module.exports = function (isDebug) {
filename: 'app.js'
},
- cache: isDebug,
- debug: isDebug,
+ cache: !release,
+ debug: !release,
devtool: false,
entry: './src/app.jsx',
stats: {
colors: true,
- reasons: isDebug
+ reasons: !release
},
- plugins: isDebug ? [] : [
+ plugins: release ? [
new webpack.DefinePlugin({'process.env.NODE_ENV': '"production"'}),
new webpack.optimize.DedupePlugin(),
new webpack.optimize.UglifyJsPlugin(),
new webpack.optimize.OccurenceOrderPlugin(),
new webpack.optimize.AggressiveMergingPlugin()
- ],
+ ] : [],
module: {
preLoaders: [
diff --git a/gulpfile.js b/gulpfile.js
@@ -9,33 +9,53 @@
// See: https://github.com/gulpjs/gulp/blob/master/docs/API.md
var gulp = require('gulp');
var $ = require('gulp-load-plugins')();
-var es = require('event-stream');
+var del = require('del');
var path = require('path');
+var merge = require('merge-stream');
var runSequence = require('run-sequence');
var webpack = require('webpack');
+var browserSync = require('browser-sync');
var argv = require('minimist')(process.argv.slice(2));
// Settings
-var DEST = './build';
-var DEBUG = !argv.release;
-var WATCH = !!argv.watch;
-var LOG = !!argv.log;
+var DEST = './build'; // The build output folder
+var RELEASE = !!argv.release; // Minimize and optimize during a build?
+var GOOGLE_ANALYTICS_ID = 'UA-XXXXX-X'; // https://www.google.com/analytics/web/
+var AUTOPREFIXER_BROWSERS = [ // https://github.com/ai/autoprefixer
+ 'ie >= 10',
+ 'ie_mob >= 10',
+ 'ff >= 30',
+ 'chrome >= 34',
+ 'safari >= 7',
+ 'opera >= 23',
+ 'ios >= 7',
+ 'android >= 4.4',
+ 'bb >= 10'
+];
+
+var src = {};
+var watch = false;
+var reload = browserSync.reload;
+var pkgs = (function () {
+ var temp = {};
+ var map = function (source) {
+ for (var key in source) {
+ temp[key.replace(/[^a-z0-9]/gi, '')] = source[key].substring(1);
+ }
+ };
+ map(require('./package.json').dependencies);
+ return temp;
+}());
-// Node.js runtime dependencies and their version numbers
-var pkgs = require('./package.json').dependencies;
-Object.keys(pkgs).forEach(function (key) { return pkgs[key] = pkgs[key].substring(1); });
+// The default task
+gulp.task('default', ['serve']);
// Clean up
-// -----------------------------------------------------------------------------
-gulp.task('clean', function (cb) {
- var rimraf = require('rimraf');
- rimraf(DEST, cb);
-});
+gulp.task('clean', del.bind(null, [DEST]));
-// Copy vendor files
-// -----------------------------------------------------------------------------
+// 3rd party libraries
gulp.task('vendor', function () {
- return es.merge(
+ return merge(
gulp.src('./node_modules/jquery/dist/**')
.pipe(gulp.dest(DEST + '/vendor/jquery-' + pkgs.jquery)),
gulp.src('./node_modules/bootstrap/dist/fonts/**')
@@ -43,50 +63,66 @@ gulp.task('vendor', function () {
);
});
-// Copy static files / assets
-// -----------------------------------------------------------------------------
+// Static files
gulp.task('assets', function () {
- return es.merge(
- gulp.src('./src/assets/**')
- .pipe(gulp.dest(DEST)),
- gulp.src('./src/images/**')
- .pipe(gulp.dest(DEST + '/images/')),
- gulp.src('./src/*.html')
- .pipe(DEBUG ? $.util.noop() : $.htmlmin({
- removeComments: true,
- collapseWhitespace: true,
- minifyJS: true
- }))
- .pipe(DEBUG ? $.embedlr() : $.util.noop())
- .pipe(gulp.dest(DEST))
- );
+ src.assets = 'src/assets/**';
+ return gulp.src(src.assets)
+ .pipe(gulp.dest(DEST))
+ .pipe($.if(watch, reload({stream: true})));
});
-// CSS stylesheets
-// -----------------------------------------------------------------------------
+// Images
+gulp.task('images', function () {
+ src.images = 'src/images/**';
+ return gulp.src(src.images)
+ .pipe($.cache($.imagemin({
+ progressive: true,
+ interlaced: true
+ })))
+ .pipe(gulp.dest(DEST + '/images'))
+ .pipe($.if(watch, reload({stream: true})));
+});
+
+// HTML pages
+gulp.task('pages', function () {
+ src.pages = 'src/**/*.html';
+ return gulp.src(src.pages)
+ .pipe($.if(RELEASE, $.htmlmin({
+ removeComments: true,
+ collapseWhitespace: true,
+ minifyJS: true
+ })))
+ .pipe(gulp.dest(DEST))
+ .pipe($.if(watch, reload({stream: true})));
+});
+
+// CSS style sheets
gulp.task('styles', function () {
- return gulp.src('./src/styles/bootstrap.less')
+ src.styles = 'src/styles/**/*.{css,less}';
+ return gulp.src('src/styles/bootstrap.less')
.pipe($.plumber())
- .pipe($.less({sourceMap: DEBUG, sourceMapBasepath: __dirname}))
+ .pipe($.less({sourceMap: !RELEASE, sourceMapBasepath: __dirname}))
.on('error', $.util.log)
- .pipe(DEBUG ? $.util.noop() : $.minifyCss())
- .pipe(gulp.dest(DEST + '/css'));
+ .pipe($.autoprefixer(AUTOPREFIXER_BROWSERS))
+ .pipe($.csscomb())
+ .pipe($.if(RELEASE, $.minifyCss()))
+ .pipe(gulp.dest(DEST + '/css'))
+ .pipe($.if(watch, reload({stream: true})));
});
-// Create JavaScript bundle
-// -----------------------------------------------------------------------------
+// Bundle
gulp.task('bundle', function (cb) {
- var bundler = webpack(require('./config/webpack.config.js')(DEBUG));
+ var bundler = webpack(require('./config/webpack.config.js')(RELEASE));
function bundle (err, stats) {
if (err) {
throw new $.util.PluginError('webpack', err);
}
- LOG && $.util.log('[webpack]', stats.toString({colors: true}));
+ //VERBOSE && $.util.log('[webpack]', stats.toString({colors: true}));
return cb();
}
- if (WATCH) {
+ if (watch) {
bundler.watch(200, bundle);
} else {
bundler.run(bundle);
@@ -94,81 +130,48 @@ gulp.task('bundle', function (cb) {
});
// Build the app from source code
-// -----------------------------------------------------------------------------
gulp.task('build', ['clean'], function (cb) {
- runSequence(['vendor', 'assets', 'styles', 'bundle'], cb);
+ runSequence(['vendor', 'assets', 'images', 'pages', 'styles', 'bundle'], cb);
});
// Launch a lightweight HTTP Server
-// -----------------------------------------------------------------------------
-gulp.task('serve', ['build'], function (next) {
- var url = require('url');
- var server = require('ecstatic')({root: './', cache: 'no-cache', showDir: true});
- var port = 3000;
-
- require('http').createServer()
- .on('request', function (req, res) {
- // For non-existent files output the contents of /index.html page in order to make HTML5 routing work
- var urlPath = url.parse(req.url).pathname;
- if (urlPath === '/') {
- req.url = DEST.substring(1) + '/index.html';
- } else if (['src', 'bower_components'].indexOf(urlPath.split('/')[1]) === -1) {
- if (urlPath.length > 3 &&
- ['src', 'bower_components'].indexOf(urlPath.split('/')[1]) === -1 &&
- ['css', 'html', 'ico', 'js', 'png', 'txt', 'xml'].indexOf(urlPath.split('.').pop()) == -1 &&
- ['fonts', 'images', 'vendor', 'views'].indexOf(urlPath.split('/')[1]) == -1) {
- req.url = DEST.substring(1) + '/index.html';
- } else {
- req.url = DEST.substring(1) + req.url;
- }
- }
- server(req, res);
- })
- .listen(port, function () {
- $.util.log('Server is listening on ' + $.util.colors.magenta('http://localhost:' + port + '/'));
- next();
- });
-});
-
-// Watch for changes in source files
-// -----------------------------------------------------------------------------
-gulp.task('watch', ['serve'], function () {
- var path = require('path');
- var lr = require('gulp-livereload');
-
- // Watch for changes in source files
- gulp.watch('./src/assets/**', ['assets']);
- gulp.watch('./src/**/*.less', ['styles']);
-
- // Watch for changes in 'compiled' files
- gulp.watch(DEST + '/**', function (file) {
- var relPath = DEST.substring(2) + '\\' + path.relative(DEST, file.path);
- $.util.log('File changed: ' + $.util.colors.magenta(relPath));
- lr.changed(file.path);
+gulp.task('serve', ['build'], function () {
+
+ browserSync({
+ notify: false,
+ // Run as an https by uncommenting 'https: true'
+ // Note: this uses an unsigned certificate which on first access
+ // will present a certificate warning in the browser.
+ // https: true,
+ server: {
+ baseDir: ['build']
+ }
});
- lr.listen();
+ gulp.watch(src.assets, ['assets']);
+ gulp.watch(src.images, ['images']);
+ gulp.watch(src.pages, ['pages']);
+ gulp.watch(src.styles, ['styles']);
+ gulp.watch(src.bundle, ['bundle']);
+ watch = true;
});
-// Deploy to GitHub Pages. See: https://pages.github.com
-// -----------------------------------------------------------------------------
-gulp.task('deploy', ['build'], function (cb) {
- var url = 'https://github.com/{name}/{name}.github.io.git';
- var exec = require('child_process').exec;
- var cwd = path.join(__dirname, DEST);
- var cmd = 'git init && git remote add origin ' + url + ' && ' +
- 'git add . && git commit -m Release && ' +
- 'git push -f origin master';
-
- exec(cmd, { 'cwd': cwd }, function (err, stdout, stderr) {
- if (err !== null) {
- cb(err);
- } else {
- $.util.log(stdout, stderr);
- cb();
- }
- });
-});
+// Deploy to GitHub Pages
+gulp.task('deploy', function () {
-// The default task
-gulp.task('default', ['watch']);
-\ No newline at end of file
+ // Remove temp folder
+ if (argv.clean) {
+ var os = require('os');
+ var path = require('path');
+ var repoPath = path.join(os.tmpdir(), 'tmpRepo');
+ $.util.log('Delete ' + $.util.colors.magenta(repoPath));
+ del.sync(repoPath, {force: true});
+ }
+
+ return gulp.src(DEST + '/**/*')
+ .pipe($.if('**/robots.txt', !argv.production ? $.replace('Disallow:', 'Disallow: /') : $.util.noop()))
+ .pipe($.ghPages({
+ remoteUrl: 'https://github.com/{name}/{name}.github.io.git',
+ branch: 'master'
+ }));
+});
diff --git a/package.json b/package.json
@@ -10,12 +10,19 @@
"react": "^0.11.1"
},
"devDependencies": {
+ "browser-sync": "^1.3.6",
+ "del": "^0.1.2",
"ecstatic": "^0.5.4",
- "event-stream": "^3.1.7",
"gulp": "^3.8.7",
+ "gulp-autoprefixer": "0.0.10",
+ "gulp-cache": "^0.2.1",
"gulp-changed": "^1.0.0",
+ "gulp-csscomb": "^3.0.2",
"gulp-embedlr": "^0.5.2",
+ "gulp-gh-pages": "^0.3.3",
"gulp-htmlmin": "^0.2.0",
+ "gulp-if": "^1.2.4",
+ "gulp-imagemin": "^1.0.1",
"gulp-jshint": "^1.8.4",
"gulp-less": "^1.3.5",
"gulp-livereload": "^2.1.1",
@@ -27,6 +34,7 @@
"jshint-stylish": "^0.4.0",
"jsx-loader": "^0.11.0",
"karma": "^0.12.23",
+ "merge-stream": "^0.1.5",
"minimist": "^1.1.0",
"protractor": "^1.1.1",
"rimraf": "^2.2.8",