mirror of
https://github.com/harttle/liquidjs.git
synced 2026-09-15 04:10:40 -07:00
feat: move filters/tags to instances, fixes #188
This commit is contained in:
@@ -19,6 +19,7 @@
|
|||||||
"no-unused-vars": "off",
|
"no-unused-vars": "off",
|
||||||
"indent": "off",
|
"indent": "off",
|
||||||
"no-dupe-class-members": "off",
|
"no-dupe-class-members": "off",
|
||||||
|
"no-useless-constructor": "off",
|
||||||
"@typescript-eslint/indent": ["error", 2],
|
"@typescript-eslint/indent": ["error", 2],
|
||||||
"@typescript-eslint/explicit-function-return-type": "off",
|
"@typescript-eslint/explicit-function-return-type": "off",
|
||||||
"@typescript-eslint/no-empty-function": "off",
|
"@typescript-eslint/no-empty-function": "off",
|
||||||
|
|||||||
Generated
+16
-16
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "liquidjs",
|
"name": "liquidjs",
|
||||||
"version": "9.6.2",
|
"version": "9.8.0",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -905,9 +905,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@types/body-parser": {
|
"@types/body-parser": {
|
||||||
"version": "1.17.0",
|
"version": "1.19.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.17.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz",
|
||||||
"integrity": "sha512-a2+YeUjPkztKJu5aIF2yArYFQQp8d51wZ7DavSHjFuY1mqVgidGyzEQ41JIVNy82fXj8yPgy2vJmfIywgESW6w==",
|
"integrity": "sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/connect": "*",
|
"@types/connect": "*",
|
||||||
@@ -930,9 +930,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@types/connect": {
|
"@types/connect": {
|
||||||
"version": "3.4.32",
|
"version": "3.4.33",
|
||||||
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.32.tgz",
|
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.33.tgz",
|
||||||
"integrity": "sha512-4r8qa0quOvh7lGD0pre62CAb1oni1OO6ecJLGCezTmhQ8Fz50Arx9RUszryR8KlgK6avuSXvviL6yWyViQABOg==",
|
"integrity": "sha512-2+FrkXY4zllzTNfJth7jOqEHC+enpLeGslEhpnTAkg21GkRrWV4SsAtqchtT4YS9/nODBU2/ZfsBY2X4J/dX7A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
@@ -963,9 +963,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@types/express": {
|
"@types/express": {
|
||||||
"version": "4.17.0",
|
"version": "4.17.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.2.tgz",
|
||||||
"integrity": "sha512-CjaMu57cjgjuZbh9DpkloeGxV45CnMGlVd+XpG7Gm9QgVrd7KFq+X4HY0vM+2v0bczS48Wg7bvnMY5TN+Xmcfw==",
|
"integrity": "sha512-5mHFNyavtLoJmnusB8OKJ5bshSzw+qkMIBAobLrIM48HJvunFva9mOa6aBwh64lBFyNwBbs0xiEFuj4eU/NjCA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/body-parser": "*",
|
"@types/body-parser": "*",
|
||||||
@@ -974,9 +974,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@types/express-serve-static-core": {
|
"@types/express-serve-static-core": {
|
||||||
"version": "4.16.7",
|
"version": "4.17.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.16.7.tgz",
|
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.2.tgz",
|
||||||
"integrity": "sha512-847KvL8Q1y3TtFLRTXcVakErLJQgdpFSaq+k043xefz9raEf0C7HalpSY7OW5PyjCnY8P7bPW5t/Co9qqp+USg==",
|
"integrity": "sha512-El9yMpctM6tORDAiBwZVLMcxoTMcqqRO9dVyYcn7ycLWbvR8klrDn8CAOwRfZujZtWD7yS/mshTdz43jMOejbg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/node": "*",
|
"@types/node": "*",
|
||||||
@@ -1060,9 +1060,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@types/serve-static": {
|
"@types/serve-static": {
|
||||||
"version": "1.13.2",
|
"version": "1.13.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.3.tgz",
|
||||||
"integrity": "sha512-/BZ4QRLpH/bNYgZgwhKEh+5AsboDBcUdlBYgzoLX0fpj3Y2gp6EApyOlM3bK53wQS/OE1SrdSYBAbux2D1528Q==",
|
"integrity": "sha512-oprSwp094zOglVrXdlo/4bAHtKTAxX6VT8FOZlBKrmyLbNvE1zxZyJ6yikMVtHIvwP45+ZQGJn+FdXGKTozq0g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/express-serve-static-core": "*",
|
"@types/express-serve-static-core": "*",
|
||||||
|
|||||||
+2
-1
@@ -66,7 +66,7 @@
|
|||||||
"@types/benchmark": "^1.0.31",
|
"@types/benchmark": "^1.0.31",
|
||||||
"@types/chai": "^4.1.7",
|
"@types/chai": "^4.1.7",
|
||||||
"@types/chai-as-promised": "^7.1.0",
|
"@types/chai-as-promised": "^7.1.0",
|
||||||
"@types/express": "^4.16.1",
|
"@types/express": "^4.17.2",
|
||||||
"@types/jsdom": "^12.2.2",
|
"@types/jsdom": "^12.2.2",
|
||||||
"@types/mocha": "^5.2.6",
|
"@types/mocha": "^5.2.6",
|
||||||
"@types/sinon": "^7.0.6",
|
"@types/sinon": "^7.0.6",
|
||||||
@@ -118,6 +118,7 @@
|
|||||||
{
|
{
|
||||||
"assets": [
|
"assets": [
|
||||||
"package.json",
|
"package.json",
|
||||||
|
"package-lock.json",
|
||||||
"docs",
|
"docs",
|
||||||
"CHANGELOG.md"
|
"CHANGELOG.md"
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { assert } from '../../util/assert'
|
import { assert } from '../../util/assert'
|
||||||
import { identifier } from '../../parser/lexical'
|
import { identifier } from '../../parser/lexical'
|
||||||
import { ITagImplOptions, TagToken, Context } from '../../types'
|
import { TagImplOptions, TagToken, Context } from '../../types'
|
||||||
|
|
||||||
const re = new RegExp(`(${identifier.source})\\s*=([^]*)`)
|
const re = new RegExp(`(${identifier.source})\\s*=([^]*)`)
|
||||||
|
|
||||||
@@ -14,4 +14,4 @@ export default {
|
|||||||
render: function * (ctx: Context) {
|
render: function * (ctx: Context) {
|
||||||
ctx.front()[this.key] = yield this.liquid._evalValue(this.value, ctx)
|
ctx.front()[this.key] = yield this.liquid._evalValue(this.value, ctx)
|
||||||
}
|
}
|
||||||
} as ITagImplOptions
|
} as TagImplOptions
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import BlockMode from '../../context/block-mode'
|
import BlockMode from '../../context/block-mode'
|
||||||
import { ParseStream, TagToken, Token, ITemplate, Context, ITagImplOptions, Emitter, Hash } from '../../types'
|
import { ParseStream, TagToken, Token, Template, Context, TagImplOptions, Emitter, Hash } from '../../types'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
parse: function (token: TagToken, remainTokens: Token[]) {
|
parse: function (token: TagToken, remainTokens: Token[]) {
|
||||||
const match = /\w+/.exec(token.args)
|
const match = /\w+/.exec(token.args)
|
||||||
this.block = match ? match[0] : ''
|
this.block = match ? match[0] : ''
|
||||||
this.tpls = [] as ITemplate[]
|
this.tpls = [] as Template[]
|
||||||
const stream: ParseStream = this.liquid.parser.parseStream(remainTokens)
|
const stream: ParseStream = this.liquid.parser.parseStream(remainTokens)
|
||||||
.on('tag:endblock', () => stream.stop())
|
.on('tag:endblock', () => stream.stop())
|
||||||
.on('template', (tpl: ITemplate) => this.tpls.push(tpl))
|
.on('template', (tpl: Template) => this.tpls.push(tpl))
|
||||||
.on('end', () => {
|
.on('end', () => {
|
||||||
throw new Error(`tag ${token.raw} not closed`)
|
throw new Error(`tag ${token.raw} not closed`)
|
||||||
})
|
})
|
||||||
@@ -28,4 +28,4 @@ export default {
|
|||||||
}
|
}
|
||||||
emitter.write(html)
|
emitter.write(html)
|
||||||
}
|
}
|
||||||
} as ITagImplOptions
|
} as TagImplOptions
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { assert } from '../../util/assert'
|
import { assert } from '../../util/assert'
|
||||||
import { identifier } from '../../parser/lexical'
|
import { identifier } from '../../parser/lexical'
|
||||||
import { ITemplate, Context, ITagImplOptions, TagToken, Token } from '../../types'
|
import { Template, Context, TagImplOptions, TagToken, Token } from '../../types'
|
||||||
|
|
||||||
const re = new RegExp(`(${identifier.source})`)
|
const re = new RegExp(`(${identifier.source})`)
|
||||||
|
|
||||||
@@ -14,7 +14,7 @@ export default {
|
|||||||
|
|
||||||
const stream = this.liquid.parser.parseStream(remainTokens)
|
const stream = this.liquid.parser.parseStream(remainTokens)
|
||||||
stream.on('tag:endcapture', () => stream.stop())
|
stream.on('tag:endcapture', () => stream.stop())
|
||||||
.on('template', (tpl: ITemplate) => this.templates.push(tpl))
|
.on('template', (tpl: Template) => this.templates.push(tpl))
|
||||||
.on('end', () => {
|
.on('end', () => {
|
||||||
throw new Error(`tag ${tagToken.raw} not closed`)
|
throw new Error(`tag ${tagToken.raw} not closed`)
|
||||||
})
|
})
|
||||||
@@ -25,4 +25,4 @@ export default {
|
|||||||
const html = yield r.renderTemplates(this.templates, ctx)
|
const html = yield r.renderTemplates(this.templates, ctx)
|
||||||
ctx.front()[this.variable] = html
|
ctx.front()[this.variable] = html
|
||||||
}
|
}
|
||||||
} as ITagImplOptions
|
} as TagImplOptions
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Expression, Hash, Emitter, TagToken, Token, Context, ITemplate, ITagImplOptions, ParseStream } from '../../types'
|
import { Expression, Hash, Emitter, TagToken, Token, Context, Template, TagImplOptions, ParseStream } from '../../types'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
parse: function (tagToken: TagToken, remainTokens: Token[]) {
|
parse: function (tagToken: TagToken, remainTokens: Token[]) {
|
||||||
@@ -6,7 +6,7 @@ export default {
|
|||||||
this.cases = []
|
this.cases = []
|
||||||
this.elseTemplates = []
|
this.elseTemplates = []
|
||||||
|
|
||||||
let p: ITemplate[] = []
|
let p: Template[] = []
|
||||||
const stream: ParseStream = this.liquid.parser.parseStream(remainTokens)
|
const stream: ParseStream = this.liquid.parser.parseStream(remainTokens)
|
||||||
.on('tag:when', (token: TagToken) => {
|
.on('tag:when', (token: TagToken) => {
|
||||||
this.cases.push({
|
this.cases.push({
|
||||||
@@ -16,7 +16,7 @@ export default {
|
|||||||
})
|
})
|
||||||
.on('tag:else', () => (p = this.elseTemplates))
|
.on('tag:else', () => (p = this.elseTemplates))
|
||||||
.on('tag:endcase', () => stream.stop())
|
.on('tag:endcase', () => stream.stop())
|
||||||
.on('template', (tpl: ITemplate) => p.push(tpl))
|
.on('template', (tpl: Template) => p.push(tpl))
|
||||||
.on('end', () => {
|
.on('end', () => {
|
||||||
throw new Error(`tag ${tagToken.raw} not closed`)
|
throw new Error(`tag ${tagToken.raw} not closed`)
|
||||||
})
|
})
|
||||||
@@ -37,4 +37,4 @@ export default {
|
|||||||
}
|
}
|
||||||
yield r.renderTemplates(this.elseTemplates, ctx, emitter)
|
yield r.renderTemplates(this.elseTemplates, ctx, emitter)
|
||||||
}
|
}
|
||||||
} as ITagImplOptions
|
} as TagImplOptions
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { TagToken } from '../../parser/tag-token'
|
import { TagToken } from '../../parser/tag-token'
|
||||||
import { Token } from '../../parser/token'
|
import { Token } from '../../parser/token'
|
||||||
import { ITagImplOptions } from '../../template/tag/itag-impl-options'
|
import { TagImplOptions } from '../../template/tag/tag-impl-options'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
parse: function (tagToken: TagToken, remainTokens: Token[]) {
|
parse: function (tagToken: TagToken, remainTokens: Token[]) {
|
||||||
@@ -14,4 +14,4 @@ export default {
|
|||||||
})
|
})
|
||||||
stream.start()
|
stream.start()
|
||||||
}
|
}
|
||||||
} as ITagImplOptions
|
} as TagImplOptions
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { assert } from '../../util/assert'
|
import { assert } from '../../util/assert'
|
||||||
import { value as rValue } from '../../parser/lexical'
|
import { value as rValue } from '../../parser/lexical'
|
||||||
import { Emitter, Expression, TagToken, Context, ITagImplOptions, Hash } from '../../types'
|
import { Emitter, Expression, TagToken, Context, TagImplOptions, Hash } from '../../types'
|
||||||
|
|
||||||
const groupRE = new RegExp(`^(?:(${rValue.source})\\s*:\\s*)?(.*)$`)
|
const groupRE = new RegExp(`^(?:(${rValue.source})\\s*:\\s*)?(.*)$`)
|
||||||
const candidatesRE = new RegExp(rValue.source, 'g')
|
const candidatesRE = new RegExp(rValue.source, 'g')
|
||||||
@@ -37,4 +37,4 @@ export default {
|
|||||||
const html = yield new Expression(candidate).value(ctx)
|
const html = yield new Expression(candidate).value(ctx)
|
||||||
emitter.write(html)
|
emitter.write(html)
|
||||||
}
|
}
|
||||||
} as ITagImplOptions
|
} as TagImplOptions
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { assert } from '../../util/assert'
|
import { assert } from '../../util/assert'
|
||||||
import { identifier } from '../../parser/lexical'
|
import { identifier } from '../../parser/lexical'
|
||||||
import { Emitter, TagToken, Context, ITagImplOptions, Hash } from '../../types'
|
import { Emitter, TagToken, Context, TagImplOptions, Hash } from '../../types'
|
||||||
import { isNumber, stringify } from '../../util/underscore'
|
import { isNumber, stringify } from '../../util/underscore'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -16,4 +16,4 @@ export default {
|
|||||||
}
|
}
|
||||||
emitter.write(stringify(--scope[this.variable]))
|
emitter.write(stringify(--scope[this.variable]))
|
||||||
}
|
}
|
||||||
} as ITagImplOptions
|
} as TagImplOptions
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Emitter, TagToken, Token, Context, ITemplate, ITagImplOptions, ParseStream } from '../../types'
|
import { Emitter, TagToken, Token, Context, Template, TagImplOptions, ParseStream } from '../../types'
|
||||||
import { isString, isObject, isArray } from '../../util/underscore'
|
import { isString, isObject, isArray } from '../../util/underscore'
|
||||||
import { Expression } from '../../render/expression'
|
import { Expression } from '../../render/expression'
|
||||||
import { assert } from '../../util/assert'
|
import { assert } from '../../util/assert'
|
||||||
@@ -29,7 +29,7 @@ export default {
|
|||||||
.on('start', () => (p = this.templates))
|
.on('start', () => (p = this.templates))
|
||||||
.on('tag:else', () => (p = this.elseTemplates))
|
.on('tag:else', () => (p = this.elseTemplates))
|
||||||
.on('tag:endfor', () => stream.stop())
|
.on('tag:endfor', () => stream.stop())
|
||||||
.on('template', (tpl: ITemplate) => p.push(tpl))
|
.on('template', (tpl: Template) => p.push(tpl))
|
||||||
.on('end', () => {
|
.on('end', () => {
|
||||||
throw new Error(`tag ${tagToken.raw} not closed`)
|
throw new Error(`tag ${tagToken.raw} not closed`)
|
||||||
})
|
})
|
||||||
@@ -72,4 +72,4 @@ export default {
|
|||||||
}
|
}
|
||||||
ctx.pop()
|
ctx.pop()
|
||||||
}
|
}
|
||||||
} as ITagImplOptions
|
} as TagImplOptions
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Hash, Emitter, isTruthy, Expression, TagToken, Token, Context, ITemplate, ITagImplOptions, ParseStream } from '../../types'
|
import { Hash, Emitter, isTruthy, Expression, TagToken, Token, Context, Template, TagImplOptions, ParseStream } from '../../types'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
parse: function (tagToken: TagToken, remainTokens: Token[]) {
|
parse: function (tagToken: TagToken, remainTokens: Token[]) {
|
||||||
@@ -19,7 +19,7 @@ export default {
|
|||||||
})
|
})
|
||||||
.on('tag:else', () => (p = this.elseTemplates))
|
.on('tag:else', () => (p = this.elseTemplates))
|
||||||
.on('tag:endif', () => stream.stop())
|
.on('tag:endif', () => stream.stop())
|
||||||
.on('template', (tpl: ITemplate) => p.push(tpl))
|
.on('template', (tpl: Template) => p.push(tpl))
|
||||||
.on('end', () => {
|
.on('end', () => {
|
||||||
throw new Error(`tag ${tagToken.raw} not closed`)
|
throw new Error(`tag ${tagToken.raw} not closed`)
|
||||||
})
|
})
|
||||||
@@ -39,4 +39,4 @@ export default {
|
|||||||
}
|
}
|
||||||
yield r.renderTemplates(this.elseTemplates, ctx, emitter)
|
yield r.renderTemplates(this.elseTemplates, ctx, emitter)
|
||||||
}
|
}
|
||||||
} as ITagImplOptions
|
} as TagImplOptions
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { assert } from '../../util/assert'
|
import { assert } from '../../util/assert'
|
||||||
import { Expression, Hash, Emitter, TagToken, Context, ITagImplOptions } from '../../types'
|
import { Expression, Hash, Emitter, TagToken, Context, TagImplOptions } from '../../types'
|
||||||
import { value, quotedLine } from '../../parser/lexical'
|
import { value, quotedLine } from '../../parser/lexical'
|
||||||
import BlockMode from '../../context/block-mode'
|
import BlockMode from '../../context/block-mode'
|
||||||
|
|
||||||
@@ -46,4 +46,4 @@ export default {
|
|||||||
ctx.setRegister('blocks', originBlocks)
|
ctx.setRegister('blocks', originBlocks)
|
||||||
ctx.setRegister('blockMode', originBlockMode)
|
ctx.setRegister('blockMode', originBlockMode)
|
||||||
}
|
}
|
||||||
} as ITagImplOptions
|
} as TagImplOptions
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { assert } from '../../util/assert'
|
import { assert } from '../../util/assert'
|
||||||
import { identifier } from '../../parser/lexical'
|
import { identifier } from '../../parser/lexical'
|
||||||
import { isNumber, stringify } from '../../util/underscore'
|
import { isNumber, stringify } from '../../util/underscore'
|
||||||
import { Emitter, TagToken, Context, ITagImplOptions, Hash } from '../../types'
|
import { Emitter, TagToken, Context, TagImplOptions, Hash } from '../../types'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
parse: function (token: TagToken) {
|
parse: function (token: TagToken) {
|
||||||
@@ -18,4 +18,4 @@ export default {
|
|||||||
scope[this.variable]++
|
scope[this.variable]++
|
||||||
emitter.write(stringify(val))
|
emitter.write(stringify(val))
|
||||||
}
|
}
|
||||||
} as ITagImplOptions
|
} as TagImplOptions
|
||||||
|
|||||||
@@ -16,9 +16,9 @@ import tablerow from './tablerow'
|
|||||||
import unless from './unless'
|
import unless from './unless'
|
||||||
import Break from './break'
|
import Break from './break'
|
||||||
import Continue from './continue'
|
import Continue from './continue'
|
||||||
import { ITagImplOptions } from '../../template/tag/itag-impl-options'
|
import { TagImplOptions } from '../../template/tag/tag-impl-options'
|
||||||
|
|
||||||
const tags: { [key: string]: ITagImplOptions } = {
|
const tags: { [key: string]: TagImplOptions } = {
|
||||||
assign, 'for': For, capture, 'case': Case, comment, include, render, decrement, increment, cycle, 'if': If, layout, block, raw, tablerow, unless, 'break': Break, 'continue': Continue
|
assign, 'for': For, capture, 'case': Case, comment, include, render, decrement, increment, cycle, 'if': If, layout, block, raw, tablerow, unless, 'break': Break, 'continue': Continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { assert } from '../../util/assert'
|
import { assert } from '../../util/assert'
|
||||||
import { value as rValue } from '../../parser/lexical'
|
import { value as rValue } from '../../parser/lexical'
|
||||||
import { Emitter, Hash, Expression, TagToken, Token, Context, ITagImplOptions } from '../../types'
|
import { Emitter, Hash, Expression, TagToken, Token, Context, TagImplOptions } from '../../types'
|
||||||
import BlockMode from '../../context/block-mode'
|
import BlockMode from '../../context/block-mode'
|
||||||
|
|
||||||
const staticFileRE = /\S+/
|
const staticFileRE = /\S+/
|
||||||
@@ -40,4 +40,4 @@ export default {
|
|||||||
ctx.pop()
|
ctx.pop()
|
||||||
emitter.write(partial)
|
emitter.write(partial)
|
||||||
}
|
}
|
||||||
} as ITagImplOptions
|
} as TagImplOptions
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { TagToken, Token, ITagImplOptions } from '../../types'
|
import { TagToken, Token, TagImplOptions } from '../../types'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
parse: function (tagToken: TagToken, remainTokens: Token[]) {
|
parse: function (tagToken: TagToken, remainTokens: Token[]) {
|
||||||
@@ -18,4 +18,4 @@ export default {
|
|||||||
render: function () {
|
render: function () {
|
||||||
return this.tokens.map((token: Token) => token.raw).join('')
|
return this.tokens.map((token: Token) => token.raw).join('')
|
||||||
}
|
}
|
||||||
} as ITagImplOptions
|
} as TagImplOptions
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { assert } from '../../util/assert'
|
import { assert } from '../../util/assert'
|
||||||
import { Expression, Hash, Emitter, TagToken, Context, ITagImplOptions } from '../../types'
|
import { Expression, Hash, Emitter, TagToken, Context, TagImplOptions } from '../../types'
|
||||||
import { value, quotedLine } from '../../parser/lexical'
|
import { value, quotedLine } from '../../parser/lexical'
|
||||||
import BlockMode from '../../context/block-mode'
|
import BlockMode from '../../context/block-mode'
|
||||||
|
|
||||||
@@ -47,4 +47,4 @@ export default {
|
|||||||
childCtx.setRegister('blocks', originBlocks)
|
childCtx.setRegister('blocks', originBlocks)
|
||||||
childCtx.setRegister('blockMode', originBlockMode)
|
childCtx.setRegister('blockMode', originBlockMode)
|
||||||
}
|
}
|
||||||
} as ITagImplOptions
|
} as TagImplOptions
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { assert } from '../../util/assert'
|
import { assert } from '../../util/assert'
|
||||||
import { Expression, Emitter, Hash, TagToken, Token, Context, ITemplate, ITagImplOptions, ParseStream } from '../../types'
|
import { Expression, Emitter, Hash, TagToken, Token, Context, Template, TagImplOptions, ParseStream } from '../../types'
|
||||||
import { identifier, value, hash } from '../../parser/lexical'
|
import { identifier, value, hash } from '../../parser/lexical'
|
||||||
import { TablerowloopDrop } from '../../drop/tablerowloop-drop'
|
import { TablerowloopDrop } from '../../drop/tablerowloop-drop'
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@ export default {
|
|||||||
const stream: ParseStream = this.liquid.parser.parseStream(remainTokens)
|
const stream: ParseStream = this.liquid.parser.parseStream(remainTokens)
|
||||||
.on('start', () => (p = this.templates))
|
.on('start', () => (p = this.templates))
|
||||||
.on('tag:endtablerow', () => stream.stop())
|
.on('tag:endtablerow', () => stream.stop())
|
||||||
.on('template', (tpl: ITemplate) => p.push(tpl))
|
.on('template', (tpl: Template) => p.push(tpl))
|
||||||
.on('end', () => {
|
.on('end', () => {
|
||||||
throw new Error(`tag ${tagToken.raw} not closed`)
|
throw new Error(`tag ${tagToken.raw} not closed`)
|
||||||
})
|
})
|
||||||
@@ -54,4 +54,4 @@ export default {
|
|||||||
if (collection.length) emitter.write('</tr>')
|
if (collection.length) emitter.write('</tr>')
|
||||||
ctx.pop()
|
ctx.pop()
|
||||||
}
|
}
|
||||||
} as ITagImplOptions
|
} as TagImplOptions
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Emitter, Expression, isFalsy, ParseStream, Context, ITagImplOptions, Token, Hash, TagToken } from '../../types'
|
import { Emitter, Expression, isFalsy, ParseStream, Context, TagImplOptions, Token, Hash, TagToken } from '../../types'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
parse: function (tagToken: TagToken, remainTokens: Token[]) {
|
parse: function (tagToken: TagToken, remainTokens: Token[]) {
|
||||||
@@ -27,4 +27,4 @@ export default {
|
|||||||
? r.renderTemplates(this.templates, ctx, emitter)
|
? r.renderTemplates(this.templates, ctx, emitter)
|
||||||
: r.renderTemplates(this.elseTemplates, ctx, emitter))
|
: r.renderTemplates(this.elseTemplates, ctx, emitter))
|
||||||
}
|
}
|
||||||
} as ITagImplOptions
|
} as TagImplOptions
|
||||||
|
|||||||
+22
-18
@@ -1,16 +1,16 @@
|
|||||||
import { Context } from './context/context'
|
import { Context } from './context/context'
|
||||||
import fs from './fs/node'
|
import fs from './fs/node'
|
||||||
import * as _ from './util/underscore'
|
import * as _ from './util/underscore'
|
||||||
import { ITemplate } from './template/itemplate'
|
import { Template } from './template/template'
|
||||||
import { Tokenizer } from './parser/tokenizer'
|
import { Tokenizer } from './parser/tokenizer'
|
||||||
import { Render } from './render/render'
|
import { Render } from './render/render'
|
||||||
import { Tag } from './template/tag/tag'
|
|
||||||
import { Filter } from './template/filter/filter'
|
|
||||||
import Parser from './parser/parser'
|
import Parser from './parser/parser'
|
||||||
import { ITagImplOptions } from './template/tag/itag-impl-options'
|
import { TagImplOptions } from './template/tag/tag-impl-options'
|
||||||
import { Value } from './template/value'
|
import { Value } from './template/value'
|
||||||
import builtinTags from './builtin/tags'
|
import builtinTags from './builtin/tags'
|
||||||
import builtinFilters from './builtin/filters'
|
import builtinFilters from './builtin/filters'
|
||||||
|
import { TagMap } from './template/tag/tag-map'
|
||||||
|
import { FilterMap } from './template/filter/filter-map'
|
||||||
import { LiquidOptions, normalizeStringArray, NormalizedFullOptions, applyDefault, normalize } from './liquid-options'
|
import { LiquidOptions, normalizeStringArray, NormalizedFullOptions, applyDefault, normalize } from './liquid-options'
|
||||||
import { FilterImplOptions } from './template/filter/filter-impl-options'
|
import { FilterImplOptions } from './template/filter/filter-impl-options'
|
||||||
import IFS from './fs/ifs'
|
import IFS from './fs/ifs'
|
||||||
@@ -22,6 +22,8 @@ export class Liquid {
|
|||||||
public options: NormalizedFullOptions
|
public options: NormalizedFullOptions
|
||||||
public renderer: Render
|
public renderer: Render
|
||||||
public parser: Parser
|
public parser: Parser
|
||||||
|
public filters: FilterMap
|
||||||
|
public tags: TagMap
|
||||||
private cache: object = {}
|
private cache: object = {}
|
||||||
private tokenizer: Tokenizer
|
private tokenizer: Tokenizer
|
||||||
private fs: IFS
|
private fs: IFS
|
||||||
@@ -32,24 +34,26 @@ export class Liquid {
|
|||||||
this.renderer = new Render()
|
this.renderer = new Render()
|
||||||
this.tokenizer = new Tokenizer(this.options)
|
this.tokenizer = new Tokenizer(this.options)
|
||||||
this.fs = opts.fs || fs
|
this.fs = opts.fs || fs
|
||||||
|
this.filters = new FilterMap(this.options.strictFilters)
|
||||||
|
this.tags = new TagMap()
|
||||||
|
|
||||||
_.forOwn(builtinTags, (conf, name) => this.registerTag(name, conf))
|
_.forOwn(builtinTags, (conf, name) => this.registerTag(name, conf))
|
||||||
_.forOwn(builtinFilters, (handler, name) => this.registerFilter(name, handler))
|
_.forOwn(builtinFilters, (handler, name) => this.registerFilter(name, handler))
|
||||||
}
|
}
|
||||||
public parse (html: string, filepath?: string): ITemplate[] {
|
public parse (html: string, filepath?: string): Template[] {
|
||||||
const tokens = this.tokenizer.tokenize(html, filepath)
|
const tokens = this.tokenizer.tokenize(html, filepath)
|
||||||
return this.parser.parse(tokens)
|
return this.parser.parse(tokens)
|
||||||
}
|
}
|
||||||
|
|
||||||
public _render (tpl: ITemplate[], scope?: object, opts?: LiquidOptions, sync?: boolean): IterableIterator<string> {
|
public _render (tpl: Template[], scope?: object, opts?: LiquidOptions, sync?: boolean): IterableIterator<string> {
|
||||||
const options = { ...this.options, ...normalize(opts) }
|
const options = { ...this.options, ...normalize(opts) }
|
||||||
const ctx = new Context(scope, options, sync)
|
const ctx = new Context(scope, options, sync)
|
||||||
return this.renderer.renderTemplates(tpl, ctx)
|
return this.renderer.renderTemplates(tpl, ctx)
|
||||||
}
|
}
|
||||||
public async render (tpl: ITemplate[], scope?: object, opts?: LiquidOptions): Promise<string> {
|
public async render (tpl: Template[], scope?: object, opts?: LiquidOptions): Promise<string> {
|
||||||
return toThenable(this._render(tpl, scope, opts, false))
|
return toThenable(this._render(tpl, scope, opts, false))
|
||||||
}
|
}
|
||||||
public renderSync (tpl: ITemplate[], scope?: object, opts?: LiquidOptions): string {
|
public renderSync (tpl: Template[], scope?: object, opts?: LiquidOptions): string {
|
||||||
return toValue(this._render(tpl, scope, opts, true))
|
return toValue(this._render(tpl, scope, opts, true))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,10 +84,10 @@ export class Liquid {
|
|||||||
}
|
}
|
||||||
throw this.lookupError(file, options.root)
|
throw this.lookupError(file, options.root)
|
||||||
}
|
}
|
||||||
public async parseFile (file: string, opts?: LiquidOptions): Promise<ITemplate[]> {
|
public async parseFile (file: string, opts?: LiquidOptions): Promise<Template[]> {
|
||||||
return toThenable(this._parseFile(file, opts, false))
|
return toThenable(this._parseFile(file, opts, false))
|
||||||
}
|
}
|
||||||
public parseFileSync (file: string, opts?: LiquidOptions): ITemplate[] {
|
public parseFileSync (file: string, opts?: LiquidOptions): Template[] {
|
||||||
return toValue(this._parseFile(file, opts, true))
|
return toValue(this._parseFile(file, opts, true))
|
||||||
}
|
}
|
||||||
public async renderFile (file: string, ctx?: object, opts?: LiquidOptions) {
|
public async renderFile (file: string, ctx?: object, opts?: LiquidOptions) {
|
||||||
@@ -97,7 +101,7 @@ export class Liquid {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public _evalValue (str: string, ctx: Context): IterableIterator<any> {
|
public _evalValue (str: string, ctx: Context): IterableIterator<any> {
|
||||||
const value = new Value(str, this.options.strictFilters)
|
const value = new Value(str, this.filters)
|
||||||
return value.value(ctx)
|
return value.value(ctx)
|
||||||
}
|
}
|
||||||
public async evalValue (str: string, ctx: Context): Promise<any> {
|
public async evalValue (str: string, ctx: Context): Promise<any> {
|
||||||
@@ -108,19 +112,19 @@ export class Liquid {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public registerFilter (name: string, filter: FilterImplOptions) {
|
public registerFilter (name: string, filter: FilterImplOptions) {
|
||||||
return Filter.register(name, filter)
|
this.filters.set(name, filter)
|
||||||
}
|
}
|
||||||
public registerTag (name: string, tag: ITagImplOptions) {
|
public registerTag (name: string, tag: TagImplOptions) {
|
||||||
return Tag.register(name, tag)
|
this.tags.set(name, tag)
|
||||||
}
|
}
|
||||||
public plugin (plugin: (this: Liquid, L: typeof Liquid) => void) {
|
public plugin (plugin: (this: Liquid, L: typeof Liquid) => void) {
|
||||||
return plugin.call(this, Liquid)
|
return plugin.call(this, Liquid)
|
||||||
}
|
}
|
||||||
public express () {
|
public express () {
|
||||||
const self = this // eslint-disable-line
|
const self = this // eslint-disable-line
|
||||||
return function (this: any, filePath: string, ctx: object, cb: (err: Error | null, html?: string) => void) {
|
return function (this: any, filePath: string, ctx: object, callback: (err: Error | null, rendered: string) => void) {
|
||||||
const opts = { root: [...normalizeStringArray(this.root), ...self.options.root] }
|
const opts = { root: [...normalizeStringArray(this.root), ...self.options.root] }
|
||||||
self.renderFile(filePath, ctx, opts).then(html => cb(null, html), cb)
|
self.renderFile(filePath, ctx, opts).then(html => callback(null, html) as any, callback as any)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,13 +138,13 @@ export class Liquid {
|
|||||||
/**
|
/**
|
||||||
* @deprecated use parseFile instead
|
* @deprecated use parseFile instead
|
||||||
*/
|
*/
|
||||||
public async getTemplate (file: string, opts?: LiquidOptions): Promise<ITemplate[]> {
|
public async getTemplate (file: string, opts?: LiquidOptions): Promise<Template[]> {
|
||||||
return this.parseFile(file, opts)
|
return this.parseFile(file, opts)
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* @deprecated use parseFileSync instead
|
* @deprecated use parseFileSync instead
|
||||||
*/
|
*/
|
||||||
public getTemplateSync (file: string, opts?: LiquidOptions): ITemplate[] {
|
public getTemplateSync (file: string, opts?: LiquidOptions): Template[] {
|
||||||
return this.parseFileSync(file, opts)
|
return this.parseFileSync(file, opts)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { Token } from '../parser/token'
|
import { Token } from '../parser/token'
|
||||||
import { ITemplate } from '../template/itemplate'
|
import { Template } from '../template/template'
|
||||||
import { TagToken } from './tag-token'
|
import { TagToken } from './tag-token'
|
||||||
|
|
||||||
type ParseToken = ((token: Token, remainTokens: Token[]) => ITemplate)
|
type ParseToken = ((token: Token, remainTokens: Token[]) => Template)
|
||||||
|
|
||||||
export class ParseStream {
|
export class ParseStream {
|
||||||
private tokens: Token[]
|
private tokens: Token[]
|
||||||
@@ -14,11 +14,11 @@ export class ParseStream {
|
|||||||
this.tokens = tokens
|
this.tokens = tokens
|
||||||
this.parseToken = parseToken
|
this.parseToken = parseToken
|
||||||
}
|
}
|
||||||
public on<T extends ITemplate | Token | undefined> (name: string, cb: (arg: T) => void): ParseStream {
|
public on<T extends Template | Token | undefined> (name: string, cb: (arg: T) => void): ParseStream {
|
||||||
this.handlers[name] = cb
|
this.handlers[name] = cb
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
private trigger <T extends Token | ITemplate> (event: string, arg?: T) {
|
private trigger <T extends Token | Template> (event: string, arg?: T) {
|
||||||
const h = this.handlers[event]
|
const h = this.handlers[event]
|
||||||
return h ? (h(arg), true) : false
|
return h ? (h(arg), true) : false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { OutputToken } from './output-token'
|
|||||||
import { Tag } from '../template/tag/tag'
|
import { Tag } from '../template/tag/tag'
|
||||||
import { Output } from '../template/output'
|
import { Output } from '../template/output'
|
||||||
import { HTML } from '../template/html'
|
import { HTML } from '../template/html'
|
||||||
import { ITemplate } from '../template/itemplate'
|
import { Template } from '../template/template'
|
||||||
|
|
||||||
export default class Parser {
|
export default class Parser {
|
||||||
private liquid: Liquid
|
private liquid: Liquid
|
||||||
@@ -17,7 +17,7 @@ export default class Parser {
|
|||||||
}
|
}
|
||||||
public parse (tokens: Token[]) {
|
public parse (tokens: Token[]) {
|
||||||
let token
|
let token
|
||||||
const templates: ITemplate[] = []
|
const templates: Template[] = []
|
||||||
while ((token = tokens.shift())) {
|
while ((token = tokens.shift())) {
|
||||||
templates.push(this.parseToken(token, tokens))
|
templates.push(this.parseToken(token, tokens))
|
||||||
}
|
}
|
||||||
@@ -29,7 +29,7 @@ export default class Parser {
|
|||||||
return new Tag(token, remainTokens, this.liquid)
|
return new Tag(token, remainTokens, this.liquid)
|
||||||
}
|
}
|
||||||
if (OutputToken.is(token)) {
|
if (OutputToken.is(token)) {
|
||||||
return new Output(token as OutputToken, this.liquid.options.strictFilters)
|
return new Output(token as OutputToken, this.liquid.filters)
|
||||||
}
|
}
|
||||||
return new HTML(token)
|
return new HTML(token)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { RenderError } from '../util/error'
|
import { RenderError } from '../util/error'
|
||||||
import { Context } from '../context/context'
|
import { Context } from '../context/context'
|
||||||
import { ITemplate } from '../template/itemplate'
|
import { Template } from '../template/template'
|
||||||
import { Emitter } from './emitter'
|
import { Emitter } from './emitter'
|
||||||
|
|
||||||
export class Render {
|
export class Render {
|
||||||
public * renderTemplates (templates: ITemplate[], ctx: Context, emitter = new Emitter()): IterableIterator<string> {
|
public * renderTemplates (templates: Template[], ctx: Context, emitter = new Emitter()): IterableIterator<string> {
|
||||||
for (const tpl of templates) {
|
for (const tpl of templates) {
|
||||||
try {
|
try {
|
||||||
const html = yield tpl.render(ctx, emitter)
|
const html = yield tpl.render(ctx, emitter)
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import { FilterImplOptions } from './filter-impl-options'
|
||||||
|
import { Filter, FilterArgs } from './filter'
|
||||||
|
import { assert } from '../../util/assert'
|
||||||
|
|
||||||
|
export class FilterMap {
|
||||||
|
private impls: {[key: string]: FilterImplOptions} = {}
|
||||||
|
|
||||||
|
constructor (private readonly strictFilters: boolean) {}
|
||||||
|
|
||||||
|
get (name: string) {
|
||||||
|
const impl = this.impls[name]
|
||||||
|
assert(impl || !this.strictFilters, `undefined filter: ${name}`)
|
||||||
|
return impl
|
||||||
|
}
|
||||||
|
|
||||||
|
set (name: string, impl: FilterImplOptions) {
|
||||||
|
this.impls[name] = impl
|
||||||
|
}
|
||||||
|
|
||||||
|
create (name: string, args: FilterArgs) {
|
||||||
|
return new Filter(name, this.get(name), args)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,12 +11,8 @@ export class Filter {
|
|||||||
public name: string
|
public name: string
|
||||||
public args: FilterArgs
|
public args: FilterArgs
|
||||||
private impl: FilterImplOptions
|
private impl: FilterImplOptions
|
||||||
private static impls: {[key: string]: FilterImplOptions} = {}
|
|
||||||
|
|
||||||
public constructor (name: string, args: FilterArgs, strictFilters: boolean) {
|
|
||||||
const impl = Filter.impls[name]
|
|
||||||
if (!impl && strictFilters) throw new TypeError(`undefined filter: ${name}`)
|
|
||||||
|
|
||||||
|
public constructor (name: string, impl: FilterImplOptions, args: FilterArgs) {
|
||||||
this.name = name
|
this.name = name
|
||||||
this.impl = impl || identify
|
this.impl = impl || identify
|
||||||
this.args = args
|
this.args = args
|
||||||
@@ -29,12 +25,6 @@ export class Filter {
|
|||||||
}
|
}
|
||||||
return this.impl.apply({ context }, [value, ...argv])
|
return this.impl.apply({ context }, [value, ...argv])
|
||||||
}
|
}
|
||||||
public static register (name: string, filter: FilterImplOptions) {
|
|
||||||
Filter.impls[name] = filter
|
|
||||||
}
|
|
||||||
public static clear () {
|
|
||||||
Filter.impls = {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function isKeyValuePair (arr: FilterArg): arr is KeyValuePair {
|
function isKeyValuePair (arr: FilterArg): arr is KeyValuePair {
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
|
import { TemplateImpl } from '../template/template-impl'
|
||||||
import { Template } from '../template/template'
|
import { Template } from '../template/template'
|
||||||
import { ITemplate } from '../template/itemplate'
|
|
||||||
import { HTMLToken } from '../parser/html-token'
|
import { HTMLToken } from '../parser/html-token'
|
||||||
import { Context } from '../context/context'
|
import { Context } from '../context/context'
|
||||||
import { Emitter } from '../render/emitter'
|
import { Emitter } from '../render/emitter'
|
||||||
|
|
||||||
export class HTML extends Template<HTMLToken> implements ITemplate {
|
export class HTML extends TemplateImpl<HTMLToken> implements Template {
|
||||||
private str: string
|
private str: string
|
||||||
public constructor (token: HTMLToken) {
|
public constructor (token: HTMLToken) {
|
||||||
super(token)
|
super(token)
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
import { Context } from '../context/context'
|
|
||||||
import { Token } from '../parser/token'
|
|
||||||
import { Emitter } from '../render/emitter'
|
|
||||||
|
|
||||||
export interface ITemplate {
|
|
||||||
token: Token;
|
|
||||||
render(ctx: Context, emitter: Emitter): any;
|
|
||||||
}
|
|
||||||
@@ -1,16 +1,17 @@
|
|||||||
import { Value } from './value'
|
import { Value } from './value'
|
||||||
|
import { FilterMap } from './filter/filter-map'
|
||||||
import { stringify, toValue } from '../util/underscore'
|
import { stringify, toValue } from '../util/underscore'
|
||||||
|
import { TemplateImpl } from '../template/template-impl'
|
||||||
import { Template } from '../template/template'
|
import { Template } from '../template/template'
|
||||||
import { ITemplate } from '../template/itemplate'
|
|
||||||
import { Context } from '../context/context'
|
import { Context } from '../context/context'
|
||||||
import { Emitter } from '../render/emitter'
|
import { Emitter } from '../render/emitter'
|
||||||
import { OutputToken } from '../parser/output-token'
|
import { OutputToken } from '../parser/output-token'
|
||||||
|
|
||||||
export class Output extends Template<OutputToken> implements ITemplate {
|
export class Output extends TemplateImpl<OutputToken> implements Template {
|
||||||
private value: Value
|
private value: Value
|
||||||
public constructor (token: OutputToken, strictFilters: boolean) {
|
public constructor (token: OutputToken, filters: FilterMap) {
|
||||||
super(token)
|
super(token)
|
||||||
this.value = new Value(token.value, strictFilters)
|
this.value = new Value(token.value, filters)
|
||||||
}
|
}
|
||||||
public * render (ctx: Context, emitter: Emitter) {
|
public * render (ctx: Context, emitter: Emitter) {
|
||||||
const val = yield this.value.value(ctx)
|
const val = yield this.value.value(ctx)
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
import { Liquid } from '../../liquid'
|
|
||||||
import { ITagImplOptions } from './itag-impl-options'
|
|
||||||
|
|
||||||
export interface ITagImpl extends ITagImplOptions {
|
|
||||||
liquid: Liquid;
|
|
||||||
[key: string]: any;
|
|
||||||
}
|
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
import { Context } from '../../context/context'
|
import { Context } from '../../context/context'
|
||||||
import { TagToken } from '../../parser/tag-token'
|
import { TagToken } from '../../parser/tag-token'
|
||||||
import { Token } from '../../parser/token'
|
import { Token } from '../../parser/token'
|
||||||
import { ITagImpl } from './itag-impl'
|
import { TagImpl } from './tag-impl'
|
||||||
import { Hash } from '../../template/tag/hash'
|
import { Hash } from '../../template/tag/hash'
|
||||||
import { Emitter } from '../../render/emitter'
|
import { Emitter } from '../../render/emitter'
|
||||||
|
|
||||||
export interface ITagImplOptions {
|
export interface TagImplOptions {
|
||||||
parse?: (this: ITagImpl, token: TagToken, remainingTokens: Token[]) => void;
|
parse?: (this: TagImpl, token: TagToken, remainingTokens: Token[]) => void;
|
||||||
render: (this: ITagImpl, ctx: Context, hash: Hash, emitter: Emitter) => any;
|
render: (this: TagImpl, ctx: Context, hash: Hash, emitter: Emitter) => any;
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import { Liquid } from '../../liquid'
|
||||||
|
import { TagImplOptions } from './tag-impl-options'
|
||||||
|
|
||||||
|
export interface TagImpl extends TagImplOptions {
|
||||||
|
liquid: Liquid;
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import { TagImplOptions } from './tag-impl-options'
|
||||||
|
import { assert } from '../../util/assert'
|
||||||
|
|
||||||
|
export class TagMap {
|
||||||
|
private impls: {[key: string]: TagImplOptions} = {}
|
||||||
|
|
||||||
|
get (name: string) {
|
||||||
|
const impl = this.impls[name]
|
||||||
|
assert(impl, `tag "${name}" not found`)
|
||||||
|
return impl
|
||||||
|
}
|
||||||
|
|
||||||
|
set (name: string, impl: TagImplOptions) {
|
||||||
|
this.impls[name] = impl
|
||||||
|
}
|
||||||
|
}
|
||||||
+7
-15
@@ -1,21 +1,19 @@
|
|||||||
import { isFunction } from '../../util/underscore'
|
import { isFunction } from '../../util/underscore'
|
||||||
import { assert } from '../../util/assert'
|
|
||||||
import { Liquid } from '../../liquid'
|
import { Liquid } from '../../liquid'
|
||||||
import { Template } from '../../template/template'
|
import { TemplateImpl } from '../../template/template-impl'
|
||||||
import { Emitter, Hash, Context, ITagImplOptions, TagToken, ITemplate, Token } from '../../types'
|
import { Emitter, Hash, Context, TagImplOptions, TagToken, Template, Token } from '../../types'
|
||||||
import { ITagImpl } from './itag-impl'
|
import { TagImpl } from './tag-impl'
|
||||||
|
|
||||||
export class Tag extends Template<TagToken> implements ITemplate {
|
export class Tag extends TemplateImpl<TagToken> implements Template {
|
||||||
public name: string
|
public name: string
|
||||||
private impl: ITagImpl
|
private impl: TagImpl
|
||||||
private static impls: { [key: string]: ITagImplOptions } = {}
|
private static impls: { [key: string]: TagImplOptions } = {}
|
||||||
|
|
||||||
public constructor (token: TagToken, tokens: Token[], liquid: Liquid) {
|
public constructor (token: TagToken, tokens: Token[], liquid: Liquid) {
|
||||||
super(token)
|
super(token)
|
||||||
this.name = token.name
|
this.name = token.name
|
||||||
|
|
||||||
const impl = Tag.impls[token.name]
|
const impl = liquid.tags.get(token.name)
|
||||||
assert(impl, `tag ${token.name} not found`)
|
|
||||||
|
|
||||||
this.impl = Object.create(impl)
|
this.impl = Object.create(impl)
|
||||||
this.impl.liquid = liquid
|
this.impl.liquid = liquid
|
||||||
@@ -28,10 +26,4 @@ export class Tag extends Template<TagToken> implements ITemplate {
|
|||||||
const impl = this.impl
|
const impl = this.impl
|
||||||
if (isFunction(impl.render)) return yield impl.render(ctx, hash, emitter)
|
if (isFunction(impl.render)) return yield impl.render(ctx, hash, emitter)
|
||||||
}
|
}
|
||||||
public static register (name: string, tag: ITagImplOptions) {
|
|
||||||
Tag.impls[name] = tag
|
|
||||||
}
|
|
||||||
public static clear () {
|
|
||||||
Tag.impls = {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
export abstract class TemplateImpl<T> {
|
||||||
|
public token: T;
|
||||||
|
public constructor (token: T) {
|
||||||
|
this.token = token
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
export abstract class Template<T> {
|
import { Context } from '../context/context'
|
||||||
public token: T;
|
import { Token } from '../parser/token'
|
||||||
public constructor (token: T) {
|
import { Emitter } from '../render/emitter'
|
||||||
this.token = token
|
|
||||||
}
|
export interface Template {
|
||||||
|
token: Token;
|
||||||
|
render(ctx: Context, emitter: Emitter): any;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,17 @@
|
|||||||
import { Expression } from '../render/expression'
|
import { Expression } from '../render/expression'
|
||||||
|
import { FilterMap } from '../template/filter/filter-map'
|
||||||
import { FilterArgs, Filter } from './filter/filter'
|
import { FilterArgs, Filter } from './filter/filter'
|
||||||
import { Context } from '../context/context'
|
import { Context } from '../context/context'
|
||||||
|
|
||||||
export class Value {
|
export class Value {
|
||||||
public readonly filters: Filter[] = []
|
public readonly filters: Filter[] = []
|
||||||
public readonly initial: string
|
public readonly initial: string
|
||||||
private strictFilters: boolean
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param str value string, like: "i have a dream | truncate: 3
|
* @param str value string, like: "i have a dream | truncate: 3
|
||||||
*/
|
*/
|
||||||
public constructor (str: string, strictFilters: boolean) {
|
public constructor (str: string, private readonly filterMap: FilterMap) {
|
||||||
const tokens = Value.tokenize(str)
|
const tokens = Value.tokenize(str)
|
||||||
this.strictFilters = strictFilters
|
|
||||||
this.initial = tokens[0]
|
this.initial = tokens[0]
|
||||||
this.parseFilters(tokens, 1)
|
this.parseFilters(tokens, 1)
|
||||||
}
|
}
|
||||||
@@ -45,7 +44,7 @@ export class Value {
|
|||||||
argValue = tokens[i]
|
argValue = tokens[i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.filters.push(new Filter(name, args, this.strictFilters))
|
this.filters.push(new Filter(name, this.filterMap.get(name), args))
|
||||||
}
|
}
|
||||||
public * value (ctx: Context) {
|
public * value (ctx: Context) {
|
||||||
let val = yield new Expression(this.initial).evaluate(ctx)
|
let val = yield new Expression(this.initial).evaluate(ctx)
|
||||||
|
|||||||
+2
-2
@@ -5,8 +5,8 @@ export { Expression } from './render/expression'
|
|||||||
export { isFalsy, isTruthy } from './render/boolean'
|
export { isFalsy, isTruthy } from './render/boolean'
|
||||||
export { TagToken } from './parser/tag-token'
|
export { TagToken } from './parser/tag-token'
|
||||||
export { Context } from './context/context'
|
export { Context } from './context/context'
|
||||||
export { ITemplate } from './template/itemplate'
|
export { Template } from './template/template'
|
||||||
export { ITagImplOptions } from './template/tag/itag-impl-options'
|
export { TagImplOptions } from './template/tag/tag-impl-options'
|
||||||
export { ParseStream } from './parser/parse-stream'
|
export { ParseStream } from './parser/parse-stream'
|
||||||
export { Token } from './parser/token'
|
export { Token } from './parser/token'
|
||||||
export { Hash } from './template/tag/hash'
|
export { Hash } from './template/tag/hash'
|
||||||
|
|||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
import * as _ from './underscore'
|
import * as _ from './underscore'
|
||||||
import { Token } from '../parser/token'
|
import { Token } from '../parser/token'
|
||||||
import { ITemplate } from '../template/itemplate'
|
import { Template } from '../template/template'
|
||||||
|
|
||||||
abstract class LiquidError extends Error {
|
abstract class LiquidError extends Error {
|
||||||
private token: Token
|
private token: Token
|
||||||
@@ -37,7 +37,7 @@ export class ParseError extends LiquidError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class RenderError extends LiquidError {
|
export class RenderError extends LiquidError {
|
||||||
public constructor (err: Error, tpl: ITemplate) {
|
public constructor (err: Error, tpl: Template) {
|
||||||
super(err, tpl.token)
|
super(err, tpl.token)
|
||||||
this.name = 'RenderError'
|
this.name = 'RenderError'
|
||||||
this.message = err.message
|
this.message = err.message
|
||||||
|
|||||||
@@ -9,13 +9,13 @@ describe('liquid#registerFilter()', function () {
|
|||||||
return JSON.stringify(args)
|
return JSON.stringify(args)
|
||||||
})
|
})
|
||||||
it('should support object', async () => {
|
it('should support object', async () => {
|
||||||
const src = `{{ "a" | obj_test: k1: "v1", k2: foo }}`,
|
const src = `{{ "a" | obj_test: k1: "v1", k2: foo }}`
|
||||||
const dst = '["a",["k1","v1"],["k2","bar"]]'
|
const dst = '["a",["k1","v1"],["k2","bar"]]'
|
||||||
const html = await liquid.parseAndRender(src, { foo: 'bar' })
|
const html = await liquid.parseAndRender(src, { foo: 'bar' })
|
||||||
return expect(html).to.equal(dst)
|
return expect(html).to.equal(dst)
|
||||||
})
|
})
|
||||||
it('should support mixed object', async () => {
|
it('should support mixed object', async () => {
|
||||||
const src = `{{ "a" | obj_test: "something", k1: "v1", k2: foo }}`,
|
const src = `{{ "a" | obj_test: "something", k1: "v1", k2: foo }}`
|
||||||
const dst = '["a","something",["k1","v1"],["k2","bar"]]'
|
const dst = '["a","something",["k1","v1"],["k2","bar"]]'
|
||||||
const html = await liquid.parseAndRender(src, { foo: 'bar' })
|
const html = await liquid.parseAndRender(src, { foo: 'bar' })
|
||||||
return expect(html).to.equal(dst)
|
return expect(html).to.equal(dst)
|
||||||
|
|||||||
@@ -214,12 +214,12 @@ describe('error', function () {
|
|||||||
const src = '{%if true%}\naaa{%endif%}\n{% -a %}\n3'
|
const src = '{%if true%}\naaa{%endif%}\n{% -a %}\n3'
|
||||||
const err = await expect(engine.parseAndRender(src)).be.rejected
|
const err = await expect(engine.parseAndRender(src)).be.rejected
|
||||||
expect(err.name).to.equal('ParseError')
|
expect(err.name).to.equal('ParseError')
|
||||||
expect(err.message).to.contain('tag -a not found')
|
expect(err.message).to.contain('tag "-a" not found')
|
||||||
})
|
})
|
||||||
it('should throw ParseError when tag not exist', async function () {
|
it('should throw ParseError when tag not exist', async function () {
|
||||||
const err = await expect(engine.parseAndRender('{% a %}')).be.rejected
|
const err = await expect(engine.parseAndRender('{% a %}')).be.rejected
|
||||||
expect(err.name).to.equal('ParseError')
|
expect(err.name).to.equal('ParseError')
|
||||||
expect(err.message).to.contain('tag a not found')
|
expect(err.message).to.contain('tag "a" not found')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should contain template context in err.stack', async function () {
|
it('should contain template context in err.stack', async function () {
|
||||||
@@ -231,10 +231,10 @@ describe('error', function () {
|
|||||||
' 5| 5th',
|
' 5| 5th',
|
||||||
' 6| 6th',
|
' 6| 6th',
|
||||||
' 7| 7th',
|
' 7| 7th',
|
||||||
'ParseError: tag a not found'
|
'ParseError: tag "a" not found'
|
||||||
]
|
]
|
||||||
const err = await expect(engine.parseAndRender(html.join('\n'))).be.rejected
|
const err = await expect(engine.parseAndRender(html.join('\n'))).be.rejected
|
||||||
expect(err.message).to.equal('tag a not found, line:4, col:2')
|
expect(err.message).to.equal('tag "a" not found, line:4, col:2')
|
||||||
expect(err.stack).to.contain(message.join('\n'))
|
expect(err.stack).to.contain(message.join('\n'))
|
||||||
expect(err.name).to.equal('ParseError')
|
expect(err.name).to.equal('ParseError')
|
||||||
})
|
})
|
||||||
@@ -246,10 +246,10 @@ describe('error', function () {
|
|||||||
'>> 2| X{% a %} {% enda %} Y',
|
'>> 2| X{% a %} {% enda %} Y',
|
||||||
' 3| 3rd',
|
' 3| 3rd',
|
||||||
' 4| 4th',
|
' 4| 4th',
|
||||||
'ParseError: tag a not found'
|
'ParseError: tag "a" not found'
|
||||||
]
|
]
|
||||||
const err = await expect(engine.parseAndRender(html.join('\n'))).be.rejected
|
const err = await expect(engine.parseAndRender(html.join('\n'))).be.rejected
|
||||||
expect(err.message).to.equal('tag a not found, line:2, col:2')
|
expect(err.message).to.equal('tag "a" not found, line:2, col:2')
|
||||||
expect(err.stack).to.contain(message.join('\n'))
|
expect(err.stack).to.contain(message.join('\n'))
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -261,12 +261,12 @@ describe('error', function () {
|
|||||||
|
|
||||||
it('should contain stack in err.stack', async function () {
|
it('should contain stack in err.stack', async function () {
|
||||||
const err = await expect(engine.parseAndRender('{% -a %}')).be.rejected
|
const err = await expect(engine.parseAndRender('{% -a %}')).be.rejected
|
||||||
expect(err.stack).to.contain('ParseError: tag -a not found')
|
expect(err.stack).to.contain('ParseError: tag "-a" not found')
|
||||||
expect(err.stack).to.match(/at .*:\d+:\d+\)/)
|
expect(err.stack).to.match(/at .*:\d+:\d+\)/)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
describe('sync support', function () {
|
describe('sync support', function () {
|
||||||
let engine
|
let engine: Liquid
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
engine = new Liquid({
|
engine = new Liquid({
|
||||||
root: '/'
|
root: '/'
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
import { expect } from 'chai'
|
import { expect } from 'chai'
|
||||||
import { Context } from '../../../src/context/context'
|
import { Context } from '../../../src/context/context'
|
||||||
import { Token } from '../../../src/parser/token'
|
import { Token } from '../../../src/parser/token'
|
||||||
import { Tag } from '../../../src/template/tag/tag'
|
|
||||||
import { Filter } from '../../../src/template/filter/filter'
|
|
||||||
import { Render } from '../../../src/render/render'
|
import { Render } from '../../../src/render/render'
|
||||||
import { HTML } from '../../../src/template/html'
|
import { HTML } from '../../../src/template/html'
|
||||||
import { toThenable } from '../../../src/util/async'
|
import { toThenable } from '../../../src/util/async'
|
||||||
@@ -10,8 +8,6 @@ import { toThenable } from '../../../src/util/async'
|
|||||||
describe('render', function () {
|
describe('render', function () {
|
||||||
let render: Render
|
let render: Render
|
||||||
before(function () {
|
before(function () {
|
||||||
Filter.clear()
|
|
||||||
Tag.clear()
|
|
||||||
render = new Render()
|
render = new Render()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -1,69 +1,70 @@
|
|||||||
import * as chai from 'chai'
|
import * as chai from 'chai'
|
||||||
import * as sinon from 'sinon'
|
import * as sinon from 'sinon'
|
||||||
import * as sinonChai from 'sinon-chai'
|
import * as sinonChai from 'sinon-chai'
|
||||||
import { Filter } from '../../../../src/template/filter/filter'
|
|
||||||
import { Context } from '../../../../src/context/context'
|
import { Context } from '../../../../src/context/context'
|
||||||
import { toThenable } from '../../../../src/util/async'
|
import { toThenable } from '../../../../src/util/async'
|
||||||
|
import { FilterMap } from '../../../../src/template/filter/filter-map'
|
||||||
|
|
||||||
chai.use(sinonChai)
|
chai.use(sinonChai)
|
||||||
const expect = chai.expect
|
const expect = chai.expect
|
||||||
|
|
||||||
describe('filter', function () {
|
describe('filter', function () {
|
||||||
let ctx: Context
|
let ctx: Context
|
||||||
|
let filters: FilterMap
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
Filter.clear()
|
filters = new FilterMap(false)
|
||||||
ctx = new Context()
|
ctx = new Context()
|
||||||
})
|
})
|
||||||
it('should create default filter if not registered', async function () {
|
it('should create default filter if not registered', async function () {
|
||||||
const result = new Filter('foo', [], false) as any
|
const result = filters.create('foo', []) as any
|
||||||
expect(result.name).to.equal('foo')
|
expect(result.name).to.equal('foo')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should render input if filter not registered', async function () {
|
it('should render input if filter not registered', async function () {
|
||||||
expect(await toThenable(new Filter('undefined', [], false).render('foo', ctx))).to.equal('foo')
|
expect(await toThenable(filters.create('undefined', []).render('foo', ctx))).to.equal('foo')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should call filter impl with correct arguments', async function () {
|
it('should call filter impl with correct arguments', async function () {
|
||||||
const spy = sinon.spy()
|
const spy = sinon.spy()
|
||||||
Filter.register('foo', spy)
|
filters.set('foo', spy)
|
||||||
await toThenable(new Filter('foo', ['33'], false).render('foo', ctx))
|
await toThenable(filters.create('foo', ['33']).render('foo', ctx))
|
||||||
expect(spy).to.have.been.calledWith('foo', 33)
|
expect(spy).to.have.been.calledWith('foo', 33)
|
||||||
})
|
})
|
||||||
it('should call filter impl with correct this arg', async function () {
|
it('should call filter impl with correct this arg', async function () {
|
||||||
const spy = sinon.spy()
|
const spy = sinon.spy()
|
||||||
Filter.register('foo', spy)
|
filters.set('foo', spy)
|
||||||
await toThenable(new Filter('foo', ['33'], false).render('foo', ctx))
|
await toThenable(filters.create('foo', ['33']).render('foo', ctx))
|
||||||
expect(spy).to.have.been.calledOn(sinon.match.has('context', ctx))
|
expect(spy).to.have.been.calledOn(sinon.match.has('context', ctx))
|
||||||
})
|
})
|
||||||
it('should render a simple filter', async function () {
|
it('should render a simple filter', async function () {
|
||||||
Filter.register('upcase', x => x.toUpperCase())
|
filters.set('upcase', x => x.toUpperCase())
|
||||||
expect(await toThenable(new Filter('upcase', [], false).render('foo', ctx))).to.equal('FOO')
|
expect(await toThenable(filters.create('upcase', []).render('foo', ctx))).to.equal('FOO')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should render filters with argument', async function () {
|
it('should render filters with argument', async function () {
|
||||||
Filter.register('add', (a, b) => a + b)
|
filters.set('add', (a, b) => a + b)
|
||||||
expect(await toThenable(new Filter('add', ['2'], false).render(3, ctx))).to.equal(5)
|
expect(await toThenable(filters.create('add', ['2']).render(3, ctx))).to.equal(5)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should render filters with multiple arguments', async function () {
|
it('should render filters with multiple arguments', async function () {
|
||||||
Filter.register('add', (a, b, c) => a + b + c)
|
filters.set('add', (a, b, c) => a + b + c)
|
||||||
expect(await toThenable(new Filter('add', ['2', '"c"'], false).render(3, ctx))).to.equal('5c')
|
expect(await toThenable(filters.create('add', ['2', '"c"']).render(3, ctx))).to.equal('5c')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should pass Objects/Drops as it is', async function () {
|
it('should pass Objects/Drops as it is', async function () {
|
||||||
Filter.register('name', a => a.constructor.name)
|
filters.set('name', a => a.constructor.name)
|
||||||
class Foo {}
|
class Foo {}
|
||||||
expect(await toThenable(new Filter('name', [], false).render(new Foo(), ctx))).to.equal('Foo')
|
expect(await toThenable(filters.create('name', []).render(new Foo(), ctx))).to.equal('Foo')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should not throw when filter name illegal', function () {
|
it('should not throw when filter name illegal', function () {
|
||||||
expect(function () {
|
expect(function () {
|
||||||
new Filter('/', [], false)
|
filters.create('/', [])
|
||||||
}).to.not.throw()
|
}).to.not.throw()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should support key value pairs', async function () {
|
it('should support key value pairs', async function () {
|
||||||
Filter.register('add', (a, b) => b[0] + ':' + (a + b[1]))
|
filters.set('add', (a, b) => b[0] + ':' + (a + b[1]))
|
||||||
expect(await toThenable((new Filter('add', [['num', '2']], false).render(3, ctx)))).to.equal('num:5')
|
expect(await toThenable((filters.create('add', [['num', '2']]).render(3, ctx)))).to.equal('num:5')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -3,14 +3,15 @@ import { toThenable } from '../../../src/util/async'
|
|||||||
import { Context } from '../../../src/context/context'
|
import { Context } from '../../../src/context/context'
|
||||||
import { Output } from '../../../src/template/output'
|
import { Output } from '../../../src/template/output'
|
||||||
import { OutputToken } from '../../../src/parser/output-token'
|
import { OutputToken } from '../../../src/parser/output-token'
|
||||||
import { Filter } from '../../../src/template/filter/filter'
|
import { FilterMap } from '../../../src/template/filter/filter-map'
|
||||||
|
|
||||||
const expect = chai.expect
|
const expect = chai.expect
|
||||||
|
|
||||||
describe('Output', function () {
|
describe('Output', function () {
|
||||||
const emitter: any = { write: (html: string) => (emitter.html += html), html: '' }
|
const emitter: any = { write: (html: string) => (emitter.html += html), html: '' }
|
||||||
|
let filters: FilterMap
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
Filter.clear()
|
filters = new FilterMap(false)
|
||||||
emitter.html = ''
|
emitter.html = ''
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -18,25 +19,25 @@ describe('Output', function () {
|
|||||||
const scope = new Context({
|
const scope = new Context({
|
||||||
foo: { obj: { arr: ['a', 2] } }
|
foo: { obj: { arr: ['a', 2] } }
|
||||||
})
|
})
|
||||||
const output = new Output({ value: 'foo' } as OutputToken, false)
|
const output = new Output({ value: 'foo' } as OutputToken, filters)
|
||||||
await toThenable(output.render(scope, emitter))
|
await toThenable(output.render(scope, emitter))
|
||||||
return expect(emitter.html).to.equal('[object Object]')
|
return expect(emitter.html).to.equal('[object Object]')
|
||||||
})
|
})
|
||||||
it('should skip function property', async function () {
|
it('should skip function property', async function () {
|
||||||
const scope = new Context({ obj: { foo: 'foo', bar: (x: any) => x } })
|
const scope = new Context({ obj: { foo: 'foo', bar: (x: any) => x } })
|
||||||
const output = new Output({ value: 'obj' } as OutputToken, false)
|
const output = new Output({ value: 'obj' } as OutputToken, filters)
|
||||||
await toThenable(output.render(scope, emitter))
|
await toThenable(output.render(scope, emitter))
|
||||||
return expect(emitter.html).to.equal('[object Object]')
|
return expect(emitter.html).to.equal('[object Object]')
|
||||||
})
|
})
|
||||||
it('should respect to .toString()', async () => {
|
it('should respect to .toString()', async () => {
|
||||||
const scope = new Context({ obj: { toString: () => 'FOO' } })
|
const scope = new Context({ obj: { toString: () => 'FOO' } })
|
||||||
const output = new Output({ value: 'obj' } as OutputToken, false)
|
const output = new Output({ value: 'obj' } as OutputToken, filters)
|
||||||
await toThenable(output.render(scope, emitter))
|
await toThenable(output.render(scope, emitter))
|
||||||
return expect(emitter.html).to.equal('FOO')
|
return expect(emitter.html).to.equal('FOO')
|
||||||
})
|
})
|
||||||
it('should respect to .toString()', async () => {
|
it('should respect to .toString()', async () => {
|
||||||
const scope = new Context({ obj: { toString: () => 'FOO' } })
|
const scope = new Context({ obj: { toString: () => 'FOO' } })
|
||||||
const output = new Output({ value: 'obj' } as OutputToken, false)
|
const output = new Output({ value: 'obj' } as OutputToken, filters)
|
||||||
await toThenable(output.render(scope, emitter))
|
await toThenable(output.render(scope, emitter))
|
||||||
return expect(emitter.html).to.equal('FOO')
|
return expect(emitter.html).to.equal('FOO')
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -9,10 +9,10 @@ import { toThenable } from '../../../src/util/async'
|
|||||||
|
|
||||||
chai.use(sinonChai)
|
chai.use(sinonChai)
|
||||||
const expect = chai.expect
|
const expect = chai.expect
|
||||||
const liquid = new Liquid()
|
|
||||||
|
|
||||||
describe('Tag', function () {
|
describe('Tag', function () {
|
||||||
let ctx: Context
|
let ctx: Context
|
||||||
|
let liquid: Liquid
|
||||||
const emitter: any = { write: (html: string) => (emitter.html += html), html: '' }
|
const emitter: any = { write: (html: string) => (emitter.html += html), html: '' }
|
||||||
before(function () {
|
before(function () {
|
||||||
ctx = new Context({
|
ctx = new Context({
|
||||||
@@ -22,7 +22,7 @@ describe('Tag', function () {
|
|||||||
coo: 'uoo'
|
coo: 'uoo'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
Tag.clear()
|
liquid = new Liquid()
|
||||||
})
|
})
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
emitter.html = ''
|
emitter.html = ''
|
||||||
@@ -33,24 +33,20 @@ describe('Tag', function () {
|
|||||||
new Tag({ // eslint-disable-line
|
new Tag({ // eslint-disable-line
|
||||||
type: 'tag',
|
type: 'tag',
|
||||||
value: 'foo',
|
value: 'foo',
|
||||||
name: 'foo'
|
name: 'not-exist'
|
||||||
} as TagToken, [], liquid)
|
} as TagToken, [], liquid)
|
||||||
}).to.throw(/tag foo not found/)
|
}).to.throw(/tag "not-exist" not found/)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should register simple tag', function () {
|
it('should register simple tag', function () {
|
||||||
expect(function () {
|
expect(function () {
|
||||||
Tag.register('foo', {
|
liquid.registerTag('foo', { render: () => 'bar' })
|
||||||
render: () => 'bar'
|
|
||||||
})
|
|
||||||
}).not.throw()
|
}).not.throw()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should call tag.render', async function () {
|
it('should call tag.render', async function () {
|
||||||
const spy = sinon.spy()
|
const spy = sinon.spy()
|
||||||
Tag.register('foo', {
|
liquid.registerTag('foo', { render: spy })
|
||||||
render: spy
|
|
||||||
})
|
|
||||||
const token = {
|
const token = {
|
||||||
type: 'tag',
|
type: 'tag',
|
||||||
value: 'foo',
|
value: 'foo',
|
||||||
@@ -64,9 +60,7 @@ describe('Tag', function () {
|
|||||||
let spy: sinon.SinonSpy, token: TagToken
|
let spy: sinon.SinonSpy, token: TagToken
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
spy = sinon.spy()
|
spy = sinon.spy()
|
||||||
Tag.register('foo', {
|
liquid.registerTag('foo', { render: spy })
|
||||||
render: spy
|
|
||||||
})
|
|
||||||
token = {
|
token = {
|
||||||
type: 'tag',
|
type: 'tag',
|
||||||
value: 'foo aa:foo bb: arr[0] cc: 2.3\ndd:bar.coo',
|
value: 'foo aa:foo bb: arr[0] cc: 2.3\ndd:bar.coo',
|
||||||
|
|||||||
+18
-18
@@ -1,9 +1,9 @@
|
|||||||
import * as chai from 'chai'
|
import * as chai from 'chai'
|
||||||
import { toThenable } from '../../../src/util/async'
|
import { toThenable } from '../../../src/util/async'
|
||||||
|
import { FilterMap } from '../../../src/template/filter/filter-map'
|
||||||
import * as sinonChai from 'sinon-chai'
|
import * as sinonChai from 'sinon-chai'
|
||||||
import * as sinon from 'sinon'
|
import * as sinon from 'sinon'
|
||||||
import { Context } from '../../../src/context/context'
|
import { Context } from '../../../src/context/context'
|
||||||
import { Filter } from '../../../src/template/filter/filter'
|
|
||||||
import { Value } from '../../../src/template/value'
|
import { Value } from '../../../src/template/value'
|
||||||
|
|
||||||
chai.use(sinonChai)
|
chai.use(sinonChai)
|
||||||
@@ -11,74 +11,73 @@ chai.use(sinonChai)
|
|||||||
const expect = chai.expect
|
const expect = chai.expect
|
||||||
|
|
||||||
describe('Value', function () {
|
describe('Value', function () {
|
||||||
beforeEach(() => Filter.clear())
|
|
||||||
|
|
||||||
describe('#constructor()', function () {
|
describe('#constructor()', function () {
|
||||||
|
const filterMap = new FilterMap(false)
|
||||||
it('should parse "foo', function () {
|
it('should parse "foo', function () {
|
||||||
const tpl = new Value('foo', false)
|
const tpl = new Value('foo', filterMap)
|
||||||
expect(tpl.initial).to.equal('foo')
|
expect(tpl.initial).to.equal('foo')
|
||||||
expect(tpl.filters).to.deep.equal([])
|
expect(tpl.filters).to.deep.equal([])
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should parse "foo | add"', function () {
|
it('should parse "foo | add"', function () {
|
||||||
const tpl = new Value('foo | add', false)
|
const tpl = new Value('foo | add', filterMap)
|
||||||
expect(tpl.initial).to.equal('foo')
|
expect(tpl.initial).to.equal('foo')
|
||||||
expect(tpl.filters.length).to.equal(1)
|
expect(tpl.filters.length).to.equal(1)
|
||||||
expect(tpl.filters[0].args).to.eql([])
|
expect(tpl.filters[0].args).to.eql([])
|
||||||
})
|
})
|
||||||
it('should parse "foo,foo | add"', function () {
|
it('should parse "foo,foo | add"', function () {
|
||||||
const tpl = new Value('foo,foo | add', false)
|
const tpl = new Value('foo,foo | add', filterMap)
|
||||||
expect(tpl.initial).to.equal('foo')
|
expect(tpl.initial).to.equal('foo')
|
||||||
expect(tpl.filters.length).to.equal(1)
|
expect(tpl.filters.length).to.equal(1)
|
||||||
expect(tpl.filters[0].args).to.eql([])
|
expect(tpl.filters[0].args).to.eql([])
|
||||||
})
|
})
|
||||||
it('should parse "foo | add: 3, false"', function () {
|
it('should parse "foo | add: 3, false"', function () {
|
||||||
const tpl = new Value('foo | add: 3, "foo"', false)
|
const tpl = new Value('foo | add: 3, "foo"', filterMap)
|
||||||
expect(tpl.initial).to.equal('foo')
|
expect(tpl.initial).to.equal('foo')
|
||||||
expect(tpl.filters.length).to.equal(1)
|
expect(tpl.filters.length).to.equal(1)
|
||||||
expect(tpl.filters[0].args).to.eql(['3', '"foo"'])
|
expect(tpl.filters[0].args).to.eql(['3', '"foo"'])
|
||||||
})
|
})
|
||||||
it('should parse "foo | add: "foo" bar, 3"', function () {
|
it('should parse "foo | add: "foo" bar, 3"', function () {
|
||||||
const tpl = new Value('foo | add: "foo" bar, 3', false)
|
const tpl = new Value('foo | add: "foo" bar, 3', filterMap)
|
||||||
expect(tpl.initial).to.equal('foo')
|
expect(tpl.initial).to.equal('foo')
|
||||||
expect(tpl.filters.length).to.equal(1)
|
expect(tpl.filters.length).to.equal(1)
|
||||||
expect(tpl.filters[0].name).to.eql('add')
|
expect(tpl.filters[0].name).to.eql('add')
|
||||||
expect(tpl.filters[0].args).to.eql(['"foo"', '3'])
|
expect(tpl.filters[0].args).to.eql(['"foo"', '3'])
|
||||||
})
|
})
|
||||||
it('should parse "foo | add: "|", 3', function () {
|
it('should parse "foo | add: "|", 3', function () {
|
||||||
const tpl = new Value('foo | add: "|", 3', false)
|
const tpl = new Value('foo | add: "|", 3', filterMap)
|
||||||
expect(tpl.initial).to.equal('foo')
|
expect(tpl.initial).to.equal('foo')
|
||||||
expect(tpl.filters.length).to.equal(1)
|
expect(tpl.filters.length).to.equal(1)
|
||||||
expect(tpl.filters[0].args).to.eql(['"|"', '3'])
|
expect(tpl.filters[0].args).to.eql(['"|"', '3'])
|
||||||
})
|
})
|
||||||
it('should parse "foo | add: "|", 3', function () {
|
it('should parse "foo | add: "|", 3', function () {
|
||||||
const tpl = new Value('foo | add: "|", 3', false)
|
const tpl = new Value('foo | add: "|", 3', filterMap)
|
||||||
expect(tpl.initial).to.equal('foo')
|
expect(tpl.initial).to.equal('foo')
|
||||||
expect(tpl.filters.length).to.equal(1)
|
expect(tpl.filters.length).to.equal(1)
|
||||||
expect(tpl.filters[0].args).to.eql(['"|"', '3'])
|
expect(tpl.filters[0].args).to.eql(['"|"', '3'])
|
||||||
})
|
})
|
||||||
it('should support arguments as named key/values', function () {
|
it('should support arguments as named key/values', function () {
|
||||||
const f = new Value('o | foo: key1: "literal1", key2: value2', false)
|
const f = new Value('o | foo: key1: "literal1", key2: value2', filterMap)
|
||||||
expect(f.filters[0].name).to.equal('foo')
|
expect(f.filters[0].name).to.equal('foo')
|
||||||
expect(f.filters[0].args).to.eql([['key1', '"literal1"'], ['key2', 'value2']])
|
expect(f.filters[0].args).to.eql([['key1', '"literal1"'], ['key2', 'value2']])
|
||||||
})
|
})
|
||||||
it('should support arguments as named key/values with inline literals', function () {
|
it('should support arguments as named key/values with inline literals', function () {
|
||||||
const f = new Value('o | foo: "test0", key1: "literal1", key2: value2', false)
|
const f = new Value('o | foo: "test0", key1: "literal1", key2: value2', filterMap)
|
||||||
expect(f.filters[0].name).to.equal('foo')
|
expect(f.filters[0].name).to.equal('foo')
|
||||||
expect(f.filters[0].args).to.deep.equal(['"test0"', ['key1', '"literal1"'], ['key2', 'value2']])
|
expect(f.filters[0].args).to.deep.equal(['"test0"', ['key1', '"literal1"'], ['key2', 'value2']])
|
||||||
})
|
})
|
||||||
it('should support arguments as named key/values with inline values', function () {
|
it('should support arguments as named key/values with inline values', function () {
|
||||||
const f = new Value('o | foo: test0, key1: "literal1", key2: value2', false)
|
const f = new Value('o | foo: test0, key1: "literal1", key2: value2', filterMap)
|
||||||
expect(f.filters[0].name).to.equal('foo')
|
expect(f.filters[0].name).to.equal('foo')
|
||||||
expect(f.filters[0].args).to.deep.equal(['test0', ['key1', '"literal1"'], ['key2', 'value2']])
|
expect(f.filters[0].args).to.deep.equal(['test0', ['key1', '"literal1"'], ['key2', 'value2']])
|
||||||
})
|
})
|
||||||
it('should support argument values named same as keys', function () {
|
it('should support argument values named same as keys', function () {
|
||||||
const f = new Value('o | foo: a: a', false)
|
const f = new Value('o | foo: a: a', filterMap)
|
||||||
expect(f.filters[0].name).to.equal('foo')
|
expect(f.filters[0].name).to.equal('foo')
|
||||||
expect(f.filters[0].args).to.deep.equal([['a', 'a']])
|
expect(f.filters[0].args).to.deep.equal([['a', 'a']])
|
||||||
})
|
})
|
||||||
it('should support argument literals named same as keys', function () {
|
it('should support argument literals named same as keys', function () {
|
||||||
const f = new Value('o | foo: a: "a"', false)
|
const f = new Value('o | foo: a: "a"', filterMap)
|
||||||
expect(f.filters[0].name).to.equal('foo')
|
expect(f.filters[0].name).to.equal('foo')
|
||||||
expect(f.filters[0].args).to.deep.equal([['a', '"a"']])
|
expect(f.filters[0].args).to.deep.equal([['a', '"a"']])
|
||||||
})
|
})
|
||||||
@@ -115,9 +114,10 @@ describe('Value', function () {
|
|||||||
it('should call chained filters correctly', async function () {
|
it('should call chained filters correctly', async function () {
|
||||||
const date = sinon.stub().returns('y')
|
const date = sinon.stub().returns('y')
|
||||||
const time = sinon.spy()
|
const time = sinon.spy()
|
||||||
Filter.register('date', date)
|
const filterMap = new FilterMap(false)
|
||||||
Filter.register('time', time)
|
filterMap.set('date', date)
|
||||||
const tpl = new Value('foo.bar | date: "b" | time:2', false)
|
filterMap.set('time', time)
|
||||||
|
const tpl = new Value('foo.bar | date: "b" | time:2', filterMap)
|
||||||
const scope = new Context({
|
const scope = new Context({
|
||||||
foo: { bar: 'bar' }
|
foo: { bar: 'bar' }
|
||||||
})
|
})
|
||||||
|
|||||||
+1
-1
@@ -13,5 +13,5 @@
|
|||||||
"suppressImplicitAnyIndexErrors": true
|
"suppressImplicitAnyIndexErrors": true
|
||||||
},
|
},
|
||||||
"all": true,
|
"all": true,
|
||||||
"exclude": [ "node_modules", "dist" ]
|
"exclude": [ "node_modules", "dist", "demo" ]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user