Skip to content

Commit f8a4ef0

Browse files
nex3dawaltconley
andcommitted
Add keyword arguments for Liquid shortcodes
This is an adaptation of #1733 to use the built-in Liquid argument parser that was added in #2679. Co-Authored-By: Dylan Awalt-Conley <[email protected]>
1 parent 26c0fcf commit f8a4ef0

File tree

2 files changed

+123
-30
lines changed

2 files changed

+123
-30
lines changed

src/Engines/Liquid.js

+41-30
Original file line numberDiff line numberDiff line change
@@ -172,20 +172,57 @@ class Liquid extends TemplateEngine {
172172
let tokenizer = new Tokenizer(args);
173173
let parsedArgs = [];
174174

175-
let value = tokenizer.readValue();
175+
function readValue() {
176+
let value = tokenizer.readHash() ?? tokenizer.readValue();
177+
// readHash() treats unmarked identifiers as hash keys with undefined
178+
// values, but we want to parse them as positional arguments instead.
179+
return value?.kind === 64 && value.value === undefined ? value.name : value;
180+
}
181+
182+
let value = readValue();
176183
while (value) {
177184
parsedArgs.push(value);
178185
tokenizer.skipBlank();
179186
if (tokenizer.peek() === ",") {
180187
tokenizer.advance();
181188
}
182-
value = tokenizer.readValue();
189+
value = readValue();
183190
}
184191
tokenizer.end();
185192

186193
return parsedArgs;
187194
}
188195

