UNPKG

5.52 kBJavaScriptView Raw
1'use strict';
2
3var fs = require('fs');
4var extend = require('extend-shallow');
5var parse = require('./lib/parse');
6var defaults = require('./lib/defaults');
7var stringify = require('./lib/stringify');
8var excerpt = require('./lib/excerpt');
9var engines = require('./lib/engines');
10var toFile = require('./lib/to-file');
11var utils = require('./lib/utils');
12var cache = {};
13
14/**
15 * Takes a string or object with `content` property, extracts
16 * and parses front-matter from the string, then returns an object
17 * with `data`, `content` and other [useful properties](#returned-object).
18 *
19 * ```js
20 * var matter = require('gray-matter');
21 * console.log(matter('---\ntitle: Home\n---\nOther stuff'));
22 * //=> { data: { title: 'Home'}, content: 'Other stuff' }
23 * ```
24 * @param {Object|String} `input` String, or object with `content` string
25 * @param {Object} `options`
26 * @return {Object}
27 * @api public
28 */
29
30function matter(input, options) {
31 var file = {data: {}, content: input, excerpt: '', orig: input};
32 if (input === '') return file;
33
34 file = toFile(input);
35 var cached = cache[file.content];
36
37 if (!options) {
38 if (cached) {
39 file = extend({}, cached);
40 file.orig = cached.orig;
41 return file;
42 }
43 cache[file.content] = file;
44 }
45
46 return parseMatter(file, options);
47}
48
49function parseMatter(file, options) {
50 var opts = defaults(options);
51 var open = opts.delimiters[0];
52 var close = '\n' + opts.delimiters[1];
53 var str = file.content;
54
55 if (opts.language) {
56 file.language = opts.language;
57 }
58
59 // get the length of the opening delimiter
60 var openLen = open.length;
61 if (!utils.startsWith(str, open, openLen)) {
62 excerpt(file, opts);
63 return file;
64 }
65
66 // if the next character after the opening delimiter is
67 // a character from the delimiter, then it's not a front-
68 // matter delimiter
69 if (str.charAt(openLen) === open.slice(-1)) {
70 return file;
71 }
72
73 // strip the opening delimiter
74 str = str.slice(openLen);
75 var len = str.length;
76
77 // use the language defined after first delimiter, if it exists
78 var language = matter.language(str, opts);
79 if (language.name) {
80 file.language = language.name;
81 str = str.slice(language.raw.length);
82 }
83
84 // get the index of the closing delimiter
85 var closeIndex = str.indexOf(close);
86 if (closeIndex === -1) {
87 closeIndex = len;
88 }
89
90 // get the raw front-matter block
91 file.matter = str.slice(0, closeIndex);
92
93 // create file.data by parsing the raw file.matter block
94 file.data = parse(file.language, file.matter, opts);
95
96 // update file.content
97 if (closeIndex === len) {
98 file.content = '';
99 } else {
100 file.content = str.slice(closeIndex + close.length);
101 if (file.content[0] === '\r') {
102 file.content = file.content.slice(1);
103 }
104 if (file.content[0] === '\n') {
105 file.content = file.content.slice(1);
106 }
107 }
108
109 excerpt(file, opts);
110 return file;
111}
112
113/**
114 * Expose engines
115 */
116
117matter.engines = engines;
118
119/**
120 * Stringify an object to YAML or the specified language, and
121 * append it to the given string. By default, only YAML and JSON
122 * can be stringified. See the [engines](#engines) section to learn
123 * how to stringify other languages.
124 *
125 * ```js
126 * console.log(matter.stringify('foo bar baz', {title: 'Home'}));
127 * // results in:
128 * // ---
129 * // title: Home
130 * // ---
131 * // foo bar baz
132 * ```
133 * @param {String|Object} `file` The content string to append to stringified front-matter, or a file object with `file.content` string.
134 * @param {Object} `data` Front matter to stringify.
135 * @param {Object} `options` [Options](#options) to pass to gray-matter and [js-yaml].
136 * @return {String} Returns a string created by wrapping stringified yaml with delimiters, and appending that to the given string.
137 * @api public
138 */
139
140matter.stringify = function(file, data, options) {
141 if (typeof file === 'string') {
142 file = matter(file, options);
143 }
144 return stringify(file, data, options);
145};
146
147/**
148 * Synchronously read a file from the file system and parse
149 * front matter. Returns the same object as the [main function](#matter).
150 *
151 * ```js
152 * var file = matter.read('./content/blog-post.md');
153 * ```
154 * @param {String} `filepath` file path of the file to read.
155 * @param {Object} `options` [Options](#options) to pass to gray-matter.
156 * @return {Object} Returns [an object](#returned-object) with `data` and `content`
157 * @api public
158 */
159
160matter.read = function(filepath, options) {
161 var str = fs.readFileSync(filepath, 'utf8');
162 var file = matter(str, options);
163 file.path = filepath;
164 return file;
165};
166
167/**
168 * Returns true if the given `string` has front matter.
169 * @param {String} `string`
170 * @param {Object} `options`
171 * @return {Boolean} True if front matter exists.
172 * @api public
173 */
174
175matter.test = function(str, options) {
176 var opts = defaults(options);
177 return utils.startsWith(str, opts.delimiters[0]);
178};
179
180/**
181 * Detect the language to use, if one is defined after the
182 * first front-matter delimiter.
183 * @param {String} `string`
184 * @param {Object} `options`
185 * @return {Object} Object with `raw` (actual language string), and `name`, the language with whitespace trimmed
186 */
187
188matter.language = function(str, options) {
189 var opts = defaults(options);
190 var open = opts.delimiters[0];
191
192 if (matter.test(str)) {
193 str = str.slice(open.length);
194 }
195
196 var language = str.slice(0, str.search(/\r?\n/));
197 return {
198 raw: language,
199 name: language ? language.trim() : ''
200 };
201};
202
203/**
204 * Expose `matter`
205 */
206
207module.exports = matter;