From 45e3c2bb8e14ae8b58d00b66ccc299beaaecaf80 Mon Sep 17 00:00:00 2001 From: harttle Date: Fri, 22 Mar 2019 21:09:14 +0800 Subject: [PATCH] refactor: switch Context <-> Scope concepts --- benchmark/demo.ts | 6 +- src/builtin/tags/assign.ts | 6 +- src/builtin/tags/block.ts | 14 +-- src/builtin/tags/capture.ts | 8 +- src/builtin/tags/case.ts | 12 +- src/builtin/tags/cycle.ts | 10 +- src/builtin/tags/decrement.ts | 4 +- src/builtin/tags/for.ts | 14 +-- src/builtin/tags/if.ts | 10 +- src/builtin/tags/include.ts | 34 +++--- src/builtin/tags/layout.ts | 28 ++--- src/builtin/tags/tablerow.ts | 16 +-- src/builtin/tags/unless.ts | 10 +- src/{scope => context}/block-mode.ts | 0 src/{scope/scope.ts => context/context.ts} | 26 ++--- src/{scope/context.ts => context/scope.ts} | 2 +- src/liquid.ts | 8 +- src/render/render.ts | 8 +- src/render/syntax.ts | 28 ++--- src/template/filter/filter.ts | 8 +- src/template/itemplate.ts | 4 +- src/template/output.ts | 6 +- src/template/tag/hash.ts | 6 +- src/template/tag/itag-impl-options.ts | 4 +- src/template/tag/tag.ts | 8 +- src/template/value.ts | 8 +- test/integration/builtin/tags/for.ts | 4 +- test/unit/{scope => context}/scope.ts | 126 ++++++++++----------- test/unit/render/render.ts | 4 +- test/unit/render/syntax.ts | 68 +++++------ test/unit/template/filter/filter.ts | 16 +-- test/unit/template/output.ts | 10 +- test/unit/template/tag.ts | 24 ++-- test/unit/template/value.ts | 4 +- 34 files changed, 272 insertions(+), 272 deletions(-) rename src/{scope => context}/block-mode.ts (100%) rename src/{scope/scope.ts => context/context.ts} (87%) rename src/{scope/context.ts => context/scope.ts} (71%) rename test/unit/{scope => context}/scope.ts (54%) diff --git a/benchmark/demo.ts b/benchmark/demo.ts index 986dd47e1..b71093666 100644 --- a/benchmark/demo.ts +++ b/benchmark/demo.ts @@ -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 `

${title}

` } }) diff --git a/src/builtin/tags/assign.ts b/src/builtin/tags/assign.ts index f44fc4f5a..0587dac4f 100644 --- a/src/builtin/tags/assign.ts +++ b/src/builtin/tags/assign.ts @@ -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 diff --git a/src/builtin/tags/block.ts b/src/builtin/tags/block.ts index c2a18fbfa..d01f4d535 100644 --- a/src/builtin/tags/block.ts +++ b/src/builtin/tags/block.ts @@ -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 diff --git a/src/builtin/tags/capture.ts b/src/builtin/tags/capture.ts index 326572703..993ff1d0a 100644 --- a/src/builtin/tags/capture.ts +++ b/src/builtin/tags/capture.ts @@ -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 diff --git a/src/builtin/tags/case.ts b/src/builtin/tags/case.ts index 198a7a13a..0099cc1b1 100644 --- a/src/builtin/tags/case.ts +++ b/src/builtin/tags/case.ts @@ -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 diff --git a/src/builtin/tags/cycle.ts b/src/builtin/tags/cycle.ts index 5dd7ba0c8..aec848aaa 100644 --- a/src/builtin/tags/cycle.ts +++ b/src/builtin/tags/cycle.ts @@ -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 { 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 { idx = (idx + 1) % this.candidates.length groups[fingerprint] = idx - return evalValue(candidate, scope) + return evalValue(candidate, ctx) } } diff --git a/src/builtin/tags/decrement.ts b/src/builtin/tags/decrement.ts index c24c0af51..9d40e955e 100644 --- a/src/builtin/tags/decrement.ts +++ b/src/builtin/tags/decrement.ts @@ -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 diff --git a/src/builtin/tags/for.ts b/src/builtin/tags/for.ts index d53fedfea..5d2ce8cd1 100644 --- a/src/builtin/tags/for.ts +++ b/src/builtin/tags/for.ts @@ -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 { 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 { } } 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 { 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 { } context.forloop.next() } - scope.pop() + ctx.pop() return html } } diff --git a/src/builtin/tags/if.ts b/src/builtin/tags/if.ts index 1f4a4beec..483edc045 100644 --- a/src/builtin/tags/if.ts +++ b/src/builtin/tags/if.ts @@ -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 diff --git a/src/builtin/tags/include.ts b/src/builtin/tags/include.ts index 7eb4d7a95..a7936e753 100644 --- a/src/builtin/tags/include.ts +++ b/src/builtin/tags/include.ts @@ -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 { 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 } } diff --git a/src/builtin/tags/layout.ts b/src/builtin/tags/layout.ts index 592dec448..553375948 100644 --- a/src/builtin/tags/layout.ts +++ b/src/builtin/tags/layout.ts @@ -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 diff --git a/src/builtin/tags/tablerow.ts b/src/builtin/tags/tablerow.ts index 4cf16dc9d..23452a11f 100644 --- a/src/builtin/tags/tablerow.ts +++ b/src/builtin/tags/tablerow.ts @@ -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 += '' html += `` } html += `` - html += await this.liquid.renderer.renderTemplates(this.templates, scope) + html += await this.liquid.renderer.renderTemplates(this.templates, ctx) html += '' } if (collection.length) html += '' - scope.pop(ctx) + ctx.pop(scope) return html } } as ITagImplOptions diff --git a/src/builtin/tags/unless.ts b/src/builtin/tags/unless.ts index a2a6687c2..83f8b388c 100644 --- a/src/builtin/tags/unless.ts +++ b/src/builtin/tags/unless.ts @@ -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 diff --git a/src/scope/block-mode.ts b/src/context/block-mode.ts similarity index 100% rename from src/scope/block-mode.ts rename to src/context/block-mode.ts diff --git a/src/scope/scope.ts b/src/context/context.ts similarity index 87% rename from src/scope/scope.ts rename to src/context/context.ts index 689abe574..edd9562a0 100644 --- a/src/scope/scope.ts +++ b/src/context/context.ts @@ -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 = [{}] - environments: Context + scopes: Array = [{}] + 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'] diff --git a/src/scope/context.ts b/src/context/scope.ts similarity index 71% rename from src/scope/context.ts rename to src/context/scope.ts index 71fd18879..3ae6aca30 100644 --- a/src/scope/context.ts +++ b/src/context/scope.ts @@ -5,4 +5,4 @@ type PlainObject = { toLiquid?: () => any } -export type Context = PlainObject | Drop +export type Scope = PlainObject | Drop diff --git a/src/liquid.ts b/src/liquid.ts index 2acf4d38f..06c03f64e 100644 --- a/src/liquid.ts +++ b/src/liquid.ts @@ -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, 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) diff --git a/src/render/render.ts b/src/render/render.ts index edb269cc6..6d920b5c0 100644 --- a/src/render/render.ts +++ b/src/render/render.ts @@ -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 diff --git a/src/render/syntax.ts b/src/render/syntax.ts index 3f9d0f36b..e76594eb9 100644 --- a/src/render/syntax.ts +++ b/src/render/syntax.ts @@ -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 { - assert(scope, 'unable to parseExp: scope undefined') +export async function parseExp (exp: string, ctx: Context): Promise { + 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 { - const value = await parseExp(str, scope) +export async function evalExp (str: string, ctx: Context): Promise { + const value = await parseExp(str, ctx) return value instanceof Drop ? value.valueOf() : value } -async function parseValue (str: string | undefined, scope: Scope): Promise { +async function parseValue (str: string | undefined, ctx: Context): Promise { if (!str) return null str = str.trim() @@ -88,11 +88,11 @@ async function parseValue (str: string | undefined, scope: Scope): Promise 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 } diff --git a/src/template/filter/filter.ts b/src/template/filter/filter.ts index af4c20d96..e08fbb894 100644 --- a/src/template/filter/filter.ts +++ b/src/template/filter/filter.ts @@ -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]) } diff --git a/src/template/itemplate.ts b/src/template/itemplate.ts index 9f4745a18..e0f9580ef 100644 --- a/src/template/itemplate.ts +++ b/src/template/itemplate.ts @@ -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; + render(ctx: Context): Promise; } diff --git a/src/template/output.ts b/src/template/output.ts index 317b36a18..e4730a7b8 100644 --- a/src/template/output.ts +++ b/src/template/output.ts @@ -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 implements ITemplate { @@ -11,8 +11,8 @@ export default class Output extends Template implements ITemplate { super(token) this.value = new Value(token.value, strictFilters) } - async render (scope: Scope): Promise { - const html = await this.value.value(scope) + async render (ctx: Context): Promise { + const html = await this.value.value(ctx) return stringify(html) } } diff --git a/src/template/tag/hash.ts b/src/template/tag/hash.ts index a95d29aae..65c100e51 100644 --- a/src/template/tag/hash.ts +++ b/src/template/tag/hash.ts @@ -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 } diff --git a/src/template/tag/itag-impl-options.ts b/src/template/tag/itag-impl-options.ts index 29294264c..7234fcf9e 100644 --- a/src/template/tag/itag-impl-options.ts +++ b/src/template/tag/itag-impl-options.ts @@ -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) => void - render?: (this: ITagImpl, scope: Scope, hash: Hash) => any | Promise + render?: (this: ITagImpl, ctx: Context, hash: Hash) => any | Promise } diff --git a/src/template/tag/tag.ts b/src/template/tag/tag.ts index e6e79870f..e91744729 100644 --- a/src/template/tag/tag.ts +++ b/src/template/tag/tag.ts @@ -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 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) { diff --git a/src/template/value.ts b/src/template/value.ts index 35b653405..d4301a20b 100644 --- a/src/template/value.ts +++ b/src/template/value.ts @@ -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 } diff --git a/test/integration/builtin/tags/for.ts b/test/integration/builtin/tags/for.ts index 6f68308de..96ffca041 100644 --- a/test/integration/builtin/tags/for.ts +++ b/test/integration/builtin/tags/for.ts @@ -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', { diff --git a/test/unit/scope/scope.ts b/test/unit/context/scope.ts similarity index 54% rename from test/unit/scope/scope.ts rename to test/unit/context/scope.ts index a71cf3e8f..1b42a107c 100644 --- a/test/unit/scope/scope.ts +++ b/test/unit/context/scope.ts @@ -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 [] syntax', async function () { - expect(await scope.propertyAccessSeq('foo["bar"]')) + expect(await ctx.propertyAccessSeq('foo["bar"]')) .to.deep.equal(['foo', 'bar']) }) it('should handle [] 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 [] 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 [] 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 [] 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') }) }) diff --git a/test/unit/render/render.ts b/test/unit/render/render.ts index 56f8cd6b2..bedee7671 100644 --- a/test/unit/render/render.ts +++ b/test/unit/render/render.ts @@ -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: '

' } as Token const html = await render.renderTemplates([new HTML(token)], scope) return expect(html).to.equal('

') diff --git a/test/unit/render/syntax.ts b/test/unit/render/syntax.ts index 486bf1869..4b894ec12 100644 --- a/test/unit/render/syntax.ts +++ b/test/unit/render/syntax.ts @@ -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]) }) }) }) diff --git a/test/unit/template/filter/filter.ts b/test/unit/template/filter/filter.ts index 7293313bc..db05f5e3b 100644 --- a/test/unit/template/filter/filter.ts +++ b/test/unit/template/filter/filter.ts @@ -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 () { diff --git a/test/unit/template/output.ts b/test/unit/template/output.ts index b4dad3c28..e4c26e6a0 100644 --- a/test/unit/template/output.ts +++ b/test/unit/template/output.ts @@ -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') diff --git a/test/unit/template/tag.ts b/test/unit/template/tag.ts index 4dc674b12..8d13f389b 100644 --- a/test/unit/template/tag.ts +++ b/test/unit/template/tag.ts @@ -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' }) }) diff --git a/test/unit/template/value.ts b/test/unit/template/value.ts index 760832579..381b29173 100644 --- a/test/unit/template/value.ts +++ b/test/unit/template/value.ts @@ -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)