11ty / eleventy

A simpler site generator. Transforms a directory of templates (of varying types) into HTML.
https://www.11ty.dev/
MIT License
17.32k stars 495 forks source link

JS class templates do not compile with arrow functions in data() #1645

Open yklcs opened 3 years ago

yklcs commented 3 years ago

Describe the bug JS class templates do not compile with arrow functions in data().

> Having trouble rendering 11ty.js template ./src/pages/index.11ty.js

`TemplateContentRenderError` was thrown
> Class constructor Index cannot be invoked without 'new'

`TypeError` was thrown:
    TypeError: Class constructor Index cannot be invoked without 'new'
        at JavaScript.<anonymous> (/Users/lucas/devel/luc.li/node_modules/@11ty/eleventy/src/Engines/JavaScript.js:129:43)
        at Template.render (/Users/lucas/devel/luc.li/node_modules/@11ty/eleventy/src/TemplateContent.js:199:28)

To Reproduce Render a JS page using an arrow function for data():

class Index {
  data = () => ({
    title: `Hello world!`,
    permalink: `/`
  })

  render = (data) => `
    wow ${data.title}
  `
}

module.exports = Index

Expected behavior Should compile. Arrow functions are not equivalent to normal function declarations, but the user is still making a perfectly valid class+method declaration and it is not obvious that the code does not compile.

Environment:

pdehaan commented 3 years ago

I think it works w/ anonymous arrow functions, but only if you export it as new Index():

class Index {
  data = () => ({
    title: `Hello world!`,
    permalink: `/`,
  });

  render = (data) => `wow ${data.title}`;
}

module.exports = new Index();

https://medium.com/@charpeni/arrow-functions-in-class-properties-might-not-be-as-great-as-we-think-3b3551c440b1 had an interesting point in the "Mockability" section:

Oops, since we used an arrow function in a class property our function handleClick is only defined on the initialization by the constructor and not in the prototype. So, even if we mock our function in the instantiated object, the changes won’t be seen by other objects through prototype chaining.


Not sure what benefits there are to using arrow functions on properties, versus defining data and render as methods:

class About {
  data() {
    return {
      title: `About world!`,
      permalink: `/about/`,
    };
  }

  render(data) {
    return `wow ${data.title}`;
  }
}

module.exports = About;