Skip to content

Commit

Permalink
fix: further improve rendering for if and assign tag
Browse files Browse the repository at this point in the history
  • Loading branch information
Qing Ye committed Jul 20, 2019
1 parent 6c5ef6c commit 81924f8
Show file tree
Hide file tree
Showing 7 changed files with 60 additions and 18 deletions.
2 changes: 1 addition & 1 deletion src/builtin/filters/string.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export default {
_.stringify(v).split(pattern).join(replacement),
'replace_first': (v: string, arg1: string, arg2: string) => _.stringify(v).replace(arg1, arg2),
'rstrip': (str: string) => _.stringify(str).replace(/\s+$/, ''),
'split': (v: string, arg: string) => _.stringify(v).split(arg),
'split': (v: string, arg: string) => _.stringify(v).replace(new RegExp(`(${_.escapeRegExp(arg)})+$`), '').split(arg),
'strip': (v: string) => _.stringify(v).trim(),
'strip_newlines': (v: string) => _.stringify(v).replace(/\n/g, ''),
'truncate': (v: string, l: number = 50, o: string = '...') => {
Expand Down
14 changes: 5 additions & 9 deletions src/builtin/tags/assign.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,19 @@ import Context from '../../context/context'
import ITagImplOptions from '../../template/tag/itag-impl-options'

const re = new RegExp(`(${identifier.source})\\s*=\\s*([^]*)`)
const closingOutputRe = new RegExp(`\\}\\}`, 'g')

export default {
parse: function (token: TagToken) {
const match = token.args.match(re) as RegExpMatchArray
assert(match, `illegal token ${token.raw}`)
this.key = match[1]
this.value = match[2]
this.token = token
},
render: async function (ctx: Context) {
let parsedValue
const closingCount = (this.value.match(closingOutputRe) || []).length
if (this.value.startsWith(ctx.opts.outputDelimiterLeft) && closingCount === 1) {
parsedValue = await this.liquid.parseAndRender(this.value, ctx.getAll())
} else {
parsedValue = await this.liquid.evalValue(this.value, ctx)
}
ctx.front()[this.key] = parsedValue
const value = this.value
.replace(ctx.opts.outputDelimiterLeft, '')
.replace(ctx.opts.outputDelimiterRight, '')
ctx.front()[this.key] = await this.liquid.evalValue(value, ctx)
}
} as ITagImplOptions
7 changes: 5 additions & 2 deletions src/builtin/tags/if.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import { evalExp, isTruthy } from '../../render/syntax'
import { escapeRegExp } from '../../util/underscore'
import TagToken from '../../parser/tag-token'
import Token from '../../parser/token'
import Context from '../../context/context'
import ITemplate from '../../template/itemplate'
import ITagImplOptions from '../../template/tag/itag-impl-options'
import ParseStream from '../../parser/parse-stream'

const re = new RegExp(`\\{\\{\\s*(.*?)\\s*\\}\\}`)

export default {
parse: function (tagToken: TagToken, remainTokens: Token[]) {
this.branches = []
Expand Down Expand Up @@ -36,6 +35,10 @@ export default {
},

render: async function (ctx: Context) {
const re = new RegExp(
`${escapeRegExp(ctx.opts.outputDelimiterLeft)}\\s*(.*?)\\s*${escapeRegExp(ctx.opts.outputDelimiterRight)}`
)

for (const branch of this.branches) {
const parsedCond = branch.cond.replace(re, '$1')
const cond = await evalExp(parsedCond, ctx)
Expand Down
13 changes: 7 additions & 6 deletions src/render/syntax.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as lexical from '../parser/lexical'
import assert from '../util/assert'
import Context from '../context/context'
import { range, last, isFunction } from '../util/underscore'
import { range, last, isFunction, escapeRegExp } from '../util/underscore'
import { isComparable } from '../drop/icomparable'
import { NullDrop } from '../drop/null-drop'
import { EmptyDrop } from '../drop/empty-drop'
Expand Down Expand Up @@ -87,18 +87,19 @@ async function parseValue (str: string | undefined, ctx: Context): Promise<any>
if (!isNaN(Number(str))) return Number(str)
if ((str[0] === '"' || str[0] === "'") && str[0] === last(str)) return str.slice(1, -1)

// for Braze, strip {{ and }}
const re = new RegExp(
`${escapeRegExp(ctx.opts.outputDelimiterLeft)}\\s*(.*?)\\s*${escapeRegExp(ctx.opts.outputDelimiterRight)}`
)
str = str.replace(re, '$1')

// extension for Braze attributes
let match
if ((match = str.match(lexical.attribute))) {
if (match[1]) return ctx.get(`${match[1]}['${match[2]}']`)
return ctx.get(match[2])
}

// for Braze, strip {{ and }}
str = str
.replace(ctx.opts.outputDelimiterLeft, '')
.replace(ctx.opts.outputDelimiterRight, '')

return ctx.get(str)
}

Expand Down
24 changes: 24 additions & 0 deletions src/util/underscore.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,29 @@
const toStr = Object.prototype.toString

const specials = [
'-',
'[',
']',
'/',
'{',
'}',
'(',
')',
'*',
'+',
'?',
'.',
'\\',
'^',
'$',
'|'
]
const regex = RegExp('[' + specials.join('\\') + ']', 'g')

export function escapeRegExp (str: string) {
return str.replace(regex, '\\$&')
}

/*
* Checks if value is classified as a String primitive or object.
* @param {any} value The value to check.
Expand Down
3 changes: 3 additions & 0 deletions test/integration/builtin/filters/string.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,9 @@ describe('filters/string', function () {
'{% endfor %}',
'John Paul George Ringo ')
})
it('should ignore empty string at the end', function () {
return test('{% assign a = ",123,456,," | split: "," %}{{a}}', '["", "123", "456"]')
})
it('should support strip', function () {
return test('{{ " So much room for activities! " | strip }}',
'So much room for activities!')
Expand Down
15 changes: 15 additions & 0 deletions test/integration/builtin/tags/assign.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,26 @@ describe('tags/assign', function () {
const html = await liquid.parseAndRender(src, { bar: 'baz', a: { b: 'test bar' } })
return expect(html).to.equal('test baz')
})
it('should support object in variable', async function () {
const src = '{% assign foo = {{a}} %}{{foo.b}}'
const html = await liquid.parseAndRender(src, { a: { b: 'test' } })
return expect(html).to.equal('test')
})
it('should support variable with filter', async function () {
const src = '{% assign foo = {{a | slice: 0, 2 }} %}{{foo}}'
const html = await liquid.parseAndRender(src, { a: [1, 2, 3] })
return expect(html).to.equal('[1, 2]')
})
it('should support spaces around =', async function () {
const src = '{% assign foo = {{bar}} %}{{foo}}'
const html = await liquid.parseAndRender(src, { bar: 'value_of_bar' })
return expect(html).to.equal('value_of_bar')
})
it('should support variable and filter', async function () {
const src = '{% assign foo = {{bar}} | split: "|" %}{{foo}}'
const html = await liquid.parseAndRender(src, { bar: '123|' })
return expect(html).to.equal('["123"]')
})
it('should assign var-1', async function () {
const src = '{% assign var-1 = 5 %}{{ var-1 }}'
const html = await liquid.parseAndRender(src)
Expand Down

0 comments on commit 81924f8

Please sign in to comment.