NightlyCommit / twing

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

Uncaught TwingErrorSyntax: An exception has been thrown during the compilation of a template ("parseFunction is not a function") #284

Closed Radiergummi closed 6 years ago

Radiergummi commented 6 years ago

I'm working on a Vue component that renders twig templates on the fly, so I use twing in the browser. Things generally work, except for printing variables: That fails with the error below. Any idea what's going wrong?

environment.js?15ec:540 Uncaught TwingErrorSyntax: An exception has been thrown during the compilation of a template ("parseFunction is not a function") in "main".
    at TwingEnvironmentBrowser.compileSource (webpack-internal:///./node_modules/twing/lib/environment.js:540:23)
    at TwingEnvironmentBrowser.loadTemplate (webpack-internal:///./node_modules/twing/lib/environment.js:354:32)
    at TwingEnvironmentBrowser.render (webpack-internal:///./node_modules/twing/lib/environment.js:282:21)
    at VueComponent.eval (webpack-internal:///./node_modules/babel-loader/lib/index.js!./node_modules/vue-loader/lib/selector.js?type=script&index=0!./src/components/TemplateEditor.vue:88:27)
    at later (webpack-internal:///./node_modules/babel-loader/lib/index.js!./node_modules/vue-loader/lib/selector.js?type=script&index=0!./src/components/TemplateEditor.vue:49:28)

Current template:

{# test #}
setting a variable was {% if not foo %} not {% else %} actually {% endif %} successful: {{ foo }}

Current context:

{
  "foo": "aoifjf"
}

Current output:

setting a variable was actually successful: {
ericmorand commented 6 years ago

Could't it be due to Vue messing with the double-mustache syntax of Twig?

Would you mind sending me your bundle if there is no sensitive informations? I'd like to debug it.

eric.morand@gmail.com if it's fine for you.

ericmorand commented 6 years ago

You can also try changing the variable syntax of Twing if you want to test my theory:

let env = new TwingEnvironment(...);

let lexer = new TwingLexer(env, {
    'tag_variable': ['{{', '}}'] // <= There, put something else
});

env.setLexer(lexer);

You can find some examples here:

https://ericmorand.github.io/twing/recipes.html#customizing-the-syntax

Radiergummi commented 6 years ago

Sure thing, it's just a prototype to show the team what a solution might look like :wink:

Component here: https://gist.github.com/Radiergummi/4d12d2755d0ea5d707bad7ee0abc9af3 Full project here: https://ufile.io/n1l9t

Radiergummi commented 6 years ago

Just changed the variable syntax, but the same error pops up:

environment.js?15ec:540 Uncaught TwingErrorSyntax: An exception has been thrown during the compilation of a template ("parseFunction is not a function") in "main".
    at TwingEnvironmentBrowser.compileSource (webpack-internal:///./node_modules/twing/lib/environment.js:540:23)
    at TwingEnvironmentBrowser.loadTemplate (webpack-internal:///./node_modules/twing/lib/environment.js:354:32)
    at TwingEnvironmentBrowser.render (webpack-internal:///./node_modules/twing/lib/environment.js:282:21)
    at VueComponent.eval (webpack-internal:///./node_modules/babel-loader/lib/index.js!./node_modules/vue-loader/lib/selector.js?type=script&index=0!./src/components/TemplateEditor.vue:103:27)
    at later (webpack-internal:///./node_modules/babel-loader/lib/index.js!./node_modules/vue-loader/lib/selector.js?type=script&index=0!./src/components/TemplateEditor.vue:58:28)
ericmorand commented 6 years ago

Thanks, looking at it.

ericmorand commented 6 years ago

I can reproduce the issue. Looks like webpack include the file dist/index.es.js of the parse-function package instead of dist/index.js. dist/index.es.js is declared in parse-function package.json under the "module" entry. Why would webpack use that version instead of the "main" one?

By changing parse-function package.json and set dist/index.js as the "module" entry, everything sems to work fine.

I'm heading to parse-function issue tracker to see if it's a known issue. I'm, assuming their ES6 module is not exporting the same API as their main module. Which is a huge issue.

As a workaround until I get that thing clarified, can you try to alias parse-function so that it points to its dist/index.js entry?

And by the way, I love what you're doing! Looks super promising!

Radiergummi commented 6 years ago

Oh, that sounds strange... Let's see if they'll fix it.
I'm going to try aliasing it and get back to you.

Thank you, glad you like it! The editor is just a mock for now, though we're working on a kind of WYSIWYG editor that will generate a custom twig DSL as it's output. Maybe we'll end up using twing if things work out as I intend them to :)

ericmorand commented 6 years ago

Oh, that sounds strange... Let's see if they'll fix it.

Actually, I opened an issue at Webpack: https://github.com/webpack/webpack/issues/8171

Webpack is the main culprit there: I explictly import the non-ES6 flavor of parse-function (using require('parse-function'); instead of import parseFunction from 'parse-function';') but Webpack still resolve to the index.es.js variant of parse-function.

Until they fix it (and based on my previous experience with Webpack community, I'm sure they won't), I'll fix the issue in Twing by explictely requiring parse-function/dist/index.js. Not quite clean but I have no choice at that point. I tested it and it solve your issue.

I'll have that fix as part of release 2.1.1, available later today.

ericmorand commented 6 years ago

Fixed in Twing@2.1.1. Can you give it a try and tell me if everything works as expected now?

Radiergummi commented 6 years ago

Works flawlessly, thank you Eric!

image

ericmorand commented 6 years ago

Amazing! Keep us posted about your editor. Twigfiddle is getting old and I wanted to have something more modern and robust (for example there is no way to enable extensions with twigfiddle) for some times. What you're doing could be the perfect entry point to that.

ericmorand commented 6 years ago

I plan to have an ES flavor of Twing in a near future - that could benefit from modern bundlers tree-shaking and optimizations, but I need some of Twing dependencies to fully embrace TypeScript eco-system - by providing definitions mainly.

Already asked for it to parse-function maintainer here: https://github.com/tunnckoCoreLabs/parse-function/issues/138

Until all dependencies come with a definition, I won't be able to provide a better fix to your issue than what I did.

ericmorand commented 6 years ago

Also, keep in mind that you have everything needed if you need to analyse your template syntax early - before rendering. This would allow you to render only if the syntax is valid:

const {TwingParser, TwingEnvironment, TwingLoaderArray} = require('twing');

let loader = new TwingLoaderArray({
    index: '{{foo}} {%include "partial" %}',
    partial: '{{foo}}'
});

let env = new TwingEnvironment(loader);

let indexSource = loader.getSourceContext('index');
let partialSource = loader.getSourceContext('partial');

try {
    let indexStream = env.tokenize(indexSource);
    let partialStream = env.tokenize(partialSource);

    // the syntax is fine, we can render
    console.warn(env.render('index', {
        foo: 'bar'
    }));

    // let's also output the compiled template to match twigfiddle feature
    let parser = new TwingParser(env);
    let node = parser.parse(indexStream);
    let compiledTemplates = env.compile(node);

    console.warn('INDEX', compiledTemplates);

    node = parser.parse(partialStream);
    compiledTemplates = env.compile(node);

    console.warn('PARTIAL', compiledTemplates)
}
catch (e) {
    console.warn('syntax error, may be a noop on render-as-typing');
}

Head there if you need more information:

https://ericmorand.github.io/twing/internals.html

And have fun!

Radiergummi commented 6 years ago

Amazing! Keep us posted about your editor. Twigfiddle is getting old and I wanted to have something more modern and robust (for example there is no way to enable extensions with twigfiddle) for some times. What you're doing could be the perfect entry point to that.

If you're interested, I'd publish this at GitHub and try to make an actual app from the prototype. I've got lots of ideas for building a real-time twig template editor, including some form of virtual file system for templates, partial support and plugins.
I've created a repo at Radiergummi/twigly, will add code once I've got time 😄

Also, keep in mind that you have everything needed if you need to analyse your template syntax early - before rendering.

I'll incorporate that, thanks!