mirror of
https://github.com/harttle/liquidjs.git
synced 2026-09-17 13:20:41 -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)
|
const filepath = yield this.renderFilePath(this['file'], ctx, liquid)
|
||||||
assert(filepath, () => `illegal filename "${filepath}"`)
|
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()
|
const scope = childCtx.bottom()
|
||||||
__assign(scope, yield hash.render(ctx))
|
__assign(scope, yield hash.render(ctx))
|
||||||
if (this['with']) {
|
if (this['with']) {
|
||||||
|
|||||||
+13
-5
@@ -1,6 +1,6 @@
|
|||||||
import { Drop } from '../drop/drop'
|
import { Drop } from '../drop/drop'
|
||||||
import { __assign } from 'tslib'
|
import { __assign } from 'tslib'
|
||||||
import { NormalizedFullOptions, defaultOptions } from '../liquid-options'
|
import { NormalizedFullOptions, defaultOptions, RenderOptions } from '../liquid-options'
|
||||||
import { Scope } from './scope'
|
import { Scope } from './scope'
|
||||||
import { isArray, isNil, isString, isFunction, toLiquid } from '../util/underscore'
|
import { isArray, isNil, isString, isFunction, toLiquid } from '../util/underscore'
|
||||||
import { InternalUndefinedVariableError } from '../util/error'
|
import { InternalUndefinedVariableError } from '../util/error'
|
||||||
@@ -23,12 +23,20 @@ export class Context {
|
|||||||
*/
|
*/
|
||||||
public globals: Scope
|
public globals: Scope
|
||||||
public sync: boolean
|
public sync: boolean
|
||||||
|
/**
|
||||||
|
* The normalized liquid options object
|
||||||
|
*/
|
||||||
public opts: NormalizedFullOptions
|
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.opts = opts
|
||||||
this.globals = opts.globals
|
this.globals = renderOptions.globals ?? opts.globals
|
||||||
this.environments = env
|
this.environments = env
|
||||||
|
this.strictVariables = renderOptions.strictVariables ?? this.opts.strictVariables
|
||||||
}
|
}
|
||||||
public getRegister (key: string) {
|
public getRegister (key: string) {
|
||||||
return (this.registers[key] = this.registers[key] || {})
|
return (this.registers[key] = this.registers[key] || {})
|
||||||
@@ -54,7 +62,7 @@ export class Context {
|
|||||||
if (typeof paths === 'string') paths = paths.split('.')
|
if (typeof paths === 'string') paths = paths.split('.')
|
||||||
return paths.reduce((scope, path) => {
|
return paths.reduce((scope, path) => {
|
||||||
scope = readProperty(scope, path)
|
scope = readProperty(scope, path)
|
||||||
if (isNil(scope) && this.opts.strictVariables) {
|
if (isNil(scope) && this.strictVariables) {
|
||||||
throw new InternalUndefinedVariableError(path)
|
throw new InternalUndefinedVariableError(path)
|
||||||
}
|
}
|
||||||
return scope
|
return scope
|
||||||
|
|||||||
@@ -65,6 +65,21 @@ export interface LiquidOptions {
|
|||||||
orderedFilterParameters?: boolean;
|
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 {
|
interface NormalizedOptions extends LiquidOptions {
|
||||||
root?: string[];
|
root?: string[];
|
||||||
partials?: string[];
|
partials?: string[];
|
||||||
|
|||||||
+21
-21
@@ -10,7 +10,7 @@ import builtinTags from './builtin/tags'
|
|||||||
import * as builtinFilters from './builtin/filters'
|
import * as builtinFilters from './builtin/filters'
|
||||||
import { TagMap } from './template/tag/tag-map'
|
import { TagMap } from './template/tag/tag-map'
|
||||||
import { FilterMap } from './template/filter/filter-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 { FilterImplOptions } from './template/filter/filter-impl-options'
|
||||||
import { toPromise, toValue } from './util/async'
|
import { toPromise, toValue } from './util/async'
|
||||||
|
|
||||||
@@ -39,30 +39,30 @@ export class Liquid {
|
|||||||
return this.parser.parse(html, filepath)
|
return this.parser.parse(html, filepath)
|
||||||
}
|
}
|
||||||
|
|
||||||
public _render (tpl: Template[], scope?: object, sync?: boolean): IterableIterator<any> {
|
public _render (tpl: Template[], scope: object | undefined, renderOptions: RenderOptions): IterableIterator<any> {
|
||||||
const ctx = new Context(scope, this.options, sync)
|
const ctx = new Context(scope, this.options, renderOptions)
|
||||||
return this.renderer.renderTemplates(tpl, ctx)
|
return this.renderer.renderTemplates(tpl, ctx)
|
||||||
}
|
}
|
||||||
public async render (tpl: Template[], scope?: object): Promise<any> {
|
public async render (tpl: Template[], scope?: object, renderOptions?: RenderOptions): Promise<any> {
|
||||||
return toPromise(this._render(tpl, scope, false))
|
return toPromise(this._render(tpl, scope, { ...renderOptions, sync: false }))
|
||||||
}
|
}
|
||||||
public renderSync (tpl: Template[], scope?: object): any {
|
public renderSync (tpl: Template[], scope?: object, renderOptions?: RenderOptions): any {
|
||||||
return toValue(this._render(tpl, scope, true))
|
return toValue(this._render(tpl, scope, { ...renderOptions, sync: true }))
|
||||||
}
|
}
|
||||||
public renderToNodeStream (tpl: Template[], scope?: object): NodeJS.ReadableStream {
|
public renderToNodeStream (tpl: Template[], scope?: object, renderOptions: RenderOptions = {}): NodeJS.ReadableStream {
|
||||||
const ctx = new Context(scope, this.options)
|
const ctx = new Context(scope, this.options, renderOptions)
|
||||||
return this.renderer.renderTemplatesToNodeStream(tpl, ctx)
|
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)
|
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> {
|
public async parseAndRender (html: string, scope?: object, renderOptions?: RenderOptions): Promise<any> {
|
||||||
return toPromise(this._parseAndRender(html, scope, false))
|
return toPromise(this._parseAndRender(html, scope, { ...renderOptions, sync: false }))
|
||||||
}
|
}
|
||||||
public parseAndRenderSync (html: string, scope?: object): any {
|
public parseAndRenderSync (html: string, scope?: object, renderOptions?: RenderOptions): any {
|
||||||
return toValue(this._parseAndRender(html, scope, true))
|
return toValue(this._parseAndRender(html, scope, { ...renderOptions, sync: true }))
|
||||||
}
|
}
|
||||||
|
|
||||||
public _parsePartialFile (file: string, sync?: boolean, currentFile?: string) {
|
public _parsePartialFile (file: string, sync?: boolean, currentFile?: string) {
|
||||||
@@ -77,17 +77,17 @@ export class Liquid {
|
|||||||
public parseFileSync (file: string): Template[] {
|
public parseFileSync (file: string): Template[] {
|
||||||
return toValue<Template[]>(this.parser.parseFile(file, true))
|
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)
|
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)
|
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)
|
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> {
|
public _evalValue (str: string, ctx: Context): IterableIterator<any> {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/* istanbul ignore file */
|
||||||
import * as TypeGuards from './util/type-guards'
|
import * as TypeGuards from './util/type-guards'
|
||||||
export { TypeGuards }
|
export { TypeGuards }
|
||||||
export { ParseError, TokenizationError, AssertionError } from './util/error'
|
export { ParseError, TokenizationError, AssertionError } from './util/error'
|
||||||
|
|||||||
@@ -43,6 +43,15 @@ describe('Liquid', function () {
|
|||||||
const html = await engine.parseAndRender(src, {})
|
const html = await engine.parseAndRender(src, {})
|
||||||
return expect(html).to.equal('12')
|
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 () {
|
describe('#express()', function () {
|
||||||
const liquid = new Liquid({ root: '/root' })
|
const liquid = new Liquid({ root: '/root' })
|
||||||
|
|||||||
Reference in New Issue
Block a user