mirror of
https://github.com/harttle/liquidjs.git
synced 2026-09-15 04:10:40 -07:00
feat: Access array item by negative index, closes #486
This commit is contained in:
+11
-11
@@ -5,6 +5,8 @@ import { Scope } from './scope'
|
|||||||
import { isArray, isNil, isString, isFunction, toLiquid } from '../util/underscore'
|
import { isArray, isNil, isString, isFunction, toLiquid } from '../util/underscore'
|
||||||
import { InternalUndefinedVariableError } from '../util/error'
|
import { InternalUndefinedVariableError } from '../util/error'
|
||||||
|
|
||||||
|
type PropertyKey = string | number;
|
||||||
|
|
||||||
export class Context {
|
export class Context {
|
||||||
/**
|
/**
|
||||||
* insert a Context-level empty scope,
|
* insert a Context-level empty scope,
|
||||||
@@ -54,11 +56,11 @@ export class Context {
|
|||||||
return [this.globals, this.environments, ...this.scopes]
|
return [this.globals, this.environments, ...this.scopes]
|
||||||
.reduce((ctx, val) => __assign(ctx, val), {})
|
.reduce((ctx, val) => __assign(ctx, val), {})
|
||||||
}
|
}
|
||||||
public get (paths: string[]) {
|
public get (paths: PropertyKey[]) {
|
||||||
const scope = this.findScope(paths[0])
|
const scope = this.findScope(paths[0])
|
||||||
return this.getFromScope(scope, paths)
|
return this.getFromScope(scope, paths)
|
||||||
}
|
}
|
||||||
public getFromScope (scope: object, paths: string[] | string) {
|
public getFromScope (scope: object, paths: PropertyKey[] | string) {
|
||||||
if (isString(paths)) paths = paths.split('.')
|
if (isString(paths)) paths = paths.split('.')
|
||||||
return paths.reduce((scope, path, i) => {
|
return paths.reduce((scope, path, i) => {
|
||||||
scope = readProperty(scope, path, this.opts.ownPropertyOnly)
|
scope = readProperty(scope, path, this.opts.ownPropertyOnly)
|
||||||
@@ -77,7 +79,7 @@ export class Context {
|
|||||||
public bottom () {
|
public bottom () {
|
||||||
return this.scopes[0]
|
return this.scopes[0]
|
||||||
}
|
}
|
||||||
private findScope (key: string) {
|
private findScope (key: string | number) {
|
||||||
for (let i = this.scopes.length - 1; i >= 0; i--) {
|
for (let i = this.scopes.length - 1; i >= 0; i--) {
|
||||||
const candidate = this.scopes[i]
|
const candidate = this.scopes[i]
|
||||||
if (key in candidate) return candidate
|
if (key in candidate) return candidate
|
||||||
@@ -87,21 +89,19 @@ export class Context {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function readProperty (obj: Scope, key: string, ownPropertyOnly: boolean) {
|
export function readProperty (obj: Scope, key: PropertyKey, ownPropertyOnly: boolean) {
|
||||||
if (isNil(obj)) return obj
|
if (isNil(obj)) return obj
|
||||||
obj = toLiquid(obj)
|
obj = toLiquid(obj)
|
||||||
|
if (isArray(obj) && key < 0) return obj[obj.length + +key]
|
||||||
const jsProperty = readJSProperty(obj, key, ownPropertyOnly)
|
const jsProperty = readJSProperty(obj, key, ownPropertyOnly)
|
||||||
|
if (jsProperty === undefined && obj instanceof Drop) return obj.liquidMethodMissing(key)
|
||||||
if (isFunction(jsProperty)) return jsProperty.call(obj)
|
if (isFunction(jsProperty)) return jsProperty.call(obj)
|
||||||
if (obj instanceof Drop) {
|
|
||||||
if (obj.hasOwnProperty(key)) return obj[key]
|
|
||||||
return obj.liquidMethodMissing(key)
|
|
||||||
}
|
|
||||||
if (key === 'size') return readSize(obj)
|
if (key === 'size') return readSize(obj)
|
||||||
if (key === 'first') return readFirst(obj)
|
else if (key === 'first') return readFirst(obj)
|
||||||
if (key === 'last') return readLast(obj)
|
else if (key === 'last') return readLast(obj)
|
||||||
return jsProperty
|
return jsProperty
|
||||||
}
|
}
|
||||||
export function readJSProperty (obj: Scope, key: string, ownPropertyOnly: boolean) {
|
export function readJSProperty (obj: Scope, key: PropertyKey, ownPropertyOnly: boolean) {
|
||||||
if (ownPropertyOnly && !Object.hasOwnProperty.call(obj, key)) return undefined
|
if (ownPropertyOnly && !Object.hasOwnProperty.call(obj, key)) return undefined
|
||||||
return obj[key]
|
return obj[key]
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -3,7 +3,7 @@ export abstract class Drop {
|
|||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
public liquidMethodMissing (key: string): Promise<string | undefined> | string | undefined {
|
public liquidMethodMissing (key: string | number): Promise<string | undefined> | string | undefined {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -227,4 +227,9 @@ describe('Issues', function () {
|
|||||||
const html = await engine.parseAndRender(`{{ foo | concat | json }}`)
|
const html = await engine.parseAndRender(`{{ foo | concat | json }}`)
|
||||||
expect(html).to.equal('[]')
|
expect(html).to.equal('[]')
|
||||||
})
|
})
|
||||||
|
it('#486 Access array items from the right with negative indexes', async () => {
|
||||||
|
const engine = new Liquid()
|
||||||
|
const html = await engine.parseAndRender(`{% assign a = "x,y,z" | split: ',' -%}{{ a[-1] }} {{ a[-3] }} {{ a[-8] }}`)
|
||||||
|
expect(html).to.equal('z x ')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ describe('Context', function () {
|
|||||||
zoo: 'coo',
|
zoo: 'coo',
|
||||||
'Mr.Smith': 'John',
|
'Mr.Smith': 'John',
|
||||||
arr: ['a', 'b']
|
arr: ['a', 'b']
|
||||||
}
|
},
|
||||||
|
arr: ['a', 'b', 'c', 'd']
|
||||||
}
|
}
|
||||||
ctx = new Context(scope)
|
ctx = new Context(scope)
|
||||||
})
|
})
|
||||||
@@ -69,6 +70,12 @@ describe('Context', function () {
|
|||||||
it('should read .last of array', async function () {
|
it('should read .last of array', async function () {
|
||||||
expect(ctx.get(['bar', 'arr', 'last'])).to.equal('b')
|
expect(ctx.get(['bar', 'arr', 'last'])).to.equal('b')
|
||||||
})
|
})
|
||||||
|
it('should read element of array', async function () {
|
||||||
|
expect(ctx.get(['arr', 1])).to.equal('b')
|
||||||
|
})
|
||||||
|
it('should read element of array from end', async function () {
|
||||||
|
expect(ctx.get(['arr', -2])).to.equal('c')
|
||||||
|
})
|
||||||
it('should call function', async function () {
|
it('should call function', async function () {
|
||||||
expect(ctx.get(['func'])).to.equal('FUNC')
|
expect(ctx.get(['func'])).to.equal('FUNC')
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user