mirror of
https://github.com/harttle/liquidjs.git
synced 2026-09-15 04:10:40 -07:00
refactor: switch Context <-> Scope concepts
This commit is contained in:
+3
-3
@@ -1,7 +1,7 @@
|
|||||||
import * as Benchmark from 'benchmark'
|
import * as Benchmark from 'benchmark'
|
||||||
import Liquid from '../src/liquid'
|
import Liquid from '../src/liquid'
|
||||||
import TagToken from '../src/parser/tag-token'
|
import TagToken from '../src/parser/tag-token'
|
||||||
import Scope from '../src/scope/scope'
|
import Context from '../src/context/context'
|
||||||
|
|
||||||
const engine = new Liquid({
|
const engine = new Liquid({
|
||||||
root: __dirname,
|
root: __dirname,
|
||||||
@@ -13,8 +13,8 @@ engine.registerTag('header', {
|
|||||||
const [key, val] = token.args.split(':')
|
const [key, val] = token.args.split(':')
|
||||||
this[key] = val
|
this[key] = val
|
||||||
},
|
},
|
||||||
render: function (scope: Scope) {
|
render: function (ctx: Context) {
|
||||||
const title = this.liquid.evalValue(this.content, scope)
|
const title = this.liquid.evalValue(this.content, ctx)
|
||||||
return `<h1>${title}</h1>`
|
return `<h1>${title}</h1>`
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import assert from '../../util/assert'
|
import assert from '../../util/assert'
|
||||||
import { identifier } from '../../parser/lexical'
|
import { identifier } from '../../parser/lexical'
|
||||||
import TagToken from '../../parser/tag-token'
|
import TagToken from '../../parser/tag-token'
|
||||||
import Scope from '../../scope/scope'
|
import Context from '../../context/context'
|
||||||
import ITagImplOptions from '../../template/tag/itag-impl-options'
|
import ITagImplOptions from '../../template/tag/itag-impl-options'
|
||||||
|
|
||||||
const re = new RegExp(`(${identifier.source})\\s*=([^]*)`)
|
const re = new RegExp(`(${identifier.source})\\s*=([^]*)`)
|
||||||
@@ -13,7 +13,7 @@ export default {
|
|||||||
this.key = match[1]
|
this.key = match[1]
|
||||||
this.value = match[2]
|
this.value = match[2]
|
||||||
},
|
},
|
||||||
render: async function (scope: Scope) {
|
render: async function (ctx: Context) {
|
||||||
scope.contexts[0][this.key] = await this.liquid.evalValue(this.value, scope)
|
ctx.scopes[0][this.key] = await this.liquid.evalValue(this.value, ctx)
|
||||||
}
|
}
|
||||||
} as ITagImplOptions
|
} 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 TagToken from '../../parser/tag-token'
|
||||||
import Token from '../../parser/token'
|
import Token from '../../parser/token'
|
||||||
import ITemplate from '../../template/itemplate'
|
import ITemplate from '../../template/itemplate'
|
||||||
import Scope from '../../scope/scope'
|
import Context from '../../context/context'
|
||||||
import ITagImplOptions from '../../template/tag/itag-impl-options'
|
import ITagImplOptions from '../../template/tag/itag-impl-options'
|
||||||
import ParseStream from '../../parser/parse-stream'
|
import ParseStream from '../../parser/parse-stream'
|
||||||
|
|
||||||
@@ -19,14 +19,14 @@ export default {
|
|||||||
})
|
})
|
||||||
stream.start()
|
stream.start()
|
||||||
},
|
},
|
||||||
render: async function (scope: Scope) {
|
render: async function (ctx: Context) {
|
||||||
const childDefined = scope.blocks[this.block]
|
const childDefined = ctx.blocks[this.block]
|
||||||
const html = childDefined !== undefined
|
const html = childDefined !== undefined
|
||||||
? childDefined
|
? childDefined
|
||||||
: await this.liquid.renderer.renderTemplates(this.tpls, scope)
|
: await this.liquid.renderer.renderTemplates(this.tpls, ctx)
|
||||||
|
|
||||||
if (scope.blockMode === BlockMode.STORE) {
|
if (ctx.blockMode === BlockMode.STORE) {
|
||||||
scope.blocks[this.block] = html
|
ctx.blocks[this.block] = html
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
return html
|
return html
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import assert from '../../util/assert'
|
|||||||
import { identifier } from '../../parser/lexical'
|
import { identifier } from '../../parser/lexical'
|
||||||
import TagToken from '../../parser/tag-token'
|
import TagToken from '../../parser/tag-token'
|
||||||
import Token from '../../parser/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 ITagImplOptions from '../../template/tag/itag-impl-options'
|
||||||
|
|
||||||
const re = new RegExp(`(${identifier.source})`)
|
const re = new RegExp(`(${identifier.source})`)
|
||||||
@@ -23,8 +23,8 @@ export default {
|
|||||||
})
|
})
|
||||||
stream.start()
|
stream.start()
|
||||||
},
|
},
|
||||||
render: async function (scope: Scope) {
|
render: async function (ctx: Context) {
|
||||||
const html = await this.liquid.renderer.renderTemplates(this.templates, scope)
|
const html = await this.liquid.renderer.renderTemplates(this.templates, ctx)
|
||||||
scope.contexts[0][this.variable] = html
|
ctx.scopes[0][this.variable] = html
|
||||||
}
|
}
|
||||||
} as ITagImplOptions
|
} as ITagImplOptions
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { evalExp } from '../../render/syntax'
|
import { evalExp } from '../../render/syntax'
|
||||||
import TagToken from '../../parser/tag-token'
|
import TagToken from '../../parser/tag-token'
|
||||||
import Token from '../../parser/token'
|
import Token from '../../parser/token'
|
||||||
import Scope from '../../scope/scope'
|
import Context from '../../context/context'
|
||||||
import ITemplate from '../../template/itemplate'
|
import ITemplate from '../../template/itemplate'
|
||||||
import ITagImplOptions from '../../template/tag/itag-impl-options'
|
import ITagImplOptions from '../../template/tag/itag-impl-options'
|
||||||
import ParseStream from '../../parser/parse-stream'
|
import ParseStream from '../../parser/parse-stream'
|
||||||
@@ -30,15 +30,15 @@ export default {
|
|||||||
stream.start()
|
stream.start()
|
||||||
},
|
},
|
||||||
|
|
||||||
render: async function (scope: Scope) {
|
render: async function (ctx: Context) {
|
||||||
for (let i = 0; i < this.cases.length; i++) {
|
for (let i = 0; i < this.cases.length; i++) {
|
||||||
const branch = this.cases[i]
|
const branch = this.cases[i]
|
||||||
const val = await evalExp(branch.val, scope)
|
const val = await evalExp(branch.val, ctx)
|
||||||
const cond = await evalExp(this.cond, scope)
|
const cond = await evalExp(this.cond, ctx)
|
||||||
if (val === cond) {
|
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
|
} as ITagImplOptions
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import assert from '../../util/assert'
|
|||||||
import { value as rValue } from '../../parser/lexical'
|
import { value as rValue } from '../../parser/lexical'
|
||||||
import { evalValue } from '../../render/syntax'
|
import { evalValue } from '../../render/syntax'
|
||||||
import TagToken from '../../parser/tag-token'
|
import TagToken from '../../parser/tag-token'
|
||||||
import Scope from '../../scope/scope'
|
import Context from '../../context/context'
|
||||||
import ITagImplOptions from '../../template/tag/itag-impl-options'
|
import ITagImplOptions from '../../template/tag/itag-impl-options'
|
||||||
|
|
||||||
const groupRE = new RegExp(`^(?:(${rValue.source})\\s*:\\s*)?(.*)$`)
|
const groupRE = new RegExp(`^(?:(${rValue.source})\\s*:\\s*)?(.*)$`)
|
||||||
@@ -24,10 +24,10 @@ export default <ITagImplOptions>{
|
|||||||
assert(this.candidates.length, `empty candidates: ${tagToken.raw}`)
|
assert(this.candidates.length, `empty candidates: ${tagToken.raw}`)
|
||||||
},
|
},
|
||||||
|
|
||||||
render: async function (scope: Scope) {
|
render: async function (ctx: Context) {
|
||||||
const group = await evalValue(this.group, scope)
|
const group = await evalValue(this.group, ctx)
|
||||||
const fingerprint = `cycle:${group}:` + this.candidates.join(',')
|
const fingerprint = `cycle:${group}:` + this.candidates.join(',')
|
||||||
const groups = scope.groups
|
const groups = ctx.groups
|
||||||
let idx = groups[fingerprint]
|
let idx = groups[fingerprint]
|
||||||
|
|
||||||
if (idx === undefined) {
|
if (idx === undefined) {
|
||||||
@@ -38,6 +38,6 @@ export default <ITagImplOptions>{
|
|||||||
idx = (idx + 1) % this.candidates.length
|
idx = (idx + 1) % this.candidates.length
|
||||||
groups[fingerprint] = idx
|
groups[fingerprint] = idx
|
||||||
|
|
||||||
return evalValue(candidate, scope)
|
return evalValue(candidate, ctx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import assert from '../../util/assert'
|
import assert from '../../util/assert'
|
||||||
import { identifier } from '../../parser/lexical'
|
import { identifier } from '../../parser/lexical'
|
||||||
import TagToken from '../../parser/tag-token'
|
import TagToken from '../../parser/tag-token'
|
||||||
import Scope from '../../scope/scope'
|
import Context from '../../context/context'
|
||||||
import ITagImplOptions from '../../template/tag/itag-impl-options'
|
import ITagImplOptions from '../../template/tag/itag-impl-options'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -10,7 +10,7 @@ export default {
|
|||||||
assert(match, `illegal identifier ${token.args}`)
|
assert(match, `illegal identifier ${token.args}`)
|
||||||
this.variable = match[0]
|
this.variable = match[0]
|
||||||
},
|
},
|
||||||
render: function (context: Scope) {
|
render: function (context: Context) {
|
||||||
const scope = context.environments
|
const scope = context.environments
|
||||||
if (typeof scope[this.variable] !== 'number') {
|
if (typeof scope[this.variable] !== 'number') {
|
||||||
scope[this.variable] = 0
|
scope[this.variable] = 0
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import assert from '../../util/assert'
|
|||||||
import { identifier, value, hash } from '../../parser/lexical'
|
import { identifier, value, hash } from '../../parser/lexical'
|
||||||
import TagToken from '../../parser/tag-token'
|
import TagToken from '../../parser/tag-token'
|
||||||
import Token from '../../parser/token'
|
import Token from '../../parser/token'
|
||||||
import Scope from '../../scope/scope'
|
import Context from '../../context/context'
|
||||||
import Hash from '../../template/tag/hash'
|
import Hash from '../../template/tag/hash'
|
||||||
import ITemplate from '../../template/itemplate'
|
import ITemplate from '../../template/itemplate'
|
||||||
import ITagImplOptions from '../../template/tag/itag-impl-options'
|
import ITagImplOptions from '../../template/tag/itag-impl-options'
|
||||||
@@ -41,8 +41,8 @@ export default <ITagImplOptions>{
|
|||||||
|
|
||||||
stream.start()
|
stream.start()
|
||||||
},
|
},
|
||||||
render: async function (scope: Scope, hash: Hash) {
|
render: async function (ctx: Context, hash: Hash) {
|
||||||
let collection = await evalExp(this.collection, scope)
|
let collection = await evalExp(this.collection, ctx)
|
||||||
|
|
||||||
if (!isArray(collection)) {
|
if (!isArray(collection)) {
|
||||||
if (isString(collection) && collection.length > 0) {
|
if (isString(collection) && collection.length > 0) {
|
||||||
@@ -52,7 +52,7 @@ export default <ITagImplOptions>{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!isArray(collection) || !collection.length) {
|
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
|
const offset = hash.offset || 0
|
||||||
@@ -62,12 +62,12 @@ export default <ITagImplOptions>{
|
|||||||
if (this.reversed) collection.reverse()
|
if (this.reversed) collection.reverse()
|
||||||
|
|
||||||
const context = { forloop: new ForloopDrop(collection.length) }
|
const context = { forloop: new ForloopDrop(collection.length) }
|
||||||
scope.push(context)
|
ctx.push(context)
|
||||||
let html = ''
|
let html = ''
|
||||||
for (const item of collection) {
|
for (const item of collection) {
|
||||||
context[this.variable] = item
|
context[this.variable] = item
|
||||||
try {
|
try {
|
||||||
html += await this.liquid.renderer.renderTemplates(this.templates, scope)
|
html += await this.liquid.renderer.renderTemplates(this.templates, ctx)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e.name === 'RenderBreakError') {
|
if (e.name === 'RenderBreakError') {
|
||||||
html += e.resolvedHTML
|
html += e.resolvedHTML
|
||||||
@@ -76,7 +76,7 @@ export default <ITagImplOptions>{
|
|||||||
}
|
}
|
||||||
context.forloop.next()
|
context.forloop.next()
|
||||||
}
|
}
|
||||||
scope.pop()
|
ctx.pop()
|
||||||
return html
|
return html
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { evalExp, isTruthy } from '../../render/syntax'
|
import { evalExp, isTruthy } from '../../render/syntax'
|
||||||
import TagToken from '../../parser/tag-token'
|
import TagToken from '../../parser/tag-token'
|
||||||
import Token from '../../parser/token'
|
import Token from '../../parser/token'
|
||||||
import Scope from '../../scope/scope'
|
import Context from '../../context/context'
|
||||||
import ITemplate from '../../template/itemplate'
|
import ITemplate from '../../template/itemplate'
|
||||||
import ITagImplOptions from '../../template/tag/itag-impl-options'
|
import ITagImplOptions from '../../template/tag/itag-impl-options'
|
||||||
import ParseStream from '../../parser/parse-stream'
|
import ParseStream from '../../parser/parse-stream'
|
||||||
@@ -33,13 +33,13 @@ export default {
|
|||||||
stream.start()
|
stream.start()
|
||||||
},
|
},
|
||||||
|
|
||||||
render: async function (scope: Scope) {
|
render: async function (ctx: Context) {
|
||||||
for (const branch of this.branches) {
|
for (const branch of this.branches) {
|
||||||
const cond = await evalExp(branch.cond, scope)
|
const cond = await evalExp(branch.cond, ctx)
|
||||||
if (isTruthy(cond)) {
|
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
|
} as ITagImplOptions
|
||||||
|
|||||||
+17
-17
@@ -1,9 +1,9 @@
|
|||||||
import assert from '../../util/assert'
|
import assert from '../../util/assert'
|
||||||
import { value, quotedLine } from '../../parser/lexical'
|
import { value, quotedLine } from '../../parser/lexical'
|
||||||
import { evalValue } from '../../render/syntax'
|
import { evalValue } from '../../render/syntax'
|
||||||
import BlockMode from '../../scope/block-mode'
|
import BlockMode from '../../context/block-mode'
|
||||||
import TagToken from '../../parser/tag-token'
|
import TagToken from '../../parser/tag-token'
|
||||||
import Scope from '../../scope/scope'
|
import Context from '../../context/context'
|
||||||
import Hash from '../../template/tag/hash'
|
import Hash from '../../template/tag/hash'
|
||||||
import ITagImplOptions from '../../template/tag/itag-impl-options'
|
import ITagImplOptions from '../../template/tag/itag-impl-options'
|
||||||
|
|
||||||
@@ -27,34 +27,34 @@ export default <ITagImplOptions>{
|
|||||||
this.with = match[1]
|
this.with = match[1]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
render: async function (scope: Scope, hash: Hash) {
|
render: async function (ctx: Context, hash: Hash) {
|
||||||
let filepath
|
let filepath
|
||||||
if (scope.opts.dynamicPartials) {
|
if (ctx.opts.dynamicPartials) {
|
||||||
if (quotedLine.exec(this.value)) {
|
if (quotedLine.exec(this.value)) {
|
||||||
const template = this.value.slice(1, -1)
|
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 {
|
} else {
|
||||||
filepath = await evalValue(this.value, scope)
|
filepath = await evalValue(this.value, ctx)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
filepath = this.staticValue
|
filepath = this.staticValue
|
||||||
}
|
}
|
||||||
assert(filepath, `cannot include with empty filename`)
|
assert(filepath, `cannot include with empty filename`)
|
||||||
|
|
||||||
const originBlocks = scope.blocks
|
const originBlocks = ctx.blocks
|
||||||
const originBlockMode = scope.blockMode
|
const originBlockMode = ctx.blockMode
|
||||||
|
|
||||||
scope.blocks = {}
|
ctx.blocks = {}
|
||||||
scope.blockMode = BlockMode.OUTPUT
|
ctx.blockMode = BlockMode.OUTPUT
|
||||||
if (this.with) {
|
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)
|
const templates = await this.liquid.getTemplate(filepath, ctx.opts)
|
||||||
scope.push(hash)
|
ctx.push(hash)
|
||||||
const html = await this.liquid.renderer.renderTemplates(templates, scope)
|
const html = await this.liquid.renderer.renderTemplates(templates, ctx)
|
||||||
scope.pop(hash)
|
ctx.pop(hash)
|
||||||
scope.blocks = originBlocks
|
ctx.blocks = originBlocks
|
||||||
scope.blockMode = originBlockMode
|
ctx.blockMode = originBlockMode
|
||||||
return html
|
return html
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-14
@@ -1,10 +1,10 @@
|
|||||||
import assert from '../../util/assert'
|
import assert from '../../util/assert'
|
||||||
import { value as rValue } from '../../parser/lexical'
|
import { value as rValue } from '../../parser/lexical'
|
||||||
import { evalValue } from '../../render/syntax'
|
import { evalValue } from '../../render/syntax'
|
||||||
import BlockMode from '../../scope/block-mode'
|
import BlockMode from '../../context/block-mode'
|
||||||
import TagToken from '../../parser/tag-token'
|
import TagToken from '../../parser/tag-token'
|
||||||
import Token from '../../parser/token'
|
import Token from '../../parser/token'
|
||||||
import Scope from '../../scope/scope'
|
import Context from '../../context/context'
|
||||||
import Hash from '../../template/tag/hash'
|
import Hash from '../../template/tag/hash'
|
||||||
import ITagImplOptions from '../../template/tag/itag-impl-options'
|
import ITagImplOptions from '../../template/tag/itag-impl-options'
|
||||||
|
|
||||||
@@ -24,23 +24,23 @@ export default {
|
|||||||
|
|
||||||
this.tpls = this.liquid.parser.parse(remainTokens)
|
this.tpls = this.liquid.parser.parse(remainTokens)
|
||||||
},
|
},
|
||||||
render: async function (scope: Scope, hash: Hash) {
|
render: async function (ctx: Context, hash: Hash) {
|
||||||
const layout = scope.opts.dynamicPartials
|
const layout = ctx.opts.dynamicPartials
|
||||||
? await evalValue(this.layout, scope)
|
? await evalValue(this.layout, ctx)
|
||||||
: this.staticLayout
|
: this.staticLayout
|
||||||
assert(layout, `cannot apply layout with empty filename`)
|
assert(layout, `cannot apply layout with empty filename`)
|
||||||
|
|
||||||
// render the remaining tokens immediately
|
// render the remaining tokens immediately
|
||||||
scope.blockMode = BlockMode.STORE
|
ctx.blockMode = BlockMode.STORE
|
||||||
const html = await this.liquid.renderer.renderTemplates(this.tpls, scope)
|
const html = await this.liquid.renderer.renderTemplates(this.tpls, ctx)
|
||||||
if (scope.blocks[''] === undefined) {
|
if (ctx.blocks[''] === undefined) {
|
||||||
scope.blocks[''] = html
|
ctx.blocks[''] = html
|
||||||
}
|
}
|
||||||
const templates = await this.liquid.getTemplate(layout, scope.opts)
|
const templates = await this.liquid.getTemplate(layout, ctx.opts)
|
||||||
scope.push(hash)
|
ctx.push(hash)
|
||||||
scope.blockMode = BlockMode.OUTPUT
|
ctx.blockMode = BlockMode.OUTPUT
|
||||||
const partial = await this.liquid.renderer.renderTemplates(templates, scope)
|
const partial = await this.liquid.renderer.renderTemplates(templates, ctx)
|
||||||
scope.pop(hash)
|
ctx.pop(hash)
|
||||||
return partial
|
return partial
|
||||||
}
|
}
|
||||||
} as ITagImplOptions
|
} as ITagImplOptions
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { identifier, value, hash } from '../../parser/lexical'
|
|||||||
import TagToken from '../../parser/tag-token'
|
import TagToken from '../../parser/tag-token'
|
||||||
import Token from '../../parser/token'
|
import Token from '../../parser/token'
|
||||||
import ITemplate from '../../template/itemplate'
|
import ITemplate from '../../template/itemplate'
|
||||||
import Scope from '../../scope/scope'
|
import Context from '../../context/context'
|
||||||
import Hash from '../../template/tag/hash'
|
import Hash from '../../template/tag/hash'
|
||||||
import ITagImplOptions from '../../template/tag/itag-impl-options'
|
import ITagImplOptions from '../../template/tag/itag-impl-options'
|
||||||
import ParseStream from '../../parser/parse-stream'
|
import ParseStream from '../../parser/parse-stream'
|
||||||
@@ -35,8 +35,8 @@ export default {
|
|||||||
stream.start()
|
stream.start()
|
||||||
},
|
},
|
||||||
|
|
||||||
render: async function (scope: Scope, hash: Hash) {
|
render: async function (ctx: Context, hash: Hash) {
|
||||||
let collection = await evalExp(this.collection, scope) || []
|
let collection = await evalExp(this.collection, ctx) || []
|
||||||
const offset = hash.offset || 0
|
const offset = hash.offset || 0
|
||||||
const limit = (hash.limit === undefined) ? collection.length : hash.limit
|
const limit = (hash.limit === undefined) ? collection.length : hash.limit
|
||||||
|
|
||||||
@@ -44,22 +44,22 @@ export default {
|
|||||||
const cols = hash.cols || collection.length
|
const cols = hash.cols || collection.length
|
||||||
|
|
||||||
const tablerowloop = new TablerowloopDrop(collection.length, cols)
|
const tablerowloop = new TablerowloopDrop(collection.length, cols)
|
||||||
const ctx = { tablerowloop }
|
const scope = { tablerowloop }
|
||||||
scope.push(ctx)
|
ctx.push(scope)
|
||||||
|
|
||||||
let html = ''
|
let html = ''
|
||||||
for (let idx = 0; idx < collection.length; idx++, tablerowloop.next()) {
|
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.col0() === 0) {
|
||||||
if (tablerowloop.row() !== 1) html += '</tr>'
|
if (tablerowloop.row() !== 1) html += '</tr>'
|
||||||
html += `<tr class="row${tablerowloop.row()}">`
|
html += `<tr class="row${tablerowloop.row()}">`
|
||||||
}
|
}
|
||||||
html += `<td class="col${tablerowloop.col()}">`
|
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>'
|
html += '</td>'
|
||||||
}
|
}
|
||||||
if (collection.length) html += '</tr>'
|
if (collection.length) html += '</tr>'
|
||||||
scope.pop(ctx)
|
ctx.pop(scope)
|
||||||
return html
|
return html
|
||||||
}
|
}
|
||||||
} as ITagImplOptions
|
} as ITagImplOptions
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { evalExp, isFalsy } from '../../render/syntax'
|
import { evalExp, isFalsy } from '../../render/syntax'
|
||||||
import TagToken from '../../parser/tag-token'
|
import TagToken from '../../parser/tag-token'
|
||||||
import Token from '../../parser/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 ITagImplOptions from '../../template/tag/itag-impl-options'
|
||||||
import ParseStream from '../../parser/parse-stream'
|
import ParseStream from '../../parser/parse-stream'
|
||||||
|
|
||||||
@@ -25,10 +25,10 @@ export default {
|
|||||||
stream.start()
|
stream.start()
|
||||||
},
|
},
|
||||||
|
|
||||||
render: async function (scope: Scope) {
|
render: async function (ctx: Context) {
|
||||||
const cond = await evalExp(this.cond, scope)
|
const cond = await evalExp(this.cond, ctx)
|
||||||
return isFalsy(cond)
|
return isFalsy(cond)
|
||||||
? this.liquid.renderer.renderTemplates(this.templates, scope)
|
? this.liquid.renderer.renderTemplates(this.templates, ctx)
|
||||||
: this.liquid.renderer.renderTemplates(this.elseTemplates, scope)
|
: this.liquid.renderer.renderTemplates(this.elseTemplates, ctx)
|
||||||
}
|
}
|
||||||
} as ITagImplOptions
|
} as ITagImplOptions
|
||||||
|
|||||||
@@ -4,12 +4,12 @@ import { __assign } from 'tslib'
|
|||||||
import assert from '../util/assert'
|
import assert from '../util/assert'
|
||||||
import { NormalizedFullOptions, applyDefault } from '../liquid-options'
|
import { NormalizedFullOptions, applyDefault } from '../liquid-options'
|
||||||
import BlockMode from './block-mode'
|
import BlockMode from './block-mode'
|
||||||
import { Context } from './context'
|
import { Scope } from './scope'
|
||||||
|
|
||||||
export default class Scope {
|
export default class Context {
|
||||||
opts: NormalizedFullOptions
|
opts: NormalizedFullOptions
|
||||||
contexts: Array<Context> = [{}]
|
scopes: Array<Scope> = [{}]
|
||||||
environments: Context
|
environments: Scope
|
||||||
blocks: object = {}
|
blocks: object = {}
|
||||||
groups: {[key: string]: number} = {}
|
groups: {[key: string]: number} = {}
|
||||||
blockMode: BlockMode = BlockMode.OUTPUT
|
blockMode: BlockMode = BlockMode.OUTPUT
|
||||||
@@ -18,7 +18,7 @@ export default class Scope {
|
|||||||
this.environments = ctx
|
this.environments = ctx
|
||||||
}
|
}
|
||||||
getAll () {
|
getAll () {
|
||||||
return [this.environments, ...this.contexts]
|
return [this.environments, ...this.scopes]
|
||||||
.reduce((ctx, val) => __assign(ctx, val), {})
|
.reduce((ctx, val) => __assign(ctx, val), {})
|
||||||
}
|
}
|
||||||
async get (path: string) {
|
async get (path: string) {
|
||||||
@@ -33,28 +33,28 @@ export default class Scope {
|
|||||||
return ctx
|
return ctx
|
||||||
}
|
}
|
||||||
push (ctx: object) {
|
push (ctx: object) {
|
||||||
return this.contexts.push(ctx)
|
return this.scopes.push(ctx)
|
||||||
}
|
}
|
||||||
pop (ctx?: object): object | undefined {
|
pop (ctx?: object): object | undefined {
|
||||||
if (!arguments.length) {
|
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) {
|
if (i === -1) {
|
||||||
throw new TypeError('scope not found, cannot pop')
|
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) {
|
findContextFor (key: string) {
|
||||||
for (let i = this.contexts.length - 1; i >= 0; i--) {
|
for (let i = this.scopes.length - 1; i >= 0; i--) {
|
||||||
const candidate = this.contexts[i]
|
const candidate = this.scopes[i]
|
||||||
if (key in candidate) {
|
if (key in candidate) {
|
||||||
return candidate
|
return candidate
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
private readProperty (obj: Context, key: string) {
|
private readProperty (obj: Scope, key: string) {
|
||||||
if (_.isNil(obj)) return obj
|
if (_.isNil(obj)) return obj
|
||||||
obj = _.toLiquid(obj)
|
obj = _.toLiquid(obj)
|
||||||
if (obj instanceof Drop) {
|
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 (!_.isNil(obj['size'])) return obj['size']
|
||||||
if (_.isArray(obj) || _.isString(obj)) return obj.length
|
if (_.isArray(obj) || _.isString(obj)) return obj.length
|
||||||
return obj['size']
|
return obj['size']
|
||||||
@@ -5,4 +5,4 @@ type PlainObject = {
|
|||||||
toLiquid?: () => any
|
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 * as Types from './types'
|
||||||
import fs from './fs/node'
|
import fs from './fs/node'
|
||||||
import * as _ from './util/underscore'
|
import * as _ from './util/underscore'
|
||||||
@@ -38,7 +38,7 @@ export default class Liquid {
|
|||||||
}
|
}
|
||||||
render (tpl: Array<ITemplate>, ctx?: object, opts?: LiquidOptions) {
|
render (tpl: Array<ITemplate>, ctx?: object, opts?: LiquidOptions) {
|
||||||
const options = { ...this.options, ...normalize(opts) }
|
const options = { ...this.options, ...normalize(opts) }
|
||||||
const scope = new Scope(ctx, options)
|
const scope = new Context(ctx, options)
|
||||||
return this.renderer.renderTemplates(tpl, scope)
|
return this.renderer.renderTemplates(tpl, scope)
|
||||||
}
|
}
|
||||||
async parseAndRender (html: string, ctx?: object, opts?: LiquidOptions) {
|
async parseAndRender (html: string, ctx?: object, opts?: LiquidOptions) {
|
||||||
@@ -69,8 +69,8 @@ export default class Liquid {
|
|||||||
const templates = await this.getTemplate(file, options)
|
const templates = await this.getTemplate(file, options)
|
||||||
return this.render(templates, ctx, opts)
|
return this.render(templates, ctx, opts)
|
||||||
}
|
}
|
||||||
evalValue (str: string, scope: Scope) {
|
evalValue (str: string, ctx: Context) {
|
||||||
return new Value(str, this.options.strictFilters).value(scope)
|
return new Value(str, this.options.strictFilters).value(ctx)
|
||||||
}
|
}
|
||||||
registerFilter (name: string, filter: FilterImpl) {
|
registerFilter (name: string, filter: FilterImpl) {
|
||||||
return Filter.register(name, filter)
|
return Filter.register(name, filter)
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
import { RenderError } from '../util/error'
|
import { RenderError } from '../util/error'
|
||||||
import assert from '../util/assert'
|
import assert from '../util/assert'
|
||||||
import Scope from '../scope/scope'
|
import Context from '../context/context'
|
||||||
import ITemplate from '../template/itemplate'
|
import ITemplate from '../template/itemplate'
|
||||||
|
|
||||||
export default class Render {
|
export default class Render {
|
||||||
async renderTemplates (templates: ITemplate[], scope: Scope) {
|
async renderTemplates (templates: ITemplate[], ctx: Context) {
|
||||||
assert(scope, 'unable to evalTemplates: scope undefined')
|
assert(ctx, 'unable to evalTemplates: context undefined')
|
||||||
|
|
||||||
let html = ''
|
let html = ''
|
||||||
for (const tpl of templates) {
|
for (const tpl of templates) {
|
||||||
try {
|
try {
|
||||||
html += await tpl.render(scope)
|
html += await tpl.render(ctx)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e.name === 'RenderBreakError') {
|
if (e.name === 'RenderBreakError') {
|
||||||
e.resolvedHTML = html
|
e.resolvedHTML = html
|
||||||
|
|||||||
+14
-14
@@ -1,6 +1,6 @@
|
|||||||
import * as lexical from '../parser/lexical'
|
import * as lexical from '../parser/lexical'
|
||||||
import assert from '../util/assert'
|
import assert from '../util/assert'
|
||||||
import Scope from '../scope/scope'
|
import Context from '../context/context'
|
||||||
import { range, last } from '../util/underscore'
|
import { range, last } from '../util/underscore'
|
||||||
import { isComparable } from '../drop/icomparable'
|
import { isComparable } from '../drop/icomparable'
|
||||||
import { NullDrop } from '../drop/null-drop'
|
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)
|
'or': (l: any, r: any) => isTruthy(l) || isTruthy(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function parseExp (exp: string, scope: Scope): Promise<any> {
|
export async function parseExp (exp: string, ctx: Context): Promise<any> {
|
||||||
assert(scope, 'unable to parseExp: scope undefined')
|
assert(ctx, 'unable to parseExp: scope undefined')
|
||||||
const operatorREs = lexical.operators
|
const operatorREs = lexical.operators
|
||||||
let match
|
let match
|
||||||
for (let i = 0; i < operatorREs.length; i++) {
|
for (let i = 0; i < operatorREs.length; i++) {
|
||||||
const operatorRE = operatorREs[i]
|
const operatorRE = operatorREs[i]
|
||||||
const expRE = new RegExp(`^(${lexical.quoteBalanced.source})(${operatorRE.source})(${lexical.quoteBalanced.source})$`)
|
const expRE = new RegExp(`^(${lexical.quoteBalanced.source})(${operatorRE.source})(${lexical.quoteBalanced.source})$`)
|
||||||
if ((match = exp.match(expRE))) {
|
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 op = binaryOperators[match[2].trim()]
|
||||||
const r = await parseExp(match[3], scope)
|
const r = await parseExp(match[3], ctx)
|
||||||
return op(l, r)
|
return op(l, r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((match = exp.match(lexical.rangeLine))) {
|
if ((match = exp.match(lexical.rangeLine))) {
|
||||||
const low = await evalValue(match[1], scope)
|
const low = await evalValue(match[1], ctx)
|
||||||
const high = await evalValue(match[2], scope)
|
const high = await evalValue(match[2], ctx)
|
||||||
return range(+low, +high + 1)
|
return range(+low, +high + 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
return parseValue(exp, scope)
|
return parseValue(exp, ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function evalExp (str: string, scope: Scope): Promise<any> {
|
export async function evalExp (str: string, ctx: Context): Promise<any> {
|
||||||
const value = await parseExp(str, scope)
|
const value = await parseExp(str, ctx)
|
||||||
return value instanceof Drop ? value.valueOf() : value
|
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
|
if (!str) return null
|
||||||
str = str.trim()
|
str = str.trim()
|
||||||
|
|
||||||
@@ -88,11 +88,11 @@ async function parseValue (str: string | undefined, scope: Scope): Promise<any>
|
|||||||
if (str === 'blank') return new BlankDrop()
|
if (str === 'blank') return new BlankDrop()
|
||||||
if (!isNaN(Number(str))) return Number(str)
|
if (!isNaN(Number(str))) return Number(str)
|
||||||
if ((str[0] === '"' || str[0] === "'") && str[0] === last(str)) return str.slice(1, -1)
|
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) {
|
export async function evalValue (str: string | undefined, ctx: Context) {
|
||||||
const value = await parseValue(str, scope)
|
const value = await parseValue(str, ctx)
|
||||||
return value instanceof Drop ? value.valueOf() : value
|
return value instanceof Drop ? value.valueOf() : value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { evalValue } from '../../render/syntax'
|
import { evalValue } from '../../render/syntax'
|
||||||
import Scope from '../../scope/scope'
|
import Context from '../../context/context'
|
||||||
import { isArray } from '../../util/underscore'
|
import { isArray } from '../../util/underscore'
|
||||||
import { FilterImpl } from './filter-impl'
|
import { FilterImpl } from './filter-impl'
|
||||||
|
|
||||||
@@ -19,11 +19,11 @@ export class Filter {
|
|||||||
this.impl = impl || (x => x)
|
this.impl = impl || (x => x)
|
||||||
this.args = args
|
this.args = args
|
||||||
}
|
}
|
||||||
async render (value: any, scope: Scope) {
|
async render (value: any, ctx: Context) {
|
||||||
const argv: any[] = []
|
const argv: any[] = []
|
||||||
for (const arg of this.args) {
|
for (const arg of this.args) {
|
||||||
if (isArray(arg)) argv.push([arg[0], await evalValue(arg[1], scope)])
|
if (isArray(arg)) argv.push([arg[0], await evalValue(arg[1], ctx)])
|
||||||
else argv.push(await evalValue(arg, scope))
|
else argv.push(await evalValue(arg, ctx))
|
||||||
}
|
}
|
||||||
return this.impl.apply(null, [value, ...argv])
|
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'
|
import Token from '../parser/token'
|
||||||
|
|
||||||
export default interface ITemplate {
|
export default interface ITemplate {
|
||||||
token: Token;
|
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 { stringify } from '../util/underscore'
|
||||||
import Template from '../template/template'
|
import Template from '../template/template'
|
||||||
import ITemplate from '../template/itemplate'
|
import ITemplate from '../template/itemplate'
|
||||||
import Scope from '../scope/scope'
|
import Context from '../context/context'
|
||||||
import OutputToken from '../parser/output-token'
|
import OutputToken from '../parser/output-token'
|
||||||
|
|
||||||
export default class Output extends Template<OutputToken> implements ITemplate {
|
export default class Output extends Template<OutputToken> implements ITemplate {
|
||||||
@@ -11,8 +11,8 @@ export default class Output extends Template<OutputToken> implements ITemplate {
|
|||||||
super(token)
|
super(token)
|
||||||
this.value = new Value(token.value, strictFilters)
|
this.value = new Value(token.value, strictFilters)
|
||||||
}
|
}
|
||||||
async render (scope: Scope): Promise<string> {
|
async render (ctx: Context): Promise<string> {
|
||||||
const html = await this.value.value(scope)
|
const html = await this.value.value(ctx)
|
||||||
return stringify(html)
|
return stringify(html)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { hashCapture } from '../../parser/lexical'
|
import { hashCapture } from '../../parser/lexical'
|
||||||
import { evalValue } from '../../render/syntax'
|
import { evalValue } from '../../render/syntax'
|
||||||
import Scope from '../../scope/scope'
|
import Context from '../../context/context'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Key-Value Pairs Representing Tag Arguments
|
* Key-Value Pairs Representing Tag Arguments
|
||||||
@@ -10,14 +10,14 @@ import Scope from '../../scope/scope'
|
|||||||
*/
|
*/
|
||||||
export default class Hash {
|
export default class Hash {
|
||||||
[key: string]: any
|
[key: string]: any
|
||||||
static async create (markup: string, scope: Scope) {
|
static async create (markup: string, ctx: Context) {
|
||||||
const instance = new Hash()
|
const instance = new Hash()
|
||||||
let match
|
let match
|
||||||
hashCapture.lastIndex = 0
|
hashCapture.lastIndex = 0
|
||||||
while ((match = hashCapture.exec(markup))) {
|
while ((match = hashCapture.exec(markup))) {
|
||||||
const k = match[1]
|
const k = match[1]
|
||||||
const v = match[2]
|
const v = match[2]
|
||||||
instance[k] = await evalValue(v, scope)
|
instance[k] = await evalValue(v, ctx)
|
||||||
}
|
}
|
||||||
return instance
|
return instance
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import Scope from '../../scope/scope'
|
import Context from '../../context/context'
|
||||||
import TagToken from '../../parser/tag-token'
|
import TagToken from '../../parser/tag-token'
|
||||||
import Token from '../../parser/token'
|
import Token from '../../parser/token'
|
||||||
import Hash from '../../template/tag/hash'
|
import Hash from '../../template/tag/hash'
|
||||||
@@ -6,5 +6,5 @@ import ITagImpl from './itag-impl'
|
|||||||
|
|
||||||
export default interface ITagImplOptions {
|
export default interface ITagImplOptions {
|
||||||
parse?: (this: ITagImpl, token: TagToken, remainingTokens: Array<Token>) => void
|
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 { create, stringify } from '../../util/underscore'
|
||||||
import assert from '../../util/assert'
|
import assert from '../../util/assert'
|
||||||
import Scope from '../../scope/scope'
|
import Context from '../../context/context'
|
||||||
import ITagImpl from './itag-impl'
|
import ITagImpl from './itag-impl'
|
||||||
import ITagImplOptions from './itag-impl-options'
|
import ITagImplOptions from './itag-impl-options'
|
||||||
import Liquid from '../../liquid'
|
import Liquid from '../../liquid'
|
||||||
@@ -27,13 +27,13 @@ export default class Tag extends Template<TagToken> implements ITemplate {
|
|||||||
this.impl.parse(token, tokens)
|
this.impl.parse(token, tokens)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async render (scope: Scope) {
|
async render (ctx: Context) {
|
||||||
const hash = await Hash.create(this.token.args, scope)
|
const hash = await Hash.create(this.token.args, ctx)
|
||||||
const impl = this.impl
|
const impl = this.impl
|
||||||
if (typeof impl.render !== 'function') {
|
if (typeof impl.render !== 'function') {
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
const html = await impl.render(scope, hash)
|
const html = await impl.render(ctx, hash)
|
||||||
return stringify(html)
|
return stringify(html)
|
||||||
}
|
}
|
||||||
static register (name: string, tag: ITagImplOptions) {
|
static register (name: string, tag: ITagImplOptions) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { evalExp } from '../render/syntax'
|
import { evalExp } from '../render/syntax'
|
||||||
import { FilterArgs, Filter } from './filter/filter'
|
import { FilterArgs, Filter } from './filter/filter'
|
||||||
import Scope from '../scope/scope'
|
import Context from '../context/context'
|
||||||
|
|
||||||
export default class Value {
|
export default class Value {
|
||||||
private strictFilters: boolean
|
private strictFilters: boolean
|
||||||
@@ -47,10 +47,10 @@ export default class Value {
|
|||||||
}
|
}
|
||||||
this.filters.push(new Filter(name, args, this.strictFilters))
|
this.filters.push(new Filter(name, args, this.strictFilters))
|
||||||
}
|
}
|
||||||
async value (scope: Scope) {
|
async value (ctx: Context) {
|
||||||
let val = await evalExp(this.initial, scope)
|
let val = await evalExp(this.initial, ctx)
|
||||||
for (const filter of this.filters) {
|
for (const filter of this.filters) {
|
||||||
val = await filter.render(val, scope)
|
val = await filter.render(val, ctx)
|
||||||
}
|
}
|
||||||
return val
|
return val
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import Liquid from '../../../../src/liquid'
|
import Liquid from '../../../../src/liquid'
|
||||||
import { expect, use } from 'chai'
|
import { expect, use } from 'chai'
|
||||||
import * as chaiAsPromised from 'chai-as-promised'
|
import * as chaiAsPromised from 'chai-as-promised'
|
||||||
import { Context } from '../../../../src/scope/context'
|
import { Scope } from '../../../../src/context/scope'
|
||||||
|
|
||||||
use(chaiAsPromised)
|
use(chaiAsPromised)
|
||||||
|
|
||||||
describe('tags/for', function () {
|
describe('tags/for', function () {
|
||||||
let liquid: Liquid, ctx: Context
|
let liquid: Liquid, ctx: Scope
|
||||||
before(function () {
|
before(function () {
|
||||||
liquid = new Liquid()
|
liquid = new Liquid()
|
||||||
liquid.registerTag('throwingTag', {
|
liquid.registerTag('throwingTag', {
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import * as chai from 'chai'
|
import * as chai from 'chai'
|
||||||
import Scope from '../../../src/scope/scope'
|
import Context from '../../../src/context/context'
|
||||||
import { Context } from '../../../src/scope/context'
|
import { Scope } from '../../../src/context/scope'
|
||||||
|
|
||||||
const expect = chai.expect
|
const expect = chai.expect
|
||||||
|
|
||||||
describe('scope', function () {
|
describe('scope', function () {
|
||||||
let scope: Scope, ctx: Context
|
let ctx: Context, scope: Scope
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
ctx = {
|
scope = {
|
||||||
foo: 'zoo',
|
foo: 'zoo',
|
||||||
one: 1,
|
one: 1,
|
||||||
zoo: { size: 4 },
|
zoo: { size: 4 },
|
||||||
@@ -17,76 +17,76 @@ describe('scope', function () {
|
|||||||
arr: ['a', 'b']
|
arr: ['a', 'b']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
scope = new Scope(ctx)
|
ctx = new Context(scope)
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('#propertyAccessSeq()', function () {
|
describe('#propertyAccessSeq()', function () {
|
||||||
it('should handle dot syntax', async 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'])
|
.to.deep.equal(['foo', 'bar'])
|
||||||
})
|
})
|
||||||
it('should handle [<String>] syntax', async function () {
|
it('should handle [<String>] syntax', async function () {
|
||||||
expect(await scope.propertyAccessSeq('foo["bar"]'))
|
expect(await ctx.propertyAccessSeq('foo["bar"]'))
|
||||||
.to.deep.equal(['foo', 'bar'])
|
.to.deep.equal(['foo', 'bar'])
|
||||||
})
|
})
|
||||||
it('should handle [<Identifier>] syntax', async function () {
|
it('should handle [<Identifier>] syntax', async function () {
|
||||||
expect(await scope.propertyAccessSeq('foo[foo]'))
|
expect(await ctx.propertyAccessSeq('foo[foo]'))
|
||||||
.to.deep.equal(['foo', 'zoo'])
|
.to.deep.equal(['foo', 'zoo'])
|
||||||
})
|
})
|
||||||
it('should handle nested access 1', async function () {
|
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'])
|
.to.deep.equal(['foo', 'coo'])
|
||||||
})
|
})
|
||||||
it('should handle nested access 2', async function () {
|
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'])
|
.to.deep.equal(['foo', 'coo'])
|
||||||
})
|
})
|
||||||
it('should handle nested access 3', async function () {
|
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'])
|
.to.deep.equal(['bar', 'foo', 'zoo'])
|
||||||
})
|
})
|
||||||
it('should handle nested access 4', async function () {
|
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'])
|
.to.deep.equal(['foo', '0', 'bar'])
|
||||||
})
|
})
|
||||||
it('should handle nested access 5', async function () {
|
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'])
|
.to.deep.equal(['foo', '1', 'bar'])
|
||||||
})
|
})
|
||||||
it('should handle nested access 6', async function () {
|
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'])
|
.to.deep.equal(['foo', 'undefined', 'bar'])
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('#get()', function () {
|
describe('#get()', function () {
|
||||||
it('should get direct property', async 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 () {
|
it('undefined property should yield undefined', async function () {
|
||||||
expect(scope.get('notdefined')).to.be.rejected
|
expect(ctx.get('notdefined')).to.be.rejected
|
||||||
expect(await scope.get('notdefined')).to.equal(undefined)
|
expect(await ctx.get('notdefined')).to.equal(undefined)
|
||||||
expect(await scope.get(false as any)).to.equal(undefined)
|
expect(await ctx.get(false as any)).to.equal(undefined)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should throw for invalid path', async function () {
|
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 () {
|
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 () {
|
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 () {
|
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 () {
|
it('should respect to toLiquid', async function () {
|
||||||
const scope = new Scope({ foo: {
|
const scope = new Context({ foo: {
|
||||||
toLiquid: () => ({ bar: 'BAR' }),
|
toLiquid: () => ({ bar: 'BAR' }),
|
||||||
bar: 'bar'
|
bar: 'bar'
|
||||||
} })
|
} })
|
||||||
@@ -94,94 +94,94 @@ describe('scope', function () {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should access child property via dot syntax', async function () {
|
it('should access child property via dot syntax', async function () {
|
||||||
expect(await scope.get('bar.zoo')).to.equal('coo')
|
expect(await ctx.get('bar.zoo')).to.equal('coo')
|
||||||
expect(await scope.get('bar.arr')).to.deep.equal(['a', 'b'])
|
expect(await ctx.get('bar.arr')).to.deep.equal(['a', 'b'])
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should access child property via [<String>] syntax', async function () {
|
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 () {
|
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 () {
|
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 () {
|
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 () {
|
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 () {
|
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 () {
|
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 () {
|
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 () {
|
describe('strictVariables', async function () {
|
||||||
let scope: Scope
|
let ctx: Context
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
scope = new Scope(ctx, {
|
ctx = new Context(ctx, {
|
||||||
strictVariables: true
|
strictVariables: true
|
||||||
} as any)
|
} as any)
|
||||||
})
|
})
|
||||||
it('should throw when variable not defined', function () {
|
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 () {
|
it('should throw when deep variable not exist', async function () {
|
||||||
scope.contexts.push({ 'foo': 'FOO' })
|
ctx.scopes.push({ 'foo': 'FOO' })
|
||||||
return expect(scope.get('foo.bar.not.defined')).to.be.rejectedWith(/undefined variable: bar/)
|
return expect(ctx.get('foo.bar.not.defined')).to.be.rejectedWith(/undefined variable: bar/)
|
||||||
})
|
})
|
||||||
it('should throw when itself not defined', async function () {
|
it('should throw when itself not defined', async function () {
|
||||||
scope.contexts.push({ 'foo': 'FOO' })
|
ctx.scopes.push({ 'foo': 'FOO' })
|
||||||
return expect(scope.get('foo.BAR')).to.be.rejectedWith(/undefined variable: BAR/)
|
return expect(ctx.get('foo.BAR')).to.be.rejectedWith(/undefined variable: BAR/)
|
||||||
})
|
})
|
||||||
it('should find variable in parent scope', async function () {
|
it('should find variable in parent scope', async function () {
|
||||||
scope.contexts.push({ 'foo': 'foo' })
|
ctx.scopes.push({ 'foo': 'foo' })
|
||||||
scope.push({
|
ctx.push({
|
||||||
'bar': 'bar'
|
'bar': 'bar'
|
||||||
})
|
})
|
||||||
expect(await scope.get('foo')).to.equal('foo')
|
expect(await ctx.get('foo')).to.equal('foo')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('.getAll()', function () {
|
describe('.getAll()', function () {
|
||||||
it('should get all properties when arguments empty', async 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 () {
|
describe('.push()', function () {
|
||||||
it('should push scope', async function () {
|
it('should push scope', async function () {
|
||||||
scope.contexts.push({ 'bar': 'bar' })
|
ctx.scopes.push({ 'bar': 'bar' })
|
||||||
scope.push({
|
ctx.push({
|
||||||
foo: 'foo'
|
foo: 'foo'
|
||||||
})
|
})
|
||||||
expect(await scope.get('foo')).to.equal('foo')
|
expect(await ctx.get('foo')).to.equal('foo')
|
||||||
expect(await scope.get('bar')).to.equal('bar')
|
expect(await ctx.get('bar')).to.equal('bar')
|
||||||
})
|
})
|
||||||
it('should hide deep properties by push', async function () {
|
it('should hide deep properties by push', async function () {
|
||||||
scope.contexts.push({ 'bar': { bar: 'bar' } })
|
ctx.scopes.push({ 'bar': { bar: 'bar' } })
|
||||||
scope.push({ bar: { foo: 'foo' } })
|
ctx.push({ bar: { foo: 'foo' } })
|
||||||
expect(await scope.get('bar.foo')).to.equal('foo')
|
expect(await ctx.get('bar.foo')).to.equal('foo')
|
||||||
expect(await scope.get('bar.bar')).to.equal(undefined)
|
expect(await ctx.get('bar.bar')).to.equal(undefined)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
describe('.pop()', function () {
|
describe('.pop()', function () {
|
||||||
it('should pop scope', async function () {
|
it('should pop scope', async function () {
|
||||||
scope.push({
|
ctx.push({
|
||||||
foo: 'foo'
|
foo: 'foo'
|
||||||
})
|
})
|
||||||
scope.pop()
|
ctx.pop()
|
||||||
expect(await scope.get('foo')).to.equal('zoo')
|
expect(await ctx.get('foo')).to.equal('zoo')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
it('should pop specified scope', async function () {
|
it('should pop specified scope', async function () {
|
||||||
@@ -191,18 +191,18 @@ describe('scope', function () {
|
|||||||
const scope2 = {
|
const scope2 = {
|
||||||
bar: 'bar'
|
bar: 'bar'
|
||||||
}
|
}
|
||||||
scope.push(scope1)
|
ctx.push(scope1)
|
||||||
scope.push(scope2)
|
ctx.push(scope2)
|
||||||
expect(await scope.get('foo')).to.equal('foo')
|
expect(await ctx.get('foo')).to.equal('foo')
|
||||||
expect(await scope.get('bar')).to.equal('bar')
|
expect(await ctx.get('bar')).to.equal('bar')
|
||||||
scope.pop(scope1)
|
ctx.pop(scope1)
|
||||||
expect(await scope.get('foo')).to.equal('zoo')
|
expect(await ctx.get('foo')).to.equal('zoo')
|
||||||
expect(await scope.get('bar')).to.equal('bar')
|
expect(await ctx.get('bar')).to.equal('bar')
|
||||||
})
|
})
|
||||||
it('should throw when specified scope not found', function () {
|
it('should throw when specified scope not found', function () {
|
||||||
const scope1 = {
|
const scope1 = {
|
||||||
foo: 'foo'
|
foo: 'foo'
|
||||||
}
|
}
|
||||||
expect(() => scope.pop(scope1)).to.throw('scope not found, cannot pop')
|
expect(() => ctx.pop(scope1)).to.throw('scope not found, cannot pop')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { expect } from 'chai'
|
import { expect } from 'chai'
|
||||||
import Scope from '../../../src/scope/scope'
|
import Context from '../../../src/context/context'
|
||||||
import Token from '../../../src/parser/token'
|
import Token from '../../../src/parser/token'
|
||||||
import Tag from '../../../src/template/tag/tag'
|
import Tag from '../../../src/template/tag/tag'
|
||||||
import { Filter } from '../../../src/template/filter/filter'
|
import { Filter } from '../../../src/template/filter/filter'
|
||||||
@@ -20,7 +20,7 @@ describe('render', function () {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should render html', async function () {
|
it('should render html', async function () {
|
||||||
const scope = new Scope()
|
const scope = new Context()
|
||||||
const token = { type: 'html', value: '<p>' } as Token
|
const token = { type: 'html', value: '<p>' } as Token
|
||||||
const html = await render.renderTemplates([new HTML(token)], scope)
|
const html = await render.renderTemplates([new HTML(token)], scope)
|
||||||
return expect(html).to.equal('<p>')
|
return expect(html).to.equal('<p>')
|
||||||
|
|||||||
+34
-34
@@ -1,12 +1,12 @@
|
|||||||
import Scope from '../../../src/scope/scope'
|
import Context from '../../../src/context/context'
|
||||||
import { expect } from 'chai'
|
import { expect } from 'chai'
|
||||||
import { evalExp, evalValue, isTruthy } from '../../../src/render/syntax'
|
import { evalExp, evalValue, isTruthy } from '../../../src/render/syntax'
|
||||||
|
|
||||||
describe('render/syntax', function () {
|
describe('render/syntax', function () {
|
||||||
let scope: Scope
|
let ctx: Context
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
scope = new Scope({
|
ctx = new Context({
|
||||||
one: 1,
|
one: 1,
|
||||||
two: 2,
|
two: 2,
|
||||||
empty: '',
|
empty: '',
|
||||||
@@ -19,30 +19,30 @@ describe('render/syntax', function () {
|
|||||||
|
|
||||||
describe('.evalValue()', function () {
|
describe('.evalValue()', function () {
|
||||||
it('should eval boolean literal', async function () {
|
it('should eval boolean literal', async function () {
|
||||||
expect(await evalValue('true', scope)).to.equal(true)
|
expect(await evalValue('true', ctx)).to.equal(true)
|
||||||
expect(await evalValue('TrUE', scope)).to.equal(undefined)
|
expect(await evalValue('TrUE', ctx)).to.equal(undefined)
|
||||||
expect(await evalValue('false', scope)).to.equal(false)
|
expect(await evalValue('false', ctx)).to.equal(false)
|
||||||
})
|
})
|
||||||
it('should eval number literal', async function () {
|
it('should eval number literal', async function () {
|
||||||
expect(await evalValue('2.3', scope)).to.equal(2.3)
|
expect(await evalValue('2.3', ctx)).to.equal(2.3)
|
||||||
expect(await evalValue('.32', scope)).to.equal(0.32)
|
expect(await evalValue('.32', ctx)).to.equal(0.32)
|
||||||
expect(await evalValue('-23.', scope)).to.equal(-23)
|
expect(await evalValue('-23.', ctx)).to.equal(-23)
|
||||||
expect(await evalValue('23', scope)).to.equal(23)
|
expect(await evalValue('23', ctx)).to.equal(23)
|
||||||
})
|
})
|
||||||
it('should eval string literal', async function () {
|
it('should eval string literal', async function () {
|
||||||
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'", scope)).to.equal('ab"c')
|
expect(await evalValue("'ab\"c'", ctx)).to.equal('ab"c')
|
||||||
})
|
})
|
||||||
it('should eval nil literal', async function () {
|
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 () {
|
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 () {
|
it('should eval scope variables', async function () {
|
||||||
expect(await evalValue('one', scope)).to.equal(1)
|
expect(await evalValue('one', ctx)).to.equal(1)
|
||||||
expect(await evalValue('has_value?', scope)).to.equal(true)
|
expect(await evalValue('has_value?', ctx)).to.equal(true)
|
||||||
expect(await evalValue('x', scope)).to.equal('XXX')
|
expect(await evalValue('x', ctx)).to.equal('XXX')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -66,37 +66,37 @@ describe('render/syntax', function () {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should eval simple expression', async function () {
|
it('should eval simple expression', async function () {
|
||||||
expect(await evalExp('1<2', scope)).to.equal(true)
|
expect(await evalExp('1<2', ctx)).to.equal(true)
|
||||||
expect(await evalExp('2<=2', scope)).to.equal(true)
|
expect(await evalExp('2<=2', ctx)).to.equal(true)
|
||||||
expect(await evalExp('one<=two', scope)).to.equal(true)
|
expect(await evalExp('one<=two', ctx)).to.equal(true)
|
||||||
expect(await evalExp('x contains "x"', scope)).to.equal(false)
|
expect(await evalExp('x contains "x"', ctx)).to.equal(false)
|
||||||
expect(await evalExp('x contains "X"', scope)).to.equal(true)
|
expect(await evalExp('x contains "X"', ctx)).to.equal(true)
|
||||||
expect(await evalExp('1 contains "x"', scope)).to.equal(false)
|
expect(await evalExp('1 contains "x"', ctx)).to.equal(false)
|
||||||
expect(await evalExp('y contains "x"', scope)).to.equal(false)
|
expect(await evalExp('y contains "x"', ctx)).to.equal(false)
|
||||||
expect(await evalExp('z contains "x"', scope)).to.equal(false)
|
expect(await evalExp('z contains "x"', ctx)).to.equal(false)
|
||||||
expect(await evalExp('(1..5) contains 3', scope)).to.equal(true)
|
expect(await evalExp('(1..5) contains 3', ctx)).to.equal(true)
|
||||||
expect(await evalExp('(1..5) contains 6', scope)).to.equal(false)
|
expect(await evalExp('(1..5) contains 6', ctx)).to.equal(false)
|
||||||
expect(await evalExp('"<=" == "<="', scope)).to.equal(true)
|
expect(await evalExp('"<=" == "<="', ctx)).to.equal(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('complex expression', function () {
|
describe('complex expression', function () {
|
||||||
it('should support value or value', async 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 () {
|
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 () {
|
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 () {
|
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 () {
|
it('should eval range expression', async function () {
|
||||||
expect(await evalExp('(2..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)', scope)).to.deep.equal([2, 3, 4])
|
expect(await evalExp('(two..4)', ctx)).to.deep.equal([2, 3, 4])
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -2,16 +2,16 @@ import * as chai from 'chai'
|
|||||||
import * as sinon from 'sinon'
|
import * as sinon from 'sinon'
|
||||||
import * as sinonChai from 'sinon-chai'
|
import * as sinonChai from 'sinon-chai'
|
||||||
import { Filter } from '../../../../src/template/filter/filter'
|
import { Filter } from '../../../../src/template/filter/filter'
|
||||||
import Scope from '../../../../src/scope/scope'
|
import Context from '../../../../src/context/context'
|
||||||
|
|
||||||
chai.use(sinonChai)
|
chai.use(sinonChai)
|
||||||
const expect = chai.expect
|
const expect = chai.expect
|
||||||
|
|
||||||
describe('filter', function () {
|
describe('filter', function () {
|
||||||
let scope: Scope
|
let ctx: Context
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
Filter.clear()
|
Filter.clear()
|
||||||
scope = new Scope()
|
ctx = new Context()
|
||||||
})
|
})
|
||||||
it('should create default filter if not registered', async function () {
|
it('should create default filter if not registered', async function () {
|
||||||
const result = new Filter('foo', [], false)
|
const result = new Filter('foo', [], false)
|
||||||
@@ -19,28 +19,28 @@ describe('filter', function () {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should render input if filter not registered', async 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 () {
|
it('should call filter impl with corrct arguments', async function () {
|
||||||
const spy = sinon.spy()
|
const spy = sinon.spy()
|
||||||
Filter.register('foo', 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)
|
expect(spy).to.have.been.calledWith('foo', 33)
|
||||||
})
|
})
|
||||||
it('should render a simple filter', async function () {
|
it('should render a simple filter', async function () {
|
||||||
Filter.register('upcase', x => x.toUpperCase())
|
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 () {
|
it('should render filters with argument', async function () {
|
||||||
Filter.register('add', (a, b) => a + b)
|
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 () {
|
it('should render filters with multiple arguments', async function () {
|
||||||
Filter.register('add', (a, b, c) => a + b + c)
|
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 () {
|
it('should not throw when filter name illegal', function () {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as chai from 'chai'
|
import * as chai from 'chai'
|
||||||
import Scope from '../../../src/scope/scope'
|
import Context from '../../../src/context/context'
|
||||||
import Output from '../../../src/template/output'
|
import Output from '../../../src/template/output'
|
||||||
import OutputToken from '../../../src/parser/output-token'
|
import OutputToken from '../../../src/parser/output-token'
|
||||||
import { Filter } from '../../../src/template/filter/filter'
|
import { Filter } from '../../../src/template/filter/filter'
|
||||||
@@ -12,7 +12,7 @@ describe('Output', function () {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should stringify objects', async function () {
|
it('should stringify objects', async function () {
|
||||||
const scope = new Scope({
|
const scope = new Context({
|
||||||
foo: { obj: { arr: ['a', 2] } }
|
foo: { obj: { arr: ['a', 2] } }
|
||||||
})
|
})
|
||||||
const output = new Output({ value: 'foo' } as OutputToken, false)
|
const output = new Output({ value: 'foo' } as OutputToken, false)
|
||||||
@@ -20,19 +20,19 @@ describe('Output', function () {
|
|||||||
return expect(html).to.equal('[object Object]')
|
return expect(html).to.equal('[object Object]')
|
||||||
})
|
})
|
||||||
it('should skip function property', async function () {
|
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 output = new Output({ value: 'obj' } as OutputToken, false)
|
||||||
const html = await output.render(scope)
|
const html = await output.render(scope)
|
||||||
return expect(html).to.equal('[object Object]')
|
return expect(html).to.equal('[object Object]')
|
||||||
})
|
})
|
||||||
it('should respect to .toString()', async () => {
|
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 output = new Output({ value: 'obj' } as OutputToken, false)
|
||||||
const str = await output.render(scope)
|
const str = await output.render(scope)
|
||||||
return expect(str).to.equal('FOO')
|
return expect(str).to.equal('FOO')
|
||||||
})
|
})
|
||||||
it('should respect to .toString()', async () => {
|
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 output = new Output({ value: 'obj' } as OutputToken, false)
|
||||||
const str = await output.render(scope)
|
const str = await output.render(scope)
|
||||||
return expect(str).to.equal('FOO')
|
return expect(str).to.equal('FOO')
|
||||||
|
|||||||
+12
-12
@@ -1,6 +1,6 @@
|
|||||||
import * as chai from 'chai'
|
import * as chai from 'chai'
|
||||||
import Tag from '../../../src/template/tag/tag'
|
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 sinon from 'sinon'
|
||||||
import * as sinonChai from 'sinon-chai'
|
import * as sinonChai from 'sinon-chai'
|
||||||
import Liquid from '../../../src/liquid'
|
import Liquid from '../../../src/liquid'
|
||||||
@@ -11,9 +11,9 @@ const expect = chai.expect
|
|||||||
const liquid = new Liquid()
|
const liquid = new Liquid()
|
||||||
|
|
||||||
describe('tag', function () {
|
describe('tag', function () {
|
||||||
let scope: Scope
|
let ctx: Context
|
||||||
before(function () {
|
before(function () {
|
||||||
scope = new Scope({
|
ctx = new Context({
|
||||||
foo: 'bar',
|
foo: 'bar',
|
||||||
arr: [2, 1],
|
arr: [2, 1],
|
||||||
bar: {
|
bar: {
|
||||||
@@ -51,7 +51,7 @@ describe('tag', function () {
|
|||||||
value: 'foo',
|
value: 'foo',
|
||||||
name: 'foo'
|
name: 'foo'
|
||||||
} as TagToken
|
} as TagToken
|
||||||
await new Tag(token, [], liquid).render(scope)
|
await new Tag(token, [], liquid).render(ctx)
|
||||||
expect(spy).to.have.been.called
|
expect(spy).to.have.been.called
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -70,30 +70,30 @@ describe('tag', function () {
|
|||||||
} as TagToken
|
} as TagToken
|
||||||
})
|
})
|
||||||
it('should call tag.render with scope', async function () {
|
it('should call tag.render with scope', async function () {
|
||||||
await new Tag(token, [], liquid).render(scope)
|
await new Tag(token, [], liquid).render(ctx)
|
||||||
expect(spy).to.have.been.calledWithMatch(scope)
|
expect(spy).to.have.been.calledWithMatch(ctx)
|
||||||
})
|
})
|
||||||
it('should resolve identifier hash', async function () {
|
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({}, {
|
expect(spy).to.have.been.calledWithMatch({}, {
|
||||||
aa: 'bar'
|
aa: 'bar'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
it('should accept space between key/value', async function () {
|
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({}, {
|
expect(spy).to.have.been.calledWithMatch({}, {
|
||||||
bb: 2
|
bb: 2
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
it('should resolve number value hash', async function () {
|
it('should resolve number value hash', async function () {
|
||||||
await new Tag(token, [], liquid).render(scope)
|
await new Tag(token, [], liquid).render(ctx)
|
||||||
expect(spy).to.have.been.calledWithMatch(scope, {
|
expect(spy).to.have.been.calledWithMatch(ctx, {
|
||||||
cc: 2.3
|
cc: 2.3
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
it('should resolve property access hash', async function () {
|
it('should resolve property access hash', async function () {
|
||||||
await new Tag(token, [], liquid).render(scope)
|
await new Tag(token, [], liquid).render(ctx)
|
||||||
expect(spy).to.have.been.calledWithMatch(scope, {
|
expect(spy).to.have.been.calledWithMatch(ctx, {
|
||||||
dd: 'uoo'
|
dd: 'uoo'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import * as chai from 'chai'
|
import * as chai from 'chai'
|
||||||
import * as sinonChai from 'sinon-chai'
|
import * as sinonChai from 'sinon-chai'
|
||||||
import * as sinon from 'sinon'
|
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 { Filter } from '../../../src/template/filter/filter'
|
||||||
import Value from '../../../src/template/value'
|
import Value from '../../../src/template/value'
|
||||||
|
|
||||||
@@ -105,7 +105,7 @@ describe('Value', function () {
|
|||||||
Filter.register('date', date)
|
Filter.register('date', date)
|
||||||
Filter.register('time', time)
|
Filter.register('time', time)
|
||||||
const tpl = new Value('foo.bar | date: "b" | time:2', false)
|
const tpl = new Value('foo.bar | date: "b" | time:2', false)
|
||||||
const scope = new Scope({
|
const scope = new Context({
|
||||||
foo: { bar: 'bar' }
|
foo: { bar: 'bar' }
|
||||||
})
|
})
|
||||||
await tpl.value(scope)
|
await tpl.value(scope)
|
||||||
|
|||||||
Reference in New Issue
Block a user