From 68387c31ead77862a634f857a64932ef07b85188 Mon Sep 17 00:00:00 2001 From: Yang Jun Date: Fri, 23 Aug 2024 21:13:10 +0800 Subject: [PATCH] fix: "filter is not a function" for uniq --- src/context/context.ts | 6 +++--- src/filters/array.ts | 11 +++-------- src/util/limiter.ts | 3 +++ src/util/underscore.ts | 5 +++++ test/e2e/issues.spec.ts | 11 ++++++++--- 5 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/context/context.ts b/src/context/context.ts index 94707089f4..372345a5d5 100644 --- a/src/context/context.ts +++ b/src/context/context.ts @@ -3,7 +3,7 @@ import { Drop } from '../drop/drop' import { __assign } from 'tslib' import { NormalizedFullOptions, defaultOptions, RenderOptions } from '../liquid-options' import { Scope } from './scope' -import { isArray, isNil, isUndefined, isString, isFunction, toLiquid, InternalUndefinedVariableError, toValueSync, isObject, Limiter } from '../util' +import { hasOwnProperty, isArray, isNil, isUndefined, isString, isFunction, toLiquid, InternalUndefinedVariableError, toValueSync, isObject, Limiter } from '../util' type PropertyKey = string | number; @@ -133,7 +133,7 @@ export function readProperty (obj: Scope, key: PropertyKey, ownPropertyOnly: boo return value } export function readJSProperty (obj: Scope, key: PropertyKey, ownPropertyOnly: boolean) { - if (ownPropertyOnly && !Object.hasOwnProperty.call(obj, key) && !(obj instanceof Drop)) return undefined + if (ownPropertyOnly && !hasOwnProperty.call(obj, key) && !(obj instanceof Drop)) return undefined return obj[key] } @@ -148,7 +148,7 @@ function readLast (obj: Scope) { } function readSize (obj: Scope) { - if (obj.hasOwnProperty('size') || obj['size'] !== undefined) return obj['size'] + if (hasOwnProperty.call(obj, 'size') || obj['size'] !== undefined) return obj['size'] if (isArray(obj) || isString(obj)) return obj.length if (typeof obj === 'object') return Object.keys(obj).length } diff --git a/src/filters/array.ts b/src/filters/array.ts index 211c8e026b..b57693e074 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, hasOwnProperty } from '../util' +import { toArray, argumentsToValue, toValue, stringify, caseInsensitiveCompare, isArray, isNil, last as arrayLast } from '../util' import { equals, evalToken, isTruthy } from '../render' import { Value, FilterImpl } from '../template' import { Tokenizer } from '../parser' @@ -187,14 +187,9 @@ export function * find_exp (this: FilterImpl, arr: T[], itemNa } export function uniq (this: FilterImpl, arr: T[]): T[] { - arr = toValue(arr) + arr = toArray(arr) this.context.memoryLimit.use(arr.length) - const u = {} - return (arr || []).filter(val => { - if (hasOwnProperty.call(u, String(val))) return false - u[String(val)] = true - return true - }) + return [...new Set(arr)] } export function sample (this: FilterImpl, v: T[] | string, count = 1): T | string | (T | string)[] { diff --git a/src/util/limiter.ts b/src/util/limiter.ts index 593e3b6931..01f10ae249 100644 --- a/src/util/limiter.ts +++ b/src/util/limiter.ts @@ -1,4 +1,5 @@ import { assert } from './assert' +import { toNumber } from './underscore' export class Limiter { private message: string @@ -9,10 +10,12 @@ export class Limiter { this.limit = limit } use (count: number) { + count = toNumber(count) assert(this.base + count <= this.limit, this.message) this.base += count } check (count: number) { + count = toNumber(count) assert(count <= this.limit, this.message) } } diff --git a/src/util/underscore.ts b/src/util/underscore.ts index 297bcb2c90..eb5c75ce83 100644 --- a/src/util/underscore.ts +++ b/src/util/underscore.ts @@ -66,6 +66,11 @@ export function toValue (value: any): any { return (value instanceof Drop && isFunction(value.valueOf)) ? value.valueOf() : value } +export function toNumber (value: any): number { + value = Number(value) + return isNaN(value) ? 0 : value +} + export function isNumber (value: any): value is number { return typeof value === 'number' } diff --git a/test/e2e/issues.spec.ts b/test/e2e/issues.spec.ts index d55f6da160..d95f7ca8fa 100644 --- a/test/e2e/issues.spec.ts +++ b/test/e2e/issues.spec.ts @@ -496,9 +496,14 @@ describe('Issues', function () { expect(() => liquid.parse({} as any)).not.toThrow() }) it('Unexpected "RenderError: memory alloc limit exceeded" #737', () => { - const liquid = new Liquid(); - const context = { x: ["a", "b"] }; - const template = "{{ x | join: 5 }}" + const liquid = new Liquid() + const context = { x: ['a', 'b'] } + const template = '{{ x | join: 5 }}' expect(liquid.parseAndRender(template, context)).resolves.toEqual('a5b') }) + it('{{ 123 | uniq }} throws #737', () => { + const liquid = new Liquid() + expect(liquid.parseAndRender('{{ 113 | uniq }}')).resolves.toEqual('113') + expect(liquid.parseAndRender("{{ '113' | uniq }}")).resolves.toEqual('113') + }) })