NightlyCommit / twing

First-class Twig engine for Node.js
BSD 2-Clause "Simplified" License
199 stars 23 forks source link

Adding a browser/web loader with passing arguments #388

Closed noel-schenk closed 11 months ago

noel-schenk commented 5 years ago

As the whole engine now also runs in the browser it will need a loader for the browser. For example included templates will not render without a working loader.

ericmorand commented 5 years ago

Are you talking about a webpack loader? Or something else?

noel-schenk commented 5 years ago

Ah no for example if I want to render my templates in the browser and i have templates which have to be included. It should be possible to get the templates via a loader over an ajax request. Nunjuck has a good example: https://mozilla.github.io/nunjucks/api.html#writing-a-loader unfortunately Nunjucks doesn't allow arguments for it's templates.

ericmorand commented 5 years ago

Oh yes, I see. I have plans for at least two loaders:

I won't include them in Twing. But they'll definitely be available as separate packages.

What would the arguments by needed for?

noel-schenk commented 5 years ago

The server part doesen't has to be part of the library. But for example a simplified TwingLoaderInterface

TwingWebLoaderInterface cache:boolean getSource(name:string, context:any)

Now if a template should be included the "master" loader will ask the "simple" loader for the full rendered template. Everything else can easily be handled by a developer. Also the "master" loader could handle the caching. The context can be passed either with a separate function or "hardcoded".

The arguments are really important because you can make the templates dynamic: {% include 'template.html' with {'foo': 'bar'} %}

But it would be like a wrapper for the TwingLoaderInterface to handle all the stuff currently done by the TwingLoaderFilesystem.

ericmorand commented 5 years ago

Writing a loader is actually quite simple. But for now it is not possible to write a loader that would make Ajax calls because Twing doesn't support async loaders.

For now.

It's my next move, probably for Twing 3.1 or 3.2. It's been postponed for too long.

noel-schenk commented 5 years ago

thanks for the response. I would love to try something. But I don't get how I can get the real name of the template. I looked at your filesystem example and there is this line where you fill the templates into the cache class variable: this.cache.set(name, nodePath.resolve(nodePath.join(path, shortname))); Shortname is set to this: [namespace, shortname] = this.parseName(name); parseName returns the shortname as it get's passed (as name) to the function parseName or if starts with an @ then it returns everything after the / as the shortname. But when I try to get the name I get the templateHash which looks like this: __string_template__5a13abdde6d8986ce9eb3885dd689b2881a9e002406ce18452b1f93f953f76de

I can see that this is generated in the environment.ts at line 244 but I don't get how your class get's a real name it can find in the system and mine gets just the hash.

ericmorand commented 5 years ago

__string_template__5a13abdde6d8986ce9eb3885dd689b2881a9e002406ce18452b1f93f953f76de

This is the generated name of the template when one use the template_from_string function of Twig, like this:

{{ include(template_from_string("Hello {{ foo }}")) }}

Can you show me the code of what you are trying to achieve?

noel-schenk commented 5 years ago

Ah I tried doing this:

resolve(env.createTemplate(data[0]).render(data[1]));

So it there any way to get the actual template as I probably need to give it back inside getSourceContext?

getCacheKey would be then

__string_template__5a13abdde6d8986ce9eb3885dd689b2881a9e002406ce18452b1f93f953f76de

isFresh is part of a timestamp based cache

exists just checks if the template exists

resolve just gives back the same as getSourceContext but as a string and without the name (string instead of a TwingSource)

ericmorand commented 5 years ago

So it there any way to get the actual template as I probably need to give it back inside getSourceContext?

env.createTemplate returns a template (i.e. an instance of TwingTemplate).

But I have the feeling that when you talk about template you talk about the source of your template.

It's not quite clear to me what you are trying to achieve in the end.

ericmorand commented 5 years ago

Maybe you can join me here:

https://nightlycommit.slack.com/messages/C9FNDPD0A

It would make things easier if we need to talk a lot about your issue.

noel-schenk commented 5 years ago

Thank you :) I think you need to add me first as I can't log in.

Am Di., 1. Okt. 2019 um 13:10 Uhr schrieb Eric MORAND < notifications@github.com>:

Maybe you can join me here:

https://nightlycommit.slack.com/messages/C9FNDPD0A

It would make things easier if we need to talk a lot about your issue.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/NightlyCommit/twing/issues/388?email_source=notifications&email_token=ACLYZGPJO2C4ZVIMHPRUVFTQMMVY7A5CNFSM4IYNROC2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEAA4QAQ#issuecomment-536987650, or mute the thread https://github.com/notifications/unsubscribe-auth/ACLYZGOCF4FKQGHCWLEFED3QMMVY7ANCNFSM4IYNROCQ .

noel-schenk commented 5 years ago

So it there any way to get the actual template as I probably need to give it back inside getSourceContext?

env.createTemplate returns a template (i.e. an instance of TwingTemplate).

But I have the feeling that when you talk about template you talk about the source of your template.

It's not quite clear to me what you are trying to achieve in the end.

The only thing I want to do is to make an interface communicating between my local storage (preloaded template"sources") and the loader. Because I have includes inside the template the loader should fetch them. But I don't get further than getting the string template key. I would need the names/paths of the includes when they get fetched by the loader and the respond with the corresponding template"source".

ericmorand commented 5 years ago

OK, it's clear.

First, you got it right but it's good to re-say it: templates are loaded by their name. Filesystem loader considers that the name of a template should be considered as a path, but this is an implementation detail.

You should take TwingLoaderArray as an example instead of TwingLoaderFilesystem. It's much easier and much closer to what you are trying to achieve: the local storage is a key/value store.

Here is what I would do:

Other methods of the TwingLoaderInterface can be left untouched from the parent.

Something like that, out of my head, untested:

import {TwingLoaderArray} from "./array";
import {TwingSource} from "../source";

class LocalStorageLoader extends TwingLoaderArray {
    constructor() {
        super([]);
    }

    getSourceContext(name: string, from: TwingSource): TwingSource {
        return new TwingSource(localStorage.getItem(name), name);
    }

    exists(name: string, from: TwingSource): boolean {
        return localStorage.getItem(name) !== null;
    }
}
noel-schenk commented 5 years ago

Thank you so much! I will look into that. Got kicked out of the airport because I used a power outlet I wasn't supposed to :P but should be back with my laptop at the 3rd.