mirror of
https://github.com/harttle/liquidjs.git
synced 2026-09-15 04:10:40 -07:00
refactor: wrap plain scopes in Context.push()
Centralize null-prototype scope creation in push() so callers pass plain objects; Drop instances and existing null-proto frames are pushed as-is. Remove sanitizeScope in favor of createScope via Object.assign.
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { Context } from './context'
|
||||
import { Drop } from '../drop/drop'
|
||||
import { Scope } from './scope'
|
||||
|
||||
describe('Context', function () {
|
||||
@@ -250,6 +251,21 @@ describe('Context', function () {
|
||||
expect(ctx.getSync(['bar', 'foo'])).toEqual('foo')
|
||||
expect(ctx.getSync(['bar', 'bar'])).toEqual(undefined)
|
||||
})
|
||||
it('should wrap plain objects with null prototype', function () {
|
||||
const scope = ctx.push({ foo: 'FOO' })
|
||||
expect(Object.getPrototypeOf(scope)).toBeNull()
|
||||
})
|
||||
it('should return pushed scope for in-place mutation', function () {
|
||||
const scope = ctx.push({})
|
||||
scope.item = 'ITEM'
|
||||
expect(ctx.getSync(['item'])).toEqual('ITEM')
|
||||
})
|
||||
it('should push Drop instances as-is', function () {
|
||||
class TestDrop extends Drop {}
|
||||
const drop = new TestDrop()
|
||||
const pushed = ctx.push(drop)
|
||||
expect(pushed).toBe(drop)
|
||||
})
|
||||
})
|
||||
describe('.pop()', function () {
|
||||
it('should pop scope', async function () {
|
||||
|
||||
@@ -94,8 +94,14 @@ export class Context {
|
||||
}
|
||||
return scope
|
||||
}
|
||||
public push (ctx: object) {
|
||||
return this.scopes.push(ctx)
|
||||
public push (ctx: Scope): Scope {
|
||||
const scope = ctx instanceof Drop
|
||||
? ctx
|
||||
: Object.getPrototypeOf(ctx) === null
|
||||
? ctx
|
||||
: createScope(ctx)
|
||||
this.scopes.push(scope)
|
||||
return scope
|
||||
}
|
||||
public pop () {
|
||||
return this.scopes.pop()
|
||||
|
||||
+1
-11
@@ -24,15 +24,5 @@ export function shouldBlockScopeKeyWrite (key: PropertyKey, ownPropertyOnly: boo
|
||||
}
|
||||
|
||||
export function createScope (from?: ScopeObject): ScopeObject {
|
||||
return from ? sanitizeScope(from) : Object.create(null)
|
||||
}
|
||||
|
||||
export function sanitizeScope (obj: ScopeObject): ScopeObject {
|
||||
const scope = Object.create(null)
|
||||
for (const key of Object.keys(obj)) {
|
||||
if (hasOwnProperty.call(obj, key)) {
|
||||
scope[key] = obj[key]
|
||||
}
|
||||
}
|
||||
return scope
|
||||
return Object.assign(Object.create(null), from)
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ import { arrayIncludes, equals, evalToken, isTruthy } from '../render'
|
||||
import { Value, FilterImpl } from '../template'
|
||||
import { Tokenizer } from '../parser'
|
||||
import type { Scope } from '../context'
|
||||
import { createScope } from '../context/scope'
|
||||
import { EmptyDrop } from '../drop'
|
||||
|
||||
export const join = argumentsToValue(function (this: FilterImpl, v: any[], arg: string) {
|
||||
@@ -135,7 +134,7 @@ function * filter_exp<T extends object> (this: FilterImpl, include: boolean, arr
|
||||
const keyTemplate = new Value(stringify(exp), this.liquid)
|
||||
const array = toArray(arr)
|
||||
for (const item of array) {
|
||||
this.context.push(createScope({ [itemName]: item }))
|
||||
this.context.push({ [itemName]: item })
|
||||
const value = yield keyTemplate.value(this.context)
|
||||
this.context.pop()
|
||||
if (value === include) filtered.push(item)
|
||||
@@ -176,7 +175,7 @@ export function * group_by_exp<T extends object> (this: FilterImpl, arr: T[], it
|
||||
const keyTemplate = new Value(stringify(exp), this.liquid)
|
||||
arr = toEnumerable(arr)
|
||||
for (const item of arr) {
|
||||
this.context.push(createScope({ [itemName]: item }))
|
||||
this.context.push({ [itemName]: item })
|
||||
const key = yield keyTemplate.value(this.context)
|
||||
this.context.pop()
|
||||
if (!map.has(key)) map.set(key, [])
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
import { BlockMode, createScope } from '../context'
|
||||
import { BlockMode } from '../context'
|
||||
import { isTagToken } from '../util'
|
||||
import { BlockDrop } from '../drop'
|
||||
import { Liquid, TagToken, TopLevelToken, Template, Context, Emitter, Tag } from '..'
|
||||
@@ -38,7 +38,7 @@ export default class extends Tag {
|
||||
if (stack.includes(self)) throw new Error('block tag cannot be nested')
|
||||
|
||||
stack.push(self)
|
||||
ctx.push(createScope({ block: superBlock }))
|
||||
ctx.push({ block: superBlock })
|
||||
yield liquid.renderer.renderTemplates(templates, ctx, emitter)
|
||||
ctx.pop()
|
||||
stack.pop()
|
||||
|
||||
+2
-4
@@ -1,6 +1,5 @@
|
||||
import { Hash, ValueToken, Liquid, Tag, evalToken, Emitter, TagToken, TopLevelToken, Context, Template, ParseStream } from '..'
|
||||
import { assertEmpty, isValueToken, toEnumerable } from '../util'
|
||||
import { createScope } from '../context/scope'
|
||||
import { ForloopDrop } from '../drop/forloop-drop'
|
||||
import { Parser } from '../parser'
|
||||
import { Arguments } from '../template'
|
||||
@@ -44,7 +43,7 @@ export default class extends Tag {
|
||||
* render (ctx: Context, emitter: Emitter): Generator<unknown, void | string, Template[]> {
|
||||
const r = this.liquid.renderer
|
||||
const continueKey = 'continue-' + this.variable + '-' + this.collection.getText()
|
||||
ctx.push(createScope({ continue: ctx.getRegister(continueKey, {}) }))
|
||||
ctx.push({ continue: ctx.getRegister(continueKey, {}) })
|
||||
const hash = (yield this.hash.render(ctx)) as Record<string, any>
|
||||
ctx.pop()
|
||||
|
||||
@@ -68,8 +67,7 @@ export default class extends Tag {
|
||||
|
||||
if (!this.templates.length) return
|
||||
|
||||
const scope = createScope({ forloop: new ForloopDrop(collection.length, this.collection.getText(), this.variable) })
|
||||
ctx.push(scope)
|
||||
const scope = ctx.push({ forloop: new ForloopDrop(collection.length, this.collection.getText(), this.variable) })
|
||||
for (const item of collection) {
|
||||
scope[this.variable] = item
|
||||
ctx.continueCalled = ctx.breakCalled = false
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
import { Template, ValueToken, TopLevelToken, Liquid, Tag, assert, evalToken, Hash, Emitter, TagToken, Context } from '..'
|
||||
import { BlockMode, createScope, Scope } from '../context'
|
||||
import { BlockMode, Scope } from '../context'
|
||||
import { Parser } from '../parser'
|
||||
import { Argument, Arguments, PartialScope } from '../template'
|
||||
import { isString, isValueToken } from '../util'
|
||||
@@ -37,10 +37,10 @@ export default class extends Tag {
|
||||
const saved = ctx.saveRegister('blocks', 'blockMode')
|
||||
ctx.setRegister('blocks', {})
|
||||
ctx.setRegister('blockMode', BlockMode.OUTPUT)
|
||||
const scope = createScope((yield hash.render(ctx)) as Scope)
|
||||
const scope = (yield hash.render(ctx)) as Scope
|
||||
if (withVar) scope[filepath] = yield evalToken(withVar, ctx)
|
||||
const templates = (yield liquid._parsePartialFile(filepath, ctx.sync, this.currentFile)) as Template[]
|
||||
ctx.push(ctx.opts.jekyllInclude ? createScope({ include: scope }) : scope)
|
||||
ctx.push(ctx.opts.jekyllInclude ? { include: scope } : scope)
|
||||
yield renderer.renderTemplates(templates, ctx, emitter)
|
||||
ctx.pop()
|
||||
ctx.restoreRegister(saved)
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
import { Scope, Template, Liquid, Tag, assert, Emitter, Hash, TagToken, TopLevelToken, Context } from '..'
|
||||
import { BlockMode, createScope } from '../context'
|
||||
import { BlockMode } from '../context'
|
||||
import { parseFilePath, renderFilePath, ParsedFileName } from './render'
|
||||
import { BlankDrop } from '../drop'
|
||||
import { Parser } from '../parser'
|
||||
@@ -41,7 +41,7 @@ export default class extends Tag {
|
||||
ctx.setRegister('blockMode', BlockMode.OUTPUT)
|
||||
|
||||
// render the layout file use stored blocks
|
||||
ctx.push(createScope((yield args.render(ctx)) as Scope))
|
||||
ctx.push((yield args.render(ctx)) as Scope)
|
||||
yield renderer.renderTemplates(templates, ctx, emitter)
|
||||
ctx.pop()
|
||||
ctx.depthLimit.release(1)
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { isValueToken, toEnumerable } from '../util'
|
||||
import { createScope } from '../context/scope'
|
||||
import { ValueToken, Liquid, Tag, evalToken, Emitter, Hash, TagToken, TopLevelToken, Context, Template, ParseStream } from '..'
|
||||
import { TablerowloopDrop } from '../drop/tablerowloop-drop'
|
||||
import { Parser } from '../parser'
|
||||
@@ -53,8 +52,7 @@ export default class extends Tag {
|
||||
|
||||
const r = this.liquid.renderer
|
||||
const tablerowloop = new TablerowloopDrop(collection.length, cols, this.collection.getText(), this.variable)
|
||||
const scope = createScope({ tablerowloop })
|
||||
ctx.push(scope)
|
||||
const scope = ctx.push({ tablerowloop })
|
||||
|
||||
for (let idx = 0; idx < collection.length; idx++, tablerowloop.next()) {
|
||||
scope[this.variable] = collection[idx]
|
||||
|
||||
Reference in New Issue
Block a user