refactor: switch Context <-> Scope concepts

This commit is contained in:
harttle
2019-03-25 10:36:23 +08:00
parent 76345a64c3
commit 45e3c2bb8e
34 changed files with 272 additions and 272 deletions
+3 -3
View File
@@ -1,7 +1,7 @@
import * as Benchmark from 'benchmark'
import Liquid from '../src/liquid'
import TagToken from '../src/parser/tag-token'
import Scope from '../src/scope/scope'
import Context from '../src/context/context'
const engine = new Liquid({
root: __dirname,
@@ -13,8 +13,8 @@ engine.registerTag('header', {
const [key, val] = token.args.split(':')
this[key] = val
},
render: function (scope: Scope) {
const title = this.liquid.evalValue(this.content, scope)
render: function (ctx: Context) {
const title = this.liquid.evalValue(this.content, ctx)
return `<h1>${title}</h1>`
}
})
+3 -3
View File
@@ -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
+7 -7
View File
@@ -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
+4 -4
View File
@@ -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
+6 -6
View File
@@ -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
+5 -5
View File
@@ -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)
}
}
+2 -2
View File
@@ -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
+7 -7
View File
@@ -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
}
}
+5 -5
View File
@@ -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
View File
@@ -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
View File
@@ -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
+8 -8
View File
@@ -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
+5 -5
View File
@@ -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
+13 -13
View File
@@ -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
View File
@@ -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)
+4 -4
View File
@@ -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
View File
@@ -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
}
+4 -4
View File
@@ -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])
}
+2 -2
View File
@@ -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>;
}
+3 -3
View File
@@ -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)
}
}
+3 -3
View File
@@ -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
}
+2 -2
View File
@@ -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>
}
+4 -4
View File
@@ -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) {
+4 -4
View File
@@ -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
}
+2 -2
View File
@@ -1,12 +1,12 @@
import Liquid from '../../../../src/liquid'
import { expect, use } from 'chai'
import * as chaiAsPromised from 'chai-as-promised'
import { Context } from '../../../../src/scope/context'
import { Scope } from '../../../../src/context/scope'
use(chaiAsPromised)
describe('tags/for', function () {
let liquid: Liquid, ctx: Context
let liquid: Liquid, ctx: Scope
before(function () {
liquid = new Liquid()
liquid.registerTag('throwingTag', {
@@ -1,13 +1,13 @@
import * as chai from 'chai'
import Scope from '../../../src/scope/scope'
import { Context } from '../../../src/scope/context'
import Context from '../../../src/context/context'
import { Scope } from '../../../src/context/scope'
const expect = chai.expect
describe('scope', function () {
let scope: Scope, ctx: Context
let ctx: Context, scope: Scope
beforeEach(function () {
ctx = {
scope = {
foo: 'zoo',
one: 1,
zoo: { size: 4 },
@@ -17,76 +17,76 @@ describe('scope', function () {
arr: ['a', 'b']
}
}
scope = new Scope(ctx)
ctx = new Context(scope)
})
describe('#propertyAccessSeq()', function () {
it('should handle dot syntax', async function () {
expect(await scope.propertyAccessSeq('foo.bar'))
expect(await ctx.propertyAccessSeq('foo.bar'))
.to.deep.equal(['foo', 'bar'])
})
it('should handle [<String>] syntax', async function () {
expect(await scope.propertyAccessSeq('foo["bar"]'))
expect(await ctx.propertyAccessSeq('foo["bar"]'))
.to.deep.equal(['foo', 'bar'])
})
it('should handle [<Identifier>] syntax', async function () {
expect(await scope.propertyAccessSeq('foo[foo]'))
expect(await ctx.propertyAccessSeq('foo[foo]'))
.to.deep.equal(['foo', 'zoo'])
})
it('should handle nested access 1', async function () {
expect(await scope.propertyAccessSeq('foo[bar.zoo]'))
expect(await ctx.propertyAccessSeq('foo[bar.zoo]'))
.to.deep.equal(['foo', 'coo'])
})
it('should handle nested access 2', async function () {
expect(await scope.propertyAccessSeq('foo[bar["zoo"]]'))
expect(await ctx.propertyAccessSeq('foo[bar["zoo"]]'))
.to.deep.equal(['foo', 'coo'])
})
it('should handle nested access 3', async function () {
expect(await scope.propertyAccessSeq('bar["foo"].zoo'))
expect(await ctx.propertyAccessSeq('bar["foo"].zoo'))
.to.deep.equal(['bar', 'foo', 'zoo'])
})
it('should handle nested access 4', async function () {
expect(await scope.propertyAccessSeq('foo[0].bar'))
expect(await ctx.propertyAccessSeq('foo[0].bar'))
.to.deep.equal(['foo', '0', 'bar'])
})
it('should handle nested access 5', async function () {
expect(await scope.propertyAccessSeq('foo[one].bar'))
expect(await ctx.propertyAccessSeq('foo[one].bar'))
.to.deep.equal(['foo', '1', 'bar'])
})
it('should handle nested access 6', async function () {
expect(await scope.propertyAccessSeq('foo[two].bar'))
expect(await ctx.propertyAccessSeq('foo[two].bar'))
.to.deep.equal(['foo', 'undefined', 'bar'])
})
})
describe('#get()', function () {
it('should get direct property', async function () {
expect(await await scope.get('foo')).equal('zoo')
expect(await await ctx.get('foo')).equal('zoo')
})
it('undefined property should yield undefined', async function () {
expect(scope.get('notdefined')).to.be.rejected
expect(await scope.get('notdefined')).to.equal(undefined)
expect(await scope.get(false as any)).to.equal(undefined)
expect(ctx.get('notdefined')).to.be.rejected
expect(await ctx.get('notdefined')).to.equal(undefined)
expect(await ctx.get(false as any)).to.equal(undefined)
})
it('should throw for invalid path', async function () {
expect(scope.get('')).to.be.rejectedWith('invalid path:""')
expect(ctx.get('')).to.be.rejectedWith('invalid path:""')
})
it('should throw when [] unbalanced', async function () {
expect(scope.get('foo[bar')).to.be.rejectedWith(/unbalanced \[\]/)
expect(ctx.get('foo[bar')).to.be.rejectedWith(/unbalanced \[\]/)
})
it('should throw when "" unbalanced', async function () {
expect(scope.get('foo["bar]')).to.be.rejectedWith(/unbalanced "/)
expect(ctx.get('foo["bar]')).to.be.rejectedWith(/unbalanced "/)
})
it("should throw when '' unbalanced", async function () {
expect(scope.get("foo['bar]")).to.be.rejectedWith(/unbalanced '/)
expect(ctx.get("foo['bar]")).to.be.rejectedWith(/unbalanced '/)
})
it('should respect to toLiquid', async function () {
const scope = new Scope({ foo: {
const scope = new Context({ foo: {
toLiquid: () => ({ bar: 'BAR' }),
bar: 'bar'
} })
@@ -94,94 +94,94 @@ describe('scope', function () {
})
it('should access child property via dot syntax', async function () {
expect(await scope.get('bar.zoo')).to.equal('coo')
expect(await scope.get('bar.arr')).to.deep.equal(['a', 'b'])
expect(await ctx.get('bar.zoo')).to.equal('coo')
expect(await ctx.get('bar.arr')).to.deep.equal(['a', 'b'])
})
it('should access child property via [<String>] syntax', async function () {
expect(await scope.get('bar["zoo"]')).to.equal('coo')
expect(await ctx.get('bar["zoo"]')).to.equal('coo')
})
it('should access child property via [<Number>] syntax', async function () {
expect(await scope.get('bar.arr[0]')).to.equal('a')
expect(await ctx.get('bar.arr[0]')).to.equal('a')
})
it('should access child property via [<Identifier>] syntax', async function () {
expect(await scope.get('bar[foo]')).to.equal('coo')
expect(await ctx.get('bar[foo]')).to.equal('coo')
})
it('should return undefined when not exist', async function () {
expect(await scope.get('foo.foo.foo')).to.be.undefined
expect(await ctx.get('foo.foo.foo')).to.be.undefined
})
it('should return string length as size', async function () {
expect(await scope.get('foo.size')).to.equal(3)
expect(await ctx.get('foo.size')).to.equal(3)
})
it('should return array length as size', async function () {
expect(await scope.get('bar.arr.size')).to.equal(2)
expect(await ctx.get('bar.arr.size')).to.equal(2)
})
it('should return size property if exists', async function () {
expect(await scope.get('zoo.size')).to.equal(4)
expect(await ctx.get('zoo.size')).to.equal(4)
})
it('should return undefined if do not have size and length', async function () {
expect(await scope.get('one.size')).to.equal(undefined)
expect(await ctx.get('one.size')).to.equal(undefined)
})
})
describe('strictVariables', async function () {
let scope: Scope
let ctx: Context
beforeEach(function () {
scope = new Scope(ctx, {
ctx = new Context(ctx, {
strictVariables: true
} as any)
})
it('should throw when variable not defined', function () {
return expect(scope.get('notdefined')).to.be.rejectedWith(/undefined variable: notdefined/)
return expect(ctx.get('notdefined')).to.be.rejectedWith(/undefined variable: notdefined/)
})
it('should throw when deep variable not exist', async function () {
scope.contexts.push({ 'foo': 'FOO' })
return expect(scope.get('foo.bar.not.defined')).to.be.rejectedWith(/undefined variable: bar/)
ctx.scopes.push({ 'foo': 'FOO' })
return expect(ctx.get('foo.bar.not.defined')).to.be.rejectedWith(/undefined variable: bar/)
})
it('should throw when itself not defined', async function () {
scope.contexts.push({ 'foo': 'FOO' })
return expect(scope.get('foo.BAR')).to.be.rejectedWith(/undefined variable: BAR/)
ctx.scopes.push({ 'foo': 'FOO' })
return expect(ctx.get('foo.BAR')).to.be.rejectedWith(/undefined variable: BAR/)
})
it('should find variable in parent scope', async function () {
scope.contexts.push({ 'foo': 'foo' })
scope.push({
ctx.scopes.push({ 'foo': 'foo' })
ctx.push({
'bar': 'bar'
})
expect(await scope.get('foo')).to.equal('foo')
expect(await ctx.get('foo')).to.equal('foo')
})
})
describe('.getAll()', function () {
it('should get all properties when arguments empty', async function () {
expect(await scope.getAll()).deep.equal(ctx)
expect(await ctx.getAll()).deep.equal(scope)
})
})
describe('.push()', function () {
it('should push scope', async function () {
scope.contexts.push({ 'bar': 'bar' })
scope.push({
ctx.scopes.push({ 'bar': 'bar' })
ctx.push({
foo: 'foo'
})
expect(await scope.get('foo')).to.equal('foo')
expect(await scope.get('bar')).to.equal('bar')
expect(await ctx.get('foo')).to.equal('foo')
expect(await ctx.get('bar')).to.equal('bar')
})
it('should hide deep properties by push', async function () {
scope.contexts.push({ 'bar': { bar: 'bar' } })
scope.push({ bar: { foo: 'foo' } })
expect(await scope.get('bar.foo')).to.equal('foo')
expect(await scope.get('bar.bar')).to.equal(undefined)
ctx.scopes.push({ 'bar': { bar: 'bar' } })
ctx.push({ bar: { foo: 'foo' } })
expect(await ctx.get('bar.foo')).to.equal('foo')
expect(await ctx.get('bar.bar')).to.equal(undefined)
})
})
describe('.pop()', function () {
it('should pop scope', async function () {
scope.push({
ctx.push({
foo: 'foo'
})
scope.pop()
expect(await scope.get('foo')).to.equal('zoo')
ctx.pop()
expect(await ctx.get('foo')).to.equal('zoo')
})
})
it('should pop specified scope', async function () {
@@ -191,18 +191,18 @@ describe('scope', function () {
const scope2 = {
bar: 'bar'
}
scope.push(scope1)
scope.push(scope2)
expect(await scope.get('foo')).to.equal('foo')
expect(await scope.get('bar')).to.equal('bar')
scope.pop(scope1)
expect(await scope.get('foo')).to.equal('zoo')
expect(await scope.get('bar')).to.equal('bar')
ctx.push(scope1)
ctx.push(scope2)
expect(await ctx.get('foo')).to.equal('foo')
expect(await ctx.get('bar')).to.equal('bar')
ctx.pop(scope1)
expect(await ctx.get('foo')).to.equal('zoo')
expect(await ctx.get('bar')).to.equal('bar')
})
it('should throw when specified scope not found', function () {
const scope1 = {
foo: 'foo'
}
expect(() => scope.pop(scope1)).to.throw('scope not found, cannot pop')
expect(() => ctx.pop(scope1)).to.throw('scope not found, cannot pop')
})
})
+2 -2
View File
@@ -1,5 +1,5 @@
import { expect } from 'chai'
import Scope from '../../../src/scope/scope'
import Context from '../../../src/context/context'
import Token from '../../../src/parser/token'
import Tag from '../../../src/template/tag/tag'
import { Filter } from '../../../src/template/filter/filter'
@@ -20,7 +20,7 @@ describe('render', function () {
})
it('should render html', async function () {
const scope = new Scope()
const scope = new Context()
const token = { type: 'html', value: '<p>' } as Token
const html = await render.renderTemplates([new HTML(token)], scope)
return expect(html).to.equal('<p>')
+34 -34
View File
@@ -1,12 +1,12 @@
import Scope from '../../../src/scope/scope'
import Context from '../../../src/context/context'
import { expect } from 'chai'
import { evalExp, evalValue, isTruthy } from '../../../src/render/syntax'
describe('render/syntax', function () {
let scope: Scope
let ctx: Context
beforeEach(function () {
scope = new Scope({
ctx = new Context({
one: 1,
two: 2,
empty: '',
@@ -19,30 +19,30 @@ describe('render/syntax', function () {
describe('.evalValue()', function () {
it('should eval boolean literal', async function () {
expect(await evalValue('true', scope)).to.equal(true)
expect(await evalValue('TrUE', scope)).to.equal(undefined)
expect(await evalValue('false', scope)).to.equal(false)
expect(await evalValue('true', ctx)).to.equal(true)
expect(await evalValue('TrUE', ctx)).to.equal(undefined)
expect(await evalValue('false', ctx)).to.equal(false)
})
it('should eval number literal', async function () {
expect(await evalValue('2.3', scope)).to.equal(2.3)
expect(await evalValue('.32', scope)).to.equal(0.32)
expect(await evalValue('-23.', scope)).to.equal(-23)
expect(await evalValue('23', scope)).to.equal(23)
expect(await evalValue('2.3', ctx)).to.equal(2.3)
expect(await evalValue('.32', ctx)).to.equal(0.32)
expect(await evalValue('-23.', ctx)).to.equal(-23)
expect(await evalValue('23', ctx)).to.equal(23)
})
it('should eval string literal', async function () {
expect(await evalValue('"ab\'c"', scope)).to.equal("ab'c")
expect(await evalValue("'ab\"c'", scope)).to.equal('ab"c')
expect(await evalValue('"ab\'c"', ctx)).to.equal("ab'c")
expect(await evalValue("'ab\"c'", ctx)).to.equal('ab"c')
})
it('should eval nil literal', async function () {
expect(await evalValue('nil', scope)).to.be.null
expect(await evalValue('nil', ctx)).to.be.null
})
it('should eval null literal', async function () {
expect(await evalValue('null', scope)).to.be.null
expect(await evalValue('null', ctx)).to.be.null
})
it('should eval scope variables', async function () {
expect(await evalValue('one', scope)).to.equal(1)
expect(await evalValue('has_value?', scope)).to.equal(true)
expect(await evalValue('x', scope)).to.equal('XXX')
expect(await evalValue('one', ctx)).to.equal(1)
expect(await evalValue('has_value?', ctx)).to.equal(true)
expect(await evalValue('x', ctx)).to.equal('XXX')
})
})
@@ -66,37 +66,37 @@ describe('render/syntax', function () {
})
it('should eval simple expression', async function () {
expect(await evalExp('1<2', scope)).to.equal(true)
expect(await evalExp('2<=2', scope)).to.equal(true)
expect(await evalExp('one<=two', scope)).to.equal(true)
expect(await evalExp('x contains "x"', scope)).to.equal(false)
expect(await evalExp('x contains "X"', scope)).to.equal(true)
expect(await evalExp('1 contains "x"', scope)).to.equal(false)
expect(await evalExp('y contains "x"', scope)).to.equal(false)
expect(await evalExp('z contains "x"', scope)).to.equal(false)
expect(await evalExp('(1..5) contains 3', scope)).to.equal(true)
expect(await evalExp('(1..5) contains 6', scope)).to.equal(false)
expect(await evalExp('"<=" == "<="', scope)).to.equal(true)
expect(await evalExp('1<2', ctx)).to.equal(true)
expect(await evalExp('2<=2', ctx)).to.equal(true)
expect(await evalExp('one<=two', ctx)).to.equal(true)
expect(await evalExp('x contains "x"', ctx)).to.equal(false)
expect(await evalExp('x contains "X"', ctx)).to.equal(true)
expect(await evalExp('1 contains "x"', ctx)).to.equal(false)
expect(await evalExp('y contains "x"', ctx)).to.equal(false)
expect(await evalExp('z contains "x"', ctx)).to.equal(false)
expect(await evalExp('(1..5) contains 3', ctx)).to.equal(true)
expect(await evalExp('(1..5) contains 6', ctx)).to.equal(false)
expect(await evalExp('"<=" == "<="', ctx)).to.equal(true)
})
describe('complex expression', function () {
it('should support value or value', async function () {
expect(await evalExp('false or true', scope)).to.equal(true)
expect(await evalExp('false or true', ctx)).to.equal(true)
})
it('should support < and contains', async function () {
expect(await evalExp('1<2 and x contains "x"', scope)).to.equal(false)
expect(await evalExp('1<2 and x contains "x"', ctx)).to.equal(false)
})
it('should support < or contains', async function () {
expect(await evalExp('1<2 or x contains "x"', scope)).to.equal(true)
expect(await evalExp('1<2 or x contains "x"', ctx)).to.equal(true)
})
it('should support value and !=', async function () {
expect(await evalExp('empty and empty != ""', scope)).to.equal(false)
expect(await evalExp('empty and empty != ""', ctx)).to.equal(false)
})
})
it('should eval range expression', async function () {
expect(await evalExp('(2..4)', scope)).to.deep.equal([2, 3, 4])
expect(await evalExp('(two..4)', scope)).to.deep.equal([2, 3, 4])
expect(await evalExp('(2..4)', ctx)).to.deep.equal([2, 3, 4])
expect(await evalExp('(two..4)', ctx)).to.deep.equal([2, 3, 4])
})
})
})
+8 -8
View File
@@ -2,16 +2,16 @@ import * as chai from 'chai'
import * as sinon from 'sinon'
import * as sinonChai from 'sinon-chai'
import { Filter } from '../../../../src/template/filter/filter'
import Scope from '../../../../src/scope/scope'
import Context from '../../../../src/context/context'
chai.use(sinonChai)
const expect = chai.expect
describe('filter', function () {
let scope: Scope
let ctx: Context
beforeEach(function () {
Filter.clear()
scope = new Scope()
ctx = new Context()
})
it('should create default filter if not registered', async function () {
const result = new Filter('foo', [], false)
@@ -19,28 +19,28 @@ describe('filter', function () {
})
it('should render input if filter not registered', async function () {
expect(await new Filter('undefined', [], false).render('foo', scope)).to.equal('foo')
expect(await new Filter('undefined', [], false).render('foo', ctx)).to.equal('foo')
})
it('should call filter impl with corrct arguments', async function () {
const spy = sinon.spy()
Filter.register('foo', spy)
await new Filter('foo', ['33'], false).render('foo', scope)
await new Filter('foo', ['33'], false).render('foo', ctx)
expect(spy).to.have.been.calledWith('foo', 33)
})
it('should render a simple filter', async function () {
Filter.register('upcase', x => x.toUpperCase())
expect(await new Filter('upcase', [], false).render('foo', scope)).to.equal('FOO')
expect(await new Filter('upcase', [], false).render('foo', ctx)).to.equal('FOO')
})
it('should render filters with argument', async function () {
Filter.register('add', (a, b) => a + b)
expect(await new Filter('add', ['2'], false).render(3, scope)).to.equal(5)
expect(await new Filter('add', ['2'], false).render(3, ctx)).to.equal(5)
})
it('should render filters with multiple arguments', async function () {
Filter.register('add', (a, b, c) => a + b + c)
expect(await new Filter('add', ['2', '"c"'], false).render(3, scope)).to.equal('5c')
expect(await new Filter('add', ['2', '"c"'], false).render(3, ctx)).to.equal('5c')
})
it('should not throw when filter name illegal', function () {
+5 -5
View File
@@ -1,5 +1,5 @@
import * as chai from 'chai'
import Scope from '../../../src/scope/scope'
import Context from '../../../src/context/context'
import Output from '../../../src/template/output'
import OutputToken from '../../../src/parser/output-token'
import { Filter } from '../../../src/template/filter/filter'
@@ -12,7 +12,7 @@ describe('Output', function () {
})
it('should stringify objects', async function () {
const scope = new Scope({
const scope = new Context({
foo: { obj: { arr: ['a', 2] } }
})
const output = new Output({ value: 'foo' } as OutputToken, false)
@@ -20,19 +20,19 @@ describe('Output', function () {
return expect(html).to.equal('[object Object]')
})
it('should skip function property', async function () {
const scope = new Scope({ obj: { foo: 'foo', bar: (x: any) => x } })
const scope = new Context({ obj: { foo: 'foo', bar: (x: any) => x } })
const output = new Output({ value: 'obj' } as OutputToken, false)
const html = await output.render(scope)
return expect(html).to.equal('[object Object]')
})
it('should respect to .toString()', async () => {
const scope = new Scope({ obj: { toString: () => 'FOO' } })
const scope = new Context({ obj: { toString: () => 'FOO' } })
const output = new Output({ value: 'obj' } as OutputToken, false)
const str = await output.render(scope)
return expect(str).to.equal('FOO')
})
it('should respect to .toString()', async () => {
const scope = new Scope({ obj: { toString: () => 'FOO' } })
const scope = new Context({ obj: { toString: () => 'FOO' } })
const output = new Output({ value: 'obj' } as OutputToken, false)
const str = await output.render(scope)
return expect(str).to.equal('FOO')
+12 -12
View File
@@ -1,6 +1,6 @@
import * as chai from 'chai'
import Tag from '../../../src/template/tag/tag'
import Scope from '../../../src/scope/scope'
import Context from '../../../src/context/context'
import * as sinon from 'sinon'
import * as sinonChai from 'sinon-chai'
import Liquid from '../../../src/liquid'
@@ -11,9 +11,9 @@ const expect = chai.expect
const liquid = new Liquid()
describe('tag', function () {
let scope: Scope
let ctx: Context
before(function () {
scope = new Scope({
ctx = new Context({
foo: 'bar',
arr: [2, 1],
bar: {
@@ -51,7 +51,7 @@ describe('tag', function () {
value: 'foo',
name: 'foo'
} as TagToken
await new Tag(token, [], liquid).render(scope)
await new Tag(token, [], liquid).render(ctx)
expect(spy).to.have.been.called
})
@@ -70,30 +70,30 @@ describe('tag', function () {
} as TagToken
})
it('should call tag.render with scope', async function () {
await new Tag(token, [], liquid).render(scope)
expect(spy).to.have.been.calledWithMatch(scope)
await new Tag(token, [], liquid).render(ctx)
expect(spy).to.have.been.calledWithMatch(ctx)
})
it('should resolve identifier hash', async function () {
await new Tag(token, [], liquid).render(scope)
await new Tag(token, [], liquid).render(ctx)
expect(spy).to.have.been.calledWithMatch({}, {
aa: 'bar'
})
})
it('should accept space between key/value', async function () {
await new Tag(token, [], liquid).render(scope)
await new Tag(token, [], liquid).render(ctx)
expect(spy).to.have.been.calledWithMatch({}, {
bb: 2
})
})
it('should resolve number value hash', async function () {
await new Tag(token, [], liquid).render(scope)
expect(spy).to.have.been.calledWithMatch(scope, {
await new Tag(token, [], liquid).render(ctx)
expect(spy).to.have.been.calledWithMatch(ctx, {
cc: 2.3
})
})
it('should resolve property access hash', async function () {
await new Tag(token, [], liquid).render(scope)
expect(spy).to.have.been.calledWithMatch(scope, {
await new Tag(token, [], liquid).render(ctx)
expect(spy).to.have.been.calledWithMatch(ctx, {
dd: 'uoo'
})
})
+2 -2
View File
@@ -1,7 +1,7 @@
import * as chai from 'chai'
import * as sinonChai from 'sinon-chai'
import * as sinon from 'sinon'
import Scope from '../../../src/scope/scope'
import Context from '../../../src/context/context'
import { Filter } from '../../../src/template/filter/filter'
import Value from '../../../src/template/value'
@@ -105,7 +105,7 @@ describe('Value', function () {
Filter.register('date', date)
Filter.register('time', time)
const tpl = new Value('foo.bar | date: "b" | time:2', false)
const scope = new Scope({
const scope = new Context({
foo: { bar: 'bar' }
})
await tpl.value(scope)