Feat: Add react-hot-loader

Combine webpack and browser-sync on one port
pull/10949/head
Berkeley Martinez 2016-09-27 10:57:56 -07:00
parent d3f8757e92
commit a6f9e95ed8
7 changed files with 58 additions and 83 deletions

View File

@ -28,8 +28,8 @@ import {
const isDev = Rx.config.longStackSupport = debug.enabled('fcc:*');
const log = debug('fcc:client');
const hotReloadTimeout = 5000;
const csrfToken = window.__fcc__.csrf.token;
const hotReloadTimeout = 2000;
const { csrf: { csrfToken } = {} } = window.__fcc__;
const DOMContainer = document.getElementById('fcc');
const initialState = isColdStored() ?
getColdStorage() :
@ -37,7 +37,8 @@ const initialState = isColdStored() ?
initialState.app.csrfToken = csrfToken;
initialState.toasts = flashToToast(window.__fcc__.flash);
delete window.__fcc__;
// make empty object so hot reload works
window.__fcc__ = {};
const serviceOptions = { xhrPath: '/services', context: { _csrf: csrfToken } };
@ -69,7 +70,10 @@ createApp({
})
.doOnNext(({ store }) => {
if (module.hot && typeof module.hot.accept === 'function') {
module.hot.accept('../common/app', function() {
module.hot.accept(err => {
if (err) { console.error(err); }
log('saving state and refreshing.');
log('ignore react ssr warning.');
saveToColdStorage(store.getState());
setTimeout(() => window.location.reload(), hotReloadTimeout);
});

View File

@ -25,12 +25,13 @@ var Rx = require('rx'),
// react app
webpack = require('webpack'),
webpackStream = require('webpack-stream'),
WebpackDevServer = require('webpack-dev-server'),
webpackDevMiddleware = require('webpack-dev-middleware'),
webpackHotMiddleware = require('webpack-hot-middleware'),
webpackConfig = require('./webpack.config.js'),
// server process
nodemon = require('gulp-nodemon'),
sync = require('browser-sync'),
browserSync = require('browser-sync'),
// css
less = require('gulp-less'),
@ -48,10 +49,18 @@ var Rx = require('rx'),
tapSpec = require('tap-spec');
Rx.config.longStackSupport = true;
var sync = browserSync.create('fcc-sync-server');
var reload = sync.reload.bind(sync);
// user definable
var __DEV__ = !yargs.argv.p;
var reloadDelay = 1000;
var reload = sync.reload;
var port = yargs.argv.port || process.env.PORT || '3001';
var syncPort = yargs.argv['sync-port'] || process.env.SYNC_PORT || '3000';
// make sure sync ui port does not interfere with proxy port
var syncUIPort = yargs.argv['sync-ui-port'] ||
process.env.SYNC_UI_PORT ||
parseInt(syncPort, 10) + 2;
var paths = {
server: './server/server.js',
serverIgnore: [
@ -171,25 +180,20 @@ gulp.task('serve', function(cb) {
exec: path.join(__dirname, 'node_modules/.bin/babel-node'),
env: {
NODE_ENV: process.env.NODE_ENV || 'development',
DEBUG: process.env.DEBUG || 'fcc:*'
DEBUG: process.env.DEBUG || 'fcc:*',
PORT: port
}
})
.on('start', function() {
if (!called) {
called = true;
setTimeout(function() {
cb();
}, reloadDelay);
cb();
}
})
.on('restart', function(files) {
if (files) {
debug('Files that changes: ', files);
debug('Nodemon will restart due to changes in: ', files);
}
setTimeout(function() {
debug('Restarting browsers');
reload();
}, reloadDelay);
});
});
@ -199,14 +203,34 @@ var syncDepenedents = [
'less'
];
gulp.task('sync', syncDepenedents, function() {
gulp.task('dev-server', syncDepenedents, function() {
webpackConfig.entry.bundle = [
'webpack/hot/dev-server',
'webpack-hot-middleware/client'
].concat(webpackConfig.entry.bundle);
var bundler = webpack(webpackConfig);
sync.init(null, {
proxy: 'http://localhost:3000',
ui: {
port: syncUIPort
},
proxy: {
target: `http://localhost:${port}`,
reqHeaders: ({ url: { hostname } }) => ({
host: `${hostname}:${syncPort}`
})
},
logLeval: 'debug',
files: paths.syncWatch,
port: 3001,
port: syncPort,
open: false,
reloadDelay: reloadDelay
middleware: [
webpackDevMiddleware(bundler, {
publicPath: webpackConfig.output.publicPath,
stats: 'errors-only'
}),
webpackHotMiddleware(bundler)
]
});
});
@ -273,46 +297,6 @@ gulp.task('clean-webpack-manifest', cleanDeps, function() {
});
});
var webpackCalled = false;
gulp.task('webpack-dev-server', function(cb) {
if (webpackCalled) {
console.log('webpack dev server already runnning');
return cb();
}
var devServerOptions = {
headers: {
'Access-Control-Allow-Credentials': 'true'
},
hot: true,
noInfo: true,
contentBase: false,
publicPath: '/js'
};
webpackConfig.entry.bundle = [
'webpack-dev-server/client?http://localhost:2999/',
'webpack/hot/dev-server'
].concat(webpackConfig.entry.bundle);
var compiler = webpack(webpackConfig);
var devServer = new WebpackDevServer(compiler, devServerOptions);
devServer.use(function(req, res, next) {
res.setHeader('Access-Control-Allow-Origin', req.headers.origin || '*');
next();
});
return devServer.listen('2999', 'localhost', function(err) {
if (err) {
throw new gutil.PluginError('webpack-dev-server', err);
}
if (!webpackCalled) {
gutil.log('[webpack-dev-server]', 'webpack init completed');
webpackCalled = true;
cb();
}
});
});
gulp.task('less', function() {
var manifestName = 'css-manifest.json';
var dest = paths.css;
@ -435,7 +419,7 @@ var watchDependents = [
'less',
'js',
'serve',
'sync'
'dev-server'
];
gulp.task('reload', function() {
@ -453,9 +437,8 @@ gulp.task('watch', watchDependents, function() {
gulp.task('default', [
'less',
'serve',
'webpack-dev-server',
'watch',
'sync'
'dev-server'
]);
gulp.task('test', function() {

View File

@ -156,13 +156,15 @@
"loopback-component-explorer": "^2.1.1",
"merge-stream": "^1.0.0",
"proxyquire": "^1.7.10",
"react-hot-loader": "^1.3.0",
"rev-del": "^1.0.5",
"sinon": "^1.17.3",
"sort-keys": "^1.1.1",
"tap-spec": "^4.1.1",
"tape": "^4.2.2",
"webpack": "^1.9.12",
"webpack-dev-server": "^1.14.0",
"webpack-dev-middleware": "^1.8.3",
"webpack-hot-middleware": "^2.12.2",
"webpack-manifest-plugin": "^1.0.0",
"webpack-stream": "^3.1.0",
"yargs": "^5.0.0"

View File

@ -6,9 +6,7 @@ let trusted = [
if (process.env.NODE_ENV !== 'production') {
trusted = trusted.concat([
'ws://localhost:3001',
'http://localhost:2999',
'ws://localhost:2999'
'ws://localhost:3000'
]);
}

View File

@ -1,7 +1,4 @@
import manifest from '../rev-manifest';
/* eslint-disable import/default */
import config from '../../webpack.config';
/* eslint-enable import/default */
let chunkManifest;
try {
@ -30,16 +27,9 @@ function removeOldTerms(str = '') {
return str.replace(challengesRegex, '');
}
function getBundleLocation() {
return __DEV__ ?
config.output.publicPath + '/bundle.js' :
rev('/js', 'bundle.js');
}
export default function jadeHelpers() {
return function jadeHelpersMiddleware(req, res, next) {
res.locals.removeOldTerms = removeOldTerms;
res.locals.getBundleLocation = getBundleLocation;
res.locals.rev = rev;
// static data
res.locals.user = req.user;

View File

@ -12,4 +12,4 @@ html(lang='en')
script.
window.webpackManifest = !{JSON.stringify(chunkManifest || {})};
script(src=rev('/js', 'vendor-challenges.js'))
script(src=getBundleLocation())
script(src=rev('/js', 'bundle.js'))

View File

@ -23,7 +23,7 @@ module.exports = {
'bundle-[name].js' :
'bundle-[name]-[chunkhash].js',
path: path.join(__dirname, '/public/js'),
publicPath: __DEV__ ? 'http://localhost:2999/js' : '/js'
publicPath: '/js'
},
module: {
loaders: [
@ -33,9 +33,7 @@ module.exports = {
path.join(__dirname, 'client/'),
path.join(__dirname, 'common/')
],
loaders: [
'babel-loader'
]
loaders: __DEV__ ? ['react-hot', 'babel'] : [ 'babel' ]
},
{
test: /\.json$/,