feat!: drop TagImplOptions in favor of Tag classes (#839)

Remove tag-options-adapter and the registerTag object-literal overload.
Custom tags must extend Tag.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Yang Jun
2026-07-07 22:34:14 +08:00
co-authored by Cursor
parent 9aed43ab2b
commit f0b907bcf4
9 changed files with 99 additions and 87 deletions
+2 -5
View File
@@ -1,6 +1,7 @@
import { Liquid, Context, isFalsy } from '../../../src'
import { mock, restore } from '../../stub/mockfs'
import { drainStream } from '../../stub/stream'
import { IntendedRenderErrorTag } from '../../stub/tags'
import { resolve } from 'path'
describe('Liquid', function () {
@@ -231,11 +232,7 @@ describe('Liquid', function () {
'/root/error.html': 'A{%throwingTag%}B'
})
engine = new Liquid({ root: ['/root/'] })
engine.registerTag('throwingTag', {
render: function () {
throw new Error('intended render error')
}
})
engine.registerTag('throwingTag', IntendedRenderErrorTag)
})
afterEach(restore)
it('should render a simple value', async () => {
+36 -13
View File
@@ -1,27 +1,53 @@
import { Liquid } from '../../../src/liquid'
import { Tag } from '../../../src/template/tag'
import type { Context } from '../../../src/context'
import type { TagToken, TopLevelToken } from '../../../src/tokens'
class SimpleStringTag extends Tag {
render () {
return 'B'
}
}
class AsyncStringTag extends Tag {
async render () {
return 'B'
}
}
class DynamicStringTag extends Tag {
async render (ctx: Context) {
return ctx.get(['c'])
}
}
class ArgumentReflectorTag extends Tag {
variable: string
constructor (token: TagToken, remainTokens: TopLevelToken[], liquid: Liquid) {
super(token, remainTokens, liquid)
this.variable = token.args.split('=')[1]
}
async render (ctx: Context) {
return ctx.get([this.variable])
}
}
describe('liquid#registerTag()', function () {
it('should support render to simple string', async () => {
const liquid = new Liquid()
liquid.registerTag('simple-string', {
render: () => 'B'
})
liquid.registerTag('simple-string', SimpleStringTag)
const html = await liquid.parseAndRender(`A{% simple-string %}C`)
return expect(html).toBe('ABC')
})
it('should support async tag render', async () => {
const liquid = new Liquid()
liquid.registerTag('async-string', {
render: async () => 'B'
})
liquid.registerTag('async-string', AsyncStringTag)
const html = await liquid.parseAndRender(`A{% async-string %}C`)
return expect(html).toBe('ABC')
})
it('should have access to ctx in render()', async () => {
const liquid = new Liquid()
liquid.registerTag('dynamic-string', {
render: async (ctx) => ctx.get(['c'])
})
liquid.registerTag('dynamic-string', DynamicStringTag)
const html = await liquid.parseAndRender(`A{% dynamic-string %}C`, {
c: 'B'
})
@@ -29,10 +55,7 @@ describe('liquid#registerTag()', function () {
})
it('should have access to tag arguments', async () => {
const liquid = new Liquid()
liquid.registerTag('argument-reflector', {
parse: function (token) { this.variable = token.args.split('=')[1] },
render: async function (ctx) { return ctx.get(this.variable) }
})
liquid.registerTag('argument-reflector', ArgumentReflectorTag)
const html = await liquid.parseAndRender(`A{% argument-reflector variable=c %}C`, {
c: 'B'
})