Skip to content

Commit d20a043

Browse files
committed
feat: override output/tag delimiter, fixes #54
1 parent 3dbab23 commit d20a043

File tree

8 files changed

+84
-23
lines changed

8 files changed

+84
-23
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,10 @@ Otherwise, undefined variables will cause an exception. Defaults to `false`.
196196

197197
* `trim_output_left` is similiar to `trim_output_right`, whereas the `\n` is exclusive. Defaults to `false`. See [Whitespace Control][whitespace control] for details.
198198

199+
* `tag_delimiter_left` and `tag_delimiter_right` are used to override the delimiter for liquid tags.
200+
201+
* `output_delimiter_left` and `output_delimiter_right` are used to override the delimiter for liquid outputs.
202+
199203
* `greedy` is used to specify whether `trim_left`/`trim_right` is greedy. When set to `true`, all consecutive blank characters including `\n` will be trimed regardless of line breaks. Defaults to `true`.
200204

201205
## Register Filters

src/liquid-options.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ export interface LiquidOptions {
2323
trim_output_right?: boolean
2424
/** `trim_output_left` is similar to `trim_output_right`, whereas the `\n` is exclusive. Defaults to `false`. See Whitespace Control for details. */
2525
trim_output_left?: boolean
26+
/** `tag_delimiter_left` and `tag_delimiter_right` are used to override the delimiter for liquid tags **/
27+
tag_delimiter_left?: string,
28+
tag_delimiter_right?: string,
29+
/** `output_delimiter_left` and `output_delimiter_right` are used to override the delimiter for liquid outputs **/
30+
output_delimiter_left?: string,
31+
output_delimiter_right?: string,
2632
/** `greedy` is used to specify whether `trim_left`/`trim_right` is greedy. When set to `true`, all consecutive blank characters including `\n` will be trimed regardless of line breaks. Defaults to `true`. */
2733
greedy?: boolean
2834
}
@@ -55,6 +61,10 @@ const defaultOptions: NormalizedFullOptions = {
5561
trim_output_right: false,
5662
trim_output_left: false,
5763
greedy: true,
64+
tag_delimiter_left: '{%',
65+
tag_delimiter_right: '%}',
66+
output_delimiter_left: '{{',
67+
output_delimiter_right: '}}',
5868
strict_filters: false,
5969
strict_variables: false
6070
}

