mirror of
https://github.com/harttle/liquidjs.git
synced 2026-09-16 04:40:39 -07:00
refactor: switch Context <-> Scope concepts
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import assert from '../../util/assert'
|
||||
import { identifier } from '../../parser/lexical'
|
||||
import TagToken from '../../parser/tag-token'
|
||||
import Scope from '../../scope/scope'
|
||||
import Context from '../../context/context'
|
||||
import ITagImplOptions from '../../template/tag/itag-impl-options'
|
||||
|
||||
const re = new RegExp(`(${identifier.source})\\s*=([^]*)`)
|
||||
@@ -13,7 +13,7 @@ export default {
|
||||
this.key = match[1]
|
||||
this.value = match[2]
|
||||
},
|
||||
render: async function (scope: Scope) {
|
||||
scope.contexts[0][this.key] = await this.liquid.evalValue(this.value, scope)
|
||||
render: async function (ctx: Context) {
|
||||
ctx.scopes[0][this.key] = await this.liquid.evalValue(this.value, ctx)
|
||||
}
|
||||
} as ITagImplOptions
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import BlockMode from '../../scope/block-mode'
|
||||
import BlockMode from '../../context/block-mode'
|
||||
import TagToken from '../../parser/tag-token'
|
||||
import Token from '../../parser/token'
|
||||
import ITemplate from '../../template/itemplate'
|
||||
import Scope from '../../scope/scope'
|
||||
import Context from '../../context/context'
|
||||
import ITagImplOptions from '../../template/tag/itag-impl-options'
|
||||
import ParseStream from '../../parser/parse-stream'
|
||||
|
||||
@@ -19,14 +19,14 @@ export default {
|
||||
})
|
||||
stream.start()
|
||||
},
|
||||
render: async function (scope: Scope) {
|
||||
const childDefined = scope.blocks[this.block]
|
||||
render: async function (ctx: Context) {
|
||||
const childDefined = ctx.blocks[this.block]
|
||||
const html = childDefined !== undefined
|
||||
? childDefined
|
||||
: await this.liquid.renderer.renderTemplates(this.tpls, scope)
|
||||
: await this.liquid.renderer.renderTemplates(this.tpls, ctx)
|
||||
|
||||
if (scope.blockMode === BlockMode.STORE) {
|
||||
scope.blocks[this.block] = html
|
||||
if (ctx.blockMode === BlockMode.STORE) {
|
||||
ctx.blocks[this.block] = html
|
||||
return ''
|
||||
}
|
||||
return html
|
||||
|
||||
@@ -2,7 +2,7 @@ import assert from '../../util/assert'
|
||||
import { identifier } from '../../parser/lexical'
|
||||
import TagToken from '../../parser/tag-token'
|
||||
import Token from '../../parser/token'
|
||||
import Scope from '../../scope/scope'
|
||||
import Context from '../../context/context'
|
||||
import ITagImplOptions from '../../template/tag/itag-impl-options'
|
||||
|
||||
const re = new RegExp(`(${identifier.source})`)
|
||||
@@ -23,8 +23,8 @@ export default {
|
||||
})
|
||||
stream.start()
|
||||
},
|
||||
render: async function (scope: Scope) {
|
||||
const html = await this.liquid.renderer.renderTemplates(this.templates, scope)
|
||||
scope.contexts[0][this.variable] = html
|
||||
render: async function (ctx: Context) {
|
||||
const html = await this.liquid.renderer.renderTemplates(this.templates, ctx)
|
||||
ctx.scopes[0][this.variable] = html
|
||||
}
|
||||
} as ITagImplOptions
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { evalExp } from '../../render/syntax'
|
||||
import TagToken from '../../parser/tag-token'
|
||||
import Token from '../../parser/token'
|
||||
import Scope from '../../scope/scope'
|
||||
import Context from '../../context/context'
|
||||
import ITemplate from '../../template/itemplate'
|
||||
import ITagImplOptions from '../../template/tag/itag-impl-options'
|
||||
import ParseStream from '../../parser/parse-stream'
|
||||
@@ -30,15 +30,15 @@ export default {
|
||||
stream.start()
|
||||
},
|
||||
|
||||
render: async function (scope: Scope) {
|
||||
render: async function (ctx: Context) {
|
||||
for (let i = 0; i < this.cases.length; i++) {
|
||||
const branch = this.cases[i]
|
||||
const val = await evalExp(branch.val, scope)
|
||||
const cond = await evalExp(this.cond, scope)
|
||||
const val = await evalExp(branch.val, ctx)
|
||||
const cond = await evalExp(this.cond, ctx)
|
||||
if (val === cond) {
|
||||
return this.liquid.renderer.renderTemplates(branch.templates, scope)
|
||||
return this.liquid.renderer.renderTemplates(branch.templates, ctx)
|
||||
}
|
||||
}
|
||||
return this.liquid.renderer.renderTemplates(this.elseTemplates, scope)
|
||||
return this.liquid.renderer.renderTemplates(this.elseTemplates, ctx)
|
||||
}
|
||||
} as ITagImplOptions
|
||||
|
||||
@@ -2,7 +2,7 @@ import assert from '../../util/assert'
|
||||
import { value as rValue } from '../../parser/lexical'
|
||||
import { evalValue } from '../../render/syntax'
|
||||
import TagToken from '../../parser/tag-token'
|
||||
import Scope from '../../scope/scope'
|
||||
import Context from '../../context/context'
|
||||
import ITagImplOptions from '../../template/tag/itag-impl-options'
|
||||
|
||||
const groupRE = new RegExp(`^(?:(${rValue.source})\\s*:\\s*)?(.*)$`)
|
||||
@@ -24,10 +24,10 @@ export default <ITagImplOptions>{
|
||||
assert(this.candidates.length, `empty candidates: ${tagToken.raw}`)
|
||||
},
|
||||
|
||||
render: async function (scope: Scope) {
|
||||
const group = await evalValue(this.group, scope)
|
||||
render: async function (ctx: Context) {
|
||||
const group = await evalValue(this.group, ctx)
|
||||
const fingerprint = `cycle:${group}:` + this.candidates.join(',')
|
||||
const groups = scope.groups
|
||||
const groups = ctx.groups
|
||||
let idx = groups[fingerprint]
|
||||
|
||||
if (idx === undefined) {
|
||||
@@ -38,6 +38,6 @@ export default <ITagImplOptions>{
|
||||
idx = (idx + 1) % this.candidates.length
|
||||
groups[fingerprint] = idx
|
||||
|
||||
return evalValue(candidate, scope)
|
||||
return evalValue(candidate, ctx)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import assert from '../../util/assert'
|
||||
import { identifier } from '../../parser/lexical'
|
||||
import TagToken from '../../parser/tag-token'
|
||||
import Scope from '../../scope/scope'
|
||||
import Context from '../../context/context'
|
||||
import ITagImplOptions from '../../template/tag/itag-impl-options'
|
||||
|
||||
export default {
|
||||
@@ -10,7 +10,7 @@ export default {
|
||||
assert(match, `illegal identifier ${token.args}`)
|
||||
this.variable = match[0]
|
||||
},
|
||||
render: function (context: Scope) {
|
||||
render: function (context: Context) {
|
||||
const scope = context.environments
|
||||
if (typeof scope[this.variable] !== 'number') {
|
||||
scope[this.variable] = 0
|
||||
|
||||
@@ -4,7 +4,7 @@ import assert from '../../util/assert'
|
||||
import { identifier, value, hash } from '../../parser/lexical'
|
||||
import TagToken from '../../parser/tag-token'
|
||||
import Token from '../../parser/token'
|
||||
import Scope from '../../scope/scope'
|
||||
import Context from '../../context/context'
|
||||
import Hash from '../../template/tag/hash'
|
||||
import ITemplate from '../../template/itemplate'
|
||||
import ITagImplOptions from '../../template/tag/itag-impl-options'
|
||||
@@ -41,8 +41,8 @@ export default <ITagImplOptions>{
|
||||
|
||||
stream.start()
|
||||
},
|
||||
render: async function (scope: Scope, hash: Hash) {
|
||||
let collection = await evalExp(this.collection, scope)
|
||||
render: async function (ctx: Context, hash: Hash) {
|
||||
let collection = await evalExp(this.collection, ctx)
|
||||
|
||||
if (!isArray(collection)) {
|
||||
if (isString(collection) && collection.length > 0) {
|
||||
@@ -52,7 +52,7 @@ export default <ITagImplOptions>{
|
||||
}
|
||||
}
|
||||
if (!isArray(collection) || !collection.length) {
|
||||
return this.liquid.renderer.renderTemplates(this.elseTemplates, scope)
|
||||
return this.liquid.renderer.renderTemplates(this.elseTemplates, ctx)
|
||||
}
|
||||
|
||||
const offset = hash.offset || 0
|
||||
@@ -62,12 +62,12 @@ export default <ITagImplOptions>{
|
||||
if (this.reversed) collection.reverse()
|
||||
|
||||
const context = { forloop: new ForloopDrop(collection.length) }
|
||||
scope.push(context)
|
||||
ctx.push(context)
|
||||
let html = ''
|
||||
for (const item of collection) {
|
||||
context[this.variable] = item
|
||||
try {
|
||||
html += await this.liquid.renderer.renderTemplates(this.templates, scope)
|
||||
html += await this.liquid.renderer.renderTemplates(this.templates, ctx)
|
||||
} catch (e) {
|
||||
if (e.name === 'RenderBreakError') {
|
||||
html += e.resolvedHTML
|
||||
@@ -76,7 +76,7 @@ export default <ITagImplOptions>{
|
||||
}
|
||||
context.forloop.next()
|
||||
}
|
||||
scope.pop()
|
||||
ctx.pop()
|
||||
return html
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { evalExp, isTruthy } from '../../render/syntax'
|
||||
import TagToken from '../../parser/tag-token'
|
||||
import Token from '../../parser/token'
|
||||
import Scope from '../../scope/scope'
|
||||
import Context from '../../context/context'
|
||||
import ITemplate from '../../template/itemplate'
|
||||
import ITagImplOptions from '../../template/tag/itag-impl-options'
|
||||
import ParseStream from '../../parser/parse-stream'
|
||||
@@ -33,13 +33,13 @@ export default {
|
||||
stream.start()
|
||||
},
|
||||
|
||||
render: async function (scope: Scope) {
|
||||
render: async function (ctx: Context) {
|
||||
for (const branch of this.branches) {
|
||||
const cond = await evalExp(branch.cond, scope)
|
||||
const cond = await evalExp(branch.cond, ctx)
|
||||
if (isTruthy(cond)) {
|
||||
return this.liquid.renderer.renderTemplates(branch.templates, scope)
|
||||
return this.liquid.renderer.renderTemplates(branch.templates, ctx)
|
||||
}
|
||||
}
|
||||
return this.liquid.renderer.renderTemplates(this.elseTemplates, scope)
|
||||
return this.liquid.renderer.renderTemplates(this.elseTemplates, ctx)
|
||||
}
|
||||
} as ITagImplOptions
|
||||
|
||||
+17
-17
@@ -1,9 +1,9 @@
|
||||
import assert from '../../util/assert'
|
||||
import { value, quotedLine } from '../../parser/lexical'
|
||||
import { evalValue } from '../../render/syntax'
|
||||
import BlockMode from '../../scope/block-mode'
|
||||
import BlockMode from '../../context/block-mode'
|
||||
import TagToken from '../../parser/tag-token'
|
||||
import Scope from '../../scope/scope'
|
||||
import Context from '../../context/context'
|
||||
import Hash from '../../template/tag/hash'
|
||||
import ITagImplOptions from '../../template/tag/itag-impl-options'
|
||||
|
||||
@@ -27,34 +27,34 @@ export default <ITagImplOptions>{
|
||||
this.with = match[1]
|
||||
}
|
||||
},
|
||||
render: async function (scope: Scope, hash: Hash) {
|
||||
render: async function (ctx: Context, hash: Hash) {
|
||||
let filepath
|
||||
if (scope.opts.dynamicPartials) {
|
||||
if (ctx.opts.dynamicPartials) {
|
||||
if (quotedLine.exec(this.value)) {
|
||||
const template = this.value.slice(1, -1)
|
||||
filepath = await this.liquid.parseAndRender(template, scope.getAll(), scope.opts)
|
||||
filepath = await this.liquid.parseAndRender(template, ctx.getAll(), ctx.opts)
|
||||
} else {
|
||||
filepath = await evalValue(this.value, scope)
|
||||
filepath = await evalValue(this.value, ctx)
|
||||
}
|
||||
} else {
|
||||
filepath = this.staticValue
|
||||
}
|
||||
assert(filepath, `cannot include with empty filename`)
|
||||
|
||||
const originBlocks = scope.blocks
|
||||
const originBlockMode = scope.blockMode
|
||||
const originBlocks = ctx.blocks
|
||||
const originBlockMode = ctx.blockMode
|
||||
|
||||
scope.blocks = {}
|
||||
scope.blockMode = BlockMode.OUTPUT
|
||||
ctx.blocks = {}
|
||||
ctx.blockMode = BlockMode.OUTPUT
|
||||
if (this.with) {
|
||||
hash[filepath] = await evalValue(this.with, scope)
|
||||
hash[filepath] = await evalValue(this.with, ctx)
|
||||
}
|
||||
const templates = await this.liquid.getTemplate(filepath, scope.opts)
|
||||
scope.push(hash)
|
||||
const html = await this.liquid.renderer.renderTemplates(templates, scope)
|
||||
scope.pop(hash)
|
||||
scope.blocks = originBlocks
|
||||
scope.blockMode = originBlockMode
|
||||
const templates = await this.liquid.getTemplate(filepath, ctx.opts)
|
||||
ctx.push(hash)
|
||||
const html = await this.liquid.renderer.renderTemplates(templates, ctx)
|
||||
ctx.pop(hash)
|
||||
ctx.blocks = originBlocks
|
||||
ctx.blockMode = originBlockMode
|
||||
return html
|
||||
}
|
||||
}
|
||||
|
||||
+14
-14
@@ -1,10 +1,10 @@
|
||||
import assert from '../../util/assert'
|
||||
import { value as rValue } from '../../parser/lexical'
|
||||
import { evalValue } from '../../render/syntax'
|
||||
import BlockMode from '../../scope/block-mode'
|
||||
import BlockMode from '../../context/block-mode'
|
||||
import TagToken from '../../parser/tag-token'
|
||||
import Token from '../../parser/token'
|
||||
import Scope from '../../scope/scope'
|
||||
import Context from '../../context/context'
|
||||
import Hash from '../../template/tag/hash'
|
||||
import ITagImplOptions from '../../template/tag/itag-impl-options'
|
||||
|
||||
@@ -24,23 +24,23 @@ export default {
|
||||
|
||||
this.tpls = this.liquid.parser.parse(remainTokens)
|
||||
},
|
||||
render: async function (scope: Scope, hash: Hash) {
|
||||
const layout = scope.opts.dynamicPartials
|
||||
? await evalValue(this.layout, scope)
|
||||
render: async function (ctx: Context, hash: Hash) {
|
||||
const layout = ctx.opts.dynamicPartials
|
||||
? await evalValue(this.layout, ctx)
|
||||
: this.staticLayout
|
||||
assert(layout, `cannot apply layout with empty filename`)
|
||||
|
||||
// render the remaining tokens immediately
|
||||
scope.blockMode = BlockMode.STORE
|
||||
const html = await this.liquid.renderer.renderTemplates(this.tpls, scope)
|
||||
if (scope.blocks[''] === undefined) {
|
||||
scope.blocks[''] = html
|
||||
ctx.blockMode = BlockMode.STORE
|
||||
const html = await this.liquid.renderer.renderTemplates(this.tpls, ctx)
|
||||
if (ctx.blocks[''] === undefined) {
|
||||
ctx.blocks[''] = html
|
||||
}
|
||||
const templates = await this.liquid.getTemplate(layout, scope.opts)
|
||||
scope.push(hash)
|
||||
scope.blockMode = BlockMode.OUTPUT
|
||||
const partial = await this.liquid.renderer.renderTemplates(templates, scope)
|
||||
scope.pop(hash)
|
||||
const templates = await this.liquid.getTemplate(layout, ctx.opts)
|
||||
ctx.push(hash)
|
||||
ctx.blockMode = BlockMode.OUTPUT
|
||||
const partial = await this.liquid.renderer.renderTemplates(templates, ctx)
|
||||
ctx.pop(hash)
|
||||
return partial
|
||||
}
|
||||
} as ITagImplOptions
|
||||
|
||||
@@ -4,7 +4,7 @@ import { identifier, value, hash } from '../../parser/lexical'
|
||||
import TagToken from '../../parser/tag-token'
|
||||
import Token from '../../parser/token'
|
||||
import ITemplate from '../../template/itemplate'
|
||||
import Scope from '../../scope/scope'
|
||||
import Context from '../../context/context'
|
||||
import Hash from '../../template/tag/hash'
|
||||
import ITagImplOptions from '../../template/tag/itag-impl-options'
|
||||
import ParseStream from '../../parser/parse-stream'
|
||||
@@ -35,8 +35,8 @@ export default {
|
||||
stream.start()
|
||||
},
|
||||
|
||||
render: async function (scope: Scope, hash: Hash) {
|
||||
let collection = await evalExp(this.collection, scope) || []
|
||||
render: async function (ctx: Context, hash: Hash) {
|
||||
let collection = await evalExp(this.collection, ctx) || []
|
||||
const offset = hash.offset || 0
|
||||
const limit = (hash.limit === undefined) ? collection.length : hash.limit
|
||||
|
||||
@@ -44,22 +44,22 @@ export default {
|
||||
const cols = hash.cols || collection.length
|
||||
|
||||
const tablerowloop = new TablerowloopDrop(collection.length, cols)
|
||||
const ctx = { tablerowloop }
|
||||
scope.push(ctx)
|
||||
const scope = { tablerowloop }
|
||||
ctx.push(scope)
|
||||
|
||||
let html = ''
|
||||
for (let idx = 0; idx < collection.length; idx++, tablerowloop.next()) {
|
||||
ctx[this.variable] = collection[idx]
|
||||
scope[this.variable] = collection[idx]
|
||||
if (tablerowloop.col0() === 0) {
|
||||
if (tablerowloop.row() !== 1) html += '</tr>'
|
||||
html += `<tr class="row${tablerowloop.row()}">`
|
||||
}
|
||||
html += `<td class="col${tablerowloop.col()}">`
|
||||
html += await this.liquid.renderer.renderTemplates(this.templates, scope)
|
||||
html += await this.liquid.renderer.renderTemplates(this.templates, ctx)
|
||||
html += '</td>'
|
||||
}
|
||||
if (collection.length) html += '</tr>'
|
||||
scope.pop(ctx)
|
||||
ctx.pop(scope)
|
||||
return html
|
||||
}
|
||||
} as ITagImplOptions
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { evalExp, isFalsy } from '../../render/syntax'
|
||||
import TagToken from '../../parser/tag-token'
|
||||
import Token from '../../parser/token'
|
||||
import Scope from '../../scope/scope'
|
||||
import Context from '../../context/context'
|
||||
import ITagImplOptions from '../../template/tag/itag-impl-options'
|
||||
import ParseStream from '../../parser/parse-stream'
|
||||
|
||||
@@ -25,10 +25,10 @@ export default {
|
||||
stream.start()
|
||||
},
|
||||
|
||||
render: async function (scope: Scope) {
|
||||
const cond = await evalExp(this.cond, scope)
|
||||
render: async function (ctx: Context) {
|
||||
const cond = await evalExp(this.cond, ctx)
|
||||
return isFalsy(cond)
|
||||
? this.liquid.renderer.renderTemplates(this.templates, scope)
|
||||
: this.liquid.renderer.renderTemplates(this.elseTemplates, scope)
|
||||
? this.liquid.renderer.renderTemplates(this.templates, ctx)
|
||||
: this.liquid.renderer.renderTemplates(this.elseTemplates, ctx)
|
||||
}
|
||||
} as ITagImplOptions
|
||||
|
||||
@@ -4,12 +4,12 @@ import { __assign } from 'tslib'
|
||||
import assert from '../util/assert'
|
||||
import { NormalizedFullOptions, applyDefault } from '../liquid-options'
|
||||
import BlockMode from './block-mode'
|
||||
import { Context } from './context'
|
||||
import { Scope } from './scope'
|
||||
|
||||
export default class Scope {
|
||||
export default class Context {
|
||||
opts: NormalizedFullOptions
|
||||
contexts: Array<Context> = [{}]
|
||||
environments: Context
|
||||
scopes: Array<Scope> = [{}]
|
||||
environments: Scope
|
||||
blocks: object = {}
|
||||
groups: {[key: string]: number} = {}
|
||||
blockMode: BlockMode = BlockMode.OUTPUT
|
||||
@@ -18,7 +18,7 @@ export default class Scope {
|
||||
this.environments = ctx
|
||||
}
|
||||
getAll () {
|
||||
return [this.environments, ...this.contexts]
|
||||
return [this.environments, ...this.scopes]
|
||||
.reduce((ctx, val) => __assign(ctx, val), {})
|
||||
}
|
||||
async get (path: string) {
|
||||
@@ -33,28 +33,28 @@ export default class Scope {
|
||||
return ctx
|
||||
}
|
||||
push (ctx: object) {
|
||||
return this.contexts.push(ctx)
|
||||
return this.scopes.push(ctx)
|
||||
}
|
||||
pop (ctx?: object): object | undefined {
|
||||
if (!arguments.length) {
|
||||
return this.contexts.pop()
|
||||
return this.scopes.pop()
|
||||
}
|
||||
const i = this.contexts.findIndex(scope => scope === ctx)
|
||||
const i = this.scopes.findIndex(scope => scope === ctx)
|
||||
if (i === -1) {
|
||||
throw new TypeError('scope not found, cannot pop')
|
||||
}
|
||||
return this.contexts.splice(i, 1)[0]
|
||||
return this.scopes.splice(i, 1)[0]
|
||||
}
|
||||
findContextFor (key: string) {
|
||||
for (let i = this.contexts.length - 1; i >= 0; i--) {
|
||||
const candidate = this.contexts[i]
|
||||
for (let i = this.scopes.length - 1; i >= 0; i--) {
|
||||
const candidate = this.scopes[i]
|
||||
if (key in candidate) {
|
||||
return candidate
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
private readProperty (obj: Context, key: string) {
|
||||
private readProperty (obj: Scope, key: string) {
|
||||
if (_.isNil(obj)) return obj
|
||||
obj = _.toLiquid(obj)
|
||||
if (obj instanceof Drop) {
|
||||
@@ -125,7 +125,7 @@ export default class Scope {
|
||||
}
|
||||
}
|
||||
|
||||
function readSize (obj: Context) {
|
||||
function readSize (obj: Scope) {
|
||||
if (!_.isNil(obj['size'])) return obj['size']
|
||||
if (_.isArray(obj) || _.isString(obj)) return obj.length
|
||||
return obj['size']
|
||||
@@ -5,4 +5,4 @@ type PlainObject = {
|
||||
toLiquid?: () => any
|
||||
}
|
||||
|
||||
export type Context = PlainObject | Drop
|
||||
export type Scope = PlainObject | Drop
|
||||
+4
-4
@@ -1,4 +1,4 @@
|
||||
import Scope from './scope/scope'
|
||||
import Context from './context/context'
|
||||
import * as Types from './types'
|
||||
import fs from './fs/node'
|
||||
import * as _ from './util/underscore'
|
||||
@@ -38,7 +38,7 @@ export default class Liquid {
|
||||
}
|
||||
render (tpl: Array<ITemplate>, ctx?: object, opts?: LiquidOptions) {
|
||||
const options = { ...this.options, ...normalize(opts) }
|
||||
const scope = new Scope(ctx, options)
|
||||
const scope = new Context(ctx, options)
|
||||
return this.renderer.renderTemplates(tpl, scope)
|
||||
}
|
||||
async parseAndRender (html: string, ctx?: object, opts?: LiquidOptions) {
|
||||
@@ -69,8 +69,8 @@ export default class Liquid {
|
||||
const templates = await this.getTemplate(file, options)
|
||||
return this.render(templates, ctx, opts)
|
||||
}
|
||||
evalValue (str: string, scope: Scope) {
|
||||
return new Value(str, this.options.strictFilters).value(scope)
|
||||
evalValue (str: string, ctx: Context) {
|
||||
return new Value(str, this.options.strictFilters).value(ctx)
|
||||
}
|
||||
registerFilter (name: string, filter: FilterImpl) {
|
||||
return Filter.register(name, filter)
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { RenderError } from '../util/error'
|
||||
import assert from '../util/assert'
|
||||
import Scope from '../scope/scope'
|
||||
import Context from '../context/context'
|
||||
import ITemplate from '../template/itemplate'
|
||||
|
||||
export default class Render {
|
||||
async renderTemplates (templates: ITemplate[], scope: Scope) {
|
||||
assert(scope, 'unable to evalTemplates: scope undefined')
|
||||
async renderTemplates (templates: ITemplate[], ctx: Context) {
|
||||
assert(ctx, 'unable to evalTemplates: context undefined')
|
||||
|
||||
let html = ''
|
||||
for (const tpl of templates) {
|
||||
try {
|
||||
html += await tpl.render(scope)
|
||||
html += await tpl.render(ctx)
|
||||
} catch (e) {
|
||||
if (e.name === 'RenderBreakError') {
|
||||
e.resolvedHTML = html
|
||||
|
||||
+14
-14
@@ -1,6 +1,6 @@
|
||||
import * as lexical from '../parser/lexical'
|
||||
import assert from '../util/assert'
|
||||
import Scope from '../scope/scope'
|
||||
import Context from '../context/context'
|
||||
import { range, last } from '../util/underscore'
|
||||
import { isComparable } from '../drop/icomparable'
|
||||
import { NullDrop } from '../drop/null-drop'
|
||||
@@ -48,36 +48,36 @@ const binaryOperators: {[key: string]: (lhs: any, rhs: any) => boolean} = {
|
||||
'or': (l: any, r: any) => isTruthy(l) || isTruthy(r)
|
||||
}
|
||||
|
||||
export async function parseExp (exp: string, scope: Scope): Promise<any> {
|
||||
assert(scope, 'unable to parseExp: scope undefined')
|
||||
export async function parseExp (exp: string, ctx: Context): Promise<any> {
|
||||
assert(ctx, 'unable to parseExp: scope undefined')
|
||||
const operatorREs = lexical.operators
|
||||
let match
|
||||
for (let i = 0; i < operatorREs.length; i++) {
|
||||
const operatorRE = operatorREs[i]
|
||||
const expRE = new RegExp(`^(${lexical.quoteBalanced.source})(${operatorRE.source})(${lexical.quoteBalanced.source})$`)
|
||||
if ((match = exp.match(expRE))) {
|
||||
const l = await parseExp(match[1], scope)
|
||||
const l = await parseExp(match[1], ctx)
|
||||
const op = binaryOperators[match[2].trim()]
|
||||
const r = await parseExp(match[3], scope)
|
||||
const r = await parseExp(match[3], ctx)
|
||||
return op(l, r)
|
||||
}
|
||||
}
|
||||
|
||||
if ((match = exp.match(lexical.rangeLine))) {
|
||||
const low = await evalValue(match[1], scope)
|
||||
const high = await evalValue(match[2], scope)
|
||||
const low = await evalValue(match[1], ctx)
|
||||
const high = await evalValue(match[2], ctx)
|
||||
return range(+low, +high + 1)
|
||||
}
|
||||
|
||||
return parseValue(exp, scope)
|
||||
return parseValue(exp, ctx)
|
||||
}
|
||||
|
||||
export async function evalExp (str: string, scope: Scope): Promise<any> {
|
||||
const value = await parseExp(str, scope)
|
||||
export async function evalExp (str: string, ctx: Context): Promise<any> {
|
||||
const value = await parseExp(str, ctx)
|
||||
return value instanceof Drop ? value.valueOf() : value
|
||||
}
|
||||
|
||||
async function parseValue (str: string | undefined, scope: Scope): Promise<any> {
|
||||
async function parseValue (str: string | undefined, ctx: Context): Promise<any> {
|
||||
if (!str) return null
|
||||
str = str.trim()
|
||||
|
||||
@@ -88,11 +88,11 @@ async function parseValue (str: string | undefined, scope: Scope): Promise<any>
|
||||
if (str === 'blank') return new BlankDrop()
|
||||
if (!isNaN(Number(str))) return Number(str)
|
||||
if ((str[0] === '"' || str[0] === "'") && str[0] === last(str)) return str.slice(1, -1)
|
||||
return scope.get(str)
|
||||
return ctx.get(str)
|
||||
}
|
||||
|
||||
export async function evalValue (str: string | undefined, scope: Scope) {
|
||||
const value = await parseValue(str, scope)
|
||||
export async function evalValue (str: string | undefined, ctx: Context) {
|
||||
const value = await parseValue(str, ctx)
|
||||
return value instanceof Drop ? value.valueOf() : value
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { evalValue } from '../../render/syntax'
|
||||
import Scope from '../../scope/scope'
|
||||
import Context from '../../context/context'
|
||||
import { isArray } from '../../util/underscore'
|
||||
import { FilterImpl } from './filter-impl'
|
||||
|
||||
@@ -19,11 +19,11 @@ export class Filter {
|
||||
this.impl = impl || (x => x)
|
||||
this.args = args
|
||||
}
|
||||
async render (value: any, scope: Scope) {
|
||||
async render (value: any, ctx: Context) {
|
||||
const argv: any[] = []
|
||||
for (const arg of this.args) {
|
||||
if (isArray(arg)) argv.push([arg[0], await evalValue(arg[1], scope)])
|
||||
else argv.push(await evalValue(arg, scope))
|
||||
if (isArray(arg)) argv.push([arg[0], await evalValue(arg[1], ctx)])
|
||||
else argv.push(await evalValue(arg, ctx))
|
||||
}
|
||||
return this.impl.apply(null, [value, ...argv])
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import Scope from '../scope/scope'
|
||||
import Context from '../context/context'
|
||||
import Token from '../parser/token'
|
||||
|
||||
export default interface ITemplate {
|
||||
token: Token;
|
||||
render(scope: Scope): Promise<string>;
|
||||
render(ctx: Context): Promise<string>;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import Value from './value'
|
||||
import { stringify } from '../util/underscore'
|
||||
import Template from '../template/template'
|
||||
import ITemplate from '../template/itemplate'
|
||||
import Scope from '../scope/scope'
|
||||
import Context from '../context/context'
|
||||
import OutputToken from '../parser/output-token'
|
||||
|
||||
export default class Output extends Template<OutputToken> implements ITemplate {
|
||||
@@ -11,8 +11,8 @@ export default class Output extends Template<OutputToken> implements ITemplate {
|
||||
super(token)
|
||||
this.value = new Value(token.value, strictFilters)
|
||||
}
|
||||
async render (scope: Scope): Promise<string> {
|
||||
const html = await this.value.value(scope)
|
||||
async render (ctx: Context): Promise<string> {
|
||||
const html = await this.value.value(ctx)
|
||||
return stringify(html)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { hashCapture } from '../../parser/lexical'
|
||||
import { evalValue } from '../../render/syntax'
|
||||
import Scope from '../../scope/scope'
|
||||
import Context from '../../context/context'
|
||||
|
||||
/**
|
||||
* Key-Value Pairs Representing Tag Arguments
|
||||
@@ -10,14 +10,14 @@ import Scope from '../../scope/scope'
|
||||
*/
|
||||
export default class Hash {
|
||||
[key: string]: any
|
||||
static async create (markup: string, scope: Scope) {
|
||||
static async create (markup: string, ctx: Context) {
|
||||
const instance = new Hash()
|
||||
let match
|
||||
hashCapture.lastIndex = 0
|
||||
while ((match = hashCapture.exec(markup))) {
|
||||
const k = match[1]
|
||||
const v = match[2]
|
||||
instance[k] = await evalValue(v, scope)
|
||||
instance[k] = await evalValue(v, ctx)
|
||||
}
|
||||
return instance
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import Scope from '../../scope/scope'
|
||||
import Context from '../../context/context'
|
||||
import TagToken from '../../parser/tag-token'
|
||||
import Token from '../../parser/token'
|
||||
import Hash from '../../template/tag/hash'
|
||||
@@ -6,5 +6,5 @@ import ITagImpl from './itag-impl'
|
||||
|
||||
export default interface ITagImplOptions {
|
||||
parse?: (this: ITagImpl, token: TagToken, remainingTokens: Array<Token>) => void
|
||||
render?: (this: ITagImpl, scope: Scope, hash: Hash) => any | Promise<any>
|
||||
render?: (this: ITagImpl, ctx: Context, hash: Hash) => any | Promise<any>
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { create, stringify } from '../../util/underscore'
|
||||
import assert from '../../util/assert'
|
||||
import Scope from '../../scope/scope'
|
||||
import Context from '../../context/context'
|
||||
import ITagImpl from './itag-impl'
|
||||
import ITagImplOptions from './itag-impl-options'
|
||||
import Liquid from '../../liquid'
|
||||
@@ -27,13 +27,13 @@ export default class Tag extends Template<TagToken> implements ITemplate {
|
||||
this.impl.parse(token, tokens)
|
||||
}
|
||||
}
|
||||
async render (scope: Scope) {
|
||||
const hash = await Hash.create(this.token.args, scope)
|
||||
async render (ctx: Context) {
|
||||
const hash = await Hash.create(this.token.args, ctx)
|
||||
const impl = this.impl
|
||||
if (typeof impl.render !== 'function') {
|
||||
return ''
|
||||
}
|
||||
const html = await impl.render(scope, hash)
|
||||
const html = await impl.render(ctx, hash)
|
||||
return stringify(html)
|
||||
}
|
||||
static register (name: string, tag: ITagImplOptions) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { evalExp } from '../render/syntax'
|
||||
import { FilterArgs, Filter } from './filter/filter'
|
||||
import Scope from '../scope/scope'
|
||||
import Context from '../context/context'
|
||||
|
||||
export default class Value {
|
||||
private strictFilters: boolean
|
||||
@@ -47,10 +47,10 @@ export default class Value {
|
||||
}
|
||||
this.filters.push(new Filter(name, args, this.strictFilters))
|
||||
}
|
||||
async value (scope: Scope) {
|
||||
let val = await evalExp(this.initial, scope)
|
||||
async value (ctx: Context) {
|
||||
let val = await evalExp(this.initial, ctx)
|
||||
for (const filter of this.filters) {
|
||||
val = await filter.render(val, scope)
|
||||
val = await filter.render(val, ctx)
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user