mirror of
https://github.com/harttle/liquidjs.git
synced 2026-09-14 03:40:38 -07:00
feat: customize globals & strictVariables when calling render, see #432
This commit is contained in:
@@ -51,7 +51,7 @@ export default {
|
||||
const filepath = yield this.renderFilePath(this['file'], ctx, liquid)
|
||||
assert(filepath, () => `illegal filename "${filepath}"`)
|
||||
|
||||
const childCtx = new Context({}, ctx.opts, ctx.sync)
|
||||
const childCtx = new Context({}, ctx.opts, { sync: ctx.sync, globals: ctx.globals, strictVariables: ctx.strictVariables })
|
||||
const scope = childCtx.bottom()
|
||||
__assign(scope, yield hash.render(ctx))
|
||||
if (this['with']) {
|
||||
|
||||
+13
-5
@@ -1,6 +1,6 @@
|
||||
import { Drop } from '../drop/drop'
|
||||
import { __assign } from 'tslib'
|
||||
import { NormalizedFullOptions, defaultOptions } from '../liquid-options'
|
||||
import { NormalizedFullOptions, defaultOptions, RenderOptions } from '../liquid-options'
|
||||
import { Scope } from './scope'
|
||||
import { isArray, isNil, isString, isFunction, toLiquid } from '../util/underscore'
|
||||
import { InternalUndefinedVariableError } from '../util/error'
|
||||
@@ -23,12 +23,20 @@ export class Context {
|
||||
*/
|
||||
public globals: Scope
|
||||
public sync: boolean
|
||||
/**
|
||||
* The normalized liquid options object
|
||||
*/
|
||||
public opts: NormalizedFullOptions
|
||||
public constructor (env: object = {}, opts: NormalizedFullOptions = defaultOptions, sync = false) {
|
||||
this.sync = sync
|
||||
/**
|
||||
* Throw when accessing undefined variable?
|
||||
*/
|
||||
public strictVariables: boolean;
|
||||
public constructor (env: object = {}, opts: NormalizedFullOptions = defaultOptions, renderOptions: RenderOptions = {}) {
|
||||
this.sync = !!renderOptions.sync
|
||||
this.opts = opts
|
||||
this.globals = opts.globals
|
||||
this.globals = renderOptions.globals ?? opts.globals
|
||||
this.environments = env
|
||||
this.strictVariables = renderOptions.strictVariables ?? this.opts.strictVariables
|
||||
}
|
||||
public getRegister (key: string) {
|
||||
return (this.registers[key] = this.registers[key] || {})
|
||||
@@ -54,7 +62,7 @@ export class Context {
|
||||
if (typeof paths === 'string') paths = paths.split('.')
|
||||
return paths.reduce((scope, path) => {
|
||||
scope = readProperty(scope, path)
|
||||
if (isNil(scope) && this.opts.strictVariables) {
|
||||
if (isNil(scope) && this.strictVariables) {
|
||||
throw new InternalUndefinedVariableError(path)
|
||||
}
|
||||
return scope
|
||||
|
||||
@@ -65,6 +65,21 @@ export interface LiquidOptions {
|
||||
orderedFilterParameters?: boolean;
|
||||
}
|
||||
|
||||
export interface RenderOptions {
|
||||
/**
|
||||
* This call is sync or async? It's used by Liquid internal methods, you'll not need this.
|
||||
*/
|
||||
sync?: boolean;
|
||||
/**
|
||||
* Same as `globals` on LiquidOptions, but only for current render() call
|
||||
*/
|
||||
globals?: object;
|
||||
/**
|
||||
* Same as `strictVariables` on LiquidOptions, but only for current render() call
|
||||
*/
|
||||
strictVariables?: boolean;
|
||||
}
|
||||
|
||||
interface NormalizedOptions extends LiquidOptions {
|
||||
root?: string[];
|
||||
partials?: string[];
|
||||
|
||||
+21
-21
@@ -10,7 +10,7 @@ import builtinTags from './builtin/tags'
|
||||
import * as builtinFilters from './builtin/filters'
|
||||
import { TagMap } from './template/tag/tag-map'
|
||||
import { FilterMap } from './template/filter/filter-map'
|
||||
import { LiquidOptions, normalizeDirectoryList, NormalizedFullOptions, normalize } from './liquid-options'
|
||||
import { LiquidOptions, normalizeDirectoryList, NormalizedFullOptions, normalize, RenderOptions } from './liquid-options'
|
||||
import { FilterImplOptions } from './template/filter/filter-impl-options'
|
||||
import { toPromise, toValue } from './util/async'
|
||||
|
||||
@@ -39,30 +39,30 @@ export class Liquid {
|
||||
return this.parser.parse(html, filepath)
|
||||
}
|
||||
|
||||
public _render (tpl: Template[], scope?: object, sync?: boolean): IterableIterator<any> {
|
||||
const ctx = new Context(scope, this.options, sync)
|
||||
public _render (tpl: Template[], scope: object | undefined, renderOptions: RenderOptions): IterableIterator<any> {
|
||||
const ctx = new Context(scope, this.options, renderOptions)
|
||||
return this.renderer.renderTemplates(tpl, ctx)
|
||||
}
|
||||
public async render (tpl: Template[], scope?: object): Promise<any> {
|
||||
return toPromise(this._render(tpl, scope, false))
|
||||
public async render (tpl: Template[], scope?: object, renderOptions?: RenderOptions): Promise<any> {
|
||||
return toPromise(this._render(tpl, scope, { ...renderOptions, sync: false }))
|
||||
}
|
||||
public renderSync (tpl: Template[], scope?: object): any {
|
||||
return toValue(this._render(tpl, scope, true))
|
||||
public renderSync (tpl: Template[], scope?: object, renderOptions?: RenderOptions): any {
|
||||
return toValue(this._render(tpl, scope, { ...renderOptions, sync: true }))
|
||||
}
|
||||
public renderToNodeStream (tpl: Template[], scope?: object): NodeJS.ReadableStream {
|
||||
const ctx = new Context(scope, this.options)
|
||||
public renderToNodeStream (tpl: Template[], scope?: object, renderOptions: RenderOptions = {}): NodeJS.ReadableStream {
|
||||
const ctx = new Context(scope, this.options, renderOptions)
|
||||
return this.renderer.renderTemplatesToNodeStream(tpl, ctx)
|
||||
}
|
||||
|
||||
public _parseAndRender (html: string, scope?: object, sync?: boolean): IterableIterator<any> {
|
||||
public _parseAndRender (html: string, scope: object | undefined, renderOptions: RenderOptions): IterableIterator<any> {
|
||||
const tpl = this.parse(html)
|
||||
return this._render(tpl, scope, sync)
|
||||
return this._render(tpl, scope, renderOptions)
|
||||
}
|
||||
public async parseAndRender (html: string, scope?: object): Promise<any> {
|
||||
return toPromise(this._parseAndRender(html, scope, false))
|
||||
public async parseAndRender (html: string, scope?: object, renderOptions?: RenderOptions): Promise<any> {
|
||||
return toPromise(this._parseAndRender(html, scope, { ...renderOptions, sync: false }))
|
||||
}
|
||||
public parseAndRenderSync (html: string, scope?: object): any {
|
||||
return toValue(this._parseAndRender(html, scope, true))
|
||||
public parseAndRenderSync (html: string, scope?: object, renderOptions?: RenderOptions): any {
|
||||
return toValue(this._parseAndRender(html, scope, { ...renderOptions, sync: true }))
|
||||
}
|
||||
|
||||
public _parsePartialFile (file: string, sync?: boolean, currentFile?: string) {
|
||||
@@ -77,17 +77,17 @@ export class Liquid {
|
||||
public parseFileSync (file: string): Template[] {
|
||||
return toValue<Template[]>(this.parser.parseFile(file, true))
|
||||
}
|
||||
public async renderFile (file: string, ctx?: object) {
|
||||
public async renderFile (file: string, ctx?: object, renderOptions?: RenderOptions) {
|
||||
const templates = await this.parseFile(file)
|
||||
return this.render(templates, ctx)
|
||||
return this.render(templates, ctx, renderOptions)
|
||||
}
|
||||
public renderFileSync (file: string, ctx?: object) {
|
||||
public renderFileSync (file: string, ctx?: object, renderOptions?: RenderOptions) {
|
||||
const templates = this.parseFileSync(file)
|
||||
return this.renderSync(templates, ctx)
|
||||
return this.renderSync(templates, ctx, renderOptions)
|
||||
}
|
||||
public async renderFileToNodeStream (file: string, scope?: object) {
|
||||
public async renderFileToNodeStream (file: string, scope?: object, renderOptions?: RenderOptions) {
|
||||
const templates = await this.parseFile(file)
|
||||
return this.renderToNodeStream(templates, scope)
|
||||
return this.renderToNodeStream(templates, scope, renderOptions)
|
||||
}
|
||||
|
||||
public _evalValue (str: string, ctx: Context): IterableIterator<any> {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* istanbul ignore file */
|
||||
import * as TypeGuards from './util/type-guards'
|
||||
export { TypeGuards }
|
||||
export { ParseError, TokenizationError, AssertionError } from './util/error'
|
||||
|
||||
@@ -43,6 +43,15 @@ describe('Liquid', function () {
|
||||
const html = await engine.parseAndRender(src, {})
|
||||
return expect(html).to.equal('12')
|
||||
})
|
||||
it('should support `globals` render option', async function () {
|
||||
const src = '{{ foo }}'
|
||||
const html = await engine.parseAndRender(src, {}, { globals: { foo: 'FOO' } })
|
||||
return expect(html).to.equal('FOO')
|
||||
})
|
||||
it('should support `strictVariables` render option', function () {
|
||||
const src = '{{ foo }}'
|
||||
return expect(engine.parseAndRender(src, {}, { strictVariables: true })).rejectedWith(/undefined variable/)
|
||||
})
|
||||
})
|
||||
describe('#express()', function () {
|
||||
const liquid = new Liquid({ root: '/root' })
|
||||
|
||||
Reference in New Issue
Block a user