mirror of
https://github.com/harttle/liquidjs.git
synced 2026-09-18 14:00:39 -07:00
perf: introduce AST to avoid reparse
This commit is contained in:
@@ -6,11 +6,6 @@ use(chaiAsPromised)
|
||||
|
||||
describe('tags/decrement', function () {
|
||||
const liquid = new Liquid()
|
||||
it('should throw when variable expression illegal', function () {
|
||||
const src = '{% decrement / %}{{var}}'
|
||||
const ctx = {}
|
||||
return expect(liquid.parseAndRender(src, ctx)).to.be.rejectedWith(/illegal/)
|
||||
})
|
||||
|
||||
it('should decrement undefined variable', async function () {
|
||||
const src = '{% decrement var %}{% decrement var %}{% decrement var %}'
|
||||
|
||||
@@ -46,6 +46,12 @@ describe('tags/for', function () {
|
||||
.to.be.rejectedWith(/tag .* not closed/)
|
||||
})
|
||||
|
||||
it('should reject when for in not found', function () {
|
||||
const src = '{%for c alpha%}{{c}}'
|
||||
return expect(liquid.parseAndRender(src, scope))
|
||||
.to.be.rejectedWith('illegal tag: {%for c alpha%}, line:1, col:1')
|
||||
})
|
||||
|
||||
it('should reject when inner templates rejected', function () {
|
||||
const src = '{%for c in alpha%}{%throwingTag%}{%endfor%}'
|
||||
return expect(liquid.parseAndRender(src, scope))
|
||||
|
||||
@@ -83,6 +83,22 @@ describe('tags/include', function () {
|
||||
const html = await liquid.renderFile('with.html')
|
||||
return expect(html).to.equal('color:red, shape:rect')
|
||||
})
|
||||
it('should ignore if with value not specified', async function () {
|
||||
mock({
|
||||
'/with.html': '{% include "color" with, shape: "rect" %}',
|
||||
'/color.html': 'color:{{color}}, shape:{{shape}}'
|
||||
})
|
||||
const html = await liquid.renderFile('with.html')
|
||||
return expect(html).to.equal('color:, shape:rect')
|
||||
})
|
||||
it('should treat with as a valid key', async function () {
|
||||
mock({
|
||||
'/with.html': '{% include "color" with: "foo" %}',
|
||||
'/color.html': 'with:{{with}}'
|
||||
})
|
||||
const html = await liquid.renderFile('with.html')
|
||||
return expect(html).to.equal('with:foo')
|
||||
})
|
||||
it('should support include: with as Drop', async function () {
|
||||
class ColorDrop extends Drop {
|
||||
public valueOf (): string {
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { Liquid } from '../../../../src/liquid'
|
||||
import { expect } from 'chai'
|
||||
import { expect, use } from 'chai'
|
||||
import { mock, restore } from '../../../stub/mockfs'
|
||||
import * as chaiAsPromised from 'chai-as-promised'
|
||||
|
||||
use(chaiAsPromised)
|
||||
|
||||
describe('tags/layout', function () {
|
||||
let liquid: Liquid
|
||||
@@ -29,6 +32,15 @@ describe('tags/layout', function () {
|
||||
expect(e.message).to.match(/illegal argument ""/)
|
||||
})
|
||||
})
|
||||
it('should throw when filename resolved to falsy', function () {
|
||||
mock({
|
||||
'/parent.html': '{%layout foo%}'
|
||||
})
|
||||
return liquid.renderFile('/parent.html').catch(function (e) {
|
||||
expect(e.name).to.equal('RenderError')
|
||||
expect(e.message).to.match(/illegal filename "foo":"undefined"/)
|
||||
})
|
||||
})
|
||||
describe('anonymous block', function () {
|
||||
it('should handle anonymous block', async function () {
|
||||
mock({
|
||||
|
||||
@@ -121,9 +121,33 @@ describe('tags/render', function () {
|
||||
const html = await liquid.renderFile('index.html', { colors: ['red', 'green'] })
|
||||
expect(html).to.equal('1: red\n2: green\n')
|
||||
})
|
||||
it('should support for <non-array> as', async function () {
|
||||
mock({
|
||||
'/index.html': '{% render "item" for "green" as color %}',
|
||||
'/item.html': '{{forloop.index}}: {{color}}\n'
|
||||
})
|
||||
const html = await liquid.renderFile('index.html')
|
||||
expect(html).to.equal('1: green\n')
|
||||
})
|
||||
it('should support for without as', async function () {
|
||||
mock({
|
||||
'/index.html': '{% render "item" for colors %}',
|
||||
'/item.html': '{{forloop.index}}: {{color}}\n'
|
||||
})
|
||||
const html = await liquid.renderFile('index.html', { colors: ['red', 'green'] })
|
||||
expect(html).to.equal('1: \n2: \n')
|
||||
})
|
||||
it('should support for...as with other parameters', async function () {
|
||||
mock({
|
||||
'/index.html': '{% render "item" for colors as color with ".\n" as tail, sep: ". "%}',
|
||||
'/index.html': '{% render "item" for colors as color with ".\n" as tail sep: ". "%}',
|
||||
'/item.html': '{{forloop.index}}{{sep}}{{color}}{{tail}}'
|
||||
})
|
||||
const html = await liquid.renderFile('index.html', { colors: ['red', 'green'] })
|
||||
expect(html).to.equal('1. red.\n2. green.\n')
|
||||
})
|
||||
it('should support for...as with other parameters (comma separated)', async function () {
|
||||
mock({
|
||||
'/index.html': '{% render "item" for colors as color, with ".\n" as tail, sep: ". "%}',
|
||||
'/item.html': '{{forloop.index}}{{sep}}{{color}}{{tail}}'
|
||||
})
|
||||
const html = await liquid.renderFile('index.html', { colors: ['red', 'green'] })
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { Liquid } from '../../../../src/liquid'
|
||||
import { expect } from 'chai'
|
||||
import { expect, use } from 'chai'
|
||||
import * as chaiAsPromised from 'chai-as-promised'
|
||||
|
||||
use(chaiAsPromised)
|
||||
|
||||
describe('tags/tablerow', function () {
|
||||
const liquid = new Liquid()
|
||||
@@ -50,6 +53,12 @@ describe('tags/tablerow', function () {
|
||||
.to.be.rejectedWith(/tag .* not closed/)
|
||||
})
|
||||
|
||||
it('should throw when x in y not found', function () {
|
||||
const src = '{% tablerow i (1..3) %}{{ i }}'
|
||||
return expect(liquid.parseAndRender(src))
|
||||
.to.be.rejectedWith('illegal tag: {% tablerow i (1..3) %}, line:1, col:1')
|
||||
})
|
||||
|
||||
it('should support tablerow with range', async function () {
|
||||
const src = '{% tablerow i in (1..5) cols:2 %}{{ i }}{% endtablerow %}'
|
||||
const dst =
|
||||
|
||||
@@ -93,7 +93,7 @@ describe('Liquid', function () {
|
||||
})
|
||||
const tpls = await engine.getTemplate('mocha')
|
||||
expect(tpls.length).to.gte(1)
|
||||
expect(tpls[0].token.raw).to.contain('module.exports')
|
||||
expect(tpls[0].token.getText()).to.contain('module.exports')
|
||||
})
|
||||
})
|
||||
describe('#evalValue', function () {
|
||||
|
||||
@@ -21,7 +21,7 @@ describe('liquid#registerTag()', function () {
|
||||
it('should have access to ctx in render()', async () => {
|
||||
const liquid = new Liquid()
|
||||
liquid.registerTag('dynamic-string', {
|
||||
render: async (ctx) => ctx.get('c')
|
||||
render: async (ctx) => ctx.get(['c'])
|
||||
})
|
||||
const html = await liquid.parseAndRender(`A{% dynamic-string %}C`, {
|
||||
c: 'B'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { normalize } from '../../../src/liquid-options'
|
||||
import { expect } from 'chai'
|
||||
|
||||
describe('LiquidOptions', function () {
|
||||
describe('LiquidOptions#root', function () {
|
||||
describe('#normalize ()', function () {
|
||||
it('should normalize string typed root array', function () {
|
||||
const options = normalize({ root: 'foo' })
|
||||
@@ -45,7 +45,7 @@ describe('LiquidOptions#trimming', function () {
|
||||
const html = await engine.parseAndRender(src, ctx)
|
||||
return expect(html).to.equal('aharttle')
|
||||
})
|
||||
it('should respect to greedy:false by default', async function () {
|
||||
it('should allow greedy:false', async function () {
|
||||
const engine = new Liquid({ greedy: false } as any)
|
||||
const html = await engine.parseAndRender(src, ctx)
|
||||
return expect(html).to.equal('\n a \nharttle ')
|
||||
|
||||
@@ -0,0 +1,450 @@
|
||||
import { expect } from 'chai'
|
||||
import { Liquid } from '../../../src/liquid'
|
||||
|
||||
const liquid = new Liquid()
|
||||
|
||||
const cases = [
|
||||
{
|
||||
text: `
|
||||
<div>
|
||||
<p>
|
||||
{{ 'John' }}
|
||||
</p>
|
||||
</div>
|
||||
`,
|
||||
expected: `
|
||||
<div>
|
||||
<p>
|
||||
John
|
||||
</p>
|
||||
</div>
|
||||
`
|
||||
}, {
|
||||
text: `
|
||||
<div>
|
||||
<p>
|
||||
|
||||
|
||||
{{- 'John' -}}
|
||||
|
||||
|
||||
</p>
|
||||
</div>
|
||||
`,
|
||||
expected: `
|
||||
<div>
|
||||
<p>John</p>
|
||||
</div>
|
||||
`
|
||||
}, {
|
||||
text: `
|
||||
<div>
|
||||
<p>
|
||||
|
||||
|
||||
{%- if true -%}
|
||||
yes
|
||||
{%- endif -%}
|
||||
|
||||
|
||||
</p>
|
||||
</div>
|
||||
`,
|
||||
expected: `
|
||||
<div>
|
||||
<p>yes</p>
|
||||
</div>
|
||||
`
|
||||
}, {
|
||||
text: `
|
||||
<div>
|
||||
<p>
|
||||
{% if true %}
|
||||
yes
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
`,
|
||||
expected: `
|
||||
<div>
|
||||
<p>
|
||||
|
||||
yes
|
||||
|
||||
</p>
|
||||
</div>
|
||||
`
|
||||
}, {
|
||||
text: `
|
||||
<div>
|
||||
<p>
|
||||
{% if false %}
|
||||
no
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
`,
|
||||
expected: `
|
||||
<div>
|
||||
<p>
|
||||
|
||||
</p>
|
||||
</div>
|
||||
`
|
||||
}, {
|
||||
text: '<p>{{- \'John\' -}}</p>',
|
||||
expected: '<p>John</p>'
|
||||
}, {
|
||||
text: '<p>{%- if true -%}yes{%- endif -%}</p>',
|
||||
expected: '<p>yes</p>'
|
||||
}, {
|
||||
text: '<p>{%- if false -%}no{%- endif -%}</p>',
|
||||
expected: '<p></p>'
|
||||
}, {
|
||||
text: '<p> {%- if true %} yes {% endif -%} </p>',
|
||||
expected: '<p> yes </p>'
|
||||
}, {
|
||||
text: '<p> {%- if false %} no {% endif -%} </p>',
|
||||
expected: '<p></p>'
|
||||
}, {
|
||||
text: '<p> {% if true -%} yes {%- endif %} </p>',
|
||||
expected: '<p> yes </p>'
|
||||
}, {
|
||||
text: '<p> {% if false -%} no {%- endif %} </p>',
|
||||
expected: '<p> </p>'
|
||||
}, {
|
||||
text: '<p> {% if true -%} yes {% endif -%} </p>',
|
||||
expected: '<p> yes </p>'
|
||||
}, {
|
||||
text: '<p> {% if false -%} no {% endif -%} </p>',
|
||||
expected: '<p> </p>'
|
||||
}, {
|
||||
text: '<p> {%- if true %} yes {%- endif %} </p>',
|
||||
expected: '<p> yes </p>'
|
||||
}, {
|
||||
text: '<p> {%- if false %} no {%- endif %} </p>',
|
||||
expected: '<p> </p>'
|
||||
}, {
|
||||
text: `
|
||||
<div>
|
||||
<p>
|
||||
{{- 'John' }}
|
||||
</p>
|
||||
</div>
|
||||
`,
|
||||
expected: `
|
||||
<div>
|
||||
<p>John
|
||||
</p>
|
||||
</div>
|
||||
`
|
||||
}, {
|
||||
text: `
|
||||
<div>
|
||||
<p>
|
||||
{%- if true %}
|
||||
yes
|
||||
{%- endif %}
|
||||
</p>
|
||||
</div>
|
||||
`,
|
||||
expected: `
|
||||
<div>
|
||||
<p>
|
||||
yes
|
||||
</p>
|
||||
</div>
|
||||
`
|
||||
}, {
|
||||
text: `
|
||||
<div>
|
||||
<p>
|
||||
{%- if false %}
|
||||
no
|
||||
{%- endif %}
|
||||
</p>
|
||||
</div>
|
||||
`,
|
||||
expected: `
|
||||
<div>
|
||||
<p>
|
||||
</p>
|
||||
</div>
|
||||
`
|
||||
}, {
|
||||
text: `
|
||||
<div>
|
||||
<p>
|
||||
{{ 'John' -}}
|
||||
</p>
|
||||
</div>
|
||||
`,
|
||||
expected: `
|
||||
<div>
|
||||
<p>
|
||||
John</p>
|
||||
</div>
|
||||
`
|
||||
}, {
|
||||
text: `
|
||||
<div>
|
||||
<p>
|
||||
{% if true -%}
|
||||
yes
|
||||
{% endif -%}
|
||||
</p>
|
||||
</div>
|
||||
`,
|
||||
expected: `
|
||||
<div>
|
||||
<p>
|
||||
yes
|
||||
</p>
|
||||
</div>
|
||||
`
|
||||
}, {
|
||||
text: `
|
||||
<div>
|
||||
<p>
|
||||
{% if false -%}
|
||||
no
|
||||
{% endif -%}
|
||||
</p>
|
||||
</div>
|
||||
`,
|
||||
expected: `
|
||||
<div>
|
||||
<p>
|
||||
</p>
|
||||
</div>
|
||||
`
|
||||
}, {
|
||||
text: `
|
||||
<div>
|
||||
<p>
|
||||
{%- if true %}
|
||||
yes
|
||||
{% endif -%}
|
||||
</p>
|
||||
</div>
|
||||
`,
|
||||
expected: `
|
||||
<div>
|
||||
<p>
|
||||
yes
|
||||
</p>
|
||||
</div>
|
||||
`
|
||||
}, {
|
||||
text: `
|
||||
<div>
|
||||
<p>
|
||||
{%- if false %}
|
||||
no
|
||||
{% endif -%}
|
||||
</p>
|
||||
</div>
|
||||
`,
|
||||
expected: `
|
||||
<div>
|
||||
<p></p>
|
||||
</div>
|
||||
`
|
||||
}, {
|
||||
text: `
|
||||
<div>
|
||||
<p>
|
||||
{% if true -%}
|
||||
yes
|
||||
{%- endif %}
|
||||
</p>
|
||||
</div>
|
||||
`,
|
||||
expected: `
|
||||
<div>
|
||||
<p>
|
||||
yes
|
||||
</p>
|
||||
</div>
|
||||
`
|
||||
}, {
|
||||
text: `
|
||||
<div>
|
||||
<p>
|
||||
{% if false -%}
|
||||
no
|
||||
{%- endif %}
|
||||
</p>
|
||||
</div>
|
||||
`,
|
||||
expected: `
|
||||
<div>
|
||||
<p>
|
||||
|
||||
</p>
|
||||
</div>
|
||||
`
|
||||
}, {
|
||||
text: `
|
||||
<div>
|
||||
<p>
|
||||
{{- 'John' -}}
|
||||
</p>
|
||||
</div>
|
||||
`,
|
||||
expected: `
|
||||
<div>
|
||||
<p>John</p>
|
||||
</div>
|
||||
`
|
||||
}, {
|
||||
text: `
|
||||
<div>
|
||||
<p>
|
||||
{%- if true -%}
|
||||
yes
|
||||
{%- endif -%}
|
||||
</p>
|
||||
</div>
|
||||
`,
|
||||
expected: `
|
||||
<div>
|
||||
<p>yes</p>
|
||||
</div>
|
||||
`
|
||||
}, {
|
||||
text: `
|
||||
<div>
|
||||
<p>
|
||||
{%- if false -%}
|
||||
no
|
||||
{%- endif -%}
|
||||
</p>
|
||||
</div>
|
||||
`,
|
||||
expected: `
|
||||
<div>
|
||||
<p></p>
|
||||
</div>
|
||||
`
|
||||
}, {
|
||||
text: `
|
||||
<div>
|
||||
<p>
|
||||
{{- 'John' -}},
|
||||
{{- '30' -}}
|
||||
</p>
|
||||
</div>
|
||||
`,
|
||||
expected: `
|
||||
<div>
|
||||
<p>John,30</p>
|
||||
</div>
|
||||
`
|
||||
}, {
|
||||
text: `
|
||||
<div>
|
||||
<p>
|
||||
{%- if true -%}
|
||||
yes
|
||||
{%- endif -%}
|
||||
</p>
|
||||
</div>
|
||||
`,
|
||||
expected: `
|
||||
<div>
|
||||
<p>yes</p>
|
||||
</div>
|
||||
`
|
||||
}, {
|
||||
text: `
|
||||
<div>
|
||||
<p>
|
||||
{%- if false -%}
|
||||
no
|
||||
{%- endif -%}
|
||||
</p>
|
||||
</div>
|
||||
`,
|
||||
expected: `
|
||||
<div>
|
||||
<p></p>
|
||||
</div>
|
||||
`
|
||||
}, {
|
||||
text: `
|
||||
<div>
|
||||
<p>
|
||||
{{- 'John' -}}
|
||||
{{- '30' -}}
|
||||
</p>
|
||||
<b>
|
||||
{{ 'John' -}}
|
||||
{{- '30' }}
|
||||
</b>
|
||||
<i>
|
||||
{{- 'John' }}
|
||||
{{ '30' -}}
|
||||
</i>
|
||||
</div>
|
||||
`,
|
||||
expected: `
|
||||
<div>
|
||||
<p>John30</p>
|
||||
<b>
|
||||
John30
|
||||
</b>
|
||||
<i>John
|
||||
30</i>
|
||||
</div>
|
||||
`
|
||||
}, {
|
||||
text: `
|
||||
<div>
|
||||
{%- if true -%}
|
||||
{%- if true -%}
|
||||
<p>
|
||||
{{- 'John' -}}
|
||||
</p>
|
||||
{%- endif -%}
|
||||
{%- endif -%}
|
||||
</div>
|
||||
`,
|
||||
expected: `
|
||||
<div><p>John</p></div>
|
||||
`
|
||||
}, {
|
||||
text: `
|
||||
<div>
|
||||
{% raw %}
|
||||
{%- if true -%}
|
||||
<p>
|
||||
{{- 'John' -}}
|
||||
</p>
|
||||
{%- endif -%}
|
||||
{% endraw %}
|
||||
</div>
|
||||
`,
|
||||
expected: `
|
||||
<div>
|
||||
|
||||
{%- if true -%}
|
||||
<p>
|
||||
{{- 'John' -}}
|
||||
</p>
|
||||
{%- endif -%}
|
||||
|
||||
</div>
|
||||
`
|
||||
}
|
||||
]
|
||||
|
||||
describe('Whitespace Control', function () {
|
||||
cases.forEach(item => it(
|
||||
item.text,
|
||||
async () => {
|
||||
const html = await liquid.parseAndRender(item.text)
|
||||
expect(html).to.equal(item.expected)
|
||||
}
|
||||
))
|
||||
})
|
||||
@@ -41,11 +41,6 @@ describe('error', function () {
|
||||
const err = await expect(engine.parseAndRender(html)).be.rejected
|
||||
expect(err.token.input).to.equal(html)
|
||||
})
|
||||
it('should contain line number in err.token.line', async function () {
|
||||
const err = await expect(engine.parseAndRender('1\n2\n{% . a %}\n4')).be.rejected
|
||||
expect(err.name).to.equal('TokenizationError')
|
||||
expect(err.token.line).to.equal(3)
|
||||
})
|
||||
it('should contain stack in err.stack', async function () {
|
||||
const err = await expect(engine.parseAndRender('{% . a %}')).be.rejected
|
||||
expect(err.message).to.contain('illegal tag syntax')
|
||||
@@ -58,11 +53,11 @@ describe('error', function () {
|
||||
expect(err.stack).to.not.contain('at Object.parse')
|
||||
})
|
||||
})
|
||||
it('should throw error with line and pos if tag unmatched', async function () {
|
||||
it('should throw error with [line, col] if tag unmatched', async function () {
|
||||
const err = await expect(engine.parseAndRender('1\n2\nfoo{% assign a = 4 }\n4')).be.rejected
|
||||
console.log(err.stack)
|
||||
expect(err.name).to.equal('TokenizationError')
|
||||
expect(err.token.line).to.equal(3)
|
||||
expect(err.token.col).to.equal(4)
|
||||
expect(err.message).to.equal('tag "{% assign a =..." not closed, line:3, col:4')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -175,12 +170,6 @@ describe('error', function () {
|
||||
expect(err.stack).to.contain(message.join('\n'))
|
||||
expect(err.name).to.equal('RenderError')
|
||||
})
|
||||
it('should contain line number in err.token.line', async function () {
|
||||
const src = '1\n2\n{{1|throwingFilter}}\n4'
|
||||
const err = await expect(engine.parseAndRender(src)).be.rejected
|
||||
expect(err.token.line).to.equal(3)
|
||||
expect(err.name).to.equal('RenderError')
|
||||
})
|
||||
it('should contain stack in err.stack', async function () {
|
||||
const err = await expect(engine.parseAndRender('{%rejectingTag%}')).be.rejected
|
||||
expect(err.message).to.contain('intended render reject')
|
||||
@@ -256,12 +245,6 @@ describe('error', function () {
|
||||
expect(err.stack).to.contain(message.join('\n'))
|
||||
})
|
||||
|
||||
it('should contain line number in err.token.line', async function () {
|
||||
const html = '<html>\n<head>\n\n{% raw %}\n\n'
|
||||
const err = await expect(engine.parseAndRender(html)).be.rejected
|
||||
expect(err.token.line).to.equal(4)
|
||||
})
|
||||
|
||||
it('should contain stack in err.stack', async function () {
|
||||
const err = await expect(engine.parseAndRender('{% -a %}')).be.rejected
|
||||
expect(err.stack).to.contain('ParseError: tag "-a" not found')
|
||||
|
||||
Reference in New Issue
Block a user