refactor: track maxDepth via depthLimit Limiter on Context

Replace increaseDepth/decreaseDepth with a shared Limiter that supports
paired use/release, matching templateLimit and outputLengthLimit patterns.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Yang Jun
2026-07-14 21:48:17 +08:00
co-authored by Cursor
parent 6e96fa17e6
commit 20198c7f19
5 changed files with 16 additions and 18 deletions
+5 -12
View File
@@ -2,7 +2,7 @@ import { Drop } from '../drop/drop'
import { __assign } from 'tslib'
import { NormalizedFullOptions, defaultOptions, RenderOptions } from '../liquid-options'
import { createScope, Scope } from './scope'
import { hasOwnProperty, isArray, isNil, isUndefined, isString, isFunction, isNumber, toLiquid, InternalUndefinedVariableError, toValueSync, isObject, Limiter, toValue, readArrayElement, assert } from '../util'
import { hasOwnProperty, isArray, isNil, isUndefined, isString, isFunction, isNumber, toLiquid, InternalUndefinedVariableError, toValueSync, isObject, Limiter, toValue, readArrayElement } from '../util'
type PropertyKey = string | number;
@@ -37,8 +37,8 @@ export class Context {
public ownPropertyOnly: boolean;
public templateLimit: Limiter;
public outputLengthLimit: Limiter;
public depth: number;
public constructor (env: object = {}, opts: NormalizedFullOptions = defaultOptions, renderOptions: RenderOptions = {}, { templateLimit, outputLengthLimit, depth }: { templateLimit?: Limiter, outputLengthLimit?: Limiter, depth?: number } = {}) {
public depthLimit: Limiter;
public constructor (env: object = {}, opts: NormalizedFullOptions = defaultOptions, renderOptions: RenderOptions = {}, { templateLimit, outputLengthLimit, depthLimit }: { templateLimit?: Limiter, outputLengthLimit?: Limiter, depthLimit?: Limiter } = {}) {
this.sync = !!renderOptions.sync
this.opts = opts
this.globals = renderOptions.globals ?? opts.globals
@@ -47,14 +47,7 @@ export class Context {
this.ownPropertyOnly = renderOptions.ownPropertyOnly ?? opts.ownPropertyOnly
this.templateLimit = templateLimit ?? new Limiter('template', renderOptions.templateLimit ?? opts.templateLimit)
this.outputLengthLimit = outputLengthLimit ?? new Limiter('output length', renderOptions.outputLengthLimit ?? opts.outputLengthLimit)
this.depth = depth ?? 0
}
public increaseDepth () {
assert(this.depth < this.opts.maxDepth, 'template depth limit exceeded')
this.depth++
}
public decreaseDepth () {
this.depth--
this.depthLimit = depthLimit ?? new Limiter('template depth', opts.maxDepth)
}
public getRegister<T> (key: string, defaultValue: T = undefined as T): T {
return (this.registers[key] = this.registers[key] || defaultValue)
@@ -119,7 +112,7 @@ export class Context {
}, {
templateLimit: this.templateLimit,
outputLengthLimit: this.outputLengthLimit,
depth: this.depth
depthLimit: this.depthLimit
})
}
private findScope (key: string | number) {
+2 -2
View File
@@ -28,7 +28,7 @@ export default class extends Tag {
this.hash = new Hash(tokenizer, liquid.options.jekyllInclude || liquid.options.keyValueSeparator)
}
* render (ctx: Context, emitter: Emitter): Generator<unknown, void, unknown> {
ctx.increaseDepth()
ctx.depthLimit.use(1)
try {
const { liquid, hash, withVar } = this
const { renderer } = liquid
@@ -46,7 +46,7 @@ export default class extends Tag {
ctx.pop()
ctx.restoreRegister(saved)
} finally {
ctx.decreaseDepth()
ctx.depthLimit.release(1)
}
}
+2 -2
View File
@@ -26,7 +26,7 @@ export default class extends Tag {
yield renderer.renderTemplates(this.templates, ctx, emitter)
return
}
ctx.increaseDepth()
ctx.depthLimit.use(1)
try {
const filepath = (yield renderFilePath(this.file, ctx, liquid)) as string
assert(filepath, () => `illegal file path "${filepath}"`)
@@ -46,7 +46,7 @@ export default class extends Tag {
yield renderer.renderTemplates(templates, ctx, emitter)
ctx.pop()
} finally {
ctx.decreaseDepth()
ctx.depthLimit.release(1)
}
}
+2 -2
View File
@@ -55,7 +55,7 @@ export default class extends Tag {
this.hash = new Hash(tokenizer, liquid.options.keyValueSeparator)
}
* render (ctx: Context, emitter: Emitter): Generator<unknown, void, unknown> {
ctx.increaseDepth()
ctx.depthLimit.use(1)
try {
const { liquid, hash } = this
const filepath = (yield renderFilePath(this.file, ctx, liquid)) as string
@@ -84,7 +84,7 @@ export default class extends Tag {
yield liquid.renderer.renderTemplates(templates, childCtx, emitter)
}
} finally {
ctx.decreaseDepth()
ctx.depthLimit.release(1)
}
}
+5
View File
@@ -14,6 +14,11 @@ export class Limiter {
this.base += +count
}
}
release (count: number) {
if (+count > 0) {
this.base -= +count
}
}
check (count: number) {
if (+count > 0) {
assert(+count <= this.limit, this.message)