Skip to content

Commit

Permalink
Improvements to Async Filters in Nunjucks
Browse files Browse the repository at this point in the history
1. Adds a new `addAsyncFilter` configuration API method
2. Adds support for `async function` on standard `addFilter` method in Nunjucks
3. Throws an error if you return a promise from a `function` when using `addFilter` with Nunjucks

Fixes #1382
Fixes #2536
Fixes #2254
  • Loading branch information
zachleat committed Aug 17, 2022
1 parent 821faed commit 9bbdcd7
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 3 deletions.
38 changes: 35 additions & 3 deletions src/UserConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ const pkg = require("../package.json");

class UserConfigError extends EleventyBaseError {}

const ComparisonAsyncFunction = (async () => {}).constructor;

// API to expose configuration options in config file
class UserConfig {
constructor() {
Expand Down Expand Up @@ -236,11 +238,41 @@ class UserConfig {

// namespacing happens downstream
this.addLiquidFilter(name, callback);
this.addNunjucksFilter(name, callback);
this.addJavaScriptFunction(name, callback);

// TODO remove Handlebars helpers in Universal Filters. Use shortcodes instead (the Handlebars template syntax is the same).
this.addHandlebarsHelper(name, callback);
// This method *requires* `async function` and will not work with `function` that returns a promise
if (callback instanceof ComparisonAsyncFunction) {
this.addNunjucksAsyncFilter(name, async function (value, cb) {
let ret = await callback(value);
cb(null, ret);
});
} else {
this.addNunjucksFilter(name, function (value) {
let ret = callback(value);
if (ret instanceof Promise) {
throw new Error(
`Nunjucks *is* async-friendly with \`addFilter("${name}", async function() {})\` but you need to supply an \`async function\`. You returned a promise from \`addFilter("${name}", function() {})\`. Alternatively, use the \`addAsyncFilter("${name}")\` configuration API method.`
);
}
return ret;
});

// TODO remove Handlebars helpers in Universal Filters. Use shortcodes instead (the Handlebars template syntax is the same).
this.addHandlebarsHelper(name, callback);
}
}

// Liquid, Nunjucks, and JS only
addAsyncFilter(name, callback) {
debug("Adding universal async filter %o", this.getNamespacedName(name));

// namespacing happens downstream
this.addLiquidFilter(name, callback);
this.addJavaScriptFunction(name, callback);
this.addNunjucksAsyncFilter(name, async function (value, cb) {
let ret = await callback(value);
cb(null, ret);
});
}

getFilter(name) {
Expand Down
38 changes: 38 additions & 0 deletions test/TemplateRenderNunjucksTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -1070,3 +1070,41 @@ test("Use a precompiled Nunjucks template", async (t) => {
34`
);
});

test("Make sure addFilter is async-friendly for Nunjucks", async (t) => {
let templateConfig = new TemplateConfig();
// requires async function
templateConfig.userConfig.addFilter("fortytwo", async function () {
return getPromise(42);
});

let tr = getNewTemplateRender("njk", null, templateConfig);

let fn = await tr.getCompiledTemplate("<p>{{ 'hi' | fortytwo }}</p>");
t.is(await fn(), "<p>42</p>");
});

test("Throw an error when you return a promise in addFilter for Nunjucks", async (t) => {
let templateConfig = new TemplateConfig();
// requires async function
templateConfig.userConfig.addFilter("fortytwo", function () {
return getPromise(42);
});

let tr = getNewTemplateRender("njk", null, templateConfig);
let fn = await tr.getCompiledTemplate("<p>{{ 'hi' | fortytwo }}</p>");
await t.throwsAsync(fn);
});

test("addAsyncFilter for Nunjucks", async (t) => {
let templateConfig = new TemplateConfig();
// works without async function (can return promise)
templateConfig.userConfig.addAsyncFilter("fortytwo", function () {
return getPromise(42);
});

let tr = getNewTemplateRender("njk", null, templateConfig);

let fn = await tr.getCompiledTemplate("<p>{{ 'hi' | fortytwo }}</p>");
t.is(await fn(), "<p>42</p>");
});

0 comments on commit 9bbdcd7

Please sign in to comment.