diff --git a/jest.config.js b/jest.config.js index 1be9919eeb..2f1c62ae29 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,6 +1,7 @@ /** @type {import('ts-jest').JestConfigWithTsJest} */ module.exports = { preset: 'ts-jest', + maxWorkers: '50%', testEnvironment: 'node', testMatch: ['**/*.spec.ts'], collectCoverageFrom: [ diff --git a/src/filters/array.ts b/src/filters/array.ts index 8547fb80ec..36b50ecedc 100644 --- a/src/filters/array.ts +++ b/src/filters/array.ts @@ -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' @@ -148,7 +148,7 @@ export function * where_exp (this: FilterImpl, arr: T[], itemN export function * group_by (this: FilterImpl, arr: T[], property: string): IterableIterator { 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) { @@ -162,7 +162,7 @@ export function * group_by (this: FilterImpl, arr: T[], proper export function * group_by_exp (this: FilterImpl, arr: T[], itemName: string, exp: string): IterableIterator { 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 })) diff --git a/test/e2e/issues.spec.ts b/test/e2e/issues.spec.ts index d437379fd8..619241f109 100644 --- a/test/e2e/issues.spec.ts +++ b/test/e2e/issues.spec.ts @@ -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 }} {{ tags_with_size.name }} Posts: + {%- for post in tag[1] -%}{{post}},{%- endfor -%} + {%- endfor -%} + {%- endfor -%} + ` + const engine = new Liquid() + const html = engine.parseAndRenderSync(tpl, { site }) + expect(html).toEqual(` + JavaScript 3 Posts:page1,page2,page3, + PHP 2 Posts:page0,page2, + CSharp 2 Posts:page2,page4, + CPP 1 Posts:page0,`) + }) }) diff --git a/test/integration/filters/array.spec.ts b/test/integration/filters/array.spec.ts index f633dc9316..ede56efe11 100644 --- a/test/integration/filters/array.spec.ts +++ b/test/integration/filters/array.spec.ts @@ -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', @@ -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 = [