From b18b6eeac79016bad890fb50c7fbc208a130cf81 Mon Sep 17 00:00:00 2001 From: Joel Chen Date: Sat, 18 Jul 2020 20:31:13 -0700 Subject: [PATCH] xarc/tag-renderer support rendering sub templates --- .../xarc-tag-renderer/src/render-execute.ts | 91 ++++++++++--- .../xarc-tag-renderer/src/render-processor.ts | 121 ++++++++++-------- .../xarc-tag-renderer/src/tag-renderer.ts | 90 +++++++++---- .../xarc-tag-renderer/src/tag-template.ts | 90 ++++++++++++- .../xarc-tag-renderer/test/data/template1.ts | 10 +- .../xarc-tag-renderer/test/data/template2.ts | 6 +- .../test/data/template3.html | 51 -------- .../xarc-tag-renderer/test/data/template3.ts | 58 +++++++++ .../test/data/template4.html | 17 --- .../test/data/template5.html | 13 -- .../test/data/template6.html | 13 -- .../test/data/template7.html | 8 -- .../test/data/template8.html | 8 -- .../test/spec/tag-renderer.spec.ts | 90 +++++++++---- .../test/spec/tag-template.spec.ts | 12 +- 15 files changed, 428 insertions(+), 250 deletions(-) delete mode 100644 packages/xarc-tag-renderer/test/data/template3.html create mode 100644 packages/xarc-tag-renderer/test/data/template3.ts delete mode 100644 packages/xarc-tag-renderer/test/data/template4.html delete mode 100644 packages/xarc-tag-renderer/test/data/template5.html delete mode 100644 packages/xarc-tag-renderer/test/data/template6.html delete mode 100644 packages/xarc-tag-renderer/test/data/template7.html delete mode 100644 packages/xarc-tag-renderer/test/data/template8.html diff --git a/packages/xarc-tag-renderer/src/render-execute.ts b/packages/xarc-tag-renderer/src/render-execute.ts index 3102cf297..a234e588d 100644 --- a/packages/xarc-tag-renderer/src/render-execute.ts +++ b/packages/xarc-tag-renderer/src/render-execute.ts @@ -1,13 +1,17 @@ -/* eslint-disable complexity, max-statements */ +/* eslint-disable complexity, max-statements, max-params */ +/* eslint-disable @typescript-eslint/no-use-before-define */ import { TOKEN_HANDLER } from "@xarc/render-context"; +import { TagTemplate } from "./tag-template"; +import { TAG_TYPE } from "./symbols"; export const executeSteps = { STEP_HANDLER: 0, STEP_STR_TOKEN: 1, STEP_NO_HANDLER: 2, STEP_LITERAL_HANDLER: 3, - STEP_FUNC_HANDLER: 4 + STEP_FUNC_HANDLER: 4, + STEP_SUB_TEMPLATE: 5 }; const { @@ -15,11 +19,39 @@ const { STEP_STR_TOKEN, STEP_NO_HANDLER, STEP_LITERAL_HANDLER, - STEP_FUNC_HANDLER + STEP_FUNC_HANDLER, + STEP_SUB_TEMPLATE } = executeSteps; -export function renderNext(err: Error, xt) { - const { renderSteps, context } = xt; +function handleSubTemplate(tkId: string, step, result: any, xt: any, cb: Function) { + if (!result) { + return cb(); + } + + const handle = res => { + if (res[TAG_TYPE] && res[TAG_TYPE] === "template") { + const step2 = xt.template.handleSubTemplate(step, res); + return executeTagTemplate(step2.template, step2.tk, xt.context, true).then(cb, cb); + } else { + return xt.context.handleTokenResult(tkId, res, cb); + } + }; + + if (result.then) { + return result.then(handle, cb); + } else { + return handle(result); + } +} +/** + * Execute the next step for the token tags + * @param err - error from previous step + * @param xt - execution context + * + * @returns any - non-significant + */ +export function renderNext(err: Error, xt: any) { + const { template, tagTokens, context } = xt; if (err) { context.handleError(err); } @@ -32,30 +64,46 @@ export function renderNext(err: Error, xt) { context.output.add(`\n`); }; - if (context.isFullStop || context.isVoidStop || xt.stepIndex >= renderSteps.length) { - const r = context.output.close(); - xt.resolve(r); + if (context.isFullStop || context.isVoidStop || xt.stepIndex >= tagTokens.length) { + if (!xt.subTemplate) { + xt.resolve(context.output.close()); + } else { + xt.resolve(); + } return null; } else { - // TODO: support soft stop - const step = renderSteps[xt.stepIndex++]; - const tk = step.tk; + const tagIndex = xt.stepIndex++; + const tk = tagTokens[tagIndex]; + const step = template.getTagOpCode(tagIndex); + + if (!step) { + return renderNext(null, xt); + } + + // const tk = step.tk; const withId = step.insertTokenId; switch (step.code) { - case STEP_FUNC_HANDLER: - return context.handleTokenResult("", tk.func(context), e => { - return renderNext(e, xt); - }); - case STEP_HANDLER: + case STEP_SUB_TEMPLATE: + return executeTagTemplate(step.template, step.tk, context, true).then( + () => renderNext(null, xt), + (err2: Error) => renderNext(err2, xt) + ); + case STEP_FUNC_HANDLER: { + const result = tk(context); + return handleSubTemplate("", step, result, xt, (e: Error) => renderNext(e, xt)); + } + case STEP_HANDLER: { if (withId) { insertTokenId(tk); } - return context.handleTokenResult(tk.id, tk[TOKEN_HANDLER](context, tk), e => { + const result = tk[TOKEN_HANDLER](context, tk); + return handleSubTemplate(tk.id, step, result, xt, (e: Error) => { if (withId) { insertTokenIdEnd(tk); } return renderNext(e, xt); }); + } case STEP_STR_TOKEN: context.output.add(tk.str); break; @@ -76,9 +124,14 @@ export function renderNext(err: Error, xt) { } } -export function executeRenderSteps(renderSteps, context) { +export function executeTagTemplate( + template: TagTemplate, + tagTokens: any[], + context, + subTemplate = false +) { return new Promise(resolve => { - const xt = { stepIndex: 0, renderSteps, context, resolve }; + const xt = { stepIndex: 0, template, tagTokens, context, resolve, subTemplate }; return renderNext(null, xt); }); } diff --git a/packages/xarc-tag-renderer/src/render-processor.ts b/packages/xarc-tag-renderer/src/render-processor.ts index b1135b687..9e3a0fa02 100644 --- a/packages/xarc-tag-renderer/src/render-processor.ts +++ b/packages/xarc-tag-renderer/src/render-processor.ts @@ -1,29 +1,40 @@ /* eslint-disable max-statements */ -import { executeRenderSteps, executeSteps } from "./render-execute"; +import { executeTagTemplate, executeSteps } from "./render-execute"; import { TAG_TYPE } from "./symbols"; -import { TokenModule } from "@xarc/render-context"; +import { TagTemplate } from "./tag-template"; +import { RenderContext } from "@xarc/render-context"; const { STEP_HANDLER, STEP_STR_TOKEN, STEP_NO_HANDLER, STEP_LITERAL_HANDLER, - STEP_FUNC_HANDLER + STEP_FUNC_HANDLER, + STEP_SUB_TEMPLATE } = executeSteps; export class RenderProcessor { - renderSteps: any; _options: any; _insertTokenIds: boolean; - constructor(options) { + constructor(options: { + /** Add debugging comment to rendered output with token IDs */ + insertTokenIds?: boolean; + /** The renderer instance */ + asyncTemplate?: any; + }) { this._options = options; this._insertTokenIds = Boolean(options.insertTokenIds); - this.renderSteps = this.makeSteps(options.htmlTokens); } - makeNullRemovedStep(tk, cause) { + /** + * Generate an exec step for a tag that has a null handler + * + * @param tk - tag + * @param cause - reason a null handler is needed + */ + makeNullRemovedStep(tk: any, cause: string) { return { tk, insertTokenId: false, @@ -32,7 +43,12 @@ export class RenderProcessor { }; } - makeHandlerStep(tk) { + /** + * Make a execution step for a token with a handler + * + * @param tk + */ + makeHandlerStep(tk: any) { const options = this._options; const insertTokenIds = this._insertTokenIds; @@ -48,8 +64,7 @@ export class RenderProcessor { } const msg = `@xarc/tag-renderer: no handler found for token id ${tk.id}`; - console.error(msg); // eslint-disable-line - return { tk, code: STEP_NO_HANDLER }; + return { tk, msg, code: STEP_NO_HANDLER }; } if (typeof tkFunc !== "function") { @@ -67,61 +82,63 @@ export class RenderProcessor { return { tk, code: STEP_HANDLER, insertTokenId: insertTokenIds && !tk.props._noInsertId }; } + /** + * Make a execution step for a token tag + * @param tk - token tag + * @returns execution step + */ makeStep(tk: any) { + let opCode; + const options = this._options; const insertTokenIds = this._insertTokenIds; + if (tk[TAG_TYPE] === "function") { - return { + opCode = { tk, code: STEP_FUNC_HANDLER }; - } - - if (tk[TAG_TYPE] === "register-token-ids") { + } else if (tk[TAG_TYPE] === "register-token-ids") { tk({ asyncTemplate: options.asyncTemplate }); - return null; - } - - if (tk[TAG_TYPE] === "template") { - return this.makeSteps(tk); - } - - // token is a literal string, just add it to output - if (tk.hasOwnProperty("str")) { - return { tk, code: STEP_STR_TOKEN }; - } - - // token is not pointing to a module, so lookup from token handlers - if (!tk.isModule) { - return this.makeHandlerStep(tk); - } - - if (tk.custom === null) { + opCode = null; + } else if (tk[TAG_TYPE] === "template") { + opCode = { + tk, + template: new TagTemplate({ templateTags: tk, processor: this }), + code: STEP_SUB_TEMPLATE + }; + } else if (tk.hasOwnProperty("str")) { + // token is a literal string, just add it to output + opCode = { tk, code: STEP_STR_TOKEN }; + } else if (!tk.isModule) { + // token is not pointing to a module, so lookup from token handlers + opCode = this.makeHandlerStep(tk); + } else if (tk.custom === null) { if (insertTokenIds) { - return this.makeNullRemovedStep(tk, "process return null"); - } - return null; - } - return { - tk, - code: STEP_HANDLER, - insertTokenId: options.insertTokenIds && !tk.props._noInsertId - }; - } - - makeSteps(tokens) { - let steps = []; - for (const htk of tokens) { - const step = this.makeStep(htk); - if (step) { - steps = steps.concat(step); + opCode = this.makeNullRemovedStep(tk, "process return null"); + } else { + opCode = null; } + } else { + opCode = { + tk, + code: STEP_HANDLER, + insertTokenId: options.insertTokenIds && !tk.props._noInsertId + }; } - return steps; + return opCode; } - render(context) { - return executeRenderSteps(this.renderSteps, context); + /** + * Run rendering for a template + * @param template - the template + * @param context - RenderContext + * @param tagTokens - template tag tokens + * + * @returns Promise that resolves after rendering completed + */ + render(template: TagTemplate, context: RenderContext, tagTokens: any[]) { + return executeTagTemplate(template, tagTokens, context); } } diff --git a/packages/xarc-tag-renderer/src/tag-renderer.ts b/packages/xarc-tag-renderer/src/tag-renderer.ts index 9b057beaa..091a5fdcf 100644 --- a/packages/xarc-tag-renderer/src/tag-renderer.ts +++ b/packages/xarc-tag-renderer/src/tag-renderer.ts @@ -1,6 +1,6 @@ /* eslint-disable max-params, max-statements, no-constant-condition, no-magic-numbers */ -import { TEMPLATE_DIR, RenderContext } from "@xarc/render-context"; +import { RenderContext } from "@xarc/render-context"; import * as _ from "lodash"; import { RenderProcessor } from "./render-processor"; @@ -21,26 +21,25 @@ export class TagRenderer { _handlerContext: any; _handlersMap: {}; _tokenIdLookupMap: {}; - _renderer: any; + _processor: RenderProcessor; _tokens: any[]; - _templateDir: string; _template: TagTemplate; constructor(options: { - template: TagTemplate; + templateTags: any[]; tokenHandlers?: Function | Function[]; routeOptions?: any; insertTokenIds?: boolean; }) { this._options = options; - this._template = options.template; - this[TEMPLATE_DIR] = this._template._templateDir; - this._tokens = this._template._templateTags; + this._tokens = options.templateTags; - this._tokenHandlers = [] - .concat(this._options.tokenHandlers, this._template._tokenHandlers) + this._tokenHandlers = []; + [] + .concat(this._options.tokenHandlers) .filter(x => x) - .map(handler => ({ handler })); + .forEach(handler => this.addTokenIds("", handler)); + this._handlersMap = {}; this._tokenIdLookupMap = {}; @@ -56,28 +55,47 @@ export class TagRenderer { ); } - initializeRenderer(reset = !this._renderer) { + /** + * Initialize to get ready to do rendering + * @param reset - if true, will run even if already initialized + */ + initializeRenderer(reset = !this._processor) { if (reset) { this._initializeTokenHandlers(this._tokenHandlers); this._applyTokenLoad(this._options); - this._renderer = new RenderProcessor({ + this._processor = new RenderProcessor({ asyncTemplate: this, - insertTokenIds: this._options.insertTokenIds, - htmlTokens: this._tokens, - tokenHandlers: this._tokenHandlers + insertTokenIds: this._options.insertTokenIds + }); + + this._template = new TagTemplate({ + templateTags: this._tokens, + processor: this._processor }); + + this._template.initTagOpCode(); } } + /** + * Lookup the handler of a tag + * @param tk - tag + */ lookupTokenHandler(tk) { return this._tokenIdLookupMap[tk.id]; } + /** + * Render the template + * @param options - render context options + * + * @returns render context + */ async render(options) { let context; try { context = new RenderContext(options, this); - const result = await this._renderer.render(context); + const result = await this._processor.render(this._template, context, this._tokens); context.result = context.isVoidStop ? context.voidResult : result; return context; } catch (err) { @@ -87,14 +105,33 @@ export class TagRenderer { } } - registerTokenIds(name: string, uniqSym: symbol, handler: Function) { + /** + * Add a handler to provide token IDs for the template but will not invoke it yet + * + * @param name - name of token ids + * @param handler - handler function + * @param priority - higher value === higher priority + */ + addTokenIds(name: string, handler: Function, priority = 0) { + // remove same handler that's been registered so it goes to the end of the array + this._tokenHandlers = this._tokenHandlers.filter(h => h.handler !== handler); + this._tokenHandlers.push({ name, handler, priority }); + } + + /** + * Register a handler to provide token IDs for the template + * + * @param name - name of token ids + * @param uniqSym - unique symbol identifier + * @param handler - handler function + * @param priority - higher value === higher priority + */ + registerTokenIds(name: string, uniqSym: symbol, handler: Function, priority = 0) { if (this._handlersMap.hasOwnProperty(uniqSym)) { return; } this._handlersMap[uniqSym] = handler; - // remove same handler that's been registered so it goes to the end of the array - this._tokenHandlers = this._tokenHandlers.filter(h => h.handler !== handler); - this._tokenHandlers.push({ name, handler }); + this.addTokenIds(name, handler, priority); this._initializeTokenHandlers(this._tokenHandlers); } @@ -107,22 +144,27 @@ export class TagRenderer { } _initializeTokenHandlers(handlers) { - const tokenIds = handlers.map((h, ix) => { + const loaded = handlers.map((h, ix) => { if (h.loaded) { - return h.loaded.tokens; + return h.loaded; } const tokens = h.handler(this._handlerContext, this); - h.loaded = tokens.tokens && typeof tokens.tokens === "object" ? { ...tokens } : { tokens }; + h.loaded = + tokens.tokens && typeof tokens.tokens === "object" + ? { ...tokens, priority: h.priority } + : { tokens, priority: h.priority }; if (!h.loaded.name) { h.loaded.name = h.name || `unnamed-token-id-handler-${ix}`; } - return h.loaded.tokens; + return h.loaded; }); + const tokenIds = loaded.sort((a, b) => a.priority - b.priority).map(l => l.tokens); + // combine all token IDs into a single object for lookup. // the last registered handler wins this._tokenIdLookupMap = Object.assign({}, ...tokenIds); diff --git a/packages/xarc-tag-renderer/src/tag-template.ts b/packages/xarc-tag-renderer/src/tag-template.ts index 842c0cc37..7a4795cd2 100644 --- a/packages/xarc-tag-renderer/src/tag-template.ts +++ b/packages/xarc-tag-renderer/src/tag-template.ts @@ -2,6 +2,7 @@ import { TAG_TYPE } from "./symbols"; import { TokenModule } from "@xarc/render-context"; +import { RenderProcessor } from "./render-processor"; /** * Create a simple tag template from ES6 template literal strings @@ -9,7 +10,7 @@ import { TokenModule } from "@xarc/render-context"; * Usage: * * ```js - * import { TagTemplate, RegisterTokenIds, createTemplateTags, Token, TokenInvoke } from "@xarc/tag-renderer"; + * import { RegisterTokenIds, createTemplateTags, Token, TokenInvoke } from "@xarc/tag-renderer"; * import { myTokenHandler } from "./my-token-handler"; * import { myHtmlRenderHandler } from "./my-html-render-handler"; * import { myTokenIdRegister } from "./my-token-id-handler" @@ -21,21 +22,18 @@ import { TokenModule } from "@xarc/render-context"; * } * }}`; * - * const templateTags = createTemplateTags` + * export const templateTags = createTemplateTags` * ${RegisterTokenIds(myTokenIdRegister)} * ${Token("INITIALIZE", {})} * * ${TokenInvoke(myTokenHandler, {})} * ${subTemplate} - * + * ${() => subTemplate} + * ${() => Promise.resolve(subTemplate)} * * ${TokenInvoke(myHtmlRenderHandler, {})} * * ` - * - * export const template = TagTemplate({ - * templateTags, templateDir: __dirname - * }) * ``` * * @param literals - string array @@ -91,6 +89,26 @@ export const TokenInvoke = (handler: Function, props = {}) => { return tm; }; +/** + * Register a handler to provide token IDs that can be used in the template + * + * Example: + * + * ```js + * import { RegisterTokenIds, createTemplateTags } from "@xarc/tag-renderer"; + * + * const templateTags = createTemplateTags` + * ${RegisterTokenIds(() => { + * return { + * ID1: context => { } + * } + * })} + * ``` + * + * @param handler - handler function + * @param name - name to identify this handler + * @returns a register function + */ export const RegisterTokenIds = (handler: Function, name?: string) => { const uniqSym = Symbol("register-token-${name}"); const register = context => { @@ -100,15 +118,25 @@ export const RegisterTokenIds = (handler: Function, name?: string) => { return register; }; +/** + * Holds supporting and execution information and data for a template of tags + */ export class TagTemplate { _templateTags: any[]; + _tagOpCodes: any[]; _templateDir: string; _tokenHandlers: Function[]; + _processor: RenderProcessor; constructor(options: { + /** template tags */ templateTags: any[]; + /** directory where the template file resides */ templateDir?: string; + /** initial token handlers */ tokenHandlers?: Function | Function[]; + /** template tag processor for generating rendering steps */ + processor: RenderProcessor; }) { this._templateTags = options.templateTags.map((tag, ix) => { if (tag.hasOwnProperty(TAG_TYPE)) { @@ -118,8 +146,56 @@ export class TagTemplate { } return tag; }); + + this._tagOpCodes = new Array(this._templateTags.length); this._templateDir = options.templateDir; this._tokenHandlers = [].concat(options.tokenHandlers); + this._processor = options.processor; + } + + /** + * Returns a template tag + * @param index - index of the tag in the template tags array + * @returns template tag + */ + getTag(index: number) { + return this._templateTags[index]; + } + + /** + * Get a execution step opcode for the template tag at index + * @param index - index of the tag in the template tags array + * @returns template tag exec step opcode + */ + getTagOpCode(index: number) { + if (this._tagOpCodes[index] === undefined) { + this._tagOpCodes[index] = this._processor.makeStep(this.getTag(index)); + } + + return this._tagOpCodes[index]; + } + + /** + * Pre-initialize execution step opcode for the template tags + */ + initTagOpCode() { + for (let ix = 0; ix < this._templateTags.length; ix++) { + this.getTagOpCode(ix); + } + } + + /** + * Handle a sub template returned by executing a step + * @param step - the step that was executed + * @param templateTags - the template tags returned + * + * @returns new step for executing the sub template tags + */ + + handleSubTemplate(step: any, templateTags: any[]) { + return ( + step._subTemplateStep || (step._subTemplateStep = this._processor.makeStep(templateTags)) + ); } _findTokenIndex( diff --git a/packages/xarc-tag-renderer/test/data/template1.ts b/packages/xarc-tag-renderer/test/data/template1.ts index ad822450d..5f5c3637b 100644 --- a/packages/xarc-tag-renderer/test/data/template1.ts +++ b/packages/xarc-tag-renderer/test/data/template1.ts @@ -1,4 +1,4 @@ -import { TagTemplate, createTemplateTags, Token, TokenInvoke, RegisterTokenIds } from "../../src"; +import { createTemplateTags, Token, TokenInvoke, RegisterTokenIds } from "../../src"; import * as custom1 from "../fixtures/custom-1"; import * as tokenHandler from "../fixtures/token-handler"; @@ -25,7 +25,7 @@ const subTags = createTemplateTags`${RegisterTokenIds(tokenHandler)} })}
sub template tags${Token("X")}${subTags2}
`; -const templateTags = createTemplateTags` +export const templateTags = createTemplateTags` ${RegisterTokenIds(tokenHandler)} ${RegisterTokenIds(() => { @@ -56,9 +56,3 @@ const templateTags = createTemplateTags` ${Token("ABC")} `; - -export const template = new TagTemplate({ - templateTags, - templateDir: __dirname, - tokenHandlers: [tokenHandler] -}); diff --git a/packages/xarc-tag-renderer/test/data/template2.ts b/packages/xarc-tag-renderer/test/data/template2.ts index c6a8df970..6d1d63ea9 100644 --- a/packages/xarc-tag-renderer/test/data/template2.ts +++ b/packages/xarc-tag-renderer/test/data/template2.ts @@ -1,10 +1,10 @@ -import { TagTemplate, createTemplateTags, Token, TokenInvoke, RegisterTokenIds } from "../../src"; +import { createTemplateTags, Token, TokenInvoke, RegisterTokenIds } from "../../src"; const nullTokenProcess = () => { return null; }; -const templateTags = createTemplateTags` +export const templateTags = createTemplateTags` ${Token("ssr-content")} ${Token("webapp-header-bundles")} @@ -28,5 +28,3 @@ const templateTags = createTemplateTags` ${Token("page-title")} `; - -export const template = new TagTemplate({ templateTags, templateDir: __dirname }); diff --git a/packages/xarc-tag-renderer/test/data/template3.html b/packages/xarc-tag-renderer/test/data/template3.html deleted file mode 100644 index 33c0be87c..000000000 --- a/packages/xarc-tag-renderer/test/data/template3.html +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/packages/xarc-tag-renderer/test/data/template3.ts b/packages/xarc-tag-renderer/test/data/template3.ts new file mode 100644 index 000000000..45c9c03b3 --- /dev/null +++ b/packages/xarc-tag-renderer/test/data/template3.ts @@ -0,0 +1,58 @@ +import { createTemplateTags, Token, TokenInvoke, RegisterTokenIds } from "../../src"; +import * as tokenHandler from "../fixtures/token-handler"; + +import * as custom1 from "../fixtures/custom-1"; + +const nullTokenProcess = () => { + return null; +}; + +const subTags2 = createTemplateTags`${RegisterTokenIds(tokenHandler)} + ${RegisterTokenIds(() => { + return { + name: "sub-blah-blah-2", + tokens: { + X2: "x2" + } + }; + })} +
sub template tags 2${Token("X2")}
`; + +const subTags = createTemplateTags`${RegisterTokenIds(tokenHandler)} + ${RegisterTokenIds(() => { + return { + name: "sub-blah-blah", + tokens: { + X: "x1" + } + }; + })} +
sub template tags${Token("X")}${subTags2}
`; + +export const templateTags = createTemplateTags` + + ${Token("ssr-content")} + ${Token("webapp-header-bundles")} + ${Token("webapp-body-bundles")} + ${Token("prefetch-bundles")} + ${RegisterTokenIds(() => { + return { + NULL_ID: null + }; + })} + ${Token("NULL_ID")} +
test null id
+ + ${TokenInvoke(custom1)} + ${Token("webapp-body-bundles")} + ${Token("meta-tags")} + ${TokenInvoke(nullTokenProcess)} +
subTags
${() => subTags}
subTags
+
subTagsPromise
+ ${() => Promise.resolve(subTags)}
subTagsPromise
+ + +${Token("page-title")} +`; diff --git a/packages/xarc-tag-renderer/test/data/template4.html b/packages/xarc-tag-renderer/test/data/template4.html deleted file mode 100644 index adfea1d08..000000000 --- a/packages/xarc-tag-renderer/test/data/template4.html +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - diff --git a/packages/xarc-tag-renderer/test/data/template5.html b/packages/xarc-tag-renderer/test/data/template5.html deleted file mode 100644 index 7856e3bdc..000000000 --- a/packages/xarc-tag-renderer/test/data/template5.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - diff --git a/packages/xarc-tag-renderer/test/data/template6.html b/packages/xarc-tag-renderer/test/data/template6.html deleted file mode 100644 index 72f9c17e9..000000000 --- a/packages/xarc-tag-renderer/test/data/template6.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - diff --git a/packages/xarc-tag-renderer/test/data/template7.html b/packages/xarc-tag-renderer/test/data/template7.html deleted file mode 100644 index f08f0f36e..000000000 --- a/packages/xarc-tag-renderer/test/data/template7.html +++ /dev/null @@ -1,8 +0,0 @@ - - - - - diff --git a/packages/xarc-tag-renderer/test/data/template8.html b/packages/xarc-tag-renderer/test/data/template8.html deleted file mode 100644 index 60e9a22eb..000000000 --- a/packages/xarc-tag-renderer/test/data/template8.html +++ /dev/null @@ -1,8 +0,0 @@ - - - - - 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 f425e342e..9f311494f 100644 --- a/packages/xarc-tag-renderer/test/spec/tag-renderer.spec.ts +++ b/packages/xarc-tag-renderer/test/spec/tag-renderer.spec.ts @@ -1,15 +1,15 @@ import { expect } from "chai"; -import { TagRenderer } from "../../src"; -import { template as template1 } from "../data/template1"; -import { template as template2 } from "../data/template2"; +import { TagRenderer, createTemplateTags } from "../../src"; +import { templateTags as templateTags1 } from "../data/template1"; +import { templateTags as templateTags2 } from "../data/template2"; +import { templateTags as templateTags3 } from "../data/template3"; import { describe, it } from "mocha"; -import { TagTemplate, createTemplateTags } from "../../src"; describe("tag template", function () { it("should render a TagTemplate", async () => { const renderer = new TagRenderer({ - template: template1 + templateTags: templateTags1 }); renderer.initializeRenderer(); @@ -24,9 +24,27 @@ describe("tag template", function () { expect(context.result).contains("custom-1hello world from function: user,options"); }); + it("should render a TagTemplate with handler that returns a sub template", async () => { + const renderer = new TagRenderer({ + templateTags: templateTags3 + }); + + renderer.initializeRenderer(); + + const context = await renderer.render({}); + + expect(context.result).contains("
from custom-1
"); + expect(context.result).contains( + "
subTags
sub template tagsx1
sub template tags 2x2
subTags
" + ); + expect(context.result).contains( + "
subTagsPromise
sub template tagsx1
sub template tags 2x2
subTagsPromise
" + ); + }); + it("should render a TagTemplate with token IDs as comments", async () => { const renderer = new TagRenderer({ - template: template1, + templateTags: templateTags1, insertTokenIds: true }); @@ -54,7 +72,7 @@ ABC` it("should catch and return error as result", async () => { const renderer = new TagRenderer({ - template: template1 + templateTags: templateTags1 }); renderer.registerTokenIds("test", Symbol("test"), () => { @@ -73,11 +91,9 @@ ABC` it("should invoke context handleError if a token returns error", async () => { const renderer = new TagRenderer({ - template: new TagTemplate({ - templateTags: createTemplateTags`${() => { - return Promise.reject(new Error("test blah")); - }}` - }) + templateTags: createTemplateTags`${() => { + return Promise.reject(new Error("test blah")); + }}` }); renderer.initializeRenderer(); @@ -87,9 +103,40 @@ ABC` expect(context.result.message).equal("test blah"); }); + it("should catch and handle error from executing a sub template", async () => { + const renderer = new TagRenderer({ + templateTags: templateTags1 + }); + + renderer.initializeRenderer(); + renderer._template.initTagOpCode(); + renderer.registerTokenIds( + "test", + Symbol("test"), + () => { + return { + X: () => { + throw new Error("test"); + } + }; + }, + 1 + ); + + const context = await renderer.render({}); + + expect(context.result).to.be.an("Error"); + expect(context.result.message).equal("test"); + }); + it("should invoke RegisterTokenIds tag once only", () => { const renderer = new TagRenderer({ - template: template1 + templateTags: templateTags1, + tokenHandlers: [ + () => { + return {}; + } + ] }); const handler = () => ({}); @@ -106,7 +153,7 @@ ABC` it("should allow context to void progress and return a replacement result", async () => { const renderer = new TagRenderer({ - template: template1 + templateTags: templateTags1 }); renderer.registerTokenIds("test", Symbol("test"), () => { @@ -124,18 +171,17 @@ ABC` it("should handle token invoke handler return null", async () => { const renderer = new TagRenderer({ - template: template2 + templateTags: templateTags2 }); renderer.initializeRenderer(); const context = await renderer.render({}); - console.log(context.result); expect(context.result).contains(`
test
`); }); it("should handle token invoke handler return null with comments", async () => { const renderer = new TagRenderer({ - template: template2, + templateTags: templateTags2, insertTokenIds: true }); renderer.initializeRenderer(); @@ -145,7 +191,7 @@ ABC` it("should handle token ID being null", async () => { const renderer = new TagRenderer({ - template: template2 + templateTags: templateTags2 }); renderer.initializeRenderer(); const context = await renderer.render({}); @@ -155,7 +201,7 @@ ABC` it("should handle token ID being null with comments", async () => { const renderer = new TagRenderer({ - template: template2, + templateTags: templateTags2, insertTokenIds: true }); renderer.initializeRenderer(); @@ -166,13 +212,13 @@ ABC` describe("initializeRenderer", function () { it("should not reset already initialized renderer", () => { const renderer = new TagRenderer({ - template: template1 + templateTags: templateTags1 }); renderer.initializeRenderer(); - const save = renderer._renderer; + const save = renderer._processor; renderer.initializeRenderer(); - expect(save).equal(renderer._renderer); + expect(save).equal(renderer._processor); }); }); }); diff --git a/packages/xarc-tag-renderer/test/spec/tag-template.spec.ts b/packages/xarc-tag-renderer/test/spec/tag-template.spec.ts index ab1f5e61b..99fd7b3b6 100644 --- a/packages/xarc-tag-renderer/test/spec/tag-template.spec.ts +++ b/packages/xarc-tag-renderer/test/spec/tag-template.spec.ts @@ -1,12 +1,16 @@ import { expect } from "chai"; -import { template as template1 } from "../data/template1"; -import { template as template2 } from "../data/template2"; +import { templateTags as templateTags1 } from "../data/template1"; +import { templateTags as templateTags2 } from "../data/template2"; import { describe, it } from "mocha"; +import { TagTemplate } from "../../src"; describe("tag template", function () { + const template2 = new TagTemplate({ templateTags: templateTags2, processor: null }); + it("should create a TagTemplate from ES6 template literal strings", () => { - expect(template1._templateTags[0].str).to.equal("\n"); - const ssrToken = template1.findTokensById(`ssr-content`); + expect(templateTags1[0].str).to.equal("\n"); + const template = new TagTemplate({ templateTags: templateTags1, processor: null }); + const ssrToken = template.findTokensById(`ssr-content`); expect(ssrToken).to.exist; });