refactor: Tag class support in registerTag()

This commit is contained in:
Jun Yang
2022-11-27 14:04:01 +08:00
parent 1f6ce7c822
commit 92992689cd
145 changed files with 694 additions and 768 deletions
+2 -1
View File
@@ -1,4 +1,5 @@
import { Liquid, Drop } from '../..'
import { Liquid } from '../../src/liquid'
import { Drop } from '../../src/drop/drop'
import { expect, use } from 'chai'
import * as chaiAsPromised from 'chai-as-promised'
+2 -2
View File
@@ -1,4 +1,4 @@
import { Tokenizer, Context, Liquid, Drop, defaultOptions, toValueSync } from '../..'
import { Tokenizer, Context, Liquid, Drop, toValueSync } from '../..'
import { expect, use } from 'chai'
import * as chaiAsPromised from 'chai-as-promised'
import * as sinon from 'sinon'
@@ -261,7 +261,7 @@ describe('Issues', function () {
expect(() => engine.parse('{% assign headshot = https://testurl.com/not_enclosed_in_quotes.jpg %}')).to.throw(/unexpected token at ":/)
})
it('#527 export Liquid Expression', () => {
const tokenizer = new Tokenizer('a > b', defaultOptions.operatorsTrie)
const tokenizer = new Tokenizer('a > b')
const expression = tokenizer.readExpression()
const result = toValueSync(expression.evaluate(new Context({ a: 1, b: 2 })))
expect(result).to.equal(false)
+1 -1
View File
@@ -1,5 +1,5 @@
import { expect } from 'chai'
import { Liquid, Drop } from '../../../src/liquid'
import { Liquid, Drop } from '../../../src'
describe('drop/drop', function () {
let liquid: Liquid
@@ -1,4 +1,4 @@
import { test, render } from '../../../stub/render'
import { test, render } from '../../stub/render'
import { expect, use } from 'chai'
import * as chaiAsPromised from 'chai-as-promised'
use(chaiAsPromised)
@@ -1,5 +1,5 @@
import { LiquidOptions } from '../../../../src/liquid-options'
import { test } from '../../../stub/render'
import { LiquidOptions } from '../../../src/liquid-options'
import { test } from '../../stub/render'
describe('filters/date', function () {
it('should support date: %a %b %d %Y', function () {
@@ -1,4 +1,4 @@
import { test } from '../../../stub/render'
import { test } from '../../stub/render'
describe('filters/html', function () {
describe('escape', function () {
@@ -1,5 +1,5 @@
import { expect } from 'chai'
import { test, liquid } from '../../../stub/render'
import { test, liquid } from '../../stub/render'
describe('filters/math', function () {
describe('abs', function () {
@@ -1,4 +1,4 @@
import { Liquid } from '../../../../src/liquid'
import { Liquid } from '../../../src/liquid'
import { expect, use } from 'chai'
import * as chaiAsPromised from 'chai-as-promised'
@@ -1,4 +1,4 @@
import { test } from '../../../stub/render'
import { test } from '../../stub/render'
import { expect, use } from 'chai'
import * as chaiAsPromised from 'chai-as-promised'
@@ -1,4 +1,4 @@
import { test } from '../../../stub/render'
import { test } from '../../stub/render'
describe('filters/url', function () {
describe('url_decode', function () {
+2 -2
View File
@@ -1,7 +1,7 @@
import { Liquid, Context, isFalsy } from '../../../src/liquid'
import * as chai from 'chai'
import { mock, restore } from '../../stub/mockfs'
import * as chaiAsPromised from 'chai-as-promised'
import { Liquid, Context, isFalsy } from '../../../src'
import { mock, restore } from '../../stub/mockfs'
import { drainStream } from '../../stub/stream'
const expect = chai.expect
+1 -1
View File
@@ -1,5 +1,5 @@
import { expect } from 'chai'
import { Liquid, defaultOperators } from '../../../src/liquid'
import { Liquid, defaultOperators } from '../../../src'
describe('LiquidOptions#operators', function () {
let engine: Liquid
@@ -1,4 +1,5 @@
import { ParseError, Liquid } from '../../../../src/liquid'
import { Liquid } from '../../../src/liquid'
import { ParseError } from '../../../src'
import { expect, use } from 'chai'
import * as chaiAsPromised from 'chai-as-promised'
@@ -1,4 +1,4 @@
import { Liquid } from '../../../../src/liquid'
import { Liquid } from '../../../src/liquid'
import { expect, use } from 'chai'
import * as chaiAsPromised from 'chai-as-promised'
@@ -1,4 +1,4 @@
import { Liquid } from '../../../../src/liquid'
import { Liquid } from '../../../src/liquid'
import { expect, use } from 'chai'
import * as chaiAsPromised from 'chai-as-promised'
@@ -1,4 +1,4 @@
import { Liquid } from '../../../../src/liquid'
import { Liquid } from '../../../src/liquid'
import { expect, use } from 'chai'
import * as chaiAsPromised from 'chai-as-promised'
@@ -1,4 +1,4 @@
import { Liquid } from '../../../../src/liquid'
import { Liquid } from '../../../src/liquid'
import { expect, use } from 'chai'
import * as chaiAsPromised from 'chai-as-promised'
@@ -1,4 +1,4 @@
import { Liquid } from '../../../../src/liquid'
import { Liquid } from '../../../src/liquid'
import { expect, use } from 'chai'
import * as chaiAsPromised from 'chai-as-promised'
@@ -1,4 +1,4 @@
import { Liquid } from '../../../../src/liquid'
import { Liquid } from '../../../src/liquid'
import { expect, use } from 'chai'
import * as chaiAsPromised from 'chai-as-promised'
@@ -1,7 +1,8 @@
import { Drop, Liquid } from '../../../../src/liquid'
import { expect, use } from 'chai'
import * as chaiAsPromised from 'chai-as-promised'
import { Scope } from '../../../../src/context/scope'
import { Liquid } from '../../../src/liquid'
import { Drop } from '../../../src/drop/drop'
import { Scope } from '../../../src/context/scope'
use(chaiAsPromised)
@@ -1,4 +1,4 @@
import { Liquid } from '../../../../src/liquid'
import { Liquid } from '../../../src/liquid'
import * as chai from 'chai'
import * as chaiAsPromised from 'chai-as-promised'
@@ -1,6 +1,7 @@
import { Liquid, Drop } from '../../../../src/liquid'
import { Liquid } from '../../../src/liquid'
import { Drop } from '../../../src/drop/drop'
import { expect } from 'chai'
import { mock, restore } from '../../../stub/mockfs'
import { mock, restore } from '../../stub/mockfs'
describe('tags/include', function () {
let liquid: Liquid
@@ -1,4 +1,4 @@
import { Liquid } from '../../../../src/liquid'
import { Liquid } from '../../../src/liquid'
import { expect, use } from 'chai'
import * as chaiAsPromised from 'chai-as-promised'
@@ -1,4 +1,4 @@
import { Liquid } from '../../../../src/liquid'
import { Liquid } from '../../../src/liquid'
import { expect, use } from 'chai'
import * as chaiAsPromised from 'chai-as-promised'
@@ -1,7 +1,7 @@
import { Liquid } from '../../../../src/liquid'
import { expect, use } from 'chai'
import { mock, restore } from '../../../stub/mockfs'
import * as chaiAsPromised from 'chai-as-promised'
import { Liquid } from '../../../src/liquid'
import { mock, restore } from '../../stub/mockfs'
use(chaiAsPromised)
@@ -1,6 +1,6 @@
import { Liquid } from '../../../../src/liquid'
import { expect, use } from 'chai'
import * as chaiAsPromised from 'chai-as-promised'
import { Liquid } from '../../../src/liquid'
use(chaiAsPromised)
@@ -1,6 +1,6 @@
import { Liquid } from '../../../../src/liquid'
import * as chai from 'chai'
import * as chaiAsPromised from 'chai-as-promised'
import { Liquid } from '../../../src/liquid'
const expect = chai.expect
chai.use(chaiAsPromised)
@@ -1,7 +1,8 @@
import { Liquid, Drop } from '../../../../src/liquid'
import { expect, use } from 'chai'
import { mock, restore } from '../../../stub/mockfs'
import * as chaiAsPromised from 'chai-as-promised'
import { Liquid } from '../../../src/liquid'
import { Drop } from '../../../src/drop/drop'
import { mock, restore } from '../../stub/mockfs'
use(chaiAsPromised)
describe('tags/render', function () {
@@ -1,6 +1,6 @@
import { Liquid } from '../../../../src/liquid'
import { expect, use } from 'chai'
import * as chaiAsPromised from 'chai-as-promised'
import { Liquid } from '../../../src/liquid'
use(chaiAsPromised)
@@ -1,6 +1,6 @@
import { Liquid } from '../../../../src/liquid'
import { expect, use } from 'chai'
import * as chaiAsPromised from 'chai-as-promised'
import { Liquid } from '../../../src/liquid'
use(chaiAsPromised)
+1 -1
View File
@@ -1,6 +1,6 @@
import { expect } from 'chai'
import { matchOperator } from '../../../src/parser/match-operator'
import { defaultOperators } from '../../../src/types'
import { defaultOperators } from '../../../src'
import { createTrie } from '../../../src/util/operator-trie'
describe('parser/matchOperator()', function () {
+15
View File
@@ -0,0 +1,15 @@
import * as chai from 'chai'
import { ParseStream } from '../../../src/parser'
import { Token } from '../../../src/tokens'
const expect = chai.expect
describe('parseStream', () => {
it('should trigger "token" event', () => {
const token = { kind: 4 } as Token
const ps = new ParseStream([token], (token) => ({ token } as any))
let got
ps.on('token', token => { got = token }).start()
expect(got).to.equal(token)
})
})
+10 -12
View File
@@ -1,12 +1,11 @@
import { expect } from 'chai'
import { Context } from '../../../src/context/context'
import { HTMLToken } from '../../../src/tokens/html-token'
import { HTMLToken, TagToken } from '../../../src/tokens'
import { Render } from '../../../src/render/render'
import { HTML } from '../../../src/template/html'
import { SimpleEmitter } from '../../../src/emitters/simple-emitter'
import { toPromise } from '../../../src/util/async'
import { Tag } from '../../../src/template/tag/tag'
import { TagToken } from '../../../src/types'
import { Tag } from '../../../src/template/tag'
describe('render', function () {
let render: Render
@@ -42,17 +41,16 @@ describe('render', function () {
})
it('should render to html stream asyncly', function (done) {
const scope = new Context()
class CustomTag extends Tag {
render () {
return new Promise(
resolve => setTimeout(() => resolve('async tag'), 10)
)
}
}
const tpls = [
new HTML({ getContent: () => '<p>' } as HTMLToken),
new Tag({ content: 'foo', args: '', name: 'foo' } as TagToken, [], {
tags: {
get: () => ({
render: () => new Promise(
resolve => setTimeout(() => resolve('async tag'), 10)
)
})
}
} as any),
new CustomTag({ content: 'foo', args: '', name: 'foo' } as TagToken, [], {} as any),
new HTML({ getContent: () => '</p>' } as HTMLToken)
]
const stream = render.renderTemplatesToNodeStream(tpls, scope)
+16 -39
View File
@@ -1,81 +1,58 @@
import * as chai from 'chai'
import * as sinon from 'sinon'
import * as sinonChai from 'sinon-chai'
import { Context } from '../../../../src/context/context'
import { toPromise } from '../../../../src/util/async'
import { NumberToken } from '../../../../src/tokens/number-token'
import { QuotedToken } from '../../../../src/tokens/quoted-token'
import { IdentifierToken } from '../../../../src/tokens/identifier-token'
import { FilterMap } from '../../../../src/template/filter/filter-map'
import { Context } from '../../../../src/context'
import { toPromise } from '../../../../src/util'
import { IdentifierToken, NumberToken, QuotedToken } from '../../../../src/tokens'
import { Filter } from '../../../../src/template'
chai.use(sinonChai)
const expect = chai.expect
describe('filter', function () {
let ctx: Context
let filters: FilterMap
const ctx = new Context()
const liquid = {} as any
beforeEach(function () {
filters = new FilterMap(false, liquid)
ctx = new Context()
})
it('should create default filter if not registered', async function () {
const result = filters.create('foo', []) as any
expect(result.name).to.equal('foo')
})
it('should render input if filter not registered', async function () {
expect(await toPromise(filters.create('undefined', []).render('foo', ctx))).to.equal('foo')
it('should not change input if filter not registered', async function () {
const filter = new Filter('foo', undefined as any, [], liquid)
expect(await toPromise(filter.render('value', ctx))).to.equal('value')
})
it('should call filter impl with correct arguments', async function () {
const spy = sinon.spy()
filters.set('foo', spy)
const thirty = new NumberToken(new IdentifierToken('30', 0, 2), undefined)
await toPromise(filters.create('foo', [thirty]).render('foo', ctx))
const filter = new Filter('foo', spy, [thirty], liquid)
await toPromise(filter.render('foo', ctx))
expect(spy).to.have.been.calledWith('foo', 30)
})
it('should call filter impl with correct this', async function () {
const spy = sinon.spy()
filters.set('foo', spy)
const thirty = new NumberToken(new IdentifierToken('33', 0, 2), undefined)
await toPromise(filters.create('foo', [thirty]).render('foo', ctx))
await toPromise(new Filter('foo', spy, [thirty], liquid).render('foo', ctx))
expect(spy).to.have.been.calledOn(sinon.match.has('context', ctx))
expect(spy).to.have.been.calledOn(sinon.match.has('liquid', liquid))
})
it('should render a simple filter', async function () {
filters.set('upcase', x => x.toUpperCase())
expect(await toPromise(filters.create('upcase', []).render('foo', ctx))).to.equal('FOO')
expect(await toPromise(new Filter('upcase', (x: string) => x.toUpperCase(), [], liquid).render('foo', ctx))).to.equal('FOO')
})
it('should render filters with argument', async function () {
filters.set('add', (a, b) => a + b)
const two = new NumberToken(new IdentifierToken('2', 0, 1), undefined)
expect(await toPromise(filters.create('add', [two]).render(3, ctx))).to.equal(5)
expect(await toPromise(new Filter('add', (a: number, b: number) => a + b, [two], liquid).render(3, ctx))).to.equal(5)
})
it('should render filters with multiple arguments', async function () {
filters.set('add', (a, b, c) => a + b + c)
const two = new NumberToken(new IdentifierToken('2', 0, 1), undefined)
const c = new QuotedToken('"c"', 0, 3)
expect(await toPromise(filters.create('add', [two, c]).render(3, ctx))).to.equal('5c')
expect(await toPromise(new Filter('add', (a: number, b: number, c: number) => a + b + c, [two, c], liquid).render(3, ctx))).to.equal('5c')
})
it('should pass Objects/Drops as it is', async function () {
filters.set('name', a => a.constructor.name)
class Foo {}
expect(await toPromise(filters.create('name', []).render(new Foo(), ctx))).to.equal('Foo')
})
it('should not throw when filter name illegal', function () {
expect(function () {
filters.create('/', [])
}).to.not.throw()
expect(await toPromise(new Filter('name', (a: any) => a.constructor.name, [], liquid).render(new Foo(), ctx))).to.equal('Foo')
})
it('should support key value pairs', async function () {
filters.set('add', (a, b) => b[0] + ':' + (a + b[1]))
const two = new NumberToken(new IdentifierToken('2', 0, 1), undefined)
expect(await toPromise(filters.create('add', [['num', two]]).render(3, ctx))).to.equal('num:5')
expect(await toPromise(new Filter('add', (a: number, b: number[]) => b[0] + ':' + (a + b[1]), [['num', two]], liquid).render(3, ctx))).to.equal('num:5')
})
})
+1 -1
View File
@@ -1,6 +1,6 @@
import * as chai from 'chai'
import { toPromise } from '../../../src/util/async'
import { Hash } from '../../../src/template/tag/hash'
import { Hash } from '../../../src/template/hash'
import { Context } from '../../../src/context/context'
const expect = chai.expect
+1 -5
View File
@@ -4,16 +4,12 @@ import { Context } from '../../../src/context/context'
import { Output } from '../../../src/template/output'
import { OutputToken } from '../../../src/tokens/output-token'
import { defaultOptions } from '../../../src/liquid-options'
import { createTrie } from '../../../src/util/operator-trie'
import { defaultOperators } from '../../../src/types'
const expect = chai.expect
describe('Output', function () {
const emitter: any = { write: (html: string) => (emitter.html += html), html: '' }
const liquid = {
options: { operatorsTrie: createTrie(defaultOperators) }
} as any
const liquid = { options: {} } as any
beforeEach(() => { emitter.html = '' })
it('should stringify objects', async function () {
-16
View File
@@ -1,16 +0,0 @@
import { expect } from 'chai'
import { TagMap } from '../../../src/template/tag/tag-map'
describe('TagMap', function () {
it('should throw when not exist', function () {
const map = new TagMap()
expect(() => map.get('not-exist'))
.to.throw(/tag "not-exist" not found/)
})
it('should get previously set value', function () {
const map = new TagMap()
const impl = { render: () => 'foo' }
map.set('foo', impl)
expect(map.get('foo')).to.equal(impl)
})
})
-30
View File
@@ -1,30 +0,0 @@
import * as chai from 'chai'
import { Tag } from '../../../src/template/tag/tag'
import { Context } from '../../../src/context/context'
import * as sinon from 'sinon'
import * as sinonChai from 'sinon-chai'
import { TagToken } from '../../../src/tokens/tag-token'
import { toPromise } from '../../../src/util/async'
chai.use(sinonChai)
const expect = chai.expect
describe('Tag', function () {
const ctx = new Context()
const emitter: any = { write: (html: string) => (emitter.html += html), html: '' }
it('should call tag.render', async function () {
const spy = sinon.spy()
const token = {
content: 'foo',
args: '',
name: 'foo'
} as TagToken
await toPromise(new Tag(token, [], {
tags: {
get: () => ({ render: spy })
}
} as any).render(ctx, emitter))
expect(spy).to.have.been.called
})
})
+1 -1
View File
@@ -1,5 +1,5 @@
import * as chai from 'chai'
import t from '../../../src/util/strftime'
import { strftime as t } from '../../../src/util/strftime'
import { DateWithTimezone } from '../../stub/date-with-timezone'
const expect = chai.expect