mirror of
https://github.com/harttle/liquidjs.git
synced 2026-09-15 20:30:39 -07:00
feat: add option for keeping variable type in output
This commit is contained in:
committed by
Jun Yang
parent
e84fbf32ad
commit
cd92e77726
@@ -20,7 +20,7 @@ export default {
|
|||||||
const r = this.liquid.renderer
|
const r = this.liquid.renderer
|
||||||
const html = childDefined !== undefined
|
const html = childDefined !== undefined
|
||||||
? childDefined
|
? childDefined
|
||||||
: yield r.renderTemplates(this.tpls, ctx)
|
: yield r.renderTemplates(this.tpls, ctx, new Emitter(ctx.opts.keepOutputType))
|
||||||
|
|
||||||
if (ctx.getRegister('blockMode', BlockMode.OUTPUT) === BlockMode.STORE) {
|
if (ctx.getRegister('blockMode', BlockMode.OUTPUT) === BlockMode.STORE) {
|
||||||
blocks[this.block] = html
|
blocks[this.block] = html
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Tokenizer, assert, Template, Context, TagImplOptions, TagToken, TopLevelToken } from '../../types'
|
import { Tokenizer, assert, Template, Context, TagImplOptions, TagToken, TopLevelToken } from '../../types'
|
||||||
import { evalQuotedToken } from '../../render/expression'
|
import { evalQuotedToken } from '../../render/expression'
|
||||||
|
import { Emitter } from '../../render/emitter'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
parse: function (tagToken: TagToken, remainTokens: TopLevelToken[]) {
|
parse: function (tagToken: TagToken, remainTokens: TopLevelToken[]) {
|
||||||
@@ -19,7 +20,7 @@ export default {
|
|||||||
},
|
},
|
||||||
render: function * (ctx: Context) {
|
render: function * (ctx: Context) {
|
||||||
const r = this.liquid.renderer
|
const r = this.liquid.renderer
|
||||||
const html = yield r.renderTemplates(this.templates, ctx)
|
const html = yield r.renderTemplates(this.templates, ctx, new Emitter(ctx.opts.keepOutputType))
|
||||||
ctx.bottom()[this.variable] = html
|
ctx.bottom()[this.variable] = html
|
||||||
}
|
}
|
||||||
} as TagImplOptions
|
} as TagImplOptions
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export default {
|
|||||||
const { renderer } = liquid
|
const { renderer } = liquid
|
||||||
const filepath = ctx.opts.dynamicPartials
|
const filepath = ctx.opts.dynamicPartials
|
||||||
? (TypeGuards.isQuotedToken(file)
|
? (TypeGuards.isQuotedToken(file)
|
||||||
? yield renderer.renderTemplates(liquid.parse(evalQuotedToken(file)), ctx)
|
? yield renderer.renderTemplates(liquid.parse(evalQuotedToken(file)), ctx, new Emitter(ctx.opts.keepOutputType))
|
||||||
: yield evalToken(file, ctx))
|
: yield evalToken(file, ctx))
|
||||||
: file.getText()
|
: file.getText()
|
||||||
assert(filepath, () => `illegal filename "${file.getText()}":"${filepath}"`)
|
assert(filepath, () => `illegal filename "${file.getText()}":"${filepath}"`)
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export default {
|
|||||||
const { renderer } = liquid
|
const { renderer } = liquid
|
||||||
const filepath = ctx.opts.dynamicPartials
|
const filepath = ctx.opts.dynamicPartials
|
||||||
? (TypeGuards.isQuotedToken(file)
|
? (TypeGuards.isQuotedToken(file)
|
||||||
? yield renderer.renderTemplates(liquid.parse(evalQuotedToken(file)), ctx)
|
? yield renderer.renderTemplates(liquid.parse(evalQuotedToken(file)), ctx, new Emitter(ctx.opts.keepOutputType))
|
||||||
: 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}"`)
|
||||||
@@ -24,12 +24,12 @@ export default {
|
|||||||
// render the remaining tokens immediately
|
// render the remaining tokens immediately
|
||||||
ctx.setRegister('blockMode', BlockMode.STORE)
|
ctx.setRegister('blockMode', BlockMode.STORE)
|
||||||
const blocks = ctx.getRegister('blocks')
|
const blocks = ctx.getRegister('blocks')
|
||||||
const html = yield renderer.renderTemplates(this.tpls, ctx)
|
const html = yield renderer.renderTemplates(this.tpls, ctx, new Emitter(ctx.opts.keepOutputType))
|
||||||
if (blocks[''] === undefined) blocks[''] = html
|
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))
|
ctx.push(yield hash.render(ctx))
|
||||||
ctx.setRegister('blockMode', BlockMode.OUTPUT)
|
ctx.setRegister('blockMode', BlockMode.OUTPUT)
|
||||||
const partial = yield renderer.renderTemplates(templates, ctx)
|
const partial = yield renderer.renderTemplates(templates, ctx, new Emitter(ctx.opts.keepOutputType))
|
||||||
ctx.pop()
|
ctx.pop()
|
||||||
emitter.write(partial)
|
emitter.write(partial)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ export default {
|
|||||||
const { renderer } = liquid
|
const { renderer } = liquid
|
||||||
const filepath = ctx.opts.dynamicPartials
|
const filepath = ctx.opts.dynamicPartials
|
||||||
? (TypeGuards.isQuotedToken(file)
|
? (TypeGuards.isQuotedToken(file)
|
||||||
? yield renderer.renderTemplates(liquid.parse(evalQuotedToken(file)), ctx)
|
? yield renderer.renderTemplates(liquid.parse(evalQuotedToken(file)), ctx, new Emitter(ctx.opts.keepOutputType))
|
||||||
: evalToken(file, ctx))
|
: evalToken(file, ctx))
|
||||||
: file.getText()
|
: file.getText()
|
||||||
assert(filepath, () => `illegal filename "${file.getText()}":"${filepath}"`)
|
assert(filepath, () => `illegal filename "${file.getText()}":"${filepath}"`)
|
||||||
|
|||||||
@@ -43,6 +43,8 @@ export interface LiquidOptions {
|
|||||||
fs?: FS;
|
fs?: FS;
|
||||||
/** the global environment passed down to all partial templates, i.e. templates included by `include`, `layout` and `render` tags. */
|
/** the global environment passed down to all partial templates, i.e. templates included by `include`, `layout` and `render` tags. */
|
||||||
globals?: object;
|
globals?: object;
|
||||||
|
/** Whether or not to keep value type when writing the Output. Defaults to `false`. */
|
||||||
|
keepOutputType?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface NormalizedOptions extends LiquidOptions {
|
interface NormalizedOptions extends LiquidOptions {
|
||||||
@@ -69,6 +71,7 @@ export interface NormalizedFullOptions extends NormalizedOptions {
|
|||||||
outputDelimiterRight: string;
|
outputDelimiterRight: string;
|
||||||
greedy: boolean;
|
greedy: boolean;
|
||||||
globals: object;
|
globals: object;
|
||||||
|
keepOutputType: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const defaultOptions: NormalizedFullOptions = {
|
export const defaultOptions: NormalizedFullOptions = {
|
||||||
@@ -89,7 +92,8 @@ export const defaultOptions: NormalizedFullOptions = {
|
|||||||
strictFilters: false,
|
strictFilters: false,
|
||||||
strictVariables: false,
|
strictVariables: false,
|
||||||
lenientIf: false,
|
lenientIf: false,
|
||||||
globals: {}
|
globals: {},
|
||||||
|
keepOutputType: false
|
||||||
}
|
}
|
||||||
|
|
||||||
export function normalize (options?: LiquidOptions): NormalizedOptions {
|
export function normalize (options?: LiquidOptions): NormalizedOptions {
|
||||||
|
|||||||
+9
-7
@@ -15,6 +15,7 @@ import { LiquidOptions, normalizeStringArray, NormalizedFullOptions, applyDefaul
|
|||||||
import { FilterImplOptions } from './template/filter/filter-impl-options'
|
import { FilterImplOptions } from './template/filter/filter-impl-options'
|
||||||
import { FS } from './fs/fs'
|
import { FS } from './fs/fs'
|
||||||
import { toPromise, toValue } from './util/async'
|
import { toPromise, toValue } from './util/async'
|
||||||
|
import { Emitter } from './render/emitter'
|
||||||
|
|
||||||
export * from './types'
|
export * from './types'
|
||||||
|
|
||||||
@@ -43,26 +44,27 @@ export class Liquid {
|
|||||||
return this.parser.parse(tokens)
|
return this.parser.parse(tokens)
|
||||||
}
|
}
|
||||||
|
|
||||||
public _render (tpl: Template[], scope?: object, opts?: LiquidOptions, sync?: boolean): IterableIterator<string> {
|
public _render (tpl: Template[], scope?: object, opts?: LiquidOptions, sync?: boolean): IterableIterator<any> {
|
||||||
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)
|
const emitter = new Emitter(options.keepOutputType)
|
||||||
|
return this.renderer.renderTemplates(tpl, ctx, emitter)
|
||||||
}
|
}
|
||||||
public async render (tpl: Template[], scope?: object, opts?: LiquidOptions): Promise<string> {
|
public async render (tpl: Template[], scope?: object, opts?: LiquidOptions): Promise<any> {
|
||||||
return toPromise(this._render(tpl, scope, opts, false))
|
return toPromise(this._render(tpl, scope, opts, false))
|
||||||
}
|
}
|
||||||
public renderSync (tpl: Template[], scope?: object, opts?: LiquidOptions): string {
|
public renderSync (tpl: Template[], scope?: object, opts?: LiquidOptions): any {
|
||||||
return toValue(this._render(tpl, scope, opts, true))
|
return toValue(this._render(tpl, scope, opts, true))
|
||||||
}
|
}
|
||||||
|
|
||||||
public _parseAndRender (html: string, scope?: object, opts?: LiquidOptions, sync?: boolean): IterableIterator<string> {
|
public _parseAndRender (html: string, scope?: object, opts?: LiquidOptions, sync?: boolean): IterableIterator<any> {
|
||||||
const tpl = this.parse(html)
|
const tpl = this.parse(html)
|
||||||
return this._render(tpl, scope, opts, sync)
|
return this._render(tpl, scope, opts, sync)
|
||||||
}
|
}
|
||||||
public async parseAndRender (html: string, scope?: object, opts?: LiquidOptions): Promise<string> {
|
public async parseAndRender (html: string, scope?: object, opts?: LiquidOptions): Promise<any> {
|
||||||
return toPromise(this._parseAndRender(html, scope, opts, false))
|
return toPromise(this._parseAndRender(html, scope, opts, false))
|
||||||
}
|
}
|
||||||
public parseAndRenderSync (html: string, scope?: object, opts?: LiquidOptions): string {
|
public parseAndRenderSync (html: string, scope?: object, opts?: LiquidOptions): any {
|
||||||
return toValue(this._parseAndRender(html, scope, opts, true))
|
return toValue(this._parseAndRender(html, scope, opts, true))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+12
-3
@@ -1,9 +1,18 @@
|
|||||||
export class Emitter {
|
export class Emitter {
|
||||||
public html = '';
|
public html: any = '';
|
||||||
public break = false;
|
public break = false;
|
||||||
public continue = false;
|
public continue = false;
|
||||||
|
private keepOutputType? = false;
|
||||||
|
|
||||||
public write (html: string) {
|
constructor (keepOutputType: boolean|undefined) {
|
||||||
this.html += html
|
this.keepOutputType = keepOutputType
|
||||||
|
}
|
||||||
|
|
||||||
|
public write (html: any) {
|
||||||
|
if (this.keepOutputType && typeof html !== 'string') {
|
||||||
|
this.html = html
|
||||||
|
} else {
|
||||||
|
this.html += html as string
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { Template } from '../template/template'
|
|||||||
import { Emitter } from './emitter'
|
import { Emitter } from './emitter'
|
||||||
|
|
||||||
export class Render {
|
export class Render {
|
||||||
public * renderTemplates (templates: Template[], ctx: Context, emitter = new Emitter()): IterableIterator<string> {
|
public * renderTemplates (templates: Template[], ctx: Context, emitter: Emitter): IterableIterator<any> {
|
||||||
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)
|
||||||
|
|||||||
@@ -16,6 +16,10 @@ export class Output extends TemplateImpl<OutputToken> implements Template {
|
|||||||
}
|
}
|
||||||
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)
|
||||||
emitter.write(stringify(toValue(val)))
|
if (ctx.opts.keepOutputType) {
|
||||||
|
emitter.write(toValue(val))
|
||||||
|
} else {
|
||||||
|
emitter.write(stringify(toValue(val)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { Context } from '../../../src/context/context'
|
|||||||
import { HTMLToken } from '../../../src/tokens/html-token'
|
import { HTMLToken } from '../../../src/tokens/html-token'
|
||||||
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 { Emitter } from '../../../src/render/emitter'
|
||||||
import { toThenable } from '../../../src/util/async'
|
import { toThenable } from '../../../src/util/async'
|
||||||
|
|
||||||
describe('render', function () {
|
describe('render', function () {
|
||||||
@@ -15,7 +16,7 @@ describe('render', function () {
|
|||||||
it('should render html', async function () {
|
it('should render html', async function () {
|
||||||
const scope = new Context()
|
const scope = new Context()
|
||||||
const token = { getContent: () => '<p>' } as HTMLToken
|
const token = { getContent: () => '<p>' } as HTMLToken
|
||||||
const html = await toThenable(render.renderTemplates([new HTML(token)], scope))
|
const html = await toThenable(render.renderTemplates([new HTML(token)], scope, new Emitter(scope.opts.keepOutputType)))
|
||||||
return expect(html).to.equal('<p>')
|
return expect(html).to.equal('<p>')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user