Feat: Add react-hot-loader
Combine webpack and browser-sync on one port
This commit is contained in:
@ -28,8 +28,8 @@ import {
|
|||||||
|
|
||||||
const isDev = Rx.config.longStackSupport = debug.enabled('fcc:*');
|
const isDev = Rx.config.longStackSupport = debug.enabled('fcc:*');
|
||||||
const log = debug('fcc:client');
|
const log = debug('fcc:client');
|
||||||
const hotReloadTimeout = 5000;
|
const hotReloadTimeout = 2000;
|
||||||
const csrfToken = window.__fcc__.csrf.token;
|
const { csrf: { csrfToken } = {} } = window.__fcc__;
|
||||||
const DOMContainer = document.getElementById('fcc');
|
const DOMContainer = document.getElementById('fcc');
|
||||||
const initialState = isColdStored() ?
|
const initialState = isColdStored() ?
|
||||||
getColdStorage() :
|
getColdStorage() :
|
||||||
@ -37,7 +37,8 @@ const initialState = isColdStored() ?
|
|||||||
initialState.app.csrfToken = csrfToken;
|
initialState.app.csrfToken = csrfToken;
|
||||||
initialState.toasts = flashToToast(window.__fcc__.flash);
|
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 } };
|
const serviceOptions = { xhrPath: '/services', context: { _csrf: csrfToken } };
|
||||||
|
|
||||||
@ -69,7 +70,10 @@ createApp({
|
|||||||
})
|
})
|
||||||
.doOnNext(({ store }) => {
|
.doOnNext(({ store }) => {
|
||||||
if (module.hot && typeof module.hot.accept === 'function') {
|
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());
|
saveToColdStorage(store.getState());
|
||||||
setTimeout(() => window.location.reload(), hotReloadTimeout);
|
setTimeout(() => window.location.reload(), hotReloadTimeout);
|
||||||
});
|
});
|
||||||
|
103
gulpfile.js
103
gulpfile.js
@ -25,12 +25,13 @@ var Rx = require('rx'),
|
|||||||
// react app
|
// react app
|
||||||
webpack = require('webpack'),
|
webpack = require('webpack'),
|
||||||
webpackStream = require('webpack-stream'),
|
webpackStream = require('webpack-stream'),
|
||||||
WebpackDevServer = require('webpack-dev-server'),
|
webpackDevMiddleware = require('webpack-dev-middleware'),
|
||||||
|
webpackHotMiddleware = require('webpack-hot-middleware'),
|
||||||
webpackConfig = require('./webpack.config.js'),
|
webpackConfig = require('./webpack.config.js'),
|
||||||
|
|
||||||
// server process
|
// server process
|
||||||
nodemon = require('gulp-nodemon'),
|
nodemon = require('gulp-nodemon'),
|
||||||
sync = require('browser-sync'),
|
browserSync = require('browser-sync'),
|
||||||
|
|
||||||
// css
|
// css
|
||||||
less = require('gulp-less'),
|
less = require('gulp-less'),
|
||||||
@ -48,10 +49,18 @@ var Rx = require('rx'),
|
|||||||
tapSpec = require('tap-spec');
|
tapSpec = require('tap-spec');
|
||||||
|
|
||||||
Rx.config.longStackSupport = true;
|
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 __DEV__ = !yargs.argv.p;
|
||||||
var reloadDelay = 1000;
|
var port = yargs.argv.port || process.env.PORT || '3001';
|
||||||
var reload = sync.reload;
|
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 = {
|
var paths = {
|
||||||
server: './server/server.js',
|
server: './server/server.js',
|
||||||
serverIgnore: [
|
serverIgnore: [
|
||||||
@ -171,25 +180,20 @@ gulp.task('serve', function(cb) {
|
|||||||
exec: path.join(__dirname, 'node_modules/.bin/babel-node'),
|
exec: path.join(__dirname, 'node_modules/.bin/babel-node'),
|
||||||
env: {
|
env: {
|
||||||
NODE_ENV: process.env.NODE_ENV || 'development',
|
NODE_ENV: process.env.NODE_ENV || 'development',
|
||||||
DEBUG: process.env.DEBUG || 'fcc:*'
|
DEBUG: process.env.DEBUG || 'fcc:*',
|
||||||
|
PORT: port
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.on('start', function() {
|
.on('start', function() {
|
||||||
if (!called) {
|
if (!called) {
|
||||||
called = true;
|
called = true;
|
||||||
setTimeout(function() {
|
cb();
|
||||||
cb();
|
|
||||||
}, reloadDelay);
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.on('restart', function(files) {
|
.on('restart', function(files) {
|
||||||
if (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'
|
'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, {
|
sync.init(null, {
|
||||||
proxy: 'http://localhost:3000',
|
ui: {
|
||||||
|
port: syncUIPort
|
||||||
|
},
|
||||||
|
proxy: {
|
||||||
|
target: `http://localhost:${port}`,
|
||||||
|
reqHeaders: ({ url: { hostname } }) => ({
|
||||||
|
host: `${hostname}:${syncPort}`
|
||||||
|
})
|
||||||
|
},
|
||||||
logLeval: 'debug',
|
logLeval: 'debug',
|
||||||
files: paths.syncWatch,
|
files: paths.syncWatch,
|
||||||
port: 3001,
|
port: syncPort,
|
||||||
open: false,
|
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() {
|
gulp.task('less', function() {
|
||||||
var manifestName = 'css-manifest.json';
|
var manifestName = 'css-manifest.json';
|
||||||
var dest = paths.css;
|
var dest = paths.css;
|
||||||
@ -435,7 +419,7 @@ var watchDependents = [
|
|||||||
'less',
|
'less',
|
||||||
'js',
|
'js',
|
||||||
'serve',
|
'serve',
|
||||||
'sync'
|
'dev-server'
|
||||||
];
|
];
|
||||||
|
|
||||||
gulp.task('reload', function() {
|
gulp.task('reload', function() {
|
||||||
@ -453,9 +437,8 @@ gulp.task('watch', watchDependents, function() {
|
|||||||
gulp.task('default', [
|
gulp.task('default', [
|
||||||
'less',
|
'less',
|
||||||
'serve',
|
'serve',
|
||||||
'webpack-dev-server',
|
|
||||||
'watch',
|
'watch',
|
||||||
'sync'
|
'dev-server'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
gulp.task('test', function() {
|
gulp.task('test', function() {
|
||||||
|
@ -156,13 +156,15 @@
|
|||||||
"loopback-component-explorer": "^2.1.1",
|
"loopback-component-explorer": "^2.1.1",
|
||||||
"merge-stream": "^1.0.0",
|
"merge-stream": "^1.0.0",
|
||||||
"proxyquire": "^1.7.10",
|
"proxyquire": "^1.7.10",
|
||||||
|
"react-hot-loader": "^1.3.0",
|
||||||
"rev-del": "^1.0.5",
|
"rev-del": "^1.0.5",
|
||||||
"sinon": "^1.17.3",
|
"sinon": "^1.17.3",
|
||||||
"sort-keys": "^1.1.1",
|
"sort-keys": "^1.1.1",
|
||||||
"tap-spec": "^4.1.1",
|
"tap-spec": "^4.1.1",
|
||||||
"tape": "^4.2.2",
|
"tape": "^4.2.2",
|
||||||
"webpack": "^1.9.12",
|
"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-manifest-plugin": "^1.0.0",
|
||||||
"webpack-stream": "^3.1.0",
|
"webpack-stream": "^3.1.0",
|
||||||
"yargs": "^5.0.0"
|
"yargs": "^5.0.0"
|
||||||
|
@ -6,9 +6,7 @@ let trusted = [
|
|||||||
|
|
||||||
if (process.env.NODE_ENV !== 'production') {
|
if (process.env.NODE_ENV !== 'production') {
|
||||||
trusted = trusted.concat([
|
trusted = trusted.concat([
|
||||||
'ws://localhost:3001',
|
'ws://localhost:3000'
|
||||||
'http://localhost:2999',
|
|
||||||
'ws://localhost:2999'
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,4 @@
|
|||||||
import manifest from '../rev-manifest';
|
import manifest from '../rev-manifest';
|
||||||
/* eslint-disable import/default */
|
|
||||||
import config from '../../webpack.config';
|
|
||||||
/* eslint-enable import/default */
|
|
||||||
|
|
||||||
let chunkManifest;
|
let chunkManifest;
|
||||||
try {
|
try {
|
||||||
@ -30,16 +27,9 @@ function removeOldTerms(str = '') {
|
|||||||
return str.replace(challengesRegex, '');
|
return str.replace(challengesRegex, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
function getBundleLocation() {
|
|
||||||
return __DEV__ ?
|
|
||||||
config.output.publicPath + '/bundle.js' :
|
|
||||||
rev('/js', 'bundle.js');
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function jadeHelpers() {
|
export default function jadeHelpers() {
|
||||||
return function jadeHelpersMiddleware(req, res, next) {
|
return function jadeHelpersMiddleware(req, res, next) {
|
||||||
res.locals.removeOldTerms = removeOldTerms;
|
res.locals.removeOldTerms = removeOldTerms;
|
||||||
res.locals.getBundleLocation = getBundleLocation;
|
|
||||||
res.locals.rev = rev;
|
res.locals.rev = rev;
|
||||||
// static data
|
// static data
|
||||||
res.locals.user = req.user;
|
res.locals.user = req.user;
|
||||||
|
@ -12,4 +12,4 @@ html(lang='en')
|
|||||||
script.
|
script.
|
||||||
window.webpackManifest = !{JSON.stringify(chunkManifest || {})};
|
window.webpackManifest = !{JSON.stringify(chunkManifest || {})};
|
||||||
script(src=rev('/js', 'vendor-challenges.js'))
|
script(src=rev('/js', 'vendor-challenges.js'))
|
||||||
script(src=getBundleLocation())
|
script(src=rev('/js', 'bundle.js'))
|
||||||
|
@ -23,7 +23,7 @@ module.exports = {
|
|||||||
'bundle-[name].js' :
|
'bundle-[name].js' :
|
||||||
'bundle-[name]-[chunkhash].js',
|
'bundle-[name]-[chunkhash].js',
|
||||||
path: path.join(__dirname, '/public/js'),
|
path: path.join(__dirname, '/public/js'),
|
||||||
publicPath: __DEV__ ? 'http://localhost:2999/js' : '/js'
|
publicPath: '/js'
|
||||||
},
|
},
|
||||||
module: {
|
module: {
|
||||||
loaders: [
|
loaders: [
|
||||||
@ -33,9 +33,7 @@ module.exports = {
|
|||||||
path.join(__dirname, 'client/'),
|
path.join(__dirname, 'client/'),
|
||||||
path.join(__dirname, 'common/')
|
path.join(__dirname, 'common/')
|
||||||
],
|
],
|
||||||
loaders: [
|
loaders: __DEV__ ? ['react-hot', 'babel'] : [ 'babel' ]
|
||||||
'babel-loader'
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.json$/,
|
test: /\.json$/,
|
||||||
|
Reference in New Issue
Block a user