src/parser/delimited-token.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,15 @@ import Token from './token'
33
export default class DelimitedToken extends Token {
44
trimLeft: boolean
55
trimRight: boolean
6-
constructor (raw, pos, input, file, line) {
6+
constructor (raw, value, pos, input, file, line) {
77
super(raw, pos, input, file, line)
8-
this.trimLeft = raw[2] === '-'
9-
this.trimRight = raw[raw.length - 3] === '-'
10-
this.value = raw.slice(this.trimLeft ? 3 : 2, this.trimRight ? -3 : -2).trim()
8+
this.trimLeft = value[0] === '-'
9+
this.trimRight = value[value.length - 1] === '-'
10+
this.value = value
11+
.slice(
12+
this.trimLeft ? 1 : 0,
13+
this.trimRight ? -1 : value.length
14+
)
15+
.trim()
1116
}
1217
}

src/parser/output-token.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import DelimitedToken from './delimited-token'
22

33
export default class OutputToken extends DelimitedToken {
4-
constructor (raw, pos, input, file, line) {
5-
super(raw, pos, input, file, line)
4+
constructor (raw, value, pos, input, file, line) {
5+
super(raw, value, pos, input, file, line)
66
this.type = 'output'
77
}
88
}

src/parser/tag-token.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ import * as lexical from './lexical'
55
export default class TagToken extends DelimitedToken {
66
name: string
77
args: string
8-
constructor (raw, pos, input, file, line) {
9-
super(raw, pos, input, file, line)
8+
constructor (raw, value, pos, input, file, line) {
9+
super(raw, value, pos, input, file, line)
1010
this.type = 'tag'
1111
const match = this.value.match(lexical.tagLine)
1212
if (!match) {

src/parser/tokenizer.ts

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ export default class Tokenizer {
1515
}
1616
tokenize (input: string, file?: string) {
1717
const tokens = []
18+
const tagL = this.options.tag_delimiter_left
19+
const tagR = this.options.tag_delimiter_right
20+
const outputL = this.options.output_delimiter_left
21+
const outputR = this.options.output_delimiter_right
1822
let p = 0
1923
let curLine = 1
2024
let state = ParseState.HTML
@@ -28,30 +32,37 @@ export default class Tokenizer {
2832
curLine++
2933
lineBegin = p + 1
3034
}
31-
const bin = input.substr(p, 2)
3235
if (state === ParseState.HTML) {
33-
if (bin === '{{' || bin === '{%') {
36+
if (input.substr(p, outputL.length) === outputL) {
3437
if (buffer) tokens.push(new HTMLToken(buffer, col, input, file, line))
35-
buffer = bin
38+
buffer = outputL
3639
line = curLine
3740
col = p - lineBegin + 1
38-
p += 2
39-
state = bin === '{{' ? ParseState.OUTPUT : ParseState.TAG
41+
p += outputL.length
42+
state = ParseState.OUTPUT
43+
continue
44+
} else if (input.substr(p, tagL.length) === tagL) {
45+
if (buffer) tokens.push(new HTMLToken(buffer, col, input, file, line))
46+
buffer = tagL
47+
line = curLine
48+
col = p - lineBegin + 1
49+
p += tagL.length
50+
state = ParseState.TAG
4051
continue
4152
}
42-
} else if (state === ParseState.OUTPUT && bin === '}}') {
43-
buffer += '}}'
44-
tokens.push(new OutputToken(buffer, col, input, file, line))
45-
p += 2
53+
} else if (state === ParseState.OUTPUT && input.substr(p, outputR.length) === outputR) {
54+
buffer += outputR
55+
tokens.push(new OutputToken(buffer, buffer.slice(outputL.length, -outputR.length), col, input, file, line))
56+
p += outputR.length
4657
buffer = ''
4758
line = curLine
4859
col = p - lineBegin + 1
4960
state = ParseState.HTML
5061
continue
51-
} else if (bin === '%}') {
52-
buffer += '%}'
53-
tokens.push(new TagToken(buffer, col, input, file, line))
54-
p += 2
62+
} else if (input.substr(p, tagR.length) === tagR) {
63+
buffer += tagR
64+
tokens.push(new TagToken(buffer, buffer.slice(tagL.length, -tagR.length), col, input, file, line))
65+
p += tagR.length
5566
buffer = ''
5667
line = curLine
5768
col = p - lineBegin + 1

src/parser/whitespace-ctrl.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import DelimitedToken from 'src/parser/delimited-token'
22
import Token from 'src/parser/token'
33
import TagToken from 'src/parser/tag-token'
4-
import { LiquidOptions } from 'src/liquid-options'
4+
import { NormalizedFullOptions } from 'src/liquid-options'
55

6-
export default function whiteSpaceCtrl (tokens: Token[], options: LiquidOptions) {
6+
export default function whiteSpaceCtrl (tokens: Token[], options: NormalizedFullOptions) {
77
options = { greedy: true, ...options }
88
let inRaw = false
99

test/unit/liquid/delimiter.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { expect } from 'chai'
2+
import Liquid from 'src/liquid'
3+
4+
describe('LiquidOptions#*_delimiter_*', function () {
5+
it('should respect tag_delimiter_*', async function () {
6+
const engine = new Liquid({
7+
tag_delimiter_left: '<%=',
8+
tag_delimiter_right: '%>'
9+
})
10+
const html = await engine.parseAndRender('<%=if true%>foo<%=endif%> ')
11+
return expect(html).to.equal('foo ')
12+
})
13+
it('should respect output_delimiter_*', async function () {
14+
const engine = new Liquid({
15+
output_delimiter_left: '<<',
16+
output_delimiter_right: '>>'
17+
})
18+
const html = await engine.parseAndRender('<< "liquid" | capitalize >>')
19+
return expect(html).to.equal('Liquid')
20+
})
21+
it('should support trimming with tag_delimiter_* set', async function () {
22+
const engine = new Liquid({
23+
tag_delimiter_left: '<%=',
24+
tag_delimiter_right: '%>',
25+
trim_tag_left: true,
26+
trim_tag_right: true
27+
})
28+
const html = await engine.parseAndRender(' <%=if true%> \tfoo\t <%=endif%> ')
29+
return expect(html).to.equal('foo')
30+
})
31+
})

0 commit comments

Comments
 (0)