feat: implement liquid and echo tags, see #428

This commit is contained in:
James Prior
2021-12-19 21:09:26 +08:00
committed by Jun Yang
parent 5b2ea63b14
commit fde9924ee6
9 changed files with 243 additions and 1 deletions
+17
View File
@@ -177,4 +177,21 @@ describe('Issues', function () {
const html = await engine.render(tpl, { my_variable: 'foo' })
expect(html).to.equal('CONTENT for /tmp/prefix/foo-bar/suffix')
})
it('#428 Implement liquid/echo tags', () => {
const template = `{%- liquid
for value in array
assign double_value = value | times: 2
echo double_value | times: 2
unless forloop.last
echo '#'
endunless
endfor
echo '#'
echo double_value
-%}`
const engine = new Liquid()
const html = engine.parseAndRenderSync(template, { array: [1, 2, 3] })
expect(html).to.equal('4#8#12#6')
})
})
+43
View File
@@ -0,0 +1,43 @@
import { Liquid } from '../../../../src/liquid'
import { expect, use } from 'chai'
import * as chaiAsPromised from 'chai-as-promised'
use(chaiAsPromised)
describe('tags/echo', function () {
const liquid = new Liquid()
it('should output literals', async function () {
const src = '{% echo 1 %} {% echo "1" %} {% echo 1.1 %}'
const html = await liquid.parseAndRender(src)
return expect(html).to.equal('1 1 1.1')
})
it('should output variables', async function () {
const src = '{% echo people.users[0].name %}'
const html = await liquid.parseAndRender(src, { people: { users: [ { name: 'Sally' } ] } })
return expect(html).to.equal('Sally')
})
it('should apply filters before output', async function () {
const src = '{% echo user.name | upcase | prepend: "Hello, " | append: "!" %}'
const html = await liquid.parseAndRender(src, { user: { name: 'Sally' } })
return expect(html).to.equal('Hello, SALLY!')
})
it('should handle empty tag', async function () {
const src = '{% echo %}'
const html = await liquid.parseAndRender(src)
return expect(html).to.equal('')
})
it('should handle extra whitespace', async function () {
const src = `{% echo
user.name
| upcase | prepend:
"Hello, " | append: "!"
%}`
const html = await liquid.parseAndRender(src, { user: { name: 'Sally' } })
return expect(html).to.equal('Hello, SALLY!')
})
})
+76
View File
@@ -0,0 +1,76 @@
import { Liquid } from '../../../../src/liquid'
import { expect, use } from 'chai'
import * as chaiAsPromised from 'chai-as-promised'
use(chaiAsPromised)
describe('tags/liquid', function () {
const liquid = new Liquid()
it('should support shorthand syntax', async function () {
const src = `
{%- liquid
for value in array
echo value
unless forloop.last
echo '#'
endunless
endfor
-%}
`
const html = await liquid.parseAndRender(src, { array: [1, 2, 3] })
return expect(html).to.equal('1#2#3')
})
it('should support shorthand syntax with assignments and filters', async function () {
const src = `
{%- liquid
for value in array
assign double_value = value | times: 2
echo double_value | times: 2
unless forloop.last
echo '#'
endunless
endfor
echo '#'
echo double_value
-%}
`
const html = await liquid.parseAndRender(src, { array: [1, 2, 3] })
return expect(html).to.equal('4#8#12#6')
})
it('should handle empty tag', async function () {
const src = '{% liquid %}'
const html = await liquid.parseAndRender(src)
return expect(html).to.equal('')
})
it('should handle lines containing only whitespace', async function () {
const src = `{% liquid
echo 'hello '
\t
echo 'goodbye'
%}`
const html = await liquid.parseAndRender(src)
return expect(html).to.equal('hello goodbye')
})
it('should fail with carriage return terminated tags', async function () {
const src = [
'{%- liquid',
' for value in array',
' echo value',
' unless forloop.last',
' echo "#"',
' endunless',
'endfor',
'-%}'
].join('\r')
return expect(liquid.parseAndRender(src))
.to.be.rejectedWith(/not closed/)
})
})
+25
View File
@@ -11,6 +11,7 @@ import { OutputToken } from '../../../src/tokens/output-token'
import { HTMLToken } from '../../../src/tokens/html-token'
import { createTrie } from '../../../src/util/operator-trie'
import { defaultOperators } from '../../../src/types'
import { LiquidTagToken } from '../../../src/tokens/liquid-tag-token'
describe('Tokenizer', function () {
const trie = createTrie(defaultOperators)
@@ -500,4 +501,28 @@ describe('Tokenizer', function () {
expect(rhs.getText()).to.deep.equal('"\\""')
})
})
describe('#readLiquidTagTokens', () => {
it('should read newline terminated tokens', () => {
const tokenizer = new Tokenizer('echo \'hello\'', trie)
const tokens = tokenizer.readLiquidTagTokens()
expect(tokens.length).to.equal(1)
const tag = tokens[0]
expect(tag).instanceOf(LiquidTagToken)
expect(tag.name).to.equal('echo')
expect(tag.args).to.equal('\'hello\'')
})
it('should gracefully handle empty lines', () => {
const tokenizer = new Tokenizer(`
echo 'hello'
decrement foo
`, trie)
const tokens = tokenizer.readLiquidTagTokens()
expect(tokens.length).to.equal(2)
})
it('should throw if line does not start with an identifier', () => {
const tokenizer = new Tokenizer('!', trie)
expect(() => tokenizer.readLiquidTagTokens()).to.throw(/illegal liquid tag syntax/)
})
})
})