Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: enumerate plain objects in where/where_exp, #785 #788

Merged
merged 1 commit into from
Jan 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/** @type {import('ts-jest').JestConfigWithTsJest} */
module.exports = {
preset: 'ts-jest',
maxWorkers: '50%',
testEnvironment: 'node',
testMatch: ['**/*.spec.ts'],
collectCoverageFrom: [
Expand Down
6 changes: 3 additions & 3 deletions src/filters/array.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { toArray, argumentsToValue, toValue, stringify, caseInsensitiveCompare, isArray, isNil, last as arrayLast, isArrayLike } from '../util'
import { toArray, argumentsToValue, toValue, stringify, caseInsensitiveCompare, isArray, isNil, last as arrayLast, isArrayLike, toEnumerable } from '../util'
import { arrayIncludes, equals, evalToken, isTruthy } from '../render'
import { Value, FilterImpl } from '../template'
import { Tokenizer } from '../parser'
Expand Down Expand Up @@ -148,7 +148,7 @@ export function * where_exp<T extends object> (this: FilterImpl, arr: T[], itemN

export function * group_by<T extends object> (this: FilterImpl, arr: T[], property: string): IterableIterator<unknown> {
const map = new Map()
arr = toArray(arr)
arr = toEnumerable(arr)
const token = new Tokenizer(stringify(property)).readScopeValue()
this.context.memoryLimit.use(arr.length)
for (const item of arr) {
Expand All @@ -162,7 +162,7 @@ export function * group_by<T extends object> (this: FilterImpl, arr: T[], proper
export function * group_by_exp<T extends object> (this: FilterImpl, arr: T[], itemName: string, exp: string): IterableIterator<unknown> {
const map = new Map()
const keyTemplate = new Value(stringify(exp), this.liquid)
arr = toArray(arr)
arr = toEnumerable(arr)
this.context.memoryLimit.use(arr.length)
for (const item of arr) {
const key = yield keyTemplate.value(this.context.spawn({ [itemName]: item }))
Expand Down
27 changes: 27 additions & 0 deletions test/e2e/issues.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -531,4 +531,31 @@ describe('Issues', function () {
const tpl = `{% for i in (1..1000000000) %} {{'a'}} {% endfor %}`
expect(() => engine.parseAndRenderSync(tpl)).toThrow('memory alloc limit exceeded, line:1, col:1')
})
it('group_by_exp fails with object as input #785', () => {
const site = {
tags: {
CPP: [ 'page0' ],
PHP: [ 'page0', 'page2' ],
JavaScript: [ 'page1', 'page2', 'page3' ],
CSharp: [ 'page2', 'page4' ]
}
}
const tpl = `
{%- assign tags_by_size = site.tags | group_by_exp: 'tag', 'tag[1].size' | sort: 'name' | reverse -%}
{%- for tags_with_size in tags_by_size -%}
{%- for tag in tags_with_size.items -%}
{%- assign tag_name = tag[0] %}
{{ tag_name }} <sup>{{ tags_with_size.name }}</sup> Posts:
{%- for post in tag[1] -%}{{post}},{%- endfor -%}
{%- endfor -%}
{%- endfor -%}
`
const engine = new Liquid()
const html = engine.parseAndRenderSync(tpl, { site })
expect(html).toEqual(`
JavaScript <sup>3</sup> Posts:page1,page2,page3,
PHP <sup>2</sup> Posts:page0,page2,
CSharp <sup>2</sup> Posts:page2,page4,
CPP <sup>1</sup> Posts:page0,`)
})
})
29 changes: 29 additions & 0 deletions test/integration/filters/array.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,12 @@ describe('filters/array', function () {
{ graduation_year: 2014, name: 'John' },
{ graduation_year: 2009, name: 'Jack' }
]
const postsByTags = {
CPP: [ 'page0' ],
PHP: [ 'page0', 'page2' ],
JavaScript: [ 'page1', 'page2', 'page3' ],
CSharp: [ 'page2', 'page4' ]
}
it('should support group by expression', function () {
const expected = [{
name: '201',
Expand All @@ -572,6 +578,29 @@ describe('filters/array', function () {
{ members },
JSON.stringify(expected))
})
it('should group key/values in plain object', function () {
const expected = [{
name: 3,
items: [
['JavaScript', ['page1', 'page2', 'page3']]
]
}, {
name: 2,
items: [
['PHP', ['page0', 'page2']],
['CSharp', ['page2', 'page4']]
]
}, {
name: 1,
items: [
['CPP', ['page0']]
]
}]
return test(
`{{ postsByTags | group_by_exp: "tag", "tag[1].size" | sort: 'name' | reverse | json}}`,
{ postsByTags },
JSON.stringify(expected))
})
})
describe('find', function () {
const members = [
Expand Down
Loading