Skip to content

Commit

Permalink
feat: export Operators from operator.ts
Browse files Browse the repository at this point in the history
  • Loading branch information
JasonEtco authored and harttle committed Feb 4, 2021
1 parent bc87e19 commit 6a7c280
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 46 deletions.
4 changes: 2 additions & 2 deletions src/liquid-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Template } from './template/template'
import { Cache } from './cache/cache'
import { LRU } from './cache/lru'
import { FS } from './fs/fs'
import { operatorImpls, OperatorMap } from './render/operator'
import { Operators, OperatorMap } from './render/operator'

export interface LiquidOptions {
/** A directory or an array of directories from where to resolve layout and include templates, and the filename passed to `.renderFile()`. If it's an array, the files are looked up in the order they occur in the array. Defaults to `["."]` */
Expand Down Expand Up @@ -102,7 +102,7 @@ export const defaultOptions: NormalizedFullOptions = {
lenientIf: false,
globals: {},
keepOutputType: false,
operators: operatorImpls
operators: Operators
}

export function normalize (options?: LiquidOptions): NormalizedOptions {
Expand Down
2 changes: 1 addition & 1 deletion src/render/operator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export interface OperatorMap {
[key: string]: (lhs: any, rhs: any, ctx: Context) => boolean;
}

export const operatorImpls: OperatorMap = {
export const Operators: OperatorMap = {
'==': (l: any, r: any) => {
if (isComparable(l)) return l.equals(r)
if (isComparable(r)) return r.equals(l)
Expand Down
2 changes: 1 addition & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ export { Tokenizer } from './parser/tokenizer'
export { Hash } from './template/tag/hash'
export { evalToken, evalQuotedToken } from './render/expression'
export { toPromise, toThenable, toValue } from './util/async'
export { OperatorMap } from './render/operator'
export { Operators, OperatorMap } from './render/operator'
84 changes: 42 additions & 42 deletions test/unit/render/expression.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ import { Expression } from '../../../src/render/expression'
import { expect } from 'chai'
import { Context } from '../../../src/context/context'
import { toThenable } from '../../../src/util/async'
import { operatorImpls } from '../../../src/render/operator'
import { Operators } from '../../../src/render/operator'

describe('Expression', function () {
const ctx = new Context({})

it('should throw when context not defined', done => {
toThenable(new Expression('foo', operatorImpls).value(undefined!))
toThenable(new Expression('foo', Operators).value(undefined!))
.then(() => done(new Error('should not resolved')))
.catch(err => {
expect(err.message).to.match(/context not defined/)
Expand All @@ -18,19 +18,19 @@ describe('Expression', function () {

describe('single value', function () {
it('should eval literal', async function () {
expect(await toThenable(new Expression('2.4', operatorImpls).value(ctx))).to.equal(2.4)
expect(await toThenable(new Expression('"foo"', operatorImpls).value(ctx))).to.equal('foo')
expect(await toThenable(new Expression('false', operatorImpls).value(ctx))).to.equal(false)
expect(await toThenable(new Expression('2.4', Operators).value(ctx))).to.equal(2.4)
expect(await toThenable(new Expression('"foo"', Operators).value(ctx))).to.equal('foo')
expect(await toThenable(new Expression('false', Operators).value(ctx))).to.equal(false)
})
it('should eval range expression', async function () {
const ctx = new Context({ two: 2 })
expect(await toThenable(new Expression('(2..4)', operatorImpls).value(ctx))).to.deep.equal([2, 3, 4])
expect(await toThenable(new Expression('(two..4)', operatorImpls).value(ctx))).to.deep.equal([2, 3, 4])
expect(await toThenable(new Expression('(2..4)', Operators).value(ctx))).to.deep.equal([2, 3, 4])
expect(await toThenable(new Expression('(two..4)', Operators).value(ctx))).to.deep.equal([2, 3, 4])
})
it('should eval literal', async function () {
expect(await toThenable(new Expression('2.4', operatorImpls).value(ctx))).to.equal(2.4)
expect(await toThenable(new Expression('"foo"', operatorImpls).value(ctx))).to.equal('foo')
expect(await toThenable(new Expression('false', operatorImpls).value(ctx))).to.equal(false)
expect(await toThenable(new Expression('2.4', Operators).value(ctx))).to.equal(2.4)
expect(await toThenable(new Expression('"foo"', Operators).value(ctx))).to.equal('foo')
expect(await toThenable(new Expression('false', Operators).value(ctx))).to.equal(false)
})

it('should eval property access', async function () {
Expand All @@ -39,112 +39,112 @@ describe('Expression', function () {
coo: 'bar',
doo: { foo: 'bar', bar: { foo: 'bar' } }
})
expect(await toThenable(new Expression('foo.bar', operatorImpls).value(ctx))).to.equal('BAR')
expect(await toThenable(new Expression('foo["bar"]', operatorImpls).value(ctx))).to.equal('BAR')
expect(await toThenable(new Expression('foo[coo]', operatorImpls).value(ctx))).to.equal('BAR')
expect(await toThenable(new Expression('foo[doo.foo]', operatorImpls).value(ctx))).to.equal('BAR')
expect(await toThenable(new Expression('foo[doo["foo"]]', operatorImpls).value(ctx))).to.equal('BAR')
expect(await toThenable(new Expression('doo[coo].foo', operatorImpls).value(ctx))).to.equal('bar')
expect(await toThenable(new Expression('foo.bar', Operators).value(ctx))).to.equal('BAR')
expect(await toThenable(new Expression('foo["bar"]', Operators).value(ctx))).to.equal('BAR')
expect(await toThenable(new Expression('foo[coo]', Operators).value(ctx))).to.equal('BAR')
expect(await toThenable(new Expression('foo[doo.foo]', Operators).value(ctx))).to.equal('BAR')
expect(await toThenable(new Expression('foo[doo["foo"]]', Operators).value(ctx))).to.equal('BAR')
expect(await toThenable(new Expression('doo[coo].foo', Operators).value(ctx))).to.equal('bar')
})
})

describe('simple expression', function () {
it('should return false for "1==2"', async () => {
expect(await toThenable(new Expression('1==2', operatorImpls).value(ctx))).to.equal(false)
expect(await toThenable(new Expression('1==2', Operators).value(ctx))).to.equal(false)
})
it('should return true for "1<2"', async () => {
expect(await toThenable(new Expression('1<2', operatorImpls).value(ctx))).to.equal(true)
expect(await toThenable(new Expression('1<2', Operators).value(ctx))).to.equal(true)
})
it('should return true for "1 < 2"', async () => {
expect(await toThenable(new Expression('1 < 2', operatorImpls).value(ctx))).to.equal(true)
expect(await toThenable(new Expression('1 < 2', Operators).value(ctx))).to.equal(true)
})
it('should return true for "1 < 2"', async () => {
expect(await toThenable(new Expression('1 < 2', operatorImpls).value(ctx))).to.equal(true)
expect(await toThenable(new Expression('1 < 2', Operators).value(ctx))).to.equal(true)
})
it('should return true for "2 <= 2"', async () => {
expect(await toThenable(new Expression('2 <= 2', operatorImpls).value(ctx))).to.equal(true)
expect(await toThenable(new Expression('2 <= 2', Operators).value(ctx))).to.equal(true)
})
it('should return true for "one <= two"', async () => {
const ctx = new Context({ one: 1, two: 2 })
expect(await toThenable(new Expression('one <= two', operatorImpls).value(ctx))).to.equal(true)
expect(await toThenable(new Expression('one <= two', Operators).value(ctx))).to.equal(true)
})
it('should return false for "x contains "x""', async () => {
const ctx = new Context({ x: 'XXX' })
expect(await toThenable(new Expression('x contains "x"', operatorImpls).value(ctx))).to.equal(false)
expect(await toThenable(new Expression('x contains "x"', Operators).value(ctx))).to.equal(false)
})
it('should return true for "x contains "X""', async () => {
const ctx = new Context({ x: 'XXX' })
expect(await toThenable(new Expression('x contains "X"', operatorImpls).value(ctx))).to.equal(true)
expect(await toThenable(new Expression('x contains "X"', Operators).value(ctx))).to.equal(true)
})
it('should return false for "1 contains "x""', async () => {
const ctx = new Context({ x: 'XXX' })
expect(await toThenable(new Expression('1 contains "x"', operatorImpls).value(ctx))).to.equal(false)
expect(await toThenable(new Expression('1 contains "x"', Operators).value(ctx))).to.equal(false)
})
it('should return false for "y contains "x""', async () => {
const ctx = new Context({ x: 'XXX' })
expect(await toThenable(new Expression('y contains "x"', operatorImpls).value(ctx))).to.equal(false)
expect(await toThenable(new Expression('y contains "x"', Operators).value(ctx))).to.equal(false)
})
it('should return false for "z contains "x""', async () => {
const ctx = new Context({ x: 'XXX' })
expect(await toThenable(new Expression('z contains "x"', operatorImpls).value(ctx))).to.equal(false)
expect(await toThenable(new Expression('z contains "x"', Operators).value(ctx))).to.equal(false)
})
it('should return true for "(1..5) contains 3"', async () => {
const ctx = new Context({ x: 'XXX' })
expect(await toThenable(new Expression('(1..5) contains 3', operatorImpls).value(ctx))).to.equal(true)
expect(await toThenable(new Expression('(1..5) contains 3', Operators).value(ctx))).to.equal(true)
})
it('should return false for "(1..5) contains 6"', async () => {
const ctx = new Context({ x: 'XXX' })
expect(await toThenable(new Expression('(1..5) contains 6', operatorImpls).value(ctx))).to.equal(false)
expect(await toThenable(new Expression('(1..5) contains 6', Operators).value(ctx))).to.equal(false)
})
it('should return true for ""<=" == "<=""', async () => {
expect(await toThenable(new Expression('"<=" == "<="', operatorImpls).value(ctx))).to.equal(true)
expect(await toThenable(new Expression('"<=" == "<="', Operators).value(ctx))).to.equal(true)
})
})

it('should allow space in quoted value', async function () {
const ctx = new Context({ space: ' ' })
expect(await toThenable(new Expression('" " == space', operatorImpls).value(ctx))).to.equal(true)
expect(await toThenable(new Expression('" " == space', Operators).value(ctx))).to.equal(true)
})

describe('escape', () => {
it('should escape quote', async function () {
const ctx = new Context({ quote: '"' })
expect(await toThenable(new Expression('"\\"" == quote', operatorImpls).value(ctx))).to.equal(true)
expect(await toThenable(new Expression('"\\"" == quote', Operators).value(ctx))).to.equal(true)
})
it('should escape square bracket', async function () {
const ctx = new Context({ obj: { ']': 'bracket' } })
expect(await toThenable(new Expression('obj["]"] == "bracket"', operatorImpls).value(ctx))).to.equal(true)
expect(await toThenable(new Expression('obj["]"] == "bracket"', Operators).value(ctx))).to.equal(true)
})
})

describe('complex expression', function () {
it('should support value or value', async function () {
expect(await toThenable(new Expression('false or true', operatorImpls).value(ctx))).to.equal(true)
expect(await toThenable(new Expression('false or true', Operators).value(ctx))).to.equal(true)
})
it('should support < and contains', async function () {
expect(await toThenable(new Expression('1 < 2 and x contains "x"', operatorImpls).value(ctx))).to.equal(false)
expect(await toThenable(new Expression('1 < 2 and x contains "x"', Operators).value(ctx))).to.equal(false)
})
it('should support < or contains', async function () {
expect(await toThenable(new Expression('1 < 2 or x contains "x"', operatorImpls).value(ctx))).to.equal(true)
expect(await toThenable(new Expression('1 < 2 or x contains "x"', Operators).value(ctx))).to.equal(true)
})
it('should support value and !=', async function () {
const ctx = new Context({ empty: '' })
expect(await toThenable(new Expression('empty and empty != ""', operatorImpls).value(ctx))).to.equal(false)
expect(await toThenable(new Expression('empty and empty != ""', Operators).value(ctx))).to.equal(false)
})
it('should recognize quoted value', async function () {
expect(await toThenable(new Expression('">"', operatorImpls).value(ctx))).to.equal('>')
expect(await toThenable(new Expression('">"', Operators).value(ctx))).to.equal('>')
})
it('should evaluate from right to left', async function () {
expect(await toThenable(new Expression('true or false and false', operatorImpls).value(ctx))).to.equal(true)
expect(await toThenable(new Expression('true and false and false or true', operatorImpls).value(ctx))).to.equal(false)
expect(await toThenable(new Expression('true or false and false', Operators).value(ctx))).to.equal(true)
expect(await toThenable(new Expression('true and false and false or true', Operators).value(ctx))).to.equal(false)
})
it('should recognize property access', async function () {
const ctx = new Context({ obj: { foo: true } })
expect(await toThenable(new Expression('obj["foo"] and true', operatorImpls).value(ctx))).to.equal(true)
expect(await toThenable(new Expression('obj["foo"] and true', Operators).value(ctx))).to.equal(true)
})
it('should allow nested property access', async function () {
const ctx = new Context({ obj: { foo: 'FOO' }, keys: { "what's this": 'foo' } })
expect(await toThenable(new Expression('obj[keys["what\'s this"]]', operatorImpls).value(ctx))).to.equal('FOO')
expect(await toThenable(new Expression('obj[keys["what\'s this"]]', Operators).value(ctx))).to.equal('FOO')
})
})
})

0 comments on commit 6a7c280

Please sign in to comment.