From 8e7547d8b112c79fb925115a6a8df3fbcfaf1276 Mon Sep 17 00:00:00 2001 From: Joel Chen Date: Tue, 21 Jul 2020 12:35:08 -0700 Subject: [PATCH] add tag-renderer template to subapp-server (#1703) --- packages/subapp-server/lib/index-page.js | 72 +++++++++++++++++++ .../subapp-server/lib/template-routing.js | 41 ++++++----- packages/subapp-server/lib/utils.js | 2 - packages/subapp-web/lib/init.js | 2 + packages/xarc-tag-renderer/src/index.ts | 1 + .../xarc-tag-renderer/src/render-processor.ts | 18 ++++- .../xarc-tag-renderer/src/tag-renderer.ts | 17 +++-- .../xarc-tag-renderer/src/tag-template.ts | 12 ++++ .../xarc-tag-renderer/test/data/template3.ts | 2 +- .../test/spec/tag-renderer.spec.ts | 4 +- 10 files changed, 137 insertions(+), 34 deletions(-) create mode 100644 packages/subapp-server/lib/index-page.js diff --git a/packages/subapp-server/lib/index-page.js b/packages/subapp-server/lib/index-page.js new file mode 100644 index 000000000..17ddede16 --- /dev/null +++ b/packages/subapp-server/lib/index-page.js @@ -0,0 +1,72 @@ +"use strict"; + +/* eslint-disable new-cap */ + +const { + createTemplateTags, + createTemplateTagsFromArray, + Token, + TokenInvoke, + RegisterTokenIds +} = require("@xarc/tag-renderer"); + +const { tokenHandler } = require("@xarc/index-page"); +const { ReserveSpot } = require("subapp-web"); +const subappWebPolyfill = require("subapp-web/lib/polyfill"); +const subappWebLoad = require("subapp-web/lib/load"); +const subappWebInit = require("subapp-web/lib/init"); +const subappWebStart = require("subapp-web/lib/start"); + +const RenderSubApps = context => { + const { routeOptions } = context.user; + const { subApps } = routeOptions.__internals; + + if (subApps && subApps.length > 0) { + return createTemplateTagsFromArray( + subApps.map((info, ix) => { + const { subapp, options } = info; + const elementId = `subapp-${subapp.name}-${ix}`; + + return TokenInvoke( + subappWebLoad, + Object.assign( + { + _concurrent: true, + elementId, + timestamp: true, + useStream: false, + async: true, + serverSideRendering: true + }, + options, + { name: subapp.name } + ) + ); + }) + ); + } + + return null; +}; + +exports.templateTags = createTemplateTags` +${RegisterTokenIds(tokenHandler)} +${Token("INITIALIZE")} + + + + ${TokenInvoke(subappWebInit)} + ${context => ReserveSpot({ saveId: "headEntries" }, context)} + ${TokenInvoke(subappWebPolyfill)} + ${Token("META_TAGS")} + ${Token("PAGE_TITLE")} + ${Token("CRITICAL_CSS")} + + + ${RenderSubApps} + ${TokenInvoke(subappWebStart)} +`; diff --git a/packages/subapp-server/lib/template-routing.js b/packages/subapp-server/lib/template-routing.js index 039ba1dc1..c1919d474 100644 --- a/packages/subapp-server/lib/template-routing.js +++ b/packages/subapp-server/lib/template-routing.js @@ -4,6 +4,7 @@ const _ = require("lodash"); const Path = require("path"); const { TagRenderer } = require("@xarc/tag-renderer"); const { JsxRenderer } = require("@xarc/jsx-renderer"); +const { loadTokenModuleHandler } = require("@xarc/render-context"); const { utils: { resolvePath, @@ -12,20 +13,19 @@ const { resolveChunkSelector, loadAssetsFromStats, getStatsPath, - invokeTemplateProcessor, makeDevBundleBase } } = require("@xarc/index-page"); +const assert = require("assert"); const otherStats = getOtherStats(); /*eslint-disable max-statements*/ function initializeTemplate( - { htmlFile, templateFile, tokenHandlers, cacheId, cacheKey, options }, + { templateFile, tokenHandlers, cacheId, cacheKey, options }, routeOptions ) { - const tmplFile = templateFile || htmlFile; - cacheKey = cacheKey || (cacheId && `${tmplFile}#${cacheId}`) || tmplFile; + cacheKey = cacheKey || (cacheId && `${templateFile}#${cacheId}`) || templateFile; let asyncTemplate = routeOptions._templateCache[cacheKey]; if (asyncTemplate) { @@ -52,29 +52,34 @@ function initializeTemplate( : userTokenHandlers; } - if (!templateFile) { - asyncTemplate = new TagRenderer({ - htmlFile, + const templateFullPath = resolvePath(templateFile); + const templateModule = require(templateFullPath); // eslint-disable-line + const template = _.get(templateModule, "default", templateModule); + + if (template.children) { + // JSX + asyncTemplate = new JsxRenderer({ + templateFullPath: Path.dirname(templateFullPath), + template, tokenHandlers: finalTokenHandlers.filter(x => x), insertTokenIds: routeOptions.insertTokenIds, routeOptions }); - - invokeTemplateProcessor(asyncTemplate, routeOptions); - asyncTemplate.initializeRenderer(); } else { - const templateFullPath = resolvePath(tmplFile); - const template = require(templateFullPath); // eslint-disable-line - asyncTemplate = new JsxRenderer({ - templateFullPath: Path.dirname(templateFullPath), - template: _.get(template, "default", template), - tokenHandlers: finalTokenHandlers.filter(x => x), + // Tag + const templateTags = _.get(template, "templateTags", template); + asyncTemplate = new TagRenderer({ + templateTags, + tokenHandlers: finalTokenHandlers.map(x => loadTokenModuleHandler(x)), insertTokenIds: routeOptions.insertTokenIds, routeOptions }); - asyncTemplate.initializeRenderer(); } + // + + asyncTemplate.initializeRenderer(); + return (routeOptions._templateCache[cacheKey] = asyncTemplate); } @@ -113,6 +118,8 @@ function makeRouteTemplateSelector(routeOptions) { return render(options, selection); } + assert(!defaultSelection.htmlFile, `subapp-server doesn't support htmlFile templates`); + const asyncTemplate = initializeTemplate(defaultSelection, routeOptions); return asyncTemplate.render(options); }; diff --git a/packages/subapp-server/lib/utils.js b/packages/subapp-server/lib/utils.js index 24f5740cd..5ff821180 100644 --- a/packages/subapp-server/lib/utils.js +++ b/packages/subapp-server/lib/utils.js @@ -2,8 +2,6 @@ /* eslint-disable no-console, no-magic-numbers */ -// Copy from electrode-react-webapp for now - const assert = require("assert"); const _ = require("lodash"); const Path = require("path"); diff --git a/packages/subapp-web/lib/init.js b/packages/subapp-web/lib/init.js index 9d60ab2e8..81e9c4aaf 100644 --- a/packages/subapp-web/lib/init.js +++ b/packages/subapp-web/lib/init.js @@ -7,6 +7,7 @@ const Path = require("path"); const util = require("./util"); const subappUtil = require("subapp-util"); const _ = require("lodash"); +const assert = require("assert"); module.exports = function setup(setupContext) { const distDir = process.env.NODE_ENV === "production" ? "../dist/min" : "../dist/dev"; @@ -20,6 +21,7 @@ module.exports = function setup(setupContext) { const metricReport = _.get(setupContext, "routeOptions.reporting", {}); const { assets } = util.loadAssetsFromStats(setupContext.routeOptions.stats); + assert(assets, `subapp-web unable to load assets from ${setupContext.routeOptions.stats}`); setupContext.routeOptions.__internals.assets = assets; const cdnJsBundles = util.getCdnJsBundles(assets, setupContext.routeOptions); diff --git a/packages/xarc-tag-renderer/src/index.ts b/packages/xarc-tag-renderer/src/index.ts index 2006e376e..e6993f6cd 100644 --- a/packages/xarc-tag-renderer/src/index.ts +++ b/packages/xarc-tag-renderer/src/index.ts @@ -2,6 +2,7 @@ export { TagRenderer } from "./tag-renderer"; export { TagTemplate, createTemplateTags, + createTemplateTagsFromArray, RegisterTokenIds, Token, TokenInvoke diff --git a/packages/xarc-tag-renderer/src/render-processor.ts b/packages/xarc-tag-renderer/src/render-processor.ts index 9e3a0fa02..7570df51e 100644 --- a/packages/xarc-tag-renderer/src/render-processor.ts +++ b/packages/xarc-tag-renderer/src/render-processor.ts @@ -4,6 +4,7 @@ import { executeTagTemplate, executeSteps } from "./render-execute"; import { TAG_TYPE } from "./symbols"; import { TagTemplate } from "./tag-template"; import { RenderContext } from "@xarc/render-context"; +import { TagRenderer } from "./tag-renderer"; const { STEP_HANDLER, @@ -17,6 +18,7 @@ const { export class RenderProcessor { _options: any; _insertTokenIds: boolean; + _renderer: TagRenderer; constructor(options: { /** Add debugging comment to rendered output with token IDs */ @@ -26,6 +28,15 @@ export class RenderProcessor { }) { this._options = options; this._insertTokenIds = Boolean(options.insertTokenIds); + this._renderer = options.asyncTemplate; + } + + applyTokenModuleLoad(options, template: TagTemplate) { + for (const tokenModule of template._templateTags) { + if (tokenModule.load && typeof tokenModule.load === "function") { + tokenModule.load({ ...this._renderer._handlerContext, ...options }); + } + } } /** @@ -49,11 +60,10 @@ export class RenderProcessor { * @param tk */ makeHandlerStep(tk: any) { - const options = this._options; const insertTokenIds = this._insertTokenIds; // look for first handler that has a token function for tk.id - const tkFunc = options.asyncTemplate.lookupTokenHandler(tk); + const tkFunc = this._renderer.lookupTokenHandler(tk); // no handler has function for token if (!tkFunc) { if (tkFunc === null) { @@ -102,9 +112,11 @@ export class RenderProcessor { tk({ asyncTemplate: options.asyncTemplate }); opCode = null; } else if (tk[TAG_TYPE] === "template") { + const template = new TagTemplate({ templateTags: tk, processor: this }); + this.applyTokenModuleLoad({ insertTokenIds: this._options.insertTokenIds }, template); opCode = { tk, - template: new TagTemplate({ templateTags: tk, processor: this }), + template, code: STEP_SUB_TEMPLATE }; } else if (tk.hasOwnProperty("str")) { diff --git a/packages/xarc-tag-renderer/src/tag-renderer.ts b/packages/xarc-tag-renderer/src/tag-renderer.ts index 091a5fdcf..4f1ef2979 100644 --- a/packages/xarc-tag-renderer/src/tag-renderer.ts +++ b/packages/xarc-tag-renderer/src/tag-renderer.ts @@ -44,15 +44,13 @@ export class TagRenderer { this._tokenIdLookupMap = {}; // the same context that gets passed to each token handler's setup function - this._handlerContext = _.merge( - { - user: { - // set routeOptions in user also for consistency - routeOptions: options.routeOptions - } + this._handlerContext = { + user: { + // set routeOptions in user also for consistency + routeOptions: options.routeOptions }, - options - ); + ...options + }; } /** @@ -62,7 +60,7 @@ export class TagRenderer { initializeRenderer(reset = !this._processor) { if (reset) { this._initializeTokenHandlers(this._tokenHandlers); - this._applyTokenLoad(this._options); + this._processor = new RenderProcessor({ asyncTemplate: this, insertTokenIds: this._options.insertTokenIds @@ -73,6 +71,7 @@ export class TagRenderer { processor: this._processor }); + this._processor.applyTokenModuleLoad(this._options, this._template); this._template.initTagOpCode(); } } diff --git a/packages/xarc-tag-renderer/src/tag-template.ts b/packages/xarc-tag-renderer/src/tag-template.ts index 7a4795cd2..ad31a2a76 100644 --- a/packages/xarc-tag-renderer/src/tag-template.ts +++ b/packages/xarc-tag-renderer/src/tag-template.ts @@ -59,6 +59,18 @@ export const createTemplateTags = (literals: TemplateStringsArray, ...args: any[ return combined; }; +/** + * Create a template tags array from a plain array of tags + * + * @param tags - array of tags + * @returns tat template + */ +export const createTemplateTagsFromArray = (tags: any[]) => { + const combined = [].concat(tags); + combined[TAG_TYPE] = "template"; + return combined; +}; + /** * Create a tag to invoke a token by its ID * diff --git a/packages/xarc-tag-renderer/test/data/template3.ts b/packages/xarc-tag-renderer/test/data/template3.ts index 45c9c03b3..3e5518056 100644 --- a/packages/xarc-tag-renderer/test/data/template3.ts +++ b/packages/xarc-tag-renderer/test/data/template3.ts @@ -16,7 +16,7 @@ const subTags2 = createTemplateTags`${RegisterTokenIds(tokenHandler)} } }; })} -
sub template tags 2${Token("X2")}
`; +
sub template tags 2${TokenInvoke(custom1)}${Token("X2")}
`; const subTags = createTemplateTags`${RegisterTokenIds(tokenHandler)} ${RegisterTokenIds(() => { diff --git a/packages/xarc-tag-renderer/test/spec/tag-renderer.spec.ts b/packages/xarc-tag-renderer/test/spec/tag-renderer.spec.ts index 9f311494f..42ee5abbb 100644 --- a/packages/xarc-tag-renderer/test/spec/tag-renderer.spec.ts +++ b/packages/xarc-tag-renderer/test/spec/tag-renderer.spec.ts @@ -35,10 +35,10 @@ describe("tag template", function () { expect(context.result).contains("
from custom-1
"); expect(context.result).contains( - "
subTags
sub template tagsx1
sub template tags 2x2
subTags
" + "
subTags
sub template tagsx1
sub template tags 2
from custom-1
x2
subTags
" ); expect(context.result).contains( - "
subTagsPromise
sub template tagsx1
sub template tags 2x2
subTagsPromise
" + "
subTagsPromise
sub template tagsx1
sub template tags 2
from custom-1
x2
subTagsPromise
" ); });