Skip to content
New issue

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

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

Already on GitHub? # 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.

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

Successfully merging this pull request may close these issues.

4 participants