Skip to content

Commit

Permalink
add tag-renderer template to subapp-server (#1703)
Browse files Browse the repository at this point in the history
  • Loading branch information
jchip authored Jul 21, 2020
1 parent 572d831 commit 8e7547d
Show file tree
Hide file tree
Showing 10 changed files with 137 additions and 34 deletions.
72 changes: 72 additions & 0 deletions packages/subapp-server/lib/index-page.js
Original file line number Diff line number Diff line change
@@ -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`<!doctype html>
${RegisterTokenIds(tokenHandler)}
${Token("INITIALIZE")}
<html><head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
${TokenInvoke(subappWebInit)}
${context => ReserveSpot({ saveId: "headEntries" }, context)}
${TokenInvoke(subappWebPolyfill)}
${Token("META_TAGS")}
${Token("PAGE_TITLE")}
${Token("CRITICAL_CSS")}
</head><body>
<noscript>
<h4>JavaScript is Disabled</h4>
<p>Sorry, this webpage requires JavaScript to function correctly.</p>
<p>Please enable JavaScript in your browser and reload the page.</p>
</noscript>
${RenderSubApps}
${TokenInvoke(subappWebStart)}
</body><html>`;
41 changes: 24 additions & 17 deletions packages/subapp-server/lib/template-routing.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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) {
Expand All @@ -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);
}

Expand Down Expand Up @@ -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);
};
Expand Down
2 changes: 0 additions & 2 deletions packages/subapp-server/lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
2 changes: 2 additions & 0 deletions packages/subapp-web/lib/init.js
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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);
Expand Down
1 change: 1 addition & 0 deletions packages/xarc-tag-renderer/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export { TagRenderer } from "./tag-renderer";
export {
TagTemplate,
createTemplateTags,
createTemplateTagsFromArray,
RegisterTokenIds,
Token,
TokenInvoke
Expand Down
18 changes: 15 additions & 3 deletions packages/xarc-tag-renderer/src/render-processor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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 */
Expand All @@ -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 });
}
}
}

/**
Expand All @@ -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) {
Expand Down Expand Up @@ -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")) {
Expand Down
17 changes: 8 additions & 9 deletions packages/xarc-tag-renderer/src/tag-renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
};
}

/**
Expand All @@ -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
Expand All @@ -73,6 +71,7 @@ export class TagRenderer {
processor: this._processor
});

this._processor.applyTokenModuleLoad(this._options, this._template);
this._template.initTagOpCode();
}
}
Expand Down
12 changes: 12 additions & 0 deletions packages/xarc-tag-renderer/src/tag-template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
*
Expand Down
2 changes: 1 addition & 1 deletion packages/xarc-tag-renderer/test/data/template3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const subTags2 = createTemplateTags`${RegisterTokenIds(tokenHandler)}
}
};
})}
<div>sub template tags 2${Token("X2")}</div>`;
<div>sub template tags 2${TokenInvoke(custom1)}${Token("X2")}</div>`;

const subTags = createTemplateTags`${RegisterTokenIds(tokenHandler)}
${RegisterTokenIds(() => {
Expand Down
4 changes: 2 additions & 2 deletions packages/xarc-tag-renderer/test/spec/tag-renderer.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,10 @@ describe("tag template", function () {

expect(context.result).contains("<div>from custom-1</div>");
expect(context.result).contains(
"<div>subTags</div><div>sub template tagsx1<div>sub template tags 2x2</div></div><div>subTags</div>"
"<div>subTags</div><div>sub template tagsx1<div>sub template tags 2<div>from custom-1</div>x2</div></div><div>subTags</div>"
);
expect(context.result).contains(
"<div>subTagsPromise</div><div>sub template tagsx1<div>sub template tags 2x2</div></div><div>subTagsPromise</div>"
"<div>subTagsPromise</div><div>sub template tagsx1<div>sub template tags 2<div>from custom-1</div>x2</div></div><div>subTagsPromise</div>"
);
});

Expand Down

0 comments on commit 8e7547d

Please sign in to comment.