eta-dev / eta

Embedded JS template engine for Node, Deno, and the browser. Lighweight, fast, and pluggable. Written in TypeScript
https://eta.js.org
MIT License
1.35k stars 60 forks source link

Rendering Programmatically Defined Templates Asyncronously #281

Open paul-norman opened 3 months ago

paul-norman commented 3 months ago

Describe the Bug:

This is a documentaion bug! When using a programatically defined template the docs state: "The third argument to loadTemplate is a boolean describing whether the template is async or not. By default, Eta will assume that the template is synchronous."

Using this advice, calling a programatically defined template using renderAsync will result in an EtaNameResolution error. (includeAsync has the same issue)

To Reproduce:

const eta = new Eta();
const test = `test`;
eta.loadTemplate('@test', test, true); // As advised
const res = await eta.renderAsync('@test');
EtaNameResolutionError [EtaNameResolution Error]: Failed to get template '@test'

Expected Behaviour:

The template should be found and rendered.

Package & Environment Details

Additional Context:

Removal of the Async call will succeed:

const eta = new Eta();
const test = `test`;
eta.loadTemplate('@test', test, true); // Boolean variable is doing nothing
const res = eta.render('@test');
console.log(res);

As will passing the object that the code actually expects:

const eta = new Eta();
const test = `test`;
eta.loadTemplate('@test', test, { async: true});
const res = await eta.renderAsync('@test');
console.log(res);

Just a slighly more complete example to assist future travellers who are loading their templates from a database (or wherever!) while the documentation / examples for Eta are still rather sparse:

const eta = new Eta({
    tags:       ['<?', '?>'],   // I find <% awkward to type (long live PHP!)
    varName:    'data',         // Not a fan of `it` either
    debug:      true,
    cache:      true,
});

const layoutTest = `
    <!DOCTYPE html>
    <html>
        <head>
            <title><?= data.title ?></title>
        </head>
        <body>
            <?~ await includeAsync('@headerTest', data) ?>
            <?~ data.body ?>
            <?~ await includeAsync('@footerTest', data) ?>
        </body>
    </html>
`;

const headerTest = `
    <header>
        Header: <?= data.title ?>
    </header>
`;

const footerTest = `
    <footer>
        Footer: <?= data.title ?>
    </footer>
`;

const mainTest = `
    <? layout('@layoutTest', data) ?>
    <main>
        <h1>Main: <?= data.title ?></h1>
    </main>
`;

eta.loadTemplate('@layoutTest', layoutTest, { async: true });
eta.loadTemplate('@headerTest', headerTest, { async: true });
eta.loadTemplate('@footerTest', footerTest, { async: true });
eta.loadTemplate('@mainTest', mainTest, { async: true });

const res = await eta.renderAsync('@mainTest', { title: 'Test title' });
nebrelbug commented 3 months ago

@paul-norman thanks for catching that! I just pushed a quick clarification to the docs, but I'd be open to a PR with more details / a better explanation!

paul-norman commented 2 months ago

I've just started actually porting some EJS code to ETA today, so I'm a total beginner with the system. However, after I've used it for a little while I will take another look at the docs and see what doesn't make sense to me then...