feat: implement forloop.name as found in ruby shopify/liquid

This commit is contained in:
Liam Bigelow
2021-10-04 21:33:46 +08:00
committed by Jun Yang
parent 3b9c7d6fbe
commit 6dc7fada72
6 changed files with 27 additions and 9 deletions
+1 -1
View File
@@ -58,7 +58,7 @@ export default {
return reversed(collection) return reversed(collection)
}, collection) }, collection)
const scope = { forloop: new ForloopDrop(collection.length) } const scope = { forloop: new ForloopDrop(collection.length, this.collection.getText(), this.variable) }
ctx.push(scope) ctx.push(scope)
for (const item of collection) { for (const item of collection) {
scope[this.variable] = item scope[this.variable] = item
+1 -1
View File
@@ -62,7 +62,7 @@ export default {
const { value, alias } = this['for'] const { value, alias } = this['for']
let collection = evalToken(value, ctx) let collection = evalToken(value, ctx)
collection = toEnumerable(collection) collection = toEnumerable(collection)
scope['forloop'] = new ForloopDrop(collection.length) scope['forloop'] = new ForloopDrop(collection.length, value.getText(), alias)
for (const item of collection) { for (const item of collection) {
scope[alias] = item scope[alias] = item
const templates = yield liquid._parsePartialFile(filepath, childCtx.sync) const templates = yield liquid._parsePartialFile(filepath, childCtx.sync)
+4 -3
View File
@@ -7,12 +7,13 @@ export default {
parse: function (tagToken: TagToken, remainTokens: TopLevelToken[]) { parse: function (tagToken: TagToken, remainTokens: TopLevelToken[]) {
const tokenizer = new Tokenizer(tagToken.args, this.liquid.options.operatorsTrie) const tokenizer = new Tokenizer(tagToken.args, this.liquid.options.operatorsTrie)
this.variable = tokenizer.readIdentifier() const variable = tokenizer.readIdentifier()
tokenizer.skipBlank() tokenizer.skipBlank()
const tmp = tokenizer.readIdentifier() const tmp = tokenizer.readIdentifier()
assert(tmp && tmp.content === 'in', () => `illegal tag: ${tagToken.getText()}`) assert(tmp && tmp.content === 'in', () => `illegal tag: ${tagToken.getText()}`)
this.variable = variable.content
this.collection = tokenizer.readValue() this.collection = tokenizer.readValue()
this.hash = new Hash(tokenizer.remaining()) this.hash = new Hash(tokenizer.remaining())
this.templates = [] this.templates = []
@@ -39,12 +40,12 @@ export default {
const cols = hash.cols || collection.length const cols = hash.cols || collection.length
const r = this.liquid.renderer const r = this.liquid.renderer
const tablerowloop = new TablerowloopDrop(collection.length, cols) const tablerowloop = new TablerowloopDrop(collection.length, cols, this.collection.getText(), this.variable)
const scope = { tablerowloop } const scope = { tablerowloop }
ctx.push(scope) ctx.push(scope)
for (let idx = 0; idx < collection.length; idx++, tablerowloop.next()) { for (let idx = 0; idx < collection.length; idx++, tablerowloop.next()) {
scope[this.variable.content] = collection[idx] scope[this.variable] = collection[idx]
if (tablerowloop.col0() === 0) { if (tablerowloop.col0() === 0) {
if (tablerowloop.row() !== 1) emitter.write('</tr>') if (tablerowloop.row() !== 1) emitter.write('</tr>')
emitter.write(`<tr class="row${tablerowloop.row()}">`) emitter.write(`<tr class="row${tablerowloop.row()}">`)
+3 -1
View File
@@ -2,10 +2,12 @@ import { Drop } from './drop'
export class ForloopDrop extends Drop { export class ForloopDrop extends Drop {
protected i = 0 protected i = 0
public name: string
public length: number public length: number
public constructor (length: number) { public constructor (length: number, collection: string, variable: string) {
super() super()
this.length = length this.length = length
this.name = `${variable}-${collection}`
} }
public next () { public next () {
this.i++ this.i++
+2 -2
View File
@@ -2,8 +2,8 @@ import { ForloopDrop } from './forloop-drop'
export class TablerowloopDrop extends ForloopDrop { export class TablerowloopDrop extends ForloopDrop {
private cols: number private cols: number
public constructor (length: number, cols: number) { public constructor (length: number, cols: number, collection: string, variable: string) {
super(length) super(length, collection, variable)
this.length = length this.length = length
this.cols = cols this.cols = cols
} }
+16 -1
View File
@@ -45,7 +45,22 @@ describe('tags/for', function () {
it('should output forloop', async function () { it('should output forloop', async function () {
const src = '{%for i in (1..1)%}{{forloop}}{%endfor%}' const src = '{%for i in (1..1)%}{{forloop}}{%endfor%}'
const html = await liquid.parseAndRender(src, scope) const html = await liquid.parseAndRender(src, scope)
return expect(html).to.equal('{"i":0,"length":1}') return expect(html).to.equal('{"i":0,"length":1,"name":"i-(1..1)"}')
})
it('should output forloop collection name', async function () {
const src = '{%for c in alpha%}{{forloop.name}}-{{c}}{%endfor%}'
const html = await liquid.parseAndRender(src, scope)
return expect(html).to.equal('c-alpha-ac-alpha-bc-alpha-c')
})
it('should output forloop property accessor name', async function () {
const src = '{%for c in obj.foo%}{{forloop.name}}-{{c}}{%endfor%}'
const html = await liquid.parseAndRender(src, scope)
return expect(html).to.equal('c-obj.foo-bar')
})
it('should output forloop quoted name', async function () {
const src = '{%for str in "string"%}{{forloop.name}}-{{str}}{%endfor%}'
const html = await liquid.parseAndRender(src, scope)
return expect(html).to.equal('str-"string"-string')
}) })
describe('illegal', function () { describe('illegal', function () {
it('should reject when for not closed', function () { it('should reject when for not closed', function () {