UNPKG

@paroicms/server

Version:
172 lines 7.2 kB
import { Hash, Tag, TypeGuards, assert, evalQuotedToken, evalToken } from 'liquidjs'; import { toEnumerable } from './collection.js'; import { ForloopDrop } from './forloop-drop.js'; export default class RenderTag extends Tag { static passThroughSymbol = Symbol("passThrough"); static renderScopeSymbol = Symbol("renderScope"); file; currentFile; hash; constructor(token, remainTokens, liquid, parser) { super(token, remainTokens, liquid); const tokenizer = this.tokenizer; this.file = parseFilePath(tokenizer, this.liquid, parser); this.currentFile = token.file; while (!tokenizer.end()) { tokenizer.skipBlank(); const begin = tokenizer.p; const keyword = tokenizer.readIdentifier(); if (keyword.content === 'with' || keyword.content === 'for') { tokenizer.skipBlank(); if (tokenizer.peek() !== ':') { const value = tokenizer.readValue(); if (value) { const beforeAs = tokenizer.p; const asStr = tokenizer.readIdentifier(); let alias; if (asStr.content === 'as') alias = tokenizer.readIdentifier(); else tokenizer.p = beforeAs; this[keyword.content] = { value, alias: alias && alias.content }; tokenizer.skipBlank(); if (tokenizer.peek() === ',') tokenizer.advance(); continue; } } } tokenizer.p = begin; break; } this.hash = new Hash(tokenizer, liquid.options.keyValueSeparator); } *render(ctx, emitter) { const { liquid, hash } = this; let filepath = (yield renderFilePath(this['file'], ctx, liquid)); assert(filepath, () => `illegal file path "${filepath}"`); if (!filepath.endsWith('.liquid')) filepath += '.liquid'; const parentEnvs = ctx.environments || {}; const passThrough = parentEnvs[RenderTag.passThroughSymbol]; const renderScope = parentEnvs[RenderTag.renderScopeSymbol]; const childCtx = ctx.spawn(); if (passThrough !== undefined) childCtx.environments[RenderTag.passThroughSymbol] = passThrough; if (renderScope !== undefined) { const ok = renderScope && typeof renderScope === 'object' && !Array.isArray(renderScope); if (!ok) throw new Error('RenderTag: renderScopeSymbol payload must be a plain object'); const env = childCtx.bottom(); for (const k of Object.keys(renderScope)) { const inParent = Object.prototype.hasOwnProperty.call(parentEnvs, k); const existing = childCtx.getSync([k]); if (existing !== undefined) continue; if (inParent) { env[k] = parentEnvs[k]; continue; } env[k] = renderScope[k]; } ; childCtx.environments[RenderTag.renderScopeSymbol] = renderScope; } const scope = childCtx.bottom(); Object.assign(scope, yield hash.render(ctx)); if (this['with']) { const { value, alias } = this['with']; scope[alias || filepath] = yield evalToken(value, ctx); } if (this['for']) { const { value, alias } = this['for']; const collection = toEnumerable(yield evalToken(value, ctx)); scope['forloop'] = new ForloopDrop(collection.length, value.getText(), alias); for (const item of collection) { scope[alias] = item; const templates = (yield liquid._parsePartialFile(filepath, childCtx.sync, this['currentFile'])); yield liquid.renderer.renderTemplates(templates, childCtx, emitter); scope['forloop'].next(); } } else { const templates = (yield liquid._parsePartialFile(filepath, childCtx.sync, this['currentFile'])); yield liquid.renderer.renderTemplates(templates, childCtx, emitter); } } *children(partials, sync) { if (partials && typeof this['file'] === 'string') { return (yield this.liquid._parsePartialFile(this['file'], sync, this['currentFile'])); } return []; } partialScope() { if (typeof this['file'] === 'string') { const names = Object.keys(this.hash.hash); if (this['with']) { const { value, alias } = this['with']; if (typeof alias === 'string') { names.push([alias, value]); } else if (typeof this.file === 'string') { names.push([this.file, value]); } } if (this['for']) { const { value, alias } = this['for']; if (typeof alias === 'string') { names.push([alias, value]); } else if (typeof this.file === 'string') { names.push([this.file, value]); } } return { name: this['file'], isolated: true, scope: names }; } } *arguments() { for (const v of Object.values(this.hash.hash)) { if (TypeGuards.isValueToken ? TypeGuards.isValueToken(v) : true) yield v; } if (this['with']) { const { value } = this['with']; if (TypeGuards.isValueToken ? TypeGuards.isValueToken(value) : true) yield value; } if (this['for']) { const { value } = this['for']; if (TypeGuards.isValueToken ? TypeGuards.isValueToken(value) : true) yield value; } } } export function parseFilePath(tokenizer, liquid, parser) { if (liquid.options.dynamicPartials) { const file = tokenizer.readValue(); tokenizer.assert(file, 'illegal file path'); if (file.getText() === 'none') return; if (TypeGuards.isQuotedToken(file)) { const templates = parser.parse(evalQuotedToken(file)); return optimize(templates); } return file; } const tokens = [...tokenizer.readFileNameTemplate(liquid.options)]; const templates = optimize(parser.parseTokens(tokens)); return templates === 'none' ? undefined : templates; } function optimize(templates) { if (templates.length === 1 && TypeGuards.isHTMLToken(templates[0].token)) return templates[0].token.getContent(); return templates; } export function* renderFilePath(file, ctx, liquid) { if (typeof file === 'string') return file; if (Array.isArray(file)) return liquid.renderer.renderTemplates(file, ctx); return yield evalToken(file, ctx); } //# sourceMappingURL=render.js.map