subapp-server
Advanced tools
Comparing version 1.3.0 to 1.3.1
@@ -5,107 +5,226 @@ "use strict"; | ||
const Path = require("path"); | ||
const assert = require("assert"); | ||
const _ = require("lodash"); | ||
const xaa = require("xaa"); | ||
const HttpStatus = require("./http-status"); | ||
const subAppUtil = require("subapp-util"); | ||
const HttpStatusCodes = require("http-status-codes"); | ||
const Fs = require("fs"); | ||
const util = require("util"); | ||
const readFile = util.promisify(Fs.readFile); | ||
const { | ||
utils: { resolveChunkSelector } | ||
} = require("@xarc/index-page"); | ||
const { makeErrorStackResponse } = require("./utils"); | ||
const { getSrcDir, setupRouteRender, searchRoutesFromFile } = require("./setup-hapi-routes"); | ||
const { | ||
getSrcDir, | ||
makeErrorStackResponse, | ||
checkSSRMetricsReporting, | ||
updateFullTemplate | ||
} = require("./utils"); | ||
module.exports = { | ||
fastifyPlugin: async (fastify, pluginOpts) => { | ||
const srcDir = getSrcDir(pluginOpts); | ||
const routesFromFile = require("./routes-from-file"); | ||
const routesFromDir = require("./routes-from-dir"); | ||
const templateRouting = require("./template-routing"); | ||
// TODO: | ||
// const fromDir = await searchRoutesDir(srcDir, pluginOpts); | ||
// if (fromDir) { | ||
// // | ||
// } | ||
function makeRouteHandler({ path, routeRenderer, routeOptions }) { | ||
const useStream = routeOptions.useStream !== false; | ||
const { routes, topOpts } = searchRoutesFromFile(srcDir, pluginOpts); | ||
return async (request, reply) => { | ||
try { | ||
const context = await routeRenderer({ | ||
useStream, | ||
mode: "", | ||
request | ||
}); | ||
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.`); | ||
const data = context.result; | ||
const status = data.status; | ||
if (data instanceof Error) { | ||
// rethrow to get default error behavior below with helpful errors in dev mode | ||
throw data; | ||
} else if (status === undefined) { | ||
reply.type("text/html; charset=UTF-8").code(HttpStatusCodes.OK); | ||
return reply.send(data); | ||
} else if (HttpStatus.redirect[status]) { | ||
return reply.redirect(status, data.path); | ||
} else if (HttpStatus.displayHtml[status] || (status >= HttpStatusCodes.OK && status < 300)) { | ||
reply.type("text/html; charset=UTF-8").code(status); | ||
return reply.send(data.html !== undefined ? data.html : data); | ||
} else { | ||
reply.code(status); | ||
return reply.send(data); | ||
} | ||
} catch (err) { | ||
reply.status(HttpStatusCodes.INTERNAL_SERVER_ERROR); | ||
if (process.env.NODE_ENV !== "production") { | ||
const responseHtml = makeErrorStackResponse(path, err); | ||
reply.type("text/html; charset=UTF-8"); | ||
return reply.send(responseHtml); | ||
} else { | ||
return reply.send("Internal Server Error"); | ||
} | ||
} | ||
}; | ||
} | ||
const subApps = await subAppUtil.scanSubAppsFromDir(srcDir); | ||
const subAppsByPath = subAppUtil.getSubAppByPathMap(subApps); | ||
function getRoutePaths(route, path = null) { | ||
const defaultMethods = [].concat(route.methods || "get"); | ||
const paths = _.uniq([path].concat(route.path, route.paths).filter(x => x)).map(x => { | ||
if (typeof x === "string") { | ||
return { [x]: defaultMethods }; | ||
} | ||
return x; | ||
}); | ||
const makeRouteHandler = (path, route) => { | ||
const routeOptions = Object.assign({}, topOpts, route); | ||
return paths; | ||
} | ||
const routeRenderer = setupRouteRender({ subAppsByPath, srcDir, routeOptions }); | ||
const useStream = routeOptions.useStream !== false; | ||
async function registerFastifyRoutesFromFile({ fastify, srcDir, routes, topOpts }) { | ||
checkSSRMetricsReporting(topOpts); | ||
return async (request, reply) => { | ||
try { | ||
const context = await routeRenderer({ | ||
content: { | ||
html: "", | ||
status: 200, | ||
useStream | ||
}, | ||
mode: "", | ||
request | ||
}); | ||
const subApps = await subAppUtil.scanSubAppsFromDir(srcDir); | ||
const subAppsByPath = subAppUtil.getSubAppByPathMap(subApps); | ||
const data = context.result; | ||
const status = data.status; | ||
if (data instanceof Error) { | ||
// rethrow to get default error behavior below with helpful errors in dev mode | ||
throw data; | ||
} else if (status === undefined) { | ||
reply.type("text/html; charset=UTF-8").code(HttpStatusCodes.OK); | ||
return reply.send(data); | ||
} else if (HttpStatus.redirect[status]) { | ||
return reply.redirect(status, data.path); | ||
} else if ( | ||
HttpStatus.displayHtml[status] || | ||
(status >= HttpStatusCodes.OK && status < 300) | ||
) { | ||
reply.type("text/html; charset=UTF-8").code(status); | ||
return reply.send(data.html !== undefined ? data.html : data); | ||
} else { | ||
reply.code(status); | ||
return reply.send(data); | ||
} | ||
} catch (err) { | ||
reply.status(HttpStatusCodes.INTERNAL_SERVER_ERROR); | ||
if (process.env.NODE_ENV !== "production") { | ||
const responseHtml = makeErrorStackResponse(path, err); | ||
reply.type("text/html; charset=UTF-8"); | ||
return reply.send(responseHtml); | ||
} else { | ||
return reply.send("Internal Server Error"); | ||
} | ||
} | ||
}; | ||
}; | ||
for (const path in routes) { | ||
const route = routes[path]; | ||
for (const path in routes) { | ||
const route = routes[path]; | ||
const routeOptions = Object.assign({}, topOpts, route); | ||
const handler = makeRouteHandler(path, route); | ||
const routeRenderer = routesFromFile.setupRouteTemplate({ | ||
subAppsByPath, | ||
srcDir, | ||
routeOptions | ||
}); | ||
const defaultMethods = [].concat(route.methods || "get"); | ||
const paths = _.uniq([path].concat(route.paths).filter(x => x)).map(x => { | ||
if (typeof x === "string") { | ||
return { [x]: defaultMethods }; | ||
} | ||
return x; | ||
const handler = makeRouteHandler({ path, routeRenderer, routeOptions }); | ||
getRoutePaths(route, path).forEach(pathObj => { | ||
_.each(pathObj, (method, xpath) => { | ||
fastify.route({ | ||
...route.settings, | ||
path: xpath, | ||
method: method.map(x => x.toUpperCase()), | ||
handler | ||
}); | ||
}); | ||
}); | ||
} | ||
} | ||
paths.forEach(pathObj => { | ||
_.each(pathObj, (method, xpath) => { | ||
fastify.route({ | ||
...route.settings, | ||
path: xpath, | ||
method: method.map(x => x.toUpperCase()), | ||
handler | ||
}); | ||
async function registerFastifyRoutesFromDir({ fastify, topOpts, routes }) { | ||
checkSSRMetricsReporting(topOpts); | ||
routes.forEach(routeInfo => { | ||
const { route } = routeInfo; | ||
const routeOptions = Object.assign( | ||
{}, | ||
topOpts, | ||
_.pick(route, ["pageTitle", "bundleChunkSelector", "templateFile", "selectTemplate"]) | ||
); | ||
assert( | ||
routeOptions.templateFile, | ||
`subapp-server: route ${routeInfo.name} must define templateFile` | ||
); | ||
updateFullTemplate(routeInfo.dir, routeOptions); | ||
const chunkSelector = resolveChunkSelector(routeOptions); | ||
routeOptions.__internals = { chunkSelector }; | ||
const routeRenderer = templateRouting.makeRouteTemplateSelector(routeOptions); | ||
const paths = getRoutePaths(route); | ||
for (const pathObj of paths) { | ||
_.each(pathObj, (method, xpath) => { | ||
const routeHandler = makeRouteHandler({ path: xpath, routeRenderer, routeOptions }); | ||
fastify.route({ | ||
...route.options, | ||
path: xpath, | ||
method: method.map(x => x.toUpperCase()), | ||
handler: routeHandler | ||
}); | ||
}); | ||
} | ||
}); | ||
} | ||
async function setupRoutesFromDir(fastify, srcDir, fromDir) { | ||
const { routes, topOpts } = fromDir; | ||
topOpts.routes = _.merge({}, routes, topOpts.routes); | ||
const routesWithSetup = routes.filter(x => x.route.setup); | ||
for (const route of routesWithSetup) { | ||
await route.route.setup(fastify); | ||
} | ||
// TODO: invoke optional route intiailize hook | ||
// in case needed, add full protocol/host/port to dev bundle base URL | ||
topOpts.devBundleBase = subAppUtil.formUrl({ | ||
..._.pick(topOpts.devServer, ["protocol", "host", "port"]), | ||
path: topOpts.devBundleBase | ||
}); | ||
await registerFastifyRoutesFromDir({ fastify, srcDir, topOpts, routes }); | ||
} | ||
async function handleFavIcon(fastify, options) { | ||
// | ||
// favicon handling, turn off by setting options.favicon to false | ||
// | ||
if (options.favicon === false) { | ||
return; | ||
} | ||
// look in CWD/static | ||
let icon; | ||
const favIcons = [options.favicon, "static/favicon.ico", "static/favicon.png"].filter(_.identity); | ||
for (let i = 0; i < favIcons.length && !icon; i++) { | ||
const file = Path.resolve(favIcons[i]); | ||
icon = await xaa.try(() => readFile(file)); | ||
} | ||
fastify.route({ | ||
method: "GET", | ||
path: "/favicon.ico", | ||
handler(request, reply) { | ||
if (icon) { | ||
reply.type("image/x-icon").send(icon).status(HttpStatusCodes.OK); | ||
} else { | ||
reply.send("").status(HttpStatusCodes.NOT_FOUND); | ||
} | ||
} | ||
}); | ||
} | ||
module.exports = { | ||
fastifyPlugin: async (fastify, pluginOpts) => { | ||
const srcDir = getSrcDir(pluginOpts); | ||
await handleFavIcon(fastify, pluginOpts); | ||
const fromDir = await routesFromDir.searchRoutes(srcDir, pluginOpts); | ||
if (fromDir) { | ||
return await setupRoutesFromDir(fastify, srcDir, fromDir); | ||
} | ||
const { routes, topOpts } = routesFromFile.searchRoutes(srcDir, pluginOpts); | ||
// invoke setup callback | ||
for (const path in routes) { | ||
if (routes[path].setup) { | ||
await routes[path].setup(fastify); | ||
} | ||
} | ||
return registerFastifyRoutesFromFile({ fastify, srcDir, routes, topOpts }); | ||
} | ||
}; |
@@ -8,7 +8,13 @@ "use strict"; | ||
const HttpStatus = require("./http-status"); | ||
const { ReactWebapp } = require("electrode-react-webapp"); | ||
const { errorResponse, resolveChunkSelector, updateFullTemplate } = require("./utils"); | ||
const templateRouting = require("./template-routing"); | ||
const { errorResponse, updateFullTemplate } = require("./utils"); | ||
const { | ||
utils: { resolveChunkSelector } | ||
} = require("@xarc/index-page"); | ||
const HttpStatusCodes = require("http-status-codes"); | ||
const { checkSSRMetricsReporting } = require("./utils"); | ||
module.exports = function registerRoutes({ routes, topOpts, server }) { | ||
checkSSRMetricsReporting(topOpts); | ||
// register routes | ||
@@ -34,3 +40,3 @@ routes.forEach(routeInfo => { | ||
const routeHandler = ReactWebapp.makeRouteHandler(routeOptions); | ||
const routeHandler = templateRouting.makeRouteTemplateSelector(routeOptions); | ||
@@ -42,3 +48,3 @@ const useStream = routeOptions.useStream !== false; | ||
const context = await routeHandler({ | ||
content: { html: "", status: HttpStatusCodes.OK, useStream }, | ||
useStream, | ||
mode: "", | ||
@@ -45,0 +51,0 @@ request |
@@ -10,6 +10,3 @@ "use strict"; | ||
const Path = require("path"); | ||
const assert = require("assert"); | ||
const util = require("util"); | ||
const optionalRequire = require("optional-require")(require); | ||
const scanDir = require("filter-scan-dir"); | ||
const Boom = require("@hapi/boom"); | ||
@@ -19,63 +16,9 @@ const HttpStatus = require("./http-status"); | ||
const xaa = require("xaa"); | ||
const { ReactWebapp } = require("electrode-react-webapp"); | ||
const subAppUtil = require("subapp-util"); | ||
const registerRoutes = require("./register-routes"); | ||
const { | ||
errorResponse, | ||
resolveChunkSelector, | ||
getDefaultRouteOptions, | ||
updateFullTemplate | ||
} = require("./utils"); | ||
const routesFromFile = require("./routes-from-file"); | ||
const routesFromDir = require("./routes-from-dir"); | ||
const { errorResponse, getSrcDir } = require("./utils"); | ||
async function searchRoutesDir(srcDir, pluginOpts) { | ||
const { loadRoutesFrom } = pluginOpts; | ||
const routesDir = [ | ||
loadRoutesFrom && Path.resolve(srcDir, loadRoutesFrom), | ||
Path.resolve(srcDir, "routes"), | ||
Path.resolve(srcDir, "server", "routes"), | ||
Path.resolve(srcDir, "server-routes") | ||
].find(x => x && Fs.existsSync(x) && Fs.statSync(x).isDirectory()); | ||
// there's no routes, server/routes, or server-routes dir | ||
if (!routesDir) { | ||
return undefined; | ||
} | ||
// | ||
// look for routes under routesDir | ||
// - each dir inside is considered to be a route with name being the dir name | ||
// | ||
const dirs = await scanDir({ dir: routesDir, includeRoot: true, filter: f => f === "route.js" }); | ||
// | ||
// load options for all routes from routesDir/options.js[x] | ||
// | ||
const options = optionalRequire(Path.join(routesDir, "options"), { default: {} }); | ||
// | ||
// Generate routes: load the route.js file for each route | ||
// | ||
const routes = dirs.map(x => { | ||
const name = Path.dirname(x.substring(routesDir.length + 1)); | ||
const route = Object.assign({}, require(x)); | ||
_.defaults(route, { | ||
// the route dir that's named default is / | ||
path: name === "default" && "/", | ||
methods: options.methods || ["get"] | ||
}); | ||
assert(route.path, `subapp-server: route ${name} must define a path`); | ||
return { | ||
name, | ||
dir: Path.dirname(x), | ||
route | ||
}; | ||
}); | ||
return { options, dir: routesDir, routes }; | ||
} | ||
async function handleFavIcon(server, options) { | ||
@@ -103,41 +46,3 @@ // | ||
function setupRouteRender({ subAppsByPath, srcDir, routeOptions }) { | ||
updateFullTemplate(routeOptions.dir, routeOptions); | ||
const chunkSelector = resolveChunkSelector(routeOptions); | ||
routeOptions.__internals = { chunkSelector }; | ||
// load subapps for the route | ||
if (routeOptions.subApps) { | ||
routeOptions.__internals.subApps = [].concat(routeOptions.subApps).map(x => { | ||
let options; | ||
if (Array.isArray(x)) { | ||
options = x[1]; | ||
x = x[0]; | ||
} | ||
// absolute: use as path | ||
// module: resolve module path | ||
// else: assume dir under srcDir | ||
if (!x.startsWith(".") && !x.startsWith("/")) { | ||
const subAppPath = optionalRequire.resolve(x); | ||
if (subAppPath) { | ||
const { manifest, subAppOptions } = require(x); | ||
x = manifest ? Path.dirname(subAppPath) : x; | ||
options = options || subAppOptions; | ||
} | ||
} | ||
return { | ||
subapp: subAppsByPath[Path.isAbsolute(x) ? x : Path.resolve(srcDir, x)], | ||
options: options || {} | ||
}; | ||
}); | ||
} | ||
// const useStream = routeOptions.useStream !== false; | ||
const routeHandler = ReactWebapp.makeRouteHandler(routeOptions); | ||
return routeHandler; | ||
} | ||
async function registerHapiRoutes({ server, srcDir, routes, topOpts }) { | ||
async function registerRoutesFromFile({ server, srcDir, routes, topOpts }) { | ||
const subApps = await subAppUtil.scanSubAppsFromDir(srcDir); | ||
@@ -162,3 +67,9 @@ const subAppsByPath = subAppUtil.getSubAppByPathMap(subApps); | ||
const routeRenderer = setupRouteRender({ subAppsByPath, srcDir, routeOptions }); | ||
// setup the template for rendering the route | ||
const routeRenderer = routesFromFile.setupRouteTemplate({ | ||
subAppsByPath, | ||
srcDir, | ||
routeOptions | ||
}); | ||
const useStream = routeOptions.useStream !== false; | ||
@@ -169,7 +80,3 @@ | ||
const context = await routeRenderer({ | ||
content: { | ||
html: "", | ||
status: 200, | ||
useStream | ||
}, | ||
useStream, | ||
mode: "", | ||
@@ -227,43 +134,5 @@ request | ||
function searchRoutesFromFile(srcDir, pluginOpts) { | ||
// there should be a src/routes.js file with routes spec | ||
const { loadRoutesFrom } = pluginOpts; | ||
const routesFile = [ | ||
loadRoutesFrom && Path.resolve(srcDir, loadRoutesFrom), | ||
Path.resolve(srcDir, "routes") | ||
].find(x => x && optionalRequire(x)); | ||
const spec = routesFile ? require(routesFile) : {}; | ||
const topOpts = _.merge( | ||
getDefaultRouteOptions(), | ||
{ dir: Path.resolve(srcDir) }, | ||
_.omit(spec, ["routes", "default"]), | ||
pluginOpts | ||
); | ||
topOpts.routes = _.merge({}, spec.routes || spec.default, topOpts.routes); | ||
// routes can either be in default (es6) or routes | ||
const routes = topOpts.routes; | ||
// in case needed, add full protocol/host/port to dev bundle base URL | ||
topOpts.devBundleBase = subAppUtil.formUrl({ | ||
..._.pick(topOpts.devServer, ["protocol", "host", "port"]), | ||
path: topOpts.devBundleBase | ||
}); | ||
return { routes, topOpts }; | ||
} | ||
async function setupRoutesFromFile(srcDir, server, pluginOpts) { | ||
const { routes, topOpts } = searchRoutesFromFile(srcDir, pluginOpts); | ||
const { routes, topOpts } = routesFromFile.searchRoutes(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.`); | ||
} | ||
await handleFavIcon(server, topOpts); | ||
@@ -278,3 +147,3 @@ | ||
await registerHapiRoutes({ | ||
await registerRoutesFromFile({ | ||
server, | ||
@@ -287,17 +156,7 @@ routes, | ||
async function setupRoutesFromDir(server, pluginOpts, fromDir) { | ||
const { routes } = fromDir; | ||
async function setupRoutesFromDir(server, fromDir) { | ||
const { routes, topOpts } = 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.`); | ||
} | ||
topOpts.routes = _.merge({}, routes, topOpts.routes); | ||
updateFullTemplate(fromDir.dir, topOpts); | ||
const routesWithSetup = routes.filter(x => x.route.setup); | ||
@@ -333,16 +192,8 @@ | ||
function getSrcDir(pluginOpts) { | ||
return ( | ||
pluginOpts.srcDir || | ||
process.env.APP_SRC_DIR || | ||
(process.env.NODE_ENV === "production" ? "lib" : "src") | ||
); | ||
} | ||
async function setupSubAppHapiRoutes(server, pluginOpts) { | ||
const srcDir = getSrcDir(pluginOpts); | ||
const fromDir = await searchRoutesDir(srcDir, pluginOpts); | ||
const fromDir = await routesFromDir.searchRoutes(srcDir, pluginOpts); | ||
if (fromDir) { | ||
return await setupRoutesFromDir(server, pluginOpts, fromDir); | ||
return await setupRoutesFromDir(server, fromDir); | ||
} | ||
@@ -355,8 +206,4 @@ | ||
module.exports = { | ||
getSrcDir, | ||
searchRoutesDir, | ||
searchRoutesFromFile, | ||
setupRoutesFromFile, | ||
setupSubAppHapiRoutes, | ||
setupRouteRender | ||
setupSubAppHapiRoutes | ||
}; |
104
lib/utils.js
@@ -5,5 +5,4 @@ "use strict"; | ||
// Copy from electrode-react-webapp for now | ||
const Fs = require("fs"); | ||
const assert = require("assert"); | ||
const _ = require("lodash"); | ||
const Path = require("path"); | ||
@@ -14,47 +13,6 @@ const Boom = require("@hapi/boom"); | ||
const HttpStatusCodes = require("http-status-codes"); | ||
const { | ||
utils: { loadFuncFromModule } | ||
} = require("@xarc/index-page"); | ||
/** | ||
* 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 updateFullTemplate = (baseDir, options) => { | ||
@@ -133,10 +91,54 @@ if (options.templateFile) { | ||
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; | ||
} | ||
function getSrcDir(pluginOpts) { | ||
return ( | ||
pluginOpts.srcDir || | ||
process.env.APP_SRC_DIR || | ||
(process.env.NODE_ENV === "production" ? "lib" : "src") | ||
); | ||
} | ||
module.exports = { | ||
resolveChunkSelector, | ||
getIconStats, | ||
getCriticalCSS, | ||
getSrcDir, | ||
getDefaultRouteOptions, | ||
updateFullTemplate, | ||
errorResponse, | ||
makeErrorStackResponse | ||
makeErrorStackResponse, | ||
checkSSRMetricsReporting, | ||
invokeTemplateProcessor | ||
}; |
{ | ||
"name": "subapp-server", | ||
"version": "1.3.0", | ||
"version": "1.3.1", | ||
"description": "Electrode SubApp app server support", | ||
@@ -31,3 +31,5 @@ "main": "lib/index.js", | ||
"@hapi/boom": "^7.4.1", | ||
"electrode-react-webapp": "^3.8.10", | ||
"@xarc/index-page": "^1.0.0", | ||
"@xarc/jsx-renderer": "^1.0.0", | ||
"@xarc/tag-renderer": "^1.0.0", | ||
"filter-scan-dir": "^1.0.9", | ||
@@ -52,4 +54,6 @@ "http-status-codes": "^1.3.0", | ||
"dependencies": { | ||
"electrode-react-webapp": "../electrode-react-webapp", | ||
"subapp-util": "../subapp-util" | ||
"@xarc/index-page": "../xarc-index-page", | ||
"subapp-util": "../subapp-util", | ||
"@xarc/jsx-renderer": "../xarc-jsx-renderer", | ||
"@xarc/tag-renderer": "../xarc-tag-renderer" | ||
} | ||
@@ -56,0 +60,0 @@ }, |
@@ -8,6 +8,8 @@ "use strict"; | ||
var _template = require("../template"); | ||
var _jsxRenderer = require("@xarc/jsx-renderer"); | ||
var _subappWeb = require("subapp-web"); | ||
var _indexPage = require("@xarc/index-page"); | ||
/* @jsx createElement */ | ||
@@ -21,3 +23,3 @@ const RenderSubApps = (props, context) => { | ||
} = routeOptions.__internals; | ||
return subApps && subApps.length > 0 && (0, _template.createElement)("div", null, ...subApps.map((info, ix) => { | ||
return subApps && subApps.length > 0 && (0, _jsxRenderer.createElement)("div", null, ...subApps.map((info, ix) => { | ||
const { | ||
@@ -28,3 +30,3 @@ subapp, | ||
const elementId = props.inline ? undefined : `subapp-${subapp.name}-${ix}`; | ||
return (0, _template.createElement)(_template.Require, Object.assign({ | ||
return (0, _jsxRenderer.createElement)(_jsxRenderer.Require, Object.assign({ | ||
_concurrent: true, | ||
@@ -43,32 +45,34 @@ elementId, | ||
const Template = (0, _template.createElement)(_template.IndexPage, { | ||
const Template = (0, _jsxRenderer.createElement)(_jsxRenderer.IndexPage, { | ||
DOCTYPE: "html" | ||
}, (0, _template.createElement)(_template.Token, { | ||
}, (0, _jsxRenderer.createElement)(_jsxRenderer.RegisterTokenIds, { | ||
handler: _indexPage.tokenHandler | ||
}), (0, _jsxRenderer.createElement)(_jsxRenderer.Token, { | ||
_id: "INITIALIZE" | ||
}), (0, _template.createElement)("html", { | ||
}), (0, _jsxRenderer.createElement)("html", { | ||
lang: "en" | ||
}, (0, _template.createElement)("head", null, (0, _template.createElement)("meta", { | ||
}, (0, _jsxRenderer.createElement)("head", null, (0, _jsxRenderer.createElement)("meta", { | ||
charset: "UTF-8" | ||
}), (0, _template.createElement)("meta", { | ||
}), (0, _jsxRenderer.createElement)("meta", { | ||
name: "viewport", | ||
content: "width=device-width, initial-scale=1.0" | ||
}), (0, _template.createElement)(_subappWeb.ReserveSpot, { | ||
}), (0, _jsxRenderer.createElement)(_subappWeb.ReserveSpot, { | ||
saveId: "headEntries" | ||
}), (0, _template.createElement)(_template.Require, { | ||
}), (0, _jsxRenderer.createElement)(_jsxRenderer.Require, { | ||
_id: "subapp-web/lib/polyfill" | ||
}), (0, _template.createElement)(_template.Token, { | ||
}), (0, _jsxRenderer.createElement)(_jsxRenderer.Token, { | ||
_id: "META_TAGS" | ||
}), (0, _template.createElement)(_template.Token, { | ||
}), (0, _jsxRenderer.createElement)(_jsxRenderer.Token, { | ||
_id: "PAGE_TITLE" | ||
}), (0, _template.createElement)(_template.Require, { | ||
}), (0, _jsxRenderer.createElement)(_jsxRenderer.Require, { | ||
_id: "subapp-web/lib/init" | ||
}), (0, _template.createElement)(_template.Token, { | ||
}), (0, _jsxRenderer.createElement)(_jsxRenderer.Token, { | ||
_id: "CRITICAL_CSS" | ||
})), (0, _template.createElement)(_template.Token, { | ||
})), (0, _jsxRenderer.createElement)(_jsxRenderer.Token, { | ||
_id: "HEAD_CLOSED" | ||
}), (0, _template.createElement)("body", null, (0, _template.createElement)("noscript", null, (0, _template.createElement)("h4", null, "JavaScript is Disabled"), (0, _template.createElement)("p", null, "Sorry, this webpage requires JavaScript to function correctly."), (0, _template.createElement)("p", null, "Please enable JavaScript in your browser and reload the page.")), (0, _template.createElement)(RenderSubApps, null), (0, _template.createElement)(_template.Require, { | ||
}), (0, _jsxRenderer.createElement)("body", null, (0, _jsxRenderer.createElement)("noscript", null, (0, _jsxRenderer.createElement)("h4", null, "JavaScript is Disabled"), (0, _jsxRenderer.createElement)("p", null, "Sorry, this webpage requires JavaScript to function correctly."), (0, _jsxRenderer.createElement)("p", null, "Please enable JavaScript in your browser and reload the page.")), (0, _jsxRenderer.createElement)(RenderSubApps, null), (0, _jsxRenderer.createElement)(_jsxRenderer.Require, { | ||
_id: "subapp-web/lib/start" | ||
})), (0, _template.createElement)(_template.Token, { | ||
})), (0, _jsxRenderer.createElement)(_jsxRenderer.Token, { | ||
_id: "BODY_CLOSED" | ||
})), (0, _template.createElement)(_template.Token, { | ||
})), (0, _jsxRenderer.createElement)(_jsxRenderer.Token, { | ||
_id: "HTML_CLOSED" | ||
@@ -75,0 +79,0 @@ })); |
/* @jsx createElement */ | ||
import { IndexPage, createElement, Token, Require } from "../template"; | ||
import { IndexPage, createElement, Token, Require, RegisterTokenIds } from "@xarc/jsx-renderer"; | ||
import { ReserveSpot } from "subapp-web"; | ||
import { tokenHandler } from "@xarc/index-page"; | ||
@@ -45,2 +46,3 @@ const RenderSubApps = (props, context) => { | ||
<IndexPage DOCTYPE="html"> | ||
<RegisterTokenIds handler={tokenHandler} /> | ||
<Token _id="INITIALIZE" /> | ||
@@ -47,0 +49,0 @@ <html lang="en"> |
"use strict"; | ||
module.exports = require("electrode-react-webapp/lib/jsx"); | ||
module.exports = require("@xarc/jsx-renderer"); |
Sorry, the diff of this file is not supported yet
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 5 instances in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
44546
18
1109
9
18
+ Added@xarc/index-page@^1.0.0
+ Added@xarc/jsx-renderer@^1.0.0
+ Added@xarc/tag-renderer@^1.0.0
+ Added@xarc/index-page@1.2.4(transitive)
+ Added@xarc/jsx-renderer@1.0.16(transitive)
+ Added@xarc/render-context@1.0.13(transitive)
+ Added@xarc/tag-renderer@1.0.15(transitive)
+ Addedtslib@2.8.1(transitive)
+ Addedxaa@1.7.1(transitive)
- Removedelectrode-react-webapp@^3.8.10
- Removedbluebird@3.7.2(transitive)
- Removedelectrode-hapi-compat@1.3.3(transitive)
- Removedelectrode-react-webapp@3.8.10(transitive)
- Removedin-publish@2.0.1(transitive)
- Removedstring-array@1.0.1(transitive)