mirror of
https://github.com/harttle/liquidjs.git
synced 2026-09-16 04:40:39 -07:00
feat: support {{block.super}}, see #38
This commit is contained in:
+28
-14
@@ -1,8 +1,9 @@
|
|||||||
import BlockMode from '../../context/block-mode'
|
import BlockMode from '../../context/block-mode'
|
||||||
import { ParseStream, TagToken, TopLevelToken, Template, Context, TagImplOptions, Emitter } from '../../types'
|
import { BlockDrop } from '../../drop/block-drop'
|
||||||
|
import { ParseStream, TagToken, TopLevelToken, Template, Context, TagImpl, Emitter } from '../../types'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
parse: function (token: TagToken, remainTokens: TopLevelToken[]) {
|
parse (this: TagImpl, token: TagToken, remainTokens: TopLevelToken[]) {
|
||||||
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 Template[]
|
this.tpls = [] as Template[]
|
||||||
@@ -14,18 +15,31 @@ export default {
|
|||||||
})
|
})
|
||||||
stream.start()
|
stream.start()
|
||||||
},
|
},
|
||||||
render: function * (ctx: Context, emitter: Emitter) {
|
|
||||||
const blocks = ctx.getRegister('blocks')
|
|
||||||
const childDefined = blocks[this.block]
|
|
||||||
const r = this.liquid.renderer
|
|
||||||
const html = childDefined !== undefined
|
|
||||||
? childDefined
|
|
||||||
: yield r.renderTemplates(this.tpls, ctx)
|
|
||||||
|
|
||||||
if (ctx.getRegister('blockMode', BlockMode.OUTPUT) === BlockMode.STORE) {
|
* render (this: TagImpl, ctx: Context, emitter: Emitter) {
|
||||||
blocks[this.block] = html
|
const blockRender = this.getBlockRender(ctx)
|
||||||
return
|
yield this.emitHTML(ctx, emitter, blockRender)
|
||||||
|
},
|
||||||
|
|
||||||
|
getBlockRender (this: TagImpl, ctx: Context) {
|
||||||
|
const { liquid, tpls } = this
|
||||||
|
const extendedBlockRender = ctx.getRegister('blocks')[this.block]
|
||||||
|
const defaultBlockRender = function * (superBlock: BlockDrop) {
|
||||||
|
ctx.push({ block: superBlock })
|
||||||
|
const result = yield liquid.renderer.renderTemplates(tpls, ctx)
|
||||||
|
ctx.pop()
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
return extendedBlockRender
|
||||||
|
? (superBlock: BlockDrop) => extendedBlockRender(new BlockDrop(() => defaultBlockRender(superBlock)))
|
||||||
|
: defaultBlockRender
|
||||||
|
},
|
||||||
|
|
||||||
|
* emitHTML (this: TagImpl, ctx: Context, emitter: Emitter, blockRender: (block: BlockDrop) => string) {
|
||||||
|
if (ctx.getRegister('blockMode', BlockMode.OUTPUT) === BlockMode.STORE) {
|
||||||
|
ctx.getRegister('blocks')[this.block] = blockRender
|
||||||
|
} else {
|
||||||
|
emitter.write(yield blockRender(new BlockDrop()))
|
||||||
}
|
}
|
||||||
emitter.write(html)
|
|
||||||
}
|
}
|
||||||
} as TagImplOptions
|
}
|
||||||
|
|||||||
@@ -20,15 +20,17 @@ export default {
|
|||||||
: evalToken(this.file, ctx))
|
: evalToken(this.file, ctx))
|
||||||
: file.getText()
|
: file.getText()
|
||||||
assert(filepath, () => `illegal filename "${file.getText()}":"${filepath}"`)
|
assert(filepath, () => `illegal filename "${file.getText()}":"${filepath}"`)
|
||||||
|
|
||||||
// render the remaining tokens immediately
|
|
||||||
ctx.setRegister('blockMode', BlockMode.STORE)
|
|
||||||
const blocks = ctx.getRegister('blocks')
|
|
||||||
const html = yield renderer.renderTemplates(this.tpls, ctx)
|
|
||||||
if (blocks[''] === undefined) blocks[''] = html
|
|
||||||
const templates = yield liquid._parseFile(filepath, ctx.opts, ctx.sync)
|
const templates = yield liquid._parseFile(filepath, ctx.opts, ctx.sync)
|
||||||
ctx.push(yield hash.render(ctx))
|
|
||||||
|
// render remaining contents and store rendered results
|
||||||
|
ctx.setRegister('blockMode', BlockMode.STORE)
|
||||||
|
const html = yield renderer.renderTemplates(this.tpls, ctx)
|
||||||
|
const blocks = ctx.getRegister('blocks')
|
||||||
|
if (blocks[''] === undefined) blocks[''] = () => html
|
||||||
ctx.setRegister('blockMode', BlockMode.OUTPUT)
|
ctx.setRegister('blockMode', BlockMode.OUTPUT)
|
||||||
|
|
||||||
|
// render the layout file use stored blocks
|
||||||
|
ctx.push(yield hash.render(ctx))
|
||||||
const partial = yield renderer.renderTemplates(templates, ctx)
|
const partial = yield renderer.renderTemplates(templates, ctx)
|
||||||
ctx.pop()
|
ctx.pop()
|
||||||
emitter.write(partial)
|
emitter.write(partial)
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { Drop } from './drop'
|
||||||
|
|
||||||
|
export class BlockDrop extends Drop {
|
||||||
|
constructor (
|
||||||
|
// the block render from layout template
|
||||||
|
private superBlockRender: () => Iterable<string> = () => ''
|
||||||
|
) {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
public super () {
|
||||||
|
return this.superBlockRender()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,6 +11,7 @@ export { Context } from './context/context'
|
|||||||
export { Template } from './template/template'
|
export { Template } from './template/template'
|
||||||
export { FilterImplOptions } from './template/filter/filter-impl-options'
|
export { FilterImplOptions } from './template/filter/filter-impl-options'
|
||||||
export { TagImplOptions } from './template/tag/tag-impl-options'
|
export { TagImplOptions } from './template/tag/tag-impl-options'
|
||||||
|
export { TagImpl } from './template/tag/tag-impl'
|
||||||
export { ParseStream } from './parser/parse-stream'
|
export { ParseStream } from './parser/parse-stream'
|
||||||
export { Token } from './tokens/token'
|
export { Token } from './tokens/token'
|
||||||
export { TopLevelToken } from './tokens/toplevel-token'
|
export { TopLevelToken } from './tokens/toplevel-token'
|
||||||
|
|||||||
@@ -69,6 +69,36 @@ describe('tags/layout', function () {
|
|||||||
const html = await liquid.parseAndRender(src)
|
const html = await liquid.parseAndRender(src)
|
||||||
return expect(html).to.equal('XAYBZ')
|
return expect(html).to.equal('XAYBZ')
|
||||||
})
|
})
|
||||||
|
it('should support block.super', async function () {
|
||||||
|
mock({
|
||||||
|
'/parent.html': '{% block css %}<link href="base.css" rel="stylesheet">{% endblock %}'
|
||||||
|
})
|
||||||
|
const src = '{% layout "parent.html" %}' +
|
||||||
|
'{%block css%}{{block.super}}<link href="extra.css" rel="stylesheet">{%endblock%}'
|
||||||
|
const html = await liquid.parseAndRender(src)
|
||||||
|
const output = '<link href="base.css" rel="stylesheet"><link href="extra.css" rel="stylesheet">'
|
||||||
|
return expect(html).to.equal(output)
|
||||||
|
})
|
||||||
|
it('should render block.super to empty if no parent exists', async function () {
|
||||||
|
mock({
|
||||||
|
'/parent.html': '{% block css %}{{block.super}}<link href="base.css" rel="stylesheet">{% endblock %}'
|
||||||
|
})
|
||||||
|
const src = '{% layout "parent.html" %}' +
|
||||||
|
'{%block css%}{{block.super}}<link href="extra.css" rel="stylesheet">{%endblock%}'
|
||||||
|
const html = await liquid.parseAndRender(src)
|
||||||
|
const output = '<link href="base.css" rel="stylesheet"><link href="extra.css" rel="stylesheet">'
|
||||||
|
return expect(html).to.equal(output)
|
||||||
|
})
|
||||||
|
it('should support nested block.super', async function () {
|
||||||
|
mock({
|
||||||
|
'/root.html': '{% block css %}<link href="root.css" rel="stylesheet">{% endblock %}',
|
||||||
|
'/parent.html': '{% layout "root.html" %}{% block css %}{{block.super}}<link href="parent.css" rel="stylesheet">{% endblock %}'
|
||||||
|
})
|
||||||
|
const src = '{% layout "parent.html" %}{%block css%}{{block.super}}<link href="extra.css" rel="stylesheet">{%endblock%}'
|
||||||
|
const html = await liquid.parseAndRender(src)
|
||||||
|
const output = '<link href="root.css" rel="stylesheet"><link href="parent.css" rel="stylesheet"><link href="extra.css" rel="stylesheet">'
|
||||||
|
return expect(html).to.equal(output)
|
||||||
|
})
|
||||||
it('should support variable as layout name', async function () {
|
it('should support variable as layout name', async function () {
|
||||||
mock({
|
mock({
|
||||||
'/parent.html': 'X{% block "a"%}{% endblock %}Y'
|
'/parent.html': 'X{% block "a"%}{% endblock %}Y'
|
||||||
|
|||||||
Reference in New Issue
Block a user