Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Leaner/Clearer compiled templates #1189

Closed
stevenvachon opened this issue Feb 23, 2016 · 15 comments
Closed

Leaner/Clearer compiled templates #1189

stevenvachon opened this issue Feb 23, 2016 · 15 comments

Comments

@stevenvachon
Copy link

Currently, this:

<tag1>{{path0 (path1 "string") hash="value"}}</tag1>
<tag2>{{@path2}}</tag2>

compiles to:

(function()
{
    var template  = Handlebars.template;
    var templates = Handlebars.templates = Handlebars.templates || {};

    templates['compiled-template.hbs'] = template(
    {
        "compiler": [7, ">= 4.0.0"],

        "main": function(container, depth0, helpers, partials, data)
        {
            var helper;
            var alias1 = depth0 != null ? depth0 : {};
            var alias2 = helpers.helperMissing;
            var alias3 = container.escapeExpression;

            return "<tag1>"
                +
                alias3
                (
                    (helpers.path || (depth0 && depth0.path) || alias2).call
                    (
                        alias1,
                        (helpers.path || (depth0 && depth0.path) || alias2).call
                        (
                            alias1,
                            "string",
                            {
                                "name": "path1",
                                "hash": {},
                                "data": data
                            }
                        ),
                        {
                            "name": "path0",
                            "hash": { "hash":"value" },
                            "data": data
                        }
                    )
                )
                +
                "</tag1>\n<tag2>"
                +
                alias3
                (
                    (
                        (helper = (helper = helpers.path || (data && data.path)) != null ? helper : alias2),
                        (
                            typeof helper === "function" ? helper.call
                            (
                                alias1,
                                {
                                    "name": "path2",
                                    "hash": {},
                                    "data": data
                                }
                            ) : helper
                        )
                    )
                )
                +
                "</tag2>"
            ;
        },

        "useData": true
    });
})();

There is some, in my opinion, unnecessary redundancies in there. Could they not be moved to more descriptive functions such as maybeCallHelper()?

@stevenvachon stevenvachon changed the title Leaner compiled templates Leaner/Clearer compiled templates Feb 23, 2016
@kpdecker
Copy link
Collaborator

I looked into a number of these changes and this route was chosen in most cases over the use of helper functions as this is significantly faster for rendering and the overhead over the wire is minimal with gzip as this is procedurally generate code so it compresses very well.

That being said, if someone were to create a pull request that adds a compiler option for this use case if they feel the gzip compression is not sufficient, I'd be open to landing that.

@stevenvachon
Copy link
Author

File size is only one benefit of optimizing this. The main one is simplifying the complexity. I'm finding it difficult to figure out exactly what's going on in order for me to complete compilers that will depend on handlebars-html-parser.

@kpdecker
Copy link
Collaborator

This is true, but the compiled output of the templates was never intended for human consumption. We are optimizing for the runtime in this case since this is procedurally generated code.

If you are writing your own compile to infrastructure, you may want to consider implementing your own version of JavascriptCompiler (you'd need to do the same to implement the helper-based implementation you are proposing) and modify this to fit whatever output you find easiest for you to follow.

@stevenvachon
Copy link
Author

I'd prefer to use the handlebars runtime, but not use handlebars.compile(). I'm currently using handlebars.parse() and building a different AST for use in compiling to anything.

@kpdecker
Copy link
Collaborator

Then that implies that you'll need to work in the format that the runtime currently uses now.

You might be boxing yourself into a particular pattern unnecessarily as the handlebars runtime is highly focus on generating strings as fast as possible and does little to support other object types. HTMLbars as an example only uses the parse, to my knowledge.

@stevenvachon
Copy link
Author

The compiled Handlebars templates appear to use callbacks that return the block-level contents. They wouldn't have to return strings, but could return vDOM/similar objects. But all of the conditions to run functions could be hidden away, making my job more humanly possible.

As far as passing HTML as expression parameters, I think that wouldn't be necessary in a good app design with concerns separated. I don't want an HTML parser (or even a hacked regex one) in my runtimes.

@kpdecker
Copy link
Collaborator

If you want to submit pull requests to help support your use case, we would be glad to consider them, but there is a bit of a bar to be passed as we don't want to degrade performance and additions to the client runtime require breaking changes, so we may be limited in when we can land them.

@stevenvachon
Copy link
Author

stevenvachon commented Apr 16, 2016

How are else if and else helpers handled in the Handlebars source? I can't seem to find definitions for them. Also, based on the test suite, such appear to be used with with and unless as well -- are there others? Is this because they are not helpers, but in fact part of core Handlebars' inverse?

It's my understanding that:

{{#if path1}}
  a
{{else if path2}}
  b
{{else}}
  c
{{/if}}

…is parsed as:

{{#if path1}}
  a
{{else}}
  {{#if path2}}
    b
  {{else}}
    c
  {{/if}}
{{/if}}

@kpdecker
Copy link
Collaborator

else if is chaining two if helpers together. Any helper can be chained.

{{if foo}}
{{else if bar}}
{{else if baz}}
{{/if}}

Is effectively syntactic sugar for this:

{{if foo}}
{{else}}
  {{if bar}}
  {{else}}
    {{if baz}}
    {{/if}}
  {{/if}}
{{/if}}

Re: what the output looks like, print-script is a utility in this repo that will output the generated code to the console. I generally use this when trying to determine the structured Ex:

./print-script '{{foo}}`

@stevenvachon
Copy link
Author

These appears to be possible, then:

{{#unless foo}}
{{else if bar}}
{{else}}
{{/unless}}
{{#helper foo}}
{{else if bar}}
{{else}}
{{/helper}}

Correct?

@kpdecker
Copy link
Collaborator

Yes.

On Sat, Apr 16, 2016 at 4:20 PM Steven Vachon notifications@github.com
wrote:

These appears to be possible, then:

{{#unless foo}}{{else if bar}}{{else}}{{/unless}}

{{#helper foo}}{{else if bar}}{{else}}{{/helper}}

Correct?


You are receiving this because you modified the open/close state.
Reply to this email directly or view it on GitHub
#1189 (comment)

@stevenvachon
Copy link
Author

stevenvachon commented Apr 17, 2016

Wouldn't this conflict with a view model else property?:

require("handlebars").compile("{{#if var}} foo {{else}} baz {{/if}}")({else:"bar", var:true});
// " foo "

I might've expected the above to produce " foo bar baz ".

Is else considered a "standalone" tag? I've seen mentions of this in comments and documentation but it wasn't clear enough for me. What are other examples of such tags? Ah: http://handlebarsjs.com/expressions.html#whitespace-control

@kpdecker
Copy link
Collaborator

Users can reference the variable else with [else]. Both of these behaviors
have been like this since the early days of the project.
On Sun, Apr 17, 2016 at 10:36 AM Steven Vachon notifications@github.com
wrote:

Is else considered a "standalone" tag? I've seen mentions of this in
comments and documentation but it wasn't clear enough for me.


You are receiving this because you modified the open/close state.
Reply to this email directly or view it on GitHub
#1189 (comment)

@stevenvachon
Copy link
Author

stevenvachon commented Apr 19, 2016

Back in the early days I wasn't writing a custom handlebars compiler 😉

Is there a name to describe the special function of {{else}},{{else if}},{{^}}? Particularly something that differentiates them from {{^else}} (inverted standalone block).

Edit:
Well, I'm going with "chained, inverted standalone block".

@stevenvachon
Copy link
Author

stevenvachon commented Sep 15, 2016

@kpdecker regarding PRs that might degrade performance; is it really a concern when the tradeoff is new uses for Handlebars? I think the degradation would be marginal.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants