mikel-frontmatter 0.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +381 -0
- package/index.d.ts +46 -0
- package/index.js +165 -0
- package/package.json +31 -0
package/README.md
ADDED
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
# mikel-frontmatter
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+

|
|
5
|
+
|
|
6
|
+
A [mikel](https://github.com/jmjuanes/mikel) plugin to define new data inside templates using a frontmatter-like syntax. Supports both **YAML** and **JSON** formats.
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
You can install Mikel via npm or yarn:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
## Install using npm
|
|
14
|
+
$ npm install mikel mikel-frontmatter
|
|
15
|
+
|
|
16
|
+
## Install using yarn
|
|
17
|
+
$ yarn add mikel mikel-frontmatter
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
This plugin provides a `{{#frontmatter}}` helper that allows you to define metadata at the beginning of your templates, similar to frontmatter in Markdown files. The parsed data is stored as a variable accessible via `@frontmatter` (or a custom variable name).
|
|
23
|
+
|
|
24
|
+
The plugin automatically detects the format:
|
|
25
|
+
- **YAML format**: When the content doesn't start with `{`
|
|
26
|
+
- **JSON format**: When the content starts with `{` and ends with `}`
|
|
27
|
+
|
|
28
|
+
### YAML Example
|
|
29
|
+
|
|
30
|
+
```javascript
|
|
31
|
+
import mikel from "mikel";
|
|
32
|
+
import mikelFrontmatter from "mikel-frontmatter";
|
|
33
|
+
|
|
34
|
+
const template = `
|
|
35
|
+
{{#frontmatter}}
|
|
36
|
+
title: My Page Title
|
|
37
|
+
author: John Doe
|
|
38
|
+
date: 2026-02-03
|
|
39
|
+
tags:
|
|
40
|
+
- javascript
|
|
41
|
+
- templating
|
|
42
|
+
settings:
|
|
43
|
+
published: true
|
|
44
|
+
featured: false
|
|
45
|
+
{{/frontmatter}}
|
|
46
|
+
|
|
47
|
+
<h1>{{@frontmatter.title}}</h1>
|
|
48
|
+
<p>By {{@frontmatter.author}} on {{@frontmatter.date}}</p>
|
|
49
|
+
|
|
50
|
+
{{#if @frontmatter.settings.published}}
|
|
51
|
+
<span class="badge">Published</span>
|
|
52
|
+
{{/if}}
|
|
53
|
+
|
|
54
|
+
<ul>
|
|
55
|
+
{{#each @frontmatter.tags}}
|
|
56
|
+
<li>{{this}}</li>
|
|
57
|
+
{{/each}}
|
|
58
|
+
</ul>
|
|
59
|
+
`;
|
|
60
|
+
|
|
61
|
+
const render = mikel.create();
|
|
62
|
+
render.use(mikelFrontmatter());
|
|
63
|
+
|
|
64
|
+
const result = render(template, {});
|
|
65
|
+
console.log(result);
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### JSON Example
|
|
69
|
+
|
|
70
|
+
```javascript
|
|
71
|
+
import mikel from "mikel";
|
|
72
|
+
import mikelFrontmatter from "mikel-frontmatter";
|
|
73
|
+
|
|
74
|
+
const template = `
|
|
75
|
+
{{#frontmatter}}
|
|
76
|
+
{
|
|
77
|
+
"title": "My Page Title",
|
|
78
|
+
"author": "John Doe",
|
|
79
|
+
"date": "2026-02-03",
|
|
80
|
+
"tags": ["javascript", "templating"],
|
|
81
|
+
"settings": {
|
|
82
|
+
"published": true,
|
|
83
|
+
"featured": false
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
{{/frontmatter}}
|
|
87
|
+
|
|
88
|
+
<h1>{{@frontmatter.title}}</h1>
|
|
89
|
+
<p>By {{@frontmatter.author}} on {{@frontmatter.date}}</p>
|
|
90
|
+
|
|
91
|
+
{{#if @frontmatter.settings.published}}
|
|
92
|
+
<span class="badge">Published</span>
|
|
93
|
+
{{/if}}
|
|
94
|
+
|
|
95
|
+
<ul>
|
|
96
|
+
{{#each @frontmatter.tags}}
|
|
97
|
+
<li>{{this}}</li>
|
|
98
|
+
{{/each}}
|
|
99
|
+
</ul>
|
|
100
|
+
`;
|
|
101
|
+
|
|
102
|
+
const render = mikel.create();
|
|
103
|
+
render.use(mikelFrontmatter());
|
|
104
|
+
|
|
105
|
+
const result = render(template, {});
|
|
106
|
+
console.log(result);
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Features
|
|
110
|
+
|
|
111
|
+
### Format Auto-Detection
|
|
112
|
+
|
|
113
|
+
The plugin automatically detects whether your frontmatter is in YAML or JSON format:
|
|
114
|
+
|
|
115
|
+
```javascript
|
|
116
|
+
// YAML format (default)
|
|
117
|
+
{{#frontmatter}}
|
|
118
|
+
title: My Title
|
|
119
|
+
author: John Doe
|
|
120
|
+
{{/frontmatter}}
|
|
121
|
+
|
|
122
|
+
// JSON format (automatically detected)
|
|
123
|
+
{{#frontmatter}}
|
|
124
|
+
{
|
|
125
|
+
"title": "My Title",
|
|
126
|
+
"author": "John Doe"
|
|
127
|
+
}
|
|
128
|
+
{{/frontmatter}}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Basic Key-Value Pairs
|
|
132
|
+
|
|
133
|
+
```javascript
|
|
134
|
+
const template = `
|
|
135
|
+
{{#frontmatter}}
|
|
136
|
+
title: Hello World
|
|
137
|
+
author: Jane Smith
|
|
138
|
+
version: 1.0.0
|
|
139
|
+
{{/frontmatter}}
|
|
140
|
+
|
|
141
|
+
Title: {{@frontmatter.title}}
|
|
142
|
+
`;
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Data Types
|
|
146
|
+
|
|
147
|
+
The plugin supports common data types in both YAML and JSON:
|
|
148
|
+
|
|
149
|
+
**YAML:**
|
|
150
|
+
- **Strings**: `name: John Doe` or `name: "John Doe"` or `name: 'John Doe'`
|
|
151
|
+
- **Numbers**: `age: 25` or `price: 19.99`
|
|
152
|
+
- **Booleans**: `published: true` or `active: false` (also `yes/no`, `on/off`)
|
|
153
|
+
- **Null**: `value: null` or `value: ~` or `value:`
|
|
154
|
+
|
|
155
|
+
**JSON:**
|
|
156
|
+
- **Strings**: `"name": "John Doe"`
|
|
157
|
+
- **Numbers**: `"age": 25` or `"price": 19.99`
|
|
158
|
+
- **Booleans**: `"published": true` or `"active": false`
|
|
159
|
+
- **Null**: `"value": null`
|
|
160
|
+
|
|
161
|
+
### Nested Objects
|
|
162
|
+
|
|
163
|
+
Both YAML and JSON support nested objects:
|
|
164
|
+
|
|
165
|
+
**YAML:**
|
|
166
|
+
```javascript
|
|
167
|
+
const template = `
|
|
168
|
+
{{#frontmatter}}
|
|
169
|
+
author:
|
|
170
|
+
name: John Doe
|
|
171
|
+
email: john@example.com
|
|
172
|
+
social:
|
|
173
|
+
twitter: @johndoe
|
|
174
|
+
github: johndoe
|
|
175
|
+
{{/frontmatter}}
|
|
176
|
+
|
|
177
|
+
Author: {{@frontmatter.author.name}} ({{@frontmatter.author.email}})
|
|
178
|
+
Twitter: {{@frontmatter.author.social.twitter}}
|
|
179
|
+
`;
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
**JSON:**
|
|
183
|
+
```javascript
|
|
184
|
+
const template = `
|
|
185
|
+
{{#frontmatter}}
|
|
186
|
+
{
|
|
187
|
+
"author": {
|
|
188
|
+
"name": "John Doe",
|
|
189
|
+
"email": "john@example.com",
|
|
190
|
+
"social": {
|
|
191
|
+
"twitter": "@johndoe",
|
|
192
|
+
"github": "johndoe"
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
{{/frontmatter}}
|
|
197
|
+
|
|
198
|
+
Author: {{@frontmatter.author.name}} ({{@frontmatter.author.email}})
|
|
199
|
+
Twitter: {{@frontmatter.author.social.twitter}}
|
|
200
|
+
`;
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Arrays
|
|
204
|
+
|
|
205
|
+
Both formats support arrays:
|
|
206
|
+
|
|
207
|
+
**YAML:**
|
|
208
|
+
```javascript
|
|
209
|
+
const template = `
|
|
210
|
+
{{#frontmatter}}
|
|
211
|
+
tags:
|
|
212
|
+
- javascript
|
|
213
|
+
- nodejs
|
|
214
|
+
- template
|
|
215
|
+
colors:
|
|
216
|
+
- red
|
|
217
|
+
- green
|
|
218
|
+
- blue
|
|
219
|
+
{{/frontmatter}}
|
|
220
|
+
|
|
221
|
+
Tags: {{#each @frontmatter.tags}}{{this}}{{#unless @last}}, {{/unless}}{{/each}}
|
|
222
|
+
`;
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
**JSON:**
|
|
226
|
+
```javascript
|
|
227
|
+
const template = `
|
|
228
|
+
{{#frontmatter}}
|
|
229
|
+
{
|
|
230
|
+
"tags": ["javascript", "nodejs", "template"],
|
|
231
|
+
"colors": ["red", "green", "blue"]
|
|
232
|
+
}
|
|
233
|
+
{{/frontmatter}}
|
|
234
|
+
|
|
235
|
+
Tags: {{#each @frontmatter.tags}}{{this}}{{#unless @last}}, {{/unless}}{{/each}}
|
|
236
|
+
`;
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### Arrays of Objects
|
|
240
|
+
|
|
241
|
+
**YAML:**
|
|
242
|
+
```javascript
|
|
243
|
+
const template = `
|
|
244
|
+
{{#frontmatter}}
|
|
245
|
+
contributors:
|
|
246
|
+
- name: Alice
|
|
247
|
+
role: Developer
|
|
248
|
+
- name: Bob
|
|
249
|
+
role: Designer
|
|
250
|
+
- name: Carol
|
|
251
|
+
role: Manager
|
|
252
|
+
{{/frontmatter}}
|
|
253
|
+
|
|
254
|
+
{{#each @frontmatter.contributors}}
|
|
255
|
+
<div>{{name}} - {{role}}</div>
|
|
256
|
+
{{/each}}
|
|
257
|
+
`;
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
**JSON:**
|
|
261
|
+
```javascript
|
|
262
|
+
const template = `
|
|
263
|
+
{{#frontmatter}}
|
|
264
|
+
{
|
|
265
|
+
"contributors": [
|
|
266
|
+
{ "name": "Alice", "role": "Developer" },
|
|
267
|
+
{ "name": "Bob", "role": "Designer" },
|
|
268
|
+
{ "name": "Carol", "role": "Manager" }
|
|
269
|
+
]
|
|
270
|
+
}
|
|
271
|
+
{{/frontmatter}}
|
|
272
|
+
|
|
273
|
+
{{#each @frontmatter.contributors}}
|
|
274
|
+
<div>{{name}} - {{role}}</div>
|
|
275
|
+
{{/each}}
|
|
276
|
+
`;
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### Custom Variable Name
|
|
280
|
+
|
|
281
|
+
Use the `as` option to store the frontmatter data in a custom variable:
|
|
282
|
+
|
|
283
|
+
```javascript
|
|
284
|
+
const template = `
|
|
285
|
+
{{#frontmatter as="meta"}}
|
|
286
|
+
title: My Page
|
|
287
|
+
description: A great page
|
|
288
|
+
{{/frontmatter}}
|
|
289
|
+
|
|
290
|
+
<title>{{@meta.title}}</title>
|
|
291
|
+
<meta name="description" content="{{@meta.description}}">
|
|
292
|
+
`;
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### Multiple Frontmatter Blocks
|
|
296
|
+
|
|
297
|
+
You can have multiple `{{#frontmatter}}` blocks in a template. Later blocks will overwrite earlier ones:
|
|
298
|
+
|
|
299
|
+
```javascript
|
|
300
|
+
const template = `
|
|
301
|
+
{{#frontmatter}}
|
|
302
|
+
title: Original Title
|
|
303
|
+
author: John
|
|
304
|
+
{{/frontmatter}}
|
|
305
|
+
|
|
306
|
+
{{#frontmatter}}
|
|
307
|
+
title: Updated Title
|
|
308
|
+
status: draft
|
|
309
|
+
{{/frontmatter}}
|
|
310
|
+
|
|
311
|
+
Title: {{@frontmatter.title}}
|
|
312
|
+
Author: {{@frontmatter.author}}
|
|
313
|
+
Status: {{@frontmatter.status}}
|
|
314
|
+
`;
|
|
315
|
+
|
|
316
|
+
// Output:
|
|
317
|
+
// Title: Updated Title
|
|
318
|
+
// Author: (empty, overwritten)
|
|
319
|
+
// Status: draft
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
## API
|
|
323
|
+
|
|
324
|
+
### mikelFrontmatter(options?)
|
|
325
|
+
|
|
326
|
+
Creates a new instance of the frontmatter plugin.
|
|
327
|
+
|
|
328
|
+
**Parameters:**
|
|
329
|
+
- `options` (optional): Configuration options for the plugin.
|
|
330
|
+
- `parser` (Function): Custom parser function to override the default YAML/JSON parser.
|
|
331
|
+
|
|
332
|
+
**Returns:** A plugin object with a `frontmatter` helper.
|
|
333
|
+
|
|
334
|
+
### Helper: `{{#frontmatter}}...{{/frontmatter}}`
|
|
335
|
+
|
|
336
|
+
Parses the content inside the block as YAML or JSON (auto-detected) and stores it as a variable.
|
|
337
|
+
|
|
338
|
+
**Options:**
|
|
339
|
+
- `as`: Custom variable name (default: `"frontmatter"`). Example: `{{#frontmatter as="meta"}}`
|
|
340
|
+
|
|
341
|
+
**Output:** This helper doesn't produce any output in the rendered template.
|
|
342
|
+
|
|
343
|
+
### Custom Parser
|
|
344
|
+
|
|
345
|
+
You can provide a custom parser function if you need special parsing logic:
|
|
346
|
+
|
|
347
|
+
```javascript
|
|
348
|
+
const customParser = (content) => {
|
|
349
|
+
// Your custom parsing logic
|
|
350
|
+
return parsedData;
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
const render = mikel.create();
|
|
354
|
+
render.use(mikelFrontmatter({ parser: customParser }));
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
## YAML Syntax Support
|
|
358
|
+
|
|
359
|
+
This plugin includes a basic YAML parser that supports:
|
|
360
|
+
|
|
361
|
+
- ✅ Key-value pairs
|
|
362
|
+
- ✅ Nested objects (with indentation)
|
|
363
|
+
- ✅ Arrays (using `- item` syntax)
|
|
364
|
+
- ✅ Arrays of objects
|
|
365
|
+
- ✅ Strings, numbers, booleans, null
|
|
366
|
+
- ✅ Quoted strings (`"..."` or `'...'`)
|
|
367
|
+
- ✅ Comments (lines starting with `#`)
|
|
368
|
+
- ✅ Boolean variants (`yes/no`, `on/off`, `true/false`)
|
|
369
|
+
- ❌ Multi-line strings (with `|` or `>`)
|
|
370
|
+
- ❌ Anchors and aliases (`&anchor`, `*alias`)
|
|
371
|
+
- ❌ Complex YAML features
|
|
372
|
+
|
|
373
|
+
For most common use cases, this subset is sufficient.
|
|
374
|
+
|
|
375
|
+
## JSON Support
|
|
376
|
+
|
|
377
|
+
The plugin fully supports standard JSON syntax through the native `JSON.parse()` method. If the frontmatter content starts with `{` and ends with `}`, it will be automatically parsed as JSON.
|
|
378
|
+
|
|
379
|
+
## License
|
|
380
|
+
|
|
381
|
+
Licensed under the [MIT License](../../LICENSE).
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Options for the mikel-frontmatter plugin
|
|
3
|
+
*/
|
|
4
|
+
export interface MikelFrontmatterOptions {
|
|
5
|
+
/**
|
|
6
|
+
* Custom parser function to override the default YAML/JSON parser
|
|
7
|
+
* @param content - The raw frontmatter content string
|
|
8
|
+
* @returns Parsed data object
|
|
9
|
+
*/
|
|
10
|
+
parser?: (content: string) => Record<string, any>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* YAML parser function
|
|
15
|
+
* @param yaml - YAML string to parse
|
|
16
|
+
* @returns Parsed object
|
|
17
|
+
*/
|
|
18
|
+
export type YamlParser = (yaml: string) => Record<string, any>;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Mikel frontmatter plugin
|
|
22
|
+
* @param options - Plugin configuration options
|
|
23
|
+
* @returns Plugin object with helpers
|
|
24
|
+
*/
|
|
25
|
+
declare function mikelFrontmatter(options?: MikelFrontmatterOptions): {
|
|
26
|
+
helpers: {
|
|
27
|
+
frontmatter: (params: {
|
|
28
|
+
args: any[];
|
|
29
|
+
opt?: Record<string, any>;
|
|
30
|
+
options: Record<string, any>;
|
|
31
|
+
tokens: string[];
|
|
32
|
+
data: Record<string, any>;
|
|
33
|
+
variables: Record<string, any>;
|
|
34
|
+
fn: (blockData?: Record<string, any>, blockVars?: Record<string, any>, blockOutput?: string[]) => string;
|
|
35
|
+
}) => string;
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* YAML parser exposed for direct use
|
|
41
|
+
*/
|
|
42
|
+
declare namespace mikelFrontmatter {
|
|
43
|
+
export const yamlParser: YamlParser;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export default mikelFrontmatter;
|
package/index.js
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
// @description parse a single value from YAML
|
|
2
|
+
const parseValue = (str = "") => {
|
|
3
|
+
// str = str.trim();
|
|
4
|
+
// 1. quoted strings
|
|
5
|
+
if ((str.startsWith(`"`) && str.endsWith(`"`)) || (str.startsWith(`'`) && str.endsWith(`'`))) {
|
|
6
|
+
return str.slice(1, -1);
|
|
7
|
+
}
|
|
8
|
+
// 2. inline array
|
|
9
|
+
if (str.startsWith("[") && str.endsWith("]")) {
|
|
10
|
+
return str.slice(1, -1).split(",").map(item => parseValue(item.trim()));
|
|
11
|
+
}
|
|
12
|
+
// 3. inline object
|
|
13
|
+
if (str.startsWith("{") && str.endsWith("}")) {
|
|
14
|
+
return str.slice(1, -1).split(",").reduce((result, pair) => {
|
|
15
|
+
const [key, value] = pair.split(":");
|
|
16
|
+
if (key && value) {
|
|
17
|
+
result[key.trim()] = parseValue(value.trim());
|
|
18
|
+
}
|
|
19
|
+
return result;
|
|
20
|
+
}, {});
|
|
21
|
+
}
|
|
22
|
+
// 4. booleans
|
|
23
|
+
if (str === "true" || str === "yes" || str === "on") {
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
if (str === "false" || str === "no" || str === "off") {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
// 5. null values
|
|
30
|
+
if (str === "null" || str === "~") {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
// 6. numbers
|
|
34
|
+
if (/^-?\d+(\.\d+)?$/.test(str)) {
|
|
35
|
+
return parseFloat(str);
|
|
36
|
+
}
|
|
37
|
+
// 5. return as-is
|
|
38
|
+
return str;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// @description get the current indentation level
|
|
42
|
+
const getIndent = (line = "") => {
|
|
43
|
+
return line.length - line.trimStart().length;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// @description parse a simple YAML string into an object
|
|
47
|
+
const parseYaml = (yaml = "") => {
|
|
48
|
+
const lines = yaml.split("\n");
|
|
49
|
+
let i = 0;
|
|
50
|
+
const parse = (minIndent = 0, result = {}) => {
|
|
51
|
+
while (i < lines.length) {
|
|
52
|
+
const line = lines[i];
|
|
53
|
+
const trimmed = line.trim();
|
|
54
|
+
const indent = getIndent(line);
|
|
55
|
+
|
|
56
|
+
// skip empty and comments
|
|
57
|
+
if (!trimmed || trimmed[0] === "#") {
|
|
58
|
+
i++;
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
// Return if dedented
|
|
62
|
+
if (indent < minIndent) {
|
|
63
|
+
return result;
|
|
64
|
+
}
|
|
65
|
+
// Array item
|
|
66
|
+
if (trimmed.startsWith("- ")) {
|
|
67
|
+
// First array item - initialize array
|
|
68
|
+
if (!Array.isArray(result)) {
|
|
69
|
+
result = [];
|
|
70
|
+
}
|
|
71
|
+
const content = trimmed.slice(2).trim();
|
|
72
|
+
i++;
|
|
73
|
+
if (!content) {
|
|
74
|
+
// Nested content on next lines
|
|
75
|
+
result.push(parse(indent + 2, {}));
|
|
76
|
+
}
|
|
77
|
+
// inline content with key:value (object)
|
|
78
|
+
else if (content.includes(":")) {
|
|
79
|
+
const obj = {};
|
|
80
|
+
const colonIdx = content.indexOf(":");
|
|
81
|
+
if (colonIdx > 0) {
|
|
82
|
+
obj[content.slice(0, colonIdx).trim()] = parseValue(content.slice(colonIdx + 1).trim());
|
|
83
|
+
}
|
|
84
|
+
// Check for nested content on following lines
|
|
85
|
+
if (i < lines.length && getIndent(lines[i]) > indent) {
|
|
86
|
+
Object.assign(obj, parse(indent + 2, {}));
|
|
87
|
+
}
|
|
88
|
+
result.push(obj);
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
// Simple value
|
|
92
|
+
result.push(parseValue(content));
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// Key-value pair
|
|
96
|
+
else if (trimmed.includes(":")) {
|
|
97
|
+
const colonIdx = trimmed.indexOf(":");
|
|
98
|
+
const key = trimmed.slice(0, colonIdx).trim();
|
|
99
|
+
const value = trimmed.slice(colonIdx + 1).trim();
|
|
100
|
+
i++;
|
|
101
|
+
if (!value) {
|
|
102
|
+
// Check if next line is an array or object
|
|
103
|
+
if (i < lines.length && getIndent(lines[i]) > indent) {
|
|
104
|
+
const nextLine = lines[i].trim();
|
|
105
|
+
if (nextLine.startsWith("- ")) {
|
|
106
|
+
result[key] = parse(indent + 2, []);
|
|
107
|
+
} else {
|
|
108
|
+
result[key] = parse(indent + 2, {});
|
|
109
|
+
}
|
|
110
|
+
} else {
|
|
111
|
+
result[key] = null;
|
|
112
|
+
}
|
|
113
|
+
} else {
|
|
114
|
+
result[key] = parseValue(value);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
i++;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return result;
|
|
122
|
+
};
|
|
123
|
+
return parse(0, {});
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
// @description internal method to parse the content of the frontmatter block
|
|
127
|
+
const parseFrontmatterBlock = (content = "", parser = null) => {
|
|
128
|
+
// 1. use custom parser if provided
|
|
129
|
+
if (typeof parser === "function") {
|
|
130
|
+
return parser(content);
|
|
131
|
+
}
|
|
132
|
+
// 2. guess format (YAML or JSON)
|
|
133
|
+
const trimmed = content.trim();
|
|
134
|
+
if ((trimmed.startsWith("{") && trimmed.endsWith("}")) || (trimmed.startsWith("[") && trimmed.endsWith("]"))) {
|
|
135
|
+
return JSON.parse(content);
|
|
136
|
+
}
|
|
137
|
+
return parseYaml(content);
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
// @description plugin to register a #frontmatter helper
|
|
141
|
+
// @param {Object} options - plugin options
|
|
142
|
+
// @param {Function} options.parser - custom YAML parser function
|
|
143
|
+
const mikelFrontmatter = (options = {}) => {
|
|
144
|
+
return {
|
|
145
|
+
helpers: {
|
|
146
|
+
frontmatter: params => {
|
|
147
|
+
const variableName = params.options.as || "frontmatter";
|
|
148
|
+
// const format = params.options.format || "yaml";
|
|
149
|
+
const content = parseFrontmatterBlock(params.fn(params.data) || "", options.parser);
|
|
150
|
+
// register the variable (overwrite if it already exists)
|
|
151
|
+
Object.assign(params.variables, {
|
|
152
|
+
[variableName]: content,
|
|
153
|
+
});
|
|
154
|
+
// don't render anything
|
|
155
|
+
return "";
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
};
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
// assign additional metadata to the plugin function
|
|
162
|
+
mikelFrontmatter.yamlParser = parseYaml;
|
|
163
|
+
|
|
164
|
+
// export the plugin as default
|
|
165
|
+
export default mikelFrontmatter;
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "mikel-frontmatter",
|
|
3
|
+
"description": "A mikel plugin to define new data inside templates using a frontmatter-like syntax.",
|
|
4
|
+
"version": "0.31.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "Josemi Juanes",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/jmjuanes/mikel.git",
|
|
11
|
+
"directory": "packages/mikel-frontmatter"
|
|
12
|
+
},
|
|
13
|
+
"exports": {
|
|
14
|
+
".": "./index.js",
|
|
15
|
+
"./index.js": "./index.js",
|
|
16
|
+
"./package.json": "./package.json"
|
|
17
|
+
},
|
|
18
|
+
"types": "./index.d.ts",
|
|
19
|
+
"files": [
|
|
20
|
+
"README.md",
|
|
21
|
+
"index.js",
|
|
22
|
+
"index.d.ts"
|
|
23
|
+
],
|
|
24
|
+
"keywords": [
|
|
25
|
+
"mikel",
|
|
26
|
+
"plugin",
|
|
27
|
+
"frontmatter",
|
|
28
|
+
"metadata",
|
|
29
|
+
"yaml"
|
|
30
|
+
]
|
|
31
|
+
}
|