1 | ;
|
2 |
|
3 | var fs = require('fs');
|
4 | var extend = require('extend-shallow');
|
5 | var parse = require('./lib/parse');
|
6 | var defaults = require('./lib/defaults');
|
7 | var stringify = require('./lib/stringify');
|
8 | var excerpt = require('./lib/excerpt');
|
9 | var engines = require('./lib/engines');
|
10 | var toFile = require('./lib/to-file');
|
11 | var utils = require('./lib/utils');
|
12 | var 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 |
|
30 | function 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 |
|
49 | function 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 |
|
117 | matter.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 |
|
140 | matter.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 |
|
160 | matter.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 |
|
175 | matter.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 |
|
188 | matter.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 |
|
207 | module.exports = matter;
|