Skip to content

Commit

Permalink
fix: inconsistent continue behaviour, fixes #779
Browse files Browse the repository at this point in the history
  • Loading branch information
harttle committed Dec 22, 2024
1 parent 2af297f commit e3ef574
Show file tree
Hide file tree
Showing 7 changed files with 37 additions and 12 deletions.
2 changes: 2 additions & 0 deletions src/context/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ export class Context {
*/
public globals: Scope
public sync: boolean
public breakCalled = false
public continueCalled = false
/**
* The normalized liquid options object
*/
Expand Down
2 changes: 1 addition & 1 deletion src/render/render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export class Render {
const html = yield tpl.render(ctx, emitter)
// if not, it'll return an `html`, write to the emitter for it
html && emitter.write(html)
if (emitter['break'] || emitter['continue']) break
if (ctx.breakCalled || ctx.continueCalled) break
} catch (e) {
const err = LiquidError.is(e) ? e : new RenderError(e as Error, tpl)
if (ctx.opts.catchAllErrors) errors.push(err)
Expand Down
4 changes: 2 additions & 2 deletions src/tags/break.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Context, Emitter, Tag } from '..'

export default class extends Tag {
render (ctx: Context, emitter: Emitter) {
emitter['break'] = true
render (ctx: Context, _emitter: Emitter) {
ctx.breakCalled = true
}
}
4 changes: 2 additions & 2 deletions src/tags/continue.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Tag, Emitter, Context } from '..'

export default class extends Tag {
render (ctx: Context, emitter: Emitter) {
emitter['continue'] = true
render (ctx: Context, _emitter: Emitter) {
ctx.continueCalled = true
}
}
7 changes: 2 additions & 5 deletions src/tags/for.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,9 @@ export default class extends Tag {
ctx.push(scope)
for (const item of collection) {
scope[this.variable] = item
ctx.continueCalled = ctx.breakCalled = false
yield r.renderTemplates(this.templates, ctx, emitter)
if (emitter['break']) {
emitter['break'] = false
break
}
emitter['continue'] = false
if (ctx.breakCalled) break
scope.forloop.next()
}
ctx.pop()
Expand Down
6 changes: 4 additions & 2 deletions test/e2e/issues.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -525,8 +525,10 @@ describe('Issues', function () {
expect(result).toEqual('\n[2,12] foo')
})
it("memoryLimit doesn't work in for tag #776", () => {
const engine = new Liquid()
const engine = new Liquid({
memoryLimit: 1e5
})
const tpl = `{% for i in (1..1000000000) %} {{'a'}} {% endfor %}`
expect(() => engine.parseAndRenderSync(tpl)).toThrow(/memory out/)
expect(() => engine.parseAndRenderSync(tpl)).toThrow('memory alloc limit exceeded, line:1, col:1')
})
})
24 changes: 24 additions & 0 deletions test/integration/tags/for.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Liquid } from '../../../src/liquid'
import { Drop } from '../../../src/drop/drop'
import { Scope } from '../../../src/context/scope'
import { mock, restore } from '../../stub/mockfs'

describe('tags/for', function () {
let liquid: Liquid, scope: Scope
Expand Down Expand Up @@ -139,6 +140,7 @@ describe('tags/for', function () {
})

describe('continue', function () {
afterEach(restore)
it('should support for with continue', async function () {
const src = '{% for i in (1..5) %}' +
'{% if i == 4 %}continue{% continue %}{% endif %}{{i}}' +
Expand All @@ -154,6 +156,28 @@ describe('tags/for', function () {
const html = await liquid.parseAndRender(src, scope)
return expect(html).toBe('123continue5')
})
it('should skip snippet for rendered continue', async function () {
mock({
'snippet.liquid': ' before{% continue %}skipped'
})
const src = '{% for i in (1..2) %}' +
'{% render "snippet.liquid" %}' +
' after' +
'{% endfor %}'
const html = await liquid.parseAndRender(src, scope)
return expect(html).toBe(' before after before after')
})
it('should skip `for` body for included continue', async function () {
mock({
'snippet.liquid': ' before{% continue %}skipped'
})
const src = '{% for i in (1..2) %}' +
'{% include "snippet.liquid" %}' +
' after' +
'{% endfor %}'
const html = await liquid.parseAndRender(src, scope)
return expect(html).toBe(' before before')
})
})
describe('break', function () {
it('should support break', async function () {
Expand Down

0 comments on commit e3ef574

Please sign in to comment.