196+
static *evalArguments(tag, ctx) {
197+
let argArray = [];
198+
let namedArgs = {};
199+
if (tag.legacyArgs) {
200+
let rawArgs = Liquid.parseArguments(_t.argLexer, tag.legacyArgs);
201+
for (let arg of rawArgs) {
202+
let b = yield liquidEngine.evalValue(arg, ctx);
203+
argArray.push(b);
204+
}
205+
} else if (tag.orderedArgs) {
206+
for (let arg of tag.orderedArgs) {
207+
if (arg.kind == 64) {
208+
if (arg.value === undefined) {
209+
namedArgs[arg.name.content] = true;
210+
} else {
211+
namedArgs[arg.name.content] = yield evalToken(arg.value, ctx);
212+
}
213+
} else {
214+
let b = yield evalToken(arg, ctx);
215+
argArray.push(b);
216+
}
217+
}
218+
}
219+
220+
if (Object.keys(namedArgs).length > 0) {
221+
argArray.push(namedArgs);
222+
}
223+
return argArray;
224+
}
225+
189226
addShortcode(shortcodeName, shortcodeFn) {
190227
let _t = this;
191228
this.addTag(shortcodeName, function (liquidEngine) {
@@ -200,21 +237,7 @@ class Liquid extends TemplateEngine {
200237
}
201238
},
202239
render: function* (ctx) {
203-
let argArray = [];
204-
205-
if (this.legacyArgs) {
206-
let rawArgs = Liquid.parseArguments(_t.argLexer, this.legacyArgs);
207-
for (let arg of rawArgs) {
208-
let b = yield liquidEngine.evalValue(arg, ctx);
209-
argArray.push(b);
210-
}
211-
} else if (this.orderedArgs) {
212-
for (let arg of this.orderedArgs) {
213-
let b = yield evalToken(arg, ctx);
214-
argArray.push(b);
215-
}
216-
}
217-
240+
let argArray = yield* Liquid.evalArguments(this, ctx);
218241
let ret = yield shortcodeFn.call(Liquid.normalizeScope(ctx), ...argArray);
219242
return ret;
220243
},
@@ -249,19 +272,7 @@ class Liquid extends TemplateEngine {
249272
stream.start();
250273
},
251274
render: function* (ctx /*, emitter*/) {
252-
let argArray = [];
253-
if (this.legacyArgs) {
254-
let rawArgs = Liquid.parseArguments(_t.argLexer, this.legacyArgs);
255-
for (let arg of rawArgs) {
256-
let b = yield liquidEngine.evalValue(arg, ctx);
257-
argArray.push(b);
258-
}
259-
} else if (this.orderedArgs) {
260-
for (let arg of this.orderedArgs) {
261-
let b = yield evalToken(arg, ctx);
262-
argArray.push(b);
263-
}
264-
}
275+
let argArray = yield* Liquid.evalArguments(this, ctx);
265276

266277
const html = yield liquidEngine.renderer.renderTemplates(this.templates, ctx);
267278

test/TemplateRenderLiquidTest.js

+82
Original file line numberDiff line numberDiff line change
@@ -658,6 +658,38 @@ test("Liquid Nested Paired Shortcode", async (t) => {
658658
);
659659
});
660660

661+
test("Liquid Paired Kwargs Shortcode with Tag Inside", async (t) => {
662+
let tr = await getNewTemplateRender("liquid", "./test/stubs/");
663+
tr.engine.addPairedShortcode("postfixWithZach", function (content, kwargs) {
664+
var { str } = kwargs ?? {};
665+
return str + content + "Zach";
666+
});
667+
668+
t.is(
669+
await tr._testRender(
670+
"{% postfixWithZach str: name %}Content{% if tester %}If{% endif %}{% endpostfixWithZach %}",
671+
{ name: "test", tester: true }
672+
),
673+
"testContentIfZach"
674+
);
675+
});
676+
677+
test("Liquid Nested Paired Kwargs Shortcode", async (t) => {
678+
let tr = await getNewTemplateRender("liquid", "./test/stubs/");
679+
tr.engine.addPairedShortcode("postfixWithZach", function (content, kwargs) {
680+
var { str } = kwargs ?? {};
681+
return str + content + "Zach";
682+
});
683+
684+
t.is(
685+
await tr._testRender(
686+
"{% postfixWithZach str: name %}Content{% postfixWithZach str: name2 %}Content{% endpostfixWithZach %}{% endpostfixWithZach %}",
687+
{ name: "test", name2: "test2" }
688+
),
689+
"testContenttest2ContentZachZach"
690+
);
691+
});
692+
661693
test("Liquid Shortcode Multiple Args", async (t) => {
662694
let tr = await getNewTemplateRender("liquid", "./test/stubs/");
663695
tr.engine.addShortcode("postfixWithZach", function (str, str2) {
@@ -673,6 +705,56 @@ test("Liquid Shortcode Multiple Args", async (t) => {
673705
);
674706
});
675707

708+
test("Liquid Shortcode Keyword Arg", async (t) => {
709+
let tr = await getNewTemplateRender("liquid", "./test/stubs/");
710+
tr.engine.addShortcode("postfixWithZach", function (str, kwargs) {
711+
let { append } = kwargs ?? {};
712+
return str + "Zach" + append;
713+
});
714+
715+
t.is(
716+
await tr._testRender("{% postfixWithZach name append: other %}", {
717+
name: "test",
718+
other: "howdy",
719+
}),
720+
"testZachhowdy"
721+
);
722+
});
723+
724+
test("Liquid Shortcode Multiple Keyword Args", async (t) => {
725+
let tr = await getNewTemplateRender("liquid", "./test/stubs/");
726+
tr.engine.addShortcode("postfixWithZach", function (str, kwargs) {
727+
let { prepend, append } = kwargs ?? {};
728+
return prepend + str + "Zach" + append;
729+
});
730+
731+
t.is(
732+
await tr._testRender(
733+
"{% postfixWithZach name prepend: 'string' append: other %}",
734+
{
735+
name: "test",
736+
other: "howdy",
737+
}
738+
),
739+
"stringtestZachhowdy"
740+
);
741+
});
742+
743+
test("Liquid Shortcode Only Keyword Args", async (t) => {
744+
let tr = await getNewTemplateRender("liquid", "./test/stubs/");
745+
tr.engine.addShortcode("postfixWithZach", function (kwargs) {
746+
let { prepend, append } = kwargs ?? {};
747+
return prepend + "Zach" + append;
748+
});
749+
750+
t.is(
751+
await tr._testRender("{% postfixWithZach prepend: 'string' append: name %}", {
752+
name: "test",
753+
}),
754+
"stringZachtest"
755+
);
756+
});
757+
676758
test("Liquid Include Scope Leak", async (t) => {
677759
let tr1 = await getNewTemplateRender("liquid", "./test/stubs/");
678760
t.is(tr1.getEngineName(), "liquid");

0 commit comments

Comments
 (0)