| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226 |
- 'use strict';
- const fs = require('fs');
- const sections = require('section-matter');
- const defaults = require('./lib/defaults');
- const stringify = require('./lib/stringify');
- const excerpt = require('./lib/excerpt');
- const engines = require('./lib/engines');
- const toFile = require('./lib/to-file');
- const parse = require('./lib/parse');
- const utils = require('./lib/utils');
- /**
- * 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).
- *
- * ```js
- * const matter = require('gray-matter');
- * console.log(matter('---\ntitle: Home\n---\nOther stuff'));
- * //=> { data: { title: 'Home'}, content: 'Other stuff' }
- * ```
- * @param {Object|String} `input` String, or object with `content` string
- * @param {Object} `options`
- * @return {Object}
- * @api public
- */
- function matter(input, options) {
- if (input === '') {
- return { data: {}, content: input, excerpt: '', orig: input };
- }
- let file = toFile(input);
- const cached = matter.cache[file.content];
- if (!options) {
- if (cached) {
- file = Object.assign({}, cached);
- file.orig = cached.orig;
- return file;
- }
- // only cache if there are no options passed. if we cache when options
- // are passed, we would need to also cache options values, which would
- // negate any performance benefits of caching
- matter.cache[file.content] = file;
- }
- return parseMatter(file, options);
- }
- /**
- * Parse front matter
- */
- function parseMatter(file, options) {
- const opts = defaults(options);
- const open = opts.delimiters[0];
- const close = '\n' + opts.delimiters[1];
- let str = file.content;
- if (opts.language) {
- file.language = opts.language;
- }
- // get the length of the opening delimiter
- const openLen = open.length;
- if (!utils.startsWith(str, open, openLen)) {
- excerpt(file, opts);
- return file;
- }
- // if the next character after the opening delimiter is
- // a character from the delimiter, then it's not a front-
- // matter delimiter
- if (str.charAt(openLen) === open.slice(-1)) {
- return file;
- }
- // strip the opening delimiter
- str = str.slice(openLen);
- const len = str.length;
- // use the language defined after first delimiter, if it exists
- const language = matter.language(str, opts);
- if (language.name) {
- file.language = language.name;
- str = str.slice(language.raw.length);
- }
- // get the index of the closing delimiter
- let closeIndex = str.indexOf(close);
- if (closeIndex === -1) {
- closeIndex = len;
- }
- // get the raw front-matter block
- file.matter = str.slice(0, closeIndex);
- const block = file.matter.replace(/^\s*#[^\n]+/gm, '').trim();
- if (block === '') {
- file.isEmpty = true;
- file.empty = file.content;
- file.data = {};
- } else {
- // create file.data by parsing the raw file.matter block
- file.data = parse(file.language, file.matter, opts);
- }
- // update file.content
- if (closeIndex === len) {
- file.content = '';
- } else {
- file.content = str.slice(closeIndex + close.length);
- if (file.content[0] === '\r') {
- file.content = file.content.slice(1);
- }
- if (file.content[0] === '\n') {
- file.content = file.content.slice(1);
- }
- }
- excerpt(file, opts);
- if (opts.sections === true || typeof opts.section === 'function') {
- sections(file, opts.section);
- }
- return file;
- }
- /**
- * Expose engines
- */
- matter.engines = engines;
- /**
- * 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.
- *
- * ```js
- * console.log(matter.stringify('foo bar baz', {title: 'Home'}));
- * // results in:
- * // ---
- * // title: Home
- * // ---
- * // foo bar baz
- * ```
- * @param {String|Object} `file` The content string to append to stringified front-matter, or a file object with `file.content` string.
- * @param {Object} `data` Front matter to stringify.
- * @param {Object} `options` [Options](#options) to pass to gray-matter and [js-yaml].
- * @return {String} Returns a string created by wrapping stringified yaml with delimiters, and appending that to the given string.
- * @api public
- */
- matter.stringify = function(file, data, options) {
- if (typeof file === 'string') file = matter(file, options);
- return stringify(file, data, options);
- };
- /**
- * Synchronously read a file from the file system and parse
- * front matter. Returns the same object as the [main function](#matter).
- *
- * ```js
- * const file = matter.read('./content/blog-post.md');
- * ```
- * @param {String} `filepath` file path of the file to read.
- * @param {Object} `options` [Options](#options) to pass to gray-matter.
- * @return {Object} Returns [an object](#returned-object) with `data` and `content`
- * @api public
- */
- matter.read = function(filepath, options) {
- const str = fs.readFileSync(filepath, 'utf8');
- const file = matter(str, options);
- file.path = filepath;
- return file;
- };
- /**
- * Returns true if the given `string` has front matter.
- * @param {String} `string`
- * @param {Object} `options`
- * @return {Boolean} True if front matter exists.
- * @api public
- */
- matter.test = function(str, options) {
- return utils.startsWith(str, defaults(options).delimiters[0]);
- };
- /**
- * Detect the language to use, if one is defined after the
- * first front-matter delimiter.
- * @param {String} `string`
- * @param {Object} `options`
- * @return {Object} Object with `raw` (actual language string), and `name`, the language with whitespace trimmed
- */
- matter.language = function(str, options) {
- const opts = defaults(options);
- const open = opts.delimiters[0];
- if (matter.test(str)) {
- str = str.slice(open.length);
- }
- const language = str.slice(0, str.search(/\r?\n/));
- return {
- raw: language,
- name: language ? language.trim() : ''
- };
- };
- /**
- * Expose `matter`
- */
- matter.cache = {};
- matter.clearCache = () => (matter.cache = {});
- module.exports = matter;
|