Skip to content

Commit

Permalink
[minor] support SSR hot module reload (#840)
Browse files Browse the repository at this point in the history
  • Loading branch information
jchip authored Jun 28, 2018
1 parent 3a7e825 commit e084aee
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 21 deletions.
31 changes: 31 additions & 0 deletions packages/electrode-archetype-react-app-dev/lib/stats-mapper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
"use strict";

function extractBundles(stats) {
// Stats has modules, single bundle
if (stats.modules) return [stats];

// Stats has children, multiple bundles
if (stats.children && stats.children.length) return stats.children;

// Not sure, assume single
return [stats];
}

function getBundles(statsResult) {
// For multi-compiler, stats will be an object with a 'children' array of stats
const bundles = extractBundles(statsResult.toJson({ errorDetails: false }));
return bundles.map(stats => {
return {
name: stats.name,
time: stats.time,
hash: stats.hash,
warnings: stats.warnings || [],
errors: stats.errors || [],
modules: stats.modules.map(x => x.name)
};
});
}

module.exports = {
getBundles
};
48 changes: 37 additions & 11 deletions packages/electrode-archetype-react-app-dev/lib/webpack-dev-hapi.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const chalk = require("chalk");
const archetype = require("electrode-archetype-react-app/config/archetype");
const _ = require("lodash");
const statsUtils = require("./stats-utils");
const statsMapper = require("./stats-mapper");

function register(server, options, next) {
if (!archetype.webpack.devMiddleware) {
Expand Down Expand Up @@ -121,7 +122,13 @@ function register(server, options, next) {
}
};

let lastReporterOptions;
const webpackDev = {
lastReporterOptions: undefined,
hasErrors: false,
valid: false,
compileTime: 0
};

let returnReporter;

defaultReporter = (middlewareOptions, reporterOptions) => {
Expand All @@ -133,6 +140,10 @@ function register(server, options, next) {
const but = (notOk && chalk.yellow(" but has")) || "";
console.log(`webpack bundle is now ${chalk.green("VALID")}${but}${error}${warning}`);

webpackDev.valid = true;
webpackDev.hasErrors = stats.hasErrors();
webpackDev.compileTime = Date.now();

const baseUrl = () =>
Url.format({
hostname: process.env.HOST || "localhost",
Expand All @@ -145,31 +156,45 @@ function register(server, options, next) {
console.log(`${x} - View status and errors/warnings from your browser`);
}

if (lastReporterOptions === undefined) {
if (webpackDev.lastReporterOptions === undefined) {
returnReporter = notOk;
setTimeout(() => opn(baseUrl()), 750);
} else {
// keep returning reporter until a first success compile
returnReporter = returnReporter ? notOk : false;
}

if (!webpackDev.hasErrors) {
const bundles = statsMapper.getBundles(reporterOptions.stats);
bundles.forEach(b => {
b.modules.forEach(m => {
if (m.indexOf("node_modules") >= 0) return;
if (m.indexOf("(webpack)") >= 0) return;
if (m.startsWith("multi ")) return;
const moduleFullPath = Path.resolve(m);
delete require.cache[moduleFullPath];
});
});
}

transferIsomorphicAssets(devMiddleware.fileSystem, err => {
// reload assets to activate
if (!err) {
isomorphicExtendRequire.loadAssets(err2 => {
if (err) {
console.error("reload isomorphic assets failed", err2);
}
lastReporterOptions = reporterOptions;
webpackDev.lastReporterOptions = reporterOptions;
});
} else {
lastReporterOptions = reporterOptions;
webpackDev.lastReporterOptions = reporterOptions;
}
});
} else {
isomorphicExtendRequire.deactivate();
console.log(`webpack bundle is now ${chalk.magenta("INVALID")}`);
lastReporterOptions = false;
webpackDev.valid = false;
webpackDev.lastReporterOptions = false;
}
if (userReporter) userReporter(middlewareOptions, reporterOptions);
};
Expand All @@ -189,13 +214,12 @@ function register(server, options, next) {
req.url === webpackHotOptions.path ||
(req.url.startsWith(publicPath) && req.url.indexOf(".hot-update.") >= 0);

if (!lastReporterOptions && !isHmrRequest) {
return sendHtml(
`<html><body><div style="margin-top: 50px; padding: 20px; border-radius: 10px; border: 2px solid red;">
if (!webpackDev.lastReporterOptions && !isHmrRequest) {
return sendHtml(`<html><body>
<div style="margin-top: 50px; padding: 20px; border-radius: 10px; border: 2px solid red;">
<h2>Waiting for webpack dev middleware to finish compiling</h2>
</div><script>function doReload(x){ if (!x) location.reload(); setTimeout(doReload, 1000); }
doReload(1); </script></body></html>`
);
doReload(1); </script></body></html>`);
}

const serveStatic = (baseUrl, fileSystem, indexServer, errorHandler) => {
Expand Down Expand Up @@ -280,9 +304,11 @@ ${jumpToError}</body></html>
);
});
} else if (req.url.startsWith(reporterUrl) || returnReporter) {
return serveReporter(lastReporterOptions);
return serveReporter(webpackDev.lastReporterOptions);
}

request.app.webpackDev = webpackDev;

return devMiddleware(req, res, err => {
if (err) {
console.error("webpack dev middleware error", err);
Expand Down
13 changes: 8 additions & 5 deletions packages/electrode-archetype-react-app/arch-clap.js
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,9 @@ Individual .babelrc files were generated for you in src/client and src/server
desc: "Start your app with watch in development mode with webpack-dev-server",
dep: [".remove-log-files", ".development-env"],
task: function() {
if (!Fs.existsSync(".isomorphic-loader-config.json")) {
Fs.writeFileSync(".isomorphic-loader-config.json", JSON.stringify({}));
}
const args = taskArgs(this.argv);

return [
Expand Down Expand Up @@ -630,11 +633,11 @@ Individual .babelrc files were generated for you in src/client and src/server
dep: [".init-bundle.valid.log"],
desc: "Start app's node server in watch mode with nodemon",
task: function() {
const watches = [
archetype.webpack.devMiddleware ? "" : Path.join(eTmpDir, "bundle.valid.log"),
AppMode.src.server,
"config"
]
const watches = (archetype.webpack.devMiddleware
? []
: [Path.join(eTmpDir, "bundle.valid.log"), AppMode.src.server]
)
.concat("config")
.filter(x => x)
.map(n => `--watch ${n}`)
.join(" ");
Expand Down
23 changes: 22 additions & 1 deletion packages/generator-electrode/generators/app/templates/xclap.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,22 @@ process.env.SERVER_ES6 = true;
* This will run webpack dev server as part of your app server.
*/

// process.env.WEBPACK_DEV_MIDDLEWARE = true;
process.env.WEBPACK_DEV_MIDDLEWARE = true;

/*
* Enable webpack's NodeSourcePlugin to simulate NodeJS libs in browser
*
* This basically adds a bunch of extra JavaScript to the browser to simulate
* some NodeJS modules like `process`, `console`, `Buffer`, and `global`.
*
* Docs here:
* https://github.com/webpack/docs/wiki/internal-webpack-plugins#nodenodesourcepluginoptions
*
* Note that the extra JavaScript could be substantial and adds more than 100K
* of minified JS to your browser bundle.
*
* But if you see Errors like "Uncaught ReferenceError: global is not defined", then
* the quick fix is to uncomment the line below.
*/

// process.env.ENABLE_NODESOURCE_PLUGIN = true;
Expand All @@ -23,6 +35,11 @@ process.env.SERVER_ES6 = true;

// process.env.KARMA_BROWSER = "phantomjs";

/******************************************************************************
* Begin webpack-dev-server only settings. *
* These do not apply if WEBPACK_DEV_MIDDLEWARE is enabled *
******************************************************************************/

/*
* Turn off using electrode-webpack-reporter to show visual report of your webpack
* compile results when running in dev mode with `clap dev`
Expand All @@ -44,6 +61,10 @@ process.env.SERVER_ES6 = true;

// process.env.WEBPACK_DEV_HTTPS = true;

/******************************************************************************
* End webpack-dev-server only settings. *
******************************************************************************/

require("electrode-archetype-react-app")();

//<% if (isSingleQuote) { %>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@ import { routes } from "../../client/routes";
//
//

let routesEngine;

module.exports = req => {
const app = (req.server && req.server.app) || req.app;
if (!app.routesEngine) {
app.routesEngine = new ReduxRouterEngine({ routes });
if (!routesEngine) {
routesEngine = new ReduxRouterEngine({ routes });
}

return app.routesEngine.render(req);
return routesEngine.render(req);
};

0 comments on commit e084aee

Please sign in to comment.