Website/node_modules/gray-matter/index.js
2015-12-02 18:21:44 -05:00

212 lines
4.7 KiB
JavaScript

'use strict';
var fs = require('fs');
var extend = require('extend-shallow');
var parsers = require('./lib/parsers');
/**
* Expose `matter()`
*/
module.exports = matter;
/**
* Parses a `string` of front-matter with the given `options`,
* and returns an object.
*
* ```js
* matter('---\ntitle: foo\n---\nbar');
* //=> {data: {title: 'foo'}, content: 'bar', orig: '---\ntitle: foo\n---\nbar'}
* ```
*
* @param {String} `string` The string to parse.
* @param {Object} `options`
* @option {Array} [options] `delims` Custom delimiters formatted as an array. The default is `['---', '---']`.
* @option {Function} [options] `parser` Parser function to use. [js-yaml] is the default.
* @return {Object} Valid JSON
* @api public
*/
function matter(str, options) {
if (typeof str !== 'string') {
throw new Error('gray-matter expects a string');
}
// default results to build up
var res = {orig: str, data: {}, content: str};
if (str === '') {
return res;
}
// delimiters
var delims = arrayify((options && options.delims) || '---');
var a = delims[0];
// strip byte order marks
str = stripBom(str);
// if the first delim isn't the first thing, return
if (!isFirst(str, a)) {
return res;
}
var b = '\n' + (delims[1] || delims[0]);
var alen = a.length;
// if the next character after the first delim
// is a character in the first delim, then just
// return the default object. it's either a bad
// delim or not a delimiter at all.
if (a.indexOf(str.charAt(alen + 1)) !== -1) {
return res;
}
var len = str.length;
// find the index of the next delimiter before
// going any further. If not found, return.
var end = str.indexOf(b, alen + 1);
if (end === -1) {
end = len;
}
// detect a language, if defined
var lang = str.slice(alen, str.indexOf('\n'));
// measure the lang before trimming whitespace
var start = alen + lang.length;
var opts = options || {};
opts.lang = opts.lang || 'yaml';
lang = (lang && lang.trim()) || opts.lang;
// get the front matter (data) string
var data = str.slice(start, end).trim();
if (data) {
// if data exists, see if we have a matching parser
var fn = opts.parser || parsers[lang];
if (typeof fn === 'function') {
// run the parser on the data string
res.data = fn(data, opts);
} else {
throw new Error('gray-matter cannot find a parser for: ' + str);
}
}
// grab the content from the string, stripping
// an optional new line after the second delim
var con = str.substr(end + b.length);
if (con.charAt(0) === '\n') {
con = con.substr(1);
}
res.content = con;
return res;
}
/**
* Expose `parsers`
*
* @type {Object}
*/
matter.parsers = parsers;
/**
* Requires cache
*/
var YAML = matter.parsers.requires.yaml || (matter.parsers.requires.yaml = require('js-yaml'));
/**
* Read a file and parse front matter. Returns the same object
* as `matter()`.
*
* ```js
* matter.read('home.md');
* ```
*
* @param {String} `fp` file path of the file to read.
* @param {Object} `options` Options to pass to gray-matter.
* @return {Object}
* @api public
*/
matter.read = function(fp, options) {
var str = fs.readFileSync(fp, 'utf8');
var obj = matter(str, options);
return extend(obj, {
path: fp
});
};
/**
* Stringify an object to front-matter-formatted YAML, and
* concatenate it to the given string.
*
* ```js
* matter.stringify('foo bar baz', {title: 'Home'});
* ```
* Results in:
*
* ```yaml
* ---
* title: Home
* ---
* foo bar baz
* ```
*
* @param {String} `str` The content string to append to stringified front-matter.
* @param {Object} `data` Front matter to stringify.
* @param {Object} `options` Options to pass to js-yaml
* @return {String}
* @api public
*/
matter.stringify = function(str, data, options) {
var delims = arrayify(options && options.delims || '---');
var res = '';
res += delims[0] + '\n';
res += YAML.safeDump(data, options);
res += (delims[1] || delims[0]) + '\n';
res += str + '\n';
return res;
};
/**
* Return true if the given `string` has front matter.
*
* @param {String} `string`
* @param {Object} `options`
* @return {Boolean} True if front matter exists.
*/
matter.test = function(str, options) {
var delims = arrayify(options && options.delims || '---');
return isFirst(str, delims[0]);
};
/**
* Return true if the given `ch` the first
* thing in the string.
*/
function isFirst(str, ch) {
return str.substr(0, ch.length) === ch;
}
/**
* Utility to strip byte order marks
*/
function stripBom(str) {
return str.charAt(0) === '\uFEFF' ? str.slice(1) : str;
}
/**
* Typecast `val` to an array.
*/
function arrayify(val) {
return !Array.isArray(val) ? [val] : val;
}