@frontity/core
Version:
The core package of the Frontity framework.
108 lines (107 loc) • 4.46 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const path_1 = __importDefault(require("path"));
const require_from_string_1 = __importDefault(require("require-from-string"));
const source_map_support_1 = __importDefault(require("source-map-support"));
const interopRequireDefault = (obj) => {
return obj && obj.__esModule ? obj.default : obj;
};
const isMultiCompiler = (compiler) => {
// Duck typing as `instanceof MultiCompiler` fails when npm decides to
// install multiple instances of webpack.
return compiler && compiler.compilers;
};
const findCompiler = (multiCompiler, name) => {
return multiCompiler.compilers.filter((compiler) => compiler.name.indexOf(name) === 0);
};
const findStats = (multiStats, name) => {
return multiStats.stats.filter((stats) => stats.compilation.name.indexOf(name) === 0);
};
const getFilename = (serverStats, outputPath, chunkName) => {
const assetsByChunkName = serverStats.toJson().assetsByChunkName;
const filename = assetsByChunkName[chunkName] || "";
// If source maps are generated `assetsByChunkName.main`
// will be an array of filenames.
return path_1.default.join(outputPath, Array.isArray(filename)
? filename.find((asset) => /\.js$/.test(asset))
: filename);
};
const getServerRenderer = (filename, buffer) => {
const errMessage = `The 'server' compiler must export a function in the form of \`(options) => (req, res, next) => void\``;
const serverRenderer = interopRequireDefault((0, require_from_string_1.default)(buffer.toString(), filename));
if (typeof serverRenderer !== "function") {
throw new Error(errMessage);
}
return serverRenderer;
};
function installSourceMapSupport(fs) {
source_map_support_1.default.install({
// NOTE: If https://github.com/evanw/node-source-map-support/pull/149
// lands we can be less aggressive and explicitly invalidate the source
// map cache when Webpack recompiles.
emptyCacheBetweenOperations: true,
retrieveFile(source) {
try {
return fs.readFileSync(source, "utf8");
}
catch (ex) {
// Doesn't exist
}
},
});
}
const createConnectHandler = (error, serverRenderer) => (req, res, next) => {
if (error) {
return next(error);
}
serverRenderer(req, res, next);
};
function webpackHotServerMiddleware(multiCompiler) {
if (!isMultiCompiler(multiCompiler)) {
throw new Error(`Expected webpack compiler to contain both a 'client' and/or 'server' config`);
}
const serverCompiler = findCompiler(multiCompiler, "server")[0];
const clientCompilers = findCompiler(multiCompiler, "client");
if (!serverCompiler) {
throw new Error(`Expected a webpack compiler named 'server'`);
}
const outputFs = serverCompiler.outputFileSystem;
const outputPath = serverCompiler.outputPath;
installSourceMapSupport(outputFs);
let serverRenderer;
let error = false;
const doneHandler = (multiStats) => {
error = false;
const serverStats = findStats(multiStats, "server")[0];
// Server compilation errors need to be propagated to the client.
if (serverStats.compilation.errors.length) {
error = serverStats.compilation.errors[0];
return;
}
let clientStatsJson = null;
if (clientCompilers.length) {
const clientStats = findStats(multiStats, "client");
clientStatsJson = clientStats.map((obj) => obj.toJson());
if (clientStatsJson.length === 1) {
clientStatsJson = clientStatsJson[0];
}
}
const filename = getFilename(serverStats, outputPath, "main");
const buffer = outputFs.readFileSync(filename);
try {
serverRenderer = getServerRenderer(filename, buffer);
}
catch (ex) {
error = ex;
}
};
multiCompiler.hooks.done.tap("WebpackHotServerMiddleware", doneHandler);
return function () {
// eslint-disable-next-line prefer-spread,prefer-rest-params
return createConnectHandler(error, serverRenderer).apply(null, arguments);
};
}
exports.default = webpackHotServerMiddleware;