11ty / eleventy

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

_data/eleventyComputed.js seems to prevent a full data-merge from templates #2236

Open andystevenson opened 2 years ago

andystevenson commented 2 years ago

I implemented a _data/eleventyComputed that does the following.

module.exports = {
  layout: 'base.njk',
  title: 'hello world',
}

base.njk has the following frontmatter

---
title: 'all about the base'
allAboutTheBase: 'all about the base'
---

index.njk has the following content

---
title: 'index title'
whyNotMe: 'why not me'
---
<p>{{whyNotMe}}</p>
<p>{{allAboutTheBase}}</p>
<h1>{{title}}</h1>

This renders

<p>why not me</p>
<p></p>
<h1>hello world</h1>

Expected base.njk frontmatter to make it through the data cascade/merge Expected index.njk frontmatter title to make it through the data cascade/merge

Mac Book Pro macOS Monterey 12.2.1 @11ty/eleventy 1.0.0

Please see https://github.com/andystevenson/11ty-eleventyComputed-issue for a 'hello world' example

pdehaan commented 2 years ago

Hhmm. I would have expected that to work, per https://www.11ty.dev/docs/layouts/#front-matter-data-in-layouts&hellip; but I've always found global _data/eleventyComputed.js files a bit unpredictable (see #2058, for example).

I guess the good news is that this seems consistent w/ 0.12.1 and 1.0.0. The bad news is, I don't see the expected results in either version:

KEY VIA EXPECTED 0.12.1 1.0.0
{{ whyNotMe }} local "why not me" "why not me" "why not me"
{{ allAboutTheBase }} layout "all about the base" "" ""
{{ title }} local "index title" "hello world" "hello world"
{{ layout }} eleventyComputed "base.njk" "base.njk" "base.njk"

Interestingly (or not, you decide), if I get rid of the _data/eleventyComputed.js file, I get the expected page title of "index title" (at least in 1.0.0, didn't check in 0.12.1 yet). Similarly, if I change the eleventyComputed.js file to use functions, it sets the expected title (although I'm still not seeing a value for allAboutTheBase from the layout file):

// _data/eleventyComputed.js
module.exports = {
  layout(data) {
    return data.layout || 'base.njk';
  },
  title(data) {
    return data.title || 'hello world';
  }
};

Which I guess makes sense. It seems like the _data/eleventyComputed.js was simply overriding the values in the original version and not using the template-specific values for title. But when we switch to the function syntax, we can return an existing title [if it exists] and fall back to a default if it doesn't.

andystevenson commented 2 years ago

Thanks Peter that's helpful.

I tweaked _data/eleventyComputed.js as you suggested also adding in a function for allAboutTheBase.

module.exports = {
  layout: (data) => data.layout || 'base.njk',
  title: (data) => data.title || 'hello world',
  allAboutTheBase: (data) => data.allAboutTheBase || 'what no base!',
}

It still didn't pickup the layout frontmatter from base.njk and rendered.

<p>why not me</p>
<p>what no base!</p>
<h1>index title</h1>

So it appears that for the Data Cascade if you have a global _data/eleventyComputed.js then it completely misses out 'Front Matter Data in Layouts' and will only catch 'Front Matter Data in a Template' if eleventyComputed.js writes the function as above (checking for data-in-a-template) to override the global eleventyComputed.js invocation?

Computed Data Front Matter Data in a Template Front Matter Data in Layouts (only in 0.x) Template Data Files Directory Data Files (and ascending Parent Directories) Front Matter Data in Layouts (moved in 1.0) Configuration API Global Data Global Data Files

Not sure if there is a simple fix... my only thought was that clearly _data/eleventyComputed.js is going to get invoked multiple times in the cascade. So maybe if the function signature changed to something like eleventyComputed(data, scope = 'global') then at least the function might intelligently work out what to do?

e.g.

...

eleventyComputed(data, 'global')
eleventyComputed(data, 'layout')
eleventyComputed(data, 'template')
eleventyComputed(data, 'computed')

I tweaked the simple repository with the above eleventyComputed.js https://github.com/andystevenson/11ty-eleventyComputed-issue

pdehaan commented 2 years ago

@andystevenson Yeah, I feel like there might be a bug here. Although it's more interesting the more I stare at it.

Even if I delete the _data/eleventyComputed.js file (and either move the layout into the index.njk file, or create a super fun _data/layout.json file with the literal string "base.njk" [because that's totally valid JSON and it works]… but it still doesn't find an allAboutTheBase, even though it's defined in the layout file itself.

---
# /src/_includes/base.njk
title: 'all about the base (layout)'
allAboutTheBase: 'all about the base (layout)'
---
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{{title}}</title>
  </head>
  <body>
    <p>DEBUG.allAboutTheBase={{ allAboutTheBase }}</p>
    {{ content | safe}}
  </body>
</html>
---
# /src/index.njk
title: 'index title (template)'
whyNotMe: 'why not me (template)'
---
<p>{{ whyNotMe }}</p>
<p>{{ allAboutTheBase }}</p>
<h1>{{ title }}</h1>

OUTPUT

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>index title (template)</title>
  </head>
  <body>
    <p>DEBUG.allAboutTheBase=</p>
    <p>why not me (template)</p>
    <p></p>
    <h1>index title (template)</h1>
  </body>
</html>

I'm confused as to why <p>DEBUG.allAboutTheBase=</p> is an empty string when it's defined directly in that layout file's front matter. Although I think it shows that it may not be related to _data/eleventyComputed.js? 🤷

andystevenson commented 2 years ago

@pdehaan thanks... one for @zachleat probably.