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

Add support for gray-matter sections #2502

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open

Add support for gray-matter sections #2502

wants to merge 1 commit into from

Conversation

higby
Copy link

@higby higby commented Jul 18, 2022

gray-matter Sections

Preamble

The gray-matter package includes multiple ways to read data from files, Eleventy has already implemented two methods:

Front Matter

Input:

---
title: Hi
---

This is content

Output:

{
  content: 'This is content',
  data: { 
    title: 'Hi'
  }
}
Excerpts

Input:

---
title: Hi
---

This is an excerpt
---

This is content

Output:

{ 
  content: 'This is an excerpt. This is content',
  data: { 
    title: 'Hi' 
  },
  excerpt: 'This is an excerpt.' 
}

gray-matter however includes one more method:

Sections

Input:

---
title: hi
---

This is content

---section
title: Section One
---

This is section one

Output:

{
  content: 'This is content',
  sections: [
    {
      key: 'section',
      data: { 
        title: 'First section' 
      }
      content: 'This is section one'
    }
  ]
}

Not only do I believe this feature should be added just on the merit of Eleventy missing one-third of it's front matter capability, but sections actually come with quite a few interesting quirks.

This feature of gray-matter is not documented entirely well on it's page or in it's options, which is probably due to the fact that it is actually from a different package called sections-matter (same author) that gets built into it.

So after digging through section-matter's docs, here is what I put together.

Quirks

1) When files are processed, the entirety of the section gets removed from the content

(This can be seen in the code example above)

This would facilitate a great many creative uses, the first one that comes to my mind being a solution to this issue: #685

Use Case

index.md:

---
title: Hi
layout: layout.njk
---

This is content

---css
---
body {
  background-color: blue;
}

layout.njk:

<head>
  <style>
    {{ page.section | safe }}
  </style>
</head>

<body>
  <h1>{{ title }}</h1>
  {{ content | safe }}
</body>

The {{ page.section }} portion has been reduced for practicality. In practice it would need to be a for if loop. See: Caveat #2

index.html (result):

<head>
  <style>
    body {
      background-color: blue;
    }
  </style>
</head>

<body>
  <h1>Hi</h1>
  <p>This is content<p>
</body>

2) section-matter can run functions on the collected data

That heading probably doesn't explain it too well so here's a very simple example:

If we were to add the following to our .eleventy.js:

eleventyConfig.setFrontMatterParsingOptions({
    sections: true,
    section: {
      parse: function (section) {
        console.log('hi');
      },
    },
  });

Then the added function would write hi to our console for each section that section-matter reads.

This lends itself to a lot of potential cool uses:

Use Case

Seeing how front matter isn't rendered into eleventy, we can render the section with the function ourselves:

index.md:

---
title: Hi
layout: layout.njk
---

This is content

---list
render: md
---
- This is a list
- A list
  - A sublist

By default this section when added back in via template wouldn't be rendered in markdown. But if we added this very rough snippet to our config:

const md = require("markdown-it")();
const yaml = require("js-yaml");

config.setFrontMatterParsingOptions({
  sections: true,
  section: {
    parse: function (section) {
      section.data = yaml.load(section.data);
      if (section.data) {
        if (section.data.render == "md") {
          section.content = md.render(section.content);
        }
      }
    },
  },
});

redundant code hidden

Then the output section content would indeed be rendered as an html list.

Quick Notes

  • The delimiter can be changed
  • It does work with excerpts
  • I was unable to write the code so that an empty sections wasn't passed to {{ page }} the way that excerpts don't
  • I did all of the testing locally, but I did not write any tests for the repository as I am not familiar with the testing suite used
  • The PR is relatively small as gray-matter will already read and remove sections from files if sections: true is passed in the config, the only change I made was sending the data to the template

Caveats

  • All sections require a key following it's opening delimiter
  • The way the data is output makes using it slightly inconvenient, as the key, data, and content objects are all on the same level within the data output:
sections: [
    {
      key: 'section',
      data: { 
        title: 'First section' 
      }
      content: 'This is section one'
    }
  ]
  • All sections have to come after the page content
  • The key for inputting options is slightly funky, needing you to enable sections, then write the options to the key section
module.exports = (config) => {
  config.setFrontMatterParsingOptions({
    sections: true,
    section: {
      section_delimiter: ":::",
    },
  });
  • You will need to apply the safe filter for the output sections (at least for nunjucks)
  • In the section-matter docs it states that the callback function option is section_parse when it is acutally just parse Documentation outdated jonschlinkert/section-matter#1
  • By default anything in the data key will be stored as a string instead of an object, this can be fixed using the callback function to turn it into an object before it's used:
section.data = yaml.load(section.data);

@boehs
Copy link

boehs commented Jul 29, 2022

This is really cool!

@ribomation
Copy link

Gray-matter sections would nicely solve a problem I currently address with some ugly hacks and recursive layouts. Please, merge this into 11ty Canary.

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

Successfully merging this pull request may close these issues.

4 participants