Skip to content

Commit

Permalink
refactor index-page/jsx-renderer/subapp-server
Browse files Browse the repository at this point in the history
  • Loading branch information
jchip committed Jul 7, 2020
1 parent eda64a0 commit 91352c0
Show file tree
Hide file tree
Showing 45 changed files with 505 additions and 732 deletions.
8 changes: 2 additions & 6 deletions packages/subapp-server/lib/fastify-plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const HttpStatus = require("./http-status");
const subAppUtil = require("subapp-util");
const HttpStatusCodes = require("http-status-codes");

const { makeErrorStackResponse } = require("./utils");
const { makeErrorStackResponse, checkSSRMetricsReporting } = require("./utils");
const { getSrcDir, setupRouteRender, searchRoutesFromFile } = require("./setup-hapi-routes");

module.exports = {
Expand All @@ -22,11 +22,7 @@ module.exports = {

const { routes, topOpts } = searchRoutesFromFile(srcDir, pluginOpts);

const reporting = _.get(topOpts, "reporting", {});
if (!reporting.enable || !reporting.reporter) {
// eslint-disable-next-line
console.warn(`Warning: Metric reporting for ssr not enabled or no reporter specified.`);
}
checkSSRMetricsReporting(topOpts);

const subApps = await subAppUtil.scanSubAppsFromDir(srcDir);
const subAppsByPath = subAppUtil.getSubAppByPathMap(subApps);
Expand Down
File renamed without changes.
9 changes: 6 additions & 3 deletions packages/subapp-server/lib/register-routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@
const assert = require("assert");
const _ = require("lodash");
const HttpStatus = require("./http-status");
const Webapp = require("@xarc/index-page");
const { errorResponse, resolveChunkSelector, updateFullTemplate } = require("./utils");
const Routing = require("./routing");
const { errorResponse, updateFullTemplate } = require("./utils");
const {
utils: { resolveChunkSelector }
} = require("@xarc/index-page");
const HttpStatusCodes = require("http-status-codes");

module.exports = function registerRoutes({ routes, topOpts, server }) {
Expand All @@ -30,7 +33,7 @@ module.exports = function registerRoutes({ routes, topOpts, server }) {

routeOptions.__internals = { chunkSelector };

const routeHandler = Webapp.makeRouteHandler(routeOptions);
const routeHandler = Routing.makeRouteHandler(routeOptions);

const useStream = routeOptions.useStream !== false;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
import * as _ from "lodash";
import * as Path from "path";
import * as assert from "assert";
import { TagRenderer } from "@xarc/tag-renderer";
import { JsxRenderer } from "@xarc/jsx-renderer";
import { resolvePath } from "./react/utils";

import {
getOtherStats,
getOtherAssets,
resolveChunkSelector,
loadAssetsFromStats,
getStatsPath,
invokeTemplateProcessor,
makeDevBundleBase
} from "./react/utils";
"use strict";

const _ = require("lodash");
const Path = require("path");
const { TagRenderer } = require("@xarc/tag-renderer");
const { JsxRenderer } = require("@xarc/jsx-renderer");
const {
utils: {
resolvePath,
getOtherStats,
getOtherAssets,
resolveChunkSelector,
loadAssetsFromStats,
getStatsPath,
invokeTemplateProcessor,
makeDevBundleBase
}
} = require("@xarc/index-page");

const otherStats = getOtherStats();

Expand Down Expand Up @@ -43,7 +45,7 @@ function initializeTemplate(
// Inject the built-in react/token-handlers if it is not in user's handlers
// and replaceTokenHandlers option is false
if (!routeOptions.replaceTokenHandlers) {
const reactTokenHandlers = Path.join(__dirname, "react/token-handlers");
const reactTokenHandlers = require.resolve("@xarc/index-page");
finalTokenHandlers =
userTokenHandlers.indexOf(reactTokenHandlers) < 0
? [reactTokenHandlers].concat(userTokenHandlers)
Expand Down Expand Up @@ -189,114 +191,4 @@ const setupPathOptions = (routeOptions, path) => {
);
};

//
// The route path can supply:
//
// - a literal string
// - a function
// - an object
//
// If it's an object:
// -- if it doesn't contain content, then it's assume to be the content.
//
// If it contains content, then it can contain:
//
// - method: HTTP method for the route
// - config: route config (applicable for framework like Hapi)
// - content: second level field to define content
//
// content can be:
//
// - a literal string
// - a function
// - an object
//
// If content is an object, it can contain module, a path to the JS module to require
// to load the content.
//
const resolveContent = (pathData, xrequire = null) => {
const resolveTime = Date.now();

let content = pathData;

// If it's an object, see if contains content field
if (_.isObject(pathData) && pathData.hasOwnProperty("content")) {
content = pathData.content;
}

if (!content && !_.isString(content)) return null;

// content has module field, require it.
if (!_.isString(content) && !_.isFunction(content) && content.module) {
const mod = content.module.startsWith(".") ? Path.resolve(content.module) : content.module;

xrequire = xrequire || require;

try {
return {
fullPath: xrequire.resolve(mod),
xrequire,
resolveTime,
content: xrequire(mod)
};
} catch (error) {
const msg = `electrode-react-webapp: load SSR content ${mod} failed - ${error.message}`;
console.error(msg, "\n", error); // eslint-disable-line
return {
fullPath: null,
error,
resolveTime,
content: msg
};
}
}

return {
fullPath: null,
resolveTime,
content
};
};

const getContentResolver = (registerOptions, pathData, path) => {
let resolved;

const resolveWithDev = (webpackDev, xrequire) => {
if (!webpackDev.valid) {
resolved = resolveContent("<!-- Webpack still compiling -->");
} else if (webpackDev.hasErrors) {
resolved = resolveContent("<!-- Webpack compile has errors -->");
} else if (!resolved || resolved.resolveTime < webpackDev.compileTime) {
if (resolved && resolved.fullPath) {
delete resolved.xrequire.cache[resolved.fullPath];
}
resolved = resolveContent(pathData, xrequire);
}

return resolved.content;
};

return (webpackDev, xrequire) => {
if (webpackDev && registerOptions.serverSideRendering !== false) {
return resolveWithDev(webpackDev, xrequire);
}

if (resolved) return resolved.content;

if (registerOptions.serverSideRendering !== false) {
resolved = resolveContent(pathData);
assert(resolved, `You must define content for the webapp plugin path ${path}`);
} else {
resolved = {
content: {
status: 200,
html: "<!-- SSR disabled by options.serverSideRendering -->"
}
};
}

return resolved.content;
};
};

export { setupOptions, setupPathOptions, makeRouteHandler, resolveContent, getContentResolver };
module.exports = { setupOptions, setupPathOptions, makeRouteHandler };
24 changes: 10 additions & 14 deletions packages/subapp-server/lib/setup-hapi-routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,19 @@ const Boom = require("@hapi/boom");
const HttpStatus = require("./http-status");
const readFile = util.promisify(Fs.readFile);
const xaa = require("xaa");
const Webapp = require("@xarc/index-page");
const Routing = require("./routing");
const subAppUtil = require("subapp-util");
const registerRoutes = require("./register-routes");

const {
utils: { resolveChunkSelector }
} = require("@xarc/index-page");

const {
errorResponse,
resolveChunkSelector,
getDefaultRouteOptions,
updateFullTemplate
updateFullTemplate,
checkSSRMetricsReporting
} = require("./utils");

async function searchRoutesDir(srcDir, pluginOpts) {
Expand Down Expand Up @@ -123,7 +127,7 @@ function setupRouteRender({ subAppsByPath, srcDir, routeOptions }) {

// const useStream = routeOptions.useStream !== false;

const routeHandler = Webapp.makeRouteHandler(routeOptions);
const routeHandler = Routing.makeRouteHandler(routeOptions);

return routeHandler;
}
Expand Down Expand Up @@ -246,11 +250,7 @@ function searchRoutesFromFile(srcDir, pluginOpts) {
async function setupRoutesFromFile(srcDir, server, pluginOpts) {
const { routes, topOpts } = searchRoutesFromFile(srcDir, pluginOpts);

const reporting = _.get(topOpts, "reporting", {});
if (!reporting.enable || !reporting.reporter) {
// eslint-disable-next-line
console.warn(`Warning: Metric reporting for ssr not enabled or no reporter specified.`);
}
checkSSRMetricsReporting(topOpts);

await handleFavIcon(server, topOpts);

Expand All @@ -274,11 +274,7 @@ async function setupRoutesFromDir(server, pluginOpts, fromDir) {

const topOpts = _.merge(getDefaultRouteOptions(), fromDir.options, pluginOpts);

const reporting = _.get(topOpts, "reporting", {});
if (!reporting.enable || !reporting.reporter) {
// eslint-disable-next-line
console.warn(`Warning: Metric reporting for ssr not enabled or no reporter specified.`);
}
checkSSRMetricsReporting(topOpts);

topOpts.routes = _.merge({}, routes, topOpts.routes);

Expand Down
93 changes: 44 additions & 49 deletions packages/subapp-server/lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,56 +4,16 @@

// Copy from electrode-react-webapp for now

const Fs = require("fs");
const assert = require("assert");
const _ = require("lodash");
const Path = require("path");
const Boom = require("@hapi/boom");
const optionalRequire = require("optional-require")(require);
const getDevProxy = optionalRequire("@xarc/app-dev/config/get-dev-proxy");
const HttpStatusCodes = require("http-status-codes");

/**
* Tries to import bundle chunk selector function if the corresponding option is set in the
* webapp plugin configuration. The function takes a `request` object as an argument and
* returns the chunk name.
*
* @param {Object} options - webapp plugin configuration options
* @return {Function} function that selects the bundle based on the request object
*/
function resolveChunkSelector(options) {
if (options.bundleChunkSelector) {
return require(Path.resolve(options.bundleChunkSelector)); // eslint-disable-line
}

return () => ({
css: "main",
js: "main"
});
}

function getIconStats(iconStatsPath) {
let iconStats;
try {
iconStats = Fs.readFileSync(Path.resolve(iconStatsPath)).toString();
iconStats = JSON.parse(iconStats);
} catch (err) {
return "";
}
if (iconStats && iconStats.html) {
return iconStats.html.join("");
}
return iconStats;
}

function getCriticalCSS(path) {
const criticalCSSPath = Path.resolve(process.cwd(), path || "");

try {
const criticalCSS = Fs.readFileSync(criticalCSSPath).toString();
return criticalCSS;
} catch (err) {
return "";
}
}
const {
utils: { loadFuncFromModule }
} = require("@xarc/index-page");

const updateFullTemplate = (baseDir, options) => {
if (options.templateFile) {
Expand Down Expand Up @@ -128,12 +88,47 @@ function errorResponse({ routeName, h, err }) {
}
}

const checkSSRMetricsReporting = _.once(topOpts => {
const reporting = _.get(topOpts, "reporting", {});
if (!reporting.enable || !reporting.reporter) {
// eslint-disable-next-line
console.log(
`INFO: subapp-server disabled SSR metrics. options.report: ${JSON.stringify(reporting)}`
);
}
});

/**
* invoke user specified custom template processing hook
*
* @param {*} asyncTemplate - the template container
* @param {*} routeOptions - route options
*
* @returns {*} - result from the processing hook
*/
function invokeTemplateProcessor(asyncTemplate, routeOptions) {
const tp = routeOptions.templateProcessor;

if (tp) {
let tpFunc;
if (typeof tp === "string") {
tpFunc = loadFuncFromModule(tp, "templateProcessor");
} else {
tpFunc = tp;
assert(typeof tpFunc === "function", `templateProcessor is not a function`);
}

return tpFunc(asyncTemplate, routeOptions);
}

return undefined;
}

module.exports = {
resolveChunkSelector,
getIconStats,
getCriticalCSS,
getDefaultRouteOptions,
updateFullTemplate,
errorResponse,
makeErrorStackResponse
makeErrorStackResponse,
checkSSRMetricsReporting,
invokeTemplateProcessor
};
1 change: 1 addition & 0 deletions packages/subapp-server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"dependencies": {
"@hapi/boom": "^7.4.1",
"@xarc/index-page": "^1.0.0",
"@xarc/jsx-renderer": "^1.0.0",
"filter-scan-dir": "^1.0.9",
"http-status-codes": "^1.3.0",
"optional-require": "^1.0.0",
Expand Down
Loading

0 comments on commit 91352c0

Please sign in to comment.