@11ty/gray-matter
Version:
Parse front-matter from a string or file. Fast, reliable and easy to use. Parses YAML front matter by default, but also has support for YAML, JSON, TOML Front-Matter, with options to set custom delimiters. Used by metalsmith, assemble, verb and many other
552 lines (390 loc) • 14.7 kB
Markdown
# @11ty/gray-matter
Reduced and maintained fork of [`gray-matter` originally by Jon Schlinkert](https://www.npmjs.com/package/gray-matter).
Current differences:
- [Upstream package last released in 2019](https://github.com/jonschlinkert/gray-matter/releases/tag/4.0.2).
- Upgrades `js-yaml` major version to v4
- Uses Uint8Array internally instead of Node-env specific Buffer for more runtime compatibility.
- Removes `javascript` front matter type (removing use of `eval`)
> Parse front-matter from a string or file. Fast, reliable and easy to use. Parses YAML front matter by default, but also has support for YAML, JSON, or TOML Front-Matter, with options to set custom delimiters.
## Install
Install with [npm](https://www.npmjs.com/):
```sh
$ npm install --save @11ty/gray-matter
```
## Heads up!
Please see the [changelog](CHANGELOG.md) to learn about breaking changes that were made in v3.0.
<br />
## What does this do?
<details>
<summary><strong>Run this example</strong></summary>
Add the HTML in the following example to `example.html`, then add the following code to `example.js` and run `$ node example` (without the `$`):
```js
const fs = require('fs');
const matter = require('@11ty/gray-matter');
const str = fs.readFileSync('example.html', 'utf8');
console.log(matter(str));
```
</details>
Converts a string with front-matter, like this:
```handlebars
---
title: Hello
slug: home
---
<h1>Hello world!</h1>
```
Into an object like this:
```js
{
content: '<h1>Hello world!</h1>',
data: {
title: 'Hello',
slug: 'home'
}
}
```
## Why use gray-matter?
* **simple**: main function takes a string and returns an object
* **accurate**: better at catching and handling edge cases than front-matter parsers that rely on regex for parsing
* **fast**: faster than other front-matter parsers that use regex for parsing
* **flexible**: By default, gray-matter is capable of parsing [YAML](https://github.com/nodeca/js-yaml), [JSON](http://en.wikipedia.org/wiki/Json) and JavaScript front-matter. But other [engines](#optionsengines) may be added.
* **extensible**: Use [custom delimiters](#optionsdelimiters), or add support for [any language](#optionsengines), like [TOML](http://github.com/mojombo/toml).
* **battle-tested**: used by [assemble](https://github.com/assemble/assemble), [metalsmith](https://github.com/segmentio/metalsmith), [phenomic](https://github.com/phenomic/phenomic), [verb](https://github.com/assemble/verb), [generate](https://github.com/generate/generate), [update](https://github.com/update/update) and many others.
<details>
<summary><strong>Rationale</strong></summary>
**Why did we create gray-matter in the first place?**
We created gray-matter after trying out other libraries that failed to meet our standards and requirements.
Some libraries met most of the requirements, but _none met all of them_.
**Here are the most important**:
* Be usable, if not simple
* Use a dependable and well-supported library for parsing YAML
* Support other languages besides YAML
* Support stringifying back to YAML or another language
* Don't fail when no content exists
* Don't fail when no front matter exists
* Don't use regex for parsing. This is a relatively simple parsing operation, and regex is the slowest and most error-prone way to do it.
* Have no problem reading YAML files directly
* Have no problem with complex content, including **non-front-matter** fenced code blocks that contain examples of YAML front matter. Other parsers fail on this.
* Support stringifying back to front-matter. This is useful for linting, updating properties, etc.
* Allow custom delimiters, when it's necessary for avoiding delimiter collision.
* Should return an object with at least these three properties:
- `data`: the parsed YAML front matter, as a JSON object
- `content`: the contents as a string, without the front matter
- `orig`: the "original" content (for debugging)
</details>
## Usage
Using CommonJS:
```js
const matter = require('@11ty/gray-matter');
```
Or ESM:
```js
import matter = require('@11ty/gray-matter');
// OR
import * as matter from '@11ty/gray-matter';
```
Pass a string and [options](#options) to gray-matter:
```js
console.log(matter('---\ntitle: Front Matter\n---\nThis is content.'));
```
Returns:
```js
{
content: '\nThis is content.',
data: {
title: 'Front Matter'
}
}
```
More about the returned object in the following section.
***
## Returned object
gray-matter returns a `file` object with the following properties.
**Enumerable**
* `file.data` **{Object}**: the object created by parsing front-matter
* `file.content` **{String}**: the input string, with `matter` stripped
* `file.excerpt` **{String}**: an excerpt, if [defined on the options](#optionsexcerpt)
* `file.empty` **{String}**: when the front-matter is "empty" (either all whitespace, nothing at all, or just comments and no data), the original string is set on this property. See [#65](https://github.com/jonschlinkert/gray-matter/issues/65) for details regarding use case.
* `file.isEmpty` **{Boolean}**: true if front-matter is empty.
**Non-enumerable**
In addition, the following non-enumberable properties are added to the object to help with debugging.
* `file.orig` **{Uint8Array}**: the original input string (or Uint8Array)
* `file.language` **{String}**: the front-matter language that was parsed. `yaml` is the default
* `file.matter` **{String}**: the _raw_, un-parsed front-matter string
* `file.stringify` **{Function}**: [stringify](#stringify) the file by converting `file.data` to a string in the given language, wrapping it in delimiters and prepending it to `file.content`.
## Run the examples
If you'd like to test-drive the examples, first clone gray-matter into `my-project` (or wherever you want):
```sh
$ git clone https://github.com/jonschlinkert/gray-matter my-project
```
CD into `my-project` and install dependencies:
```sh
$ cd my-project && npm install
```
Then run any of the [examples](./examples) to see how gray-matter works:
```sh
$ node examples/<example_name>
```
**Links to examples**
* [excerpt-separator](examples/excerpt-separator.js)
* [excerpt-stringify](examples/excerpt-stringify.js)
* [excerpt](examples/excerpt.js)
* [javascript](examples/javascript.js)
* [json-stringify](examples/json-stringify.js)
* [json](examples/json.js)
* [restore-empty](examples/restore-empty.js)
* [sections-excerpt](examples/sections-excerpt.js)
* [sections](examples/sections.js)
* [toml](examples/toml.js)
* [yaml-stringify](examples/yaml-stringify.js)
* [yaml](examples/yaml.js)
## API
### [matter](index.js#L29)
Takes a string or object with `content` property, extracts and parses front-matter from the string, then returns an object with `data`, `content` and other [useful properties](#returned-object).
**Params**
* `input` **{Object|String}**: String, or object with `content` string
* `options` **{Object}**
* `returns` **{Object}**
**Example**
```js
const matter = require('@11ty/gray-matter');
console.log(matter('---\ntitle: Home\n---\nOther stuff'));
//=> { data: { title: 'Home'}, content: 'Other stuff' }
```
### [.stringify](index.js#L160)
Stringify an object to YAML or the specified language, and append it to the given string. By default, only YAML and JSON can be stringified. See the [engines](#engines) section to learn how to stringify other languages.
**Params**
* `file` **{String|Object}**: The content string to append to stringified front-matter, or a file object with `file.content` string.
* `data` **{Object}**: Front matter to stringify.
* `options` **{Object}**: [Options](#options) to pass to gray-matter and [js-yaml](https://github.com/nodeca/js-yaml).
* `returns` **{String}**: Returns a string created by wrapping stringified yaml with delimiters, and appending that to the given string.
**Example**
```js
console.log(matter.stringify('foo bar baz', {title: 'Home'}));
// results in:
// ---
// title: Home
// ---
// foo bar baz
```
### [.read](index.js#L178)
Synchronously read a file from the file system and parse front matter. Returns the same object as the [main function](#matter).
**Params**
* `filepath` **{String}**: file path of the file to read.
* `options` **{Object}**: [Options](#options) to pass to gray-matter.
* `returns` **{Object}**: Returns [an object](#returned-object) with `data` and `content`
**Example**
```js
const file = matter.read('./content/blog-post.md');
```
### [.test](index.js#L193)
Returns true if the given `string` has front matter.
**Params**
* `string` **{String}**
* `options` **{Object}**
* `returns` **{Boolean}**: True if front matter exists.
## Options
### options.excerpt
**Type**: `Boolean|Function`
**Default**: `undefined`
Extract an excerpt that directly follows front-matter, or is the first thing in the string if no front-matter exists.
If set to `excerpt: true`, it will look for the frontmatter delimiter, `---` by default and grab everything leading up to it.
**Example**
```js
const str = '---\nfoo: bar\n---\nThis is an excerpt.\n---\nThis is content';
const file = matter(str, { excerpt: true });
```
Results in:
```js
{
content: 'This is an excerpt.\n---\nThis is content',
data: { foo: 'bar' },
excerpt: 'This is an excerpt.\n'
}
```
You can also set `excerpt` to a function. This function uses the 'file' and 'options' that were initially passed to gray-matter as parameters, so you can control how the excerpt is extracted from the content.
**Example**
```js
// returns the first 4 lines of the contents
function firstFourLines(file, options) {
file.excerpt = file.content.split('\n').slice(0, 4).join(' ');
}
const file = matter([
'---',
'foo: bar',
'---',
'Only this',
'will be',
'in the',
'excerpt',
'but not this...'
].join('\n'), {excerpt: firstFourLines});
```
Results in:
```js
{
content: 'Only this\nwill be\nin the\nexcerpt\nbut not this...',
data: { foo: 'bar' },
excerpt: 'Only this will be in the excerpt'
}
```
### options.excerpt_separator
**Type**: `String`
**Default**: `undefined`
Define a custom separator to use for excerpts.
```js
console.log(matter(string, {excerpt_separator: '<!-- end -->'}));
```
**Example**
The following HTML string:
```html
---
title: Blog
---
My awesome blog.
<!-- end -->
<h1>Hello world</h1>
```
Results in:
```js
{
data: { title: 'Blog'},
excerpt: 'My awesome blog.',
content: 'My awesome blog.\n<!-- end -->\n<h1>Hello world</h1>'
}
```
### options.engines
Define custom engines for parsing and/or stringifying front-matter.
**Type**: `Object` Object of engines
**Default**: `JSON`, `YAML` and `JavaScript` are already handled by default.
**Engine format**
Engines may either be an object with `parse` and (optionally) `stringify` methods, or a function that will be used for parsing only.
**Examples**
```js
const toml = require('toml');
/**
* defined as a function
*/
const file = matter(str, {
engines: {
toml: toml.parse.bind(toml),
}
});
/**
* Or as an object
*/
const file = matter(str, {
engines: {
toml: {
parse: toml.parse.bind(toml),
// example of throwing an error to let users know stringifying is
// not supported (a TOML stringifier might exist, this is just an example)
stringify: function() {
throw new Error('cannot stringify to TOML');
}
}
}
});
console.log(file);
```
### options.language
**Type**: `String`
**Default**: `yaml`
Define the engine to use for parsing front-matter.
```js
console.log(matter(string, {language: 'toml'}));
```
**Example**
The following HTML string:
```html
---
title = "TOML"
description = "Front matter"
categories = "front matter toml"
---
This is content
```
Results in:
```js
{ content: 'This is content',
excerpt: '',
data:
{ title: 'TOML',
description: 'Front matter',
categories: 'front matter toml' } }
```
**Dynamic language detection**
Instead of defining the language on the options, gray-matter will automatically detect the language defined after the first delimiter and select the correct engine to use for parsing.
```html
---toml
title = "TOML"
description = "Front matter"
categories = "front matter toml"
---
This is content
```
### options.delimiters
**Type**: `String`
**Default**: `---`
Open and close delimiters can be passed in as an array of strings.
**Example:**
```js
// format delims as a string
matter.read('file.md', {delims: '~~~'});
// or an array (open/close)
matter.read('file.md', {delims: ['~~~', '~~~']});
```
would parse:
```html
~~~
title: Home
~~~
This is the {{title}} page.
```
## Deprecated options
### options.lang
Decrecated, please use [options.language](#optionslanguage) instead.
### options.delims
Decrecated, please use [options.delimiters](#optionsdelimiters) instead.
### options.parsers
Decrecated, please use [options.engines](#optionsengines) instead.
## About
<details>
<summary><strong>Contributing</strong></summary>
Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new).
</details>
<details>
<summary><strong>Running Tests</strong></summary>
Running and reviewing unit tests is a great way to get familiarized with a library and its API. You can install dependencies and run tests with the following command:
```sh
$ npm install && npm test
```
</details>
### Contributors
| **Commits** | **Contributor** |
| --- | --- |
| 179 | [jonschlinkert](https://github.com/jonschlinkert) |
| 13 | [robertmassaioli](https://github.com/robertmassaioli) |
| 7 | [RobLoach](https://github.com/RobLoach) |
| 5 | [doowb](https://github.com/doowb) |
| 5 | [heymind](https://github.com/heymind) |
| 3 | [aljopro](https://github.com/aljopro) |
| 3 | [shawnbot](https://github.com/shawnbot) |
| 2 | [reccanti](https://github.com/reccanti) |
| 2 | [onokumus](https://github.com/onokumus) |
| 2 | [moozzyk](https://github.com/moozzyk) |
| 2 | [ajaymathur](https://github.com/ajaymathur) |
| 1 | [Ajedi32](https://github.com/Ajedi32) |
| 1 | [arlair](https://github.com/arlair) |
| 1 | [caesar](https://github.com/caesar) |
| 1 | [ianstormtaylor](https://github.com/ianstormtaylor) |
| 1 | [qm3ster](https://github.com/qm3ster) |
| 1 | [zachwhaley](https://github.com/zachwhaley) |
### Author
**Jon Schlinkert**
* [GitHub Profile](https://github.com/jonschlinkert)
* [Twitter Profile](https://twitter.com/jonschlinkert)
* [LinkedIn Profile](https://linkedin.com/in/jonschlinkert)
### License
Copyright © 2023, [Jon Schlinkert](https://github.com/jonschlinkert).
Released under the [MIT License](LICENSE).