squirrellyjs / squirrelly

Semi-embedded JS template engine that supports helpers, filters, partials, and template inheritance. 4KB minzipped, written in TypeScript ⛺
https://squirrelly.js.org
MIT License
571 stars 81 forks source link

Content of each helper doesn't support async filters #207

Closed joas8211 closed 3 years ago

joas8211 commented 3 years ago

each helper with async filter within it compiles to invalid code as each helper doesn't use async function, but instead a normal function for rendering it's contents. I imagine there could also be other helpers that doesn't support async.

Running following code reproduces the bug. I've also made a fiddle with the same code if anyone wants to easily run it themselves (see console for the error).

Sqrl.filters.define('myAsyncFilter', function async (input) {
  return input;
});

const fn = Sqrl.compile(
  `{{@each(it.items) => item}}
    {{item | async myAsyncFilter}}
  {{/each}}`,
  { async: true }
 );
const data = { items: ['item1', 'item2'] };
const html = fn(data, Sqrl.defaultConfig);
document.body.innerHTML = html;

Running the code produces: Syntax Error: missing ) after argument list. That just because JavaScript parser isn't expecting to find await in non-async function.

nebrelbug commented 3 years ago

Hey @joas8211, thanks for the great question (and the reproducible demo)!

Actually, this can be fixed by replacing @each with @async each. Squirrelly doesn't infer that a helper is asynchronous just because it has asynchronous inner content, so you have to explicitly mark it as asynchronous :) Hopefully someone will make a linter for Squirrelly that catches errors like this :wink:

Sqrl.filters.define('myAsyncFilter', function async (input) {
    return input.toUpperCase();
});

const fn = Sqrl.compile(
  `{{@async each(it.items) => item}}
    {{item | async myAsyncFilter}}
  {{/each}}`,
  { async: true }
 );

const data = { items: ['item1', 'item2'] };
fn(data, Sqrl.defaultConfig).then(html => document.body.innerHTML = html)
// ITEM1 ITEM2

And here's a fork of your JSFiddle: https://jsfiddle.net/qa4Ljxd9/

joas8211 commented 3 years ago

Thank you for your response @nebrelbug. I sertainly missed this fact while reading the docs. I'll close this issue because it wasn't a bug, but a user error instead.

But this gets me thinking that is marking helpers and filters async necessary at all if the template is compiled as async? Isn't it redundant to mark them separately if we could assume that every helper and filter is asynchronous? Doesn't synchronous functions work within asynchronous ones perfectly fine? Or is there some function for having mixed sync and async helpers / filters?