Skip to content

Commit f14e25a

Browse files
3w36zj6azu
andauthored
feat(markdown-to-ast): add support for TOML and JSON frontmatter (#2012)
close #2006 ## Changes This PR adds support for TOML and JSON frontmatter to the `markdown-to-ast` package. ### TOML Similar to YAML, `micromark-extension-frontmatter` supports TOML by default via its presets[^1]. Therefore, we simply need to enable TOML in `remark-frontmatter`. [^1]: https://github.com/micromark/micromark-extension-frontmatter/blob/727a56e89c80e9829b3d6bb2a1bca9ec21630c84/dev/lib/to-matters.js#L8 ### JSON I wasn't entirely sure about the JSON frontmatter specifications, so I looked into it. It turns out there are several dialects across different tools like VitePress, 11ty, Hugo, and Hexo[^2]. This is likely why `remark-frontmatter` lacks a predefined marker for JSON. [^2]: eslint/markdown#404 In this PR, I have added support for what I consider to be the major formats by setting up custom markers. #### VitePress[^3] [^3]: https://vitepress.dev/guide/frontmatter#alternative-frontmatter-formats ```md --- { "title": "My Article Title", "date": "2024-01-15", "author": "John Doe", "tags": ["markdown", "tutorial", "web"] } --- # Article Content ``` In the textlint AST, this is parsed as YAML. However, since textlint doesn't officially target YAML nodes for linting, I believe this won't cause any immediate issues. It is also possible to check if the YAML content can be parsed as JSON and convert it into a JSON node during a post-processing step. #### 11ty[^4] [^4]: https://www.11ty.dev/docs/data-frontmatter/#json-front-matter ```md ---json { "title": "My Article Title", "date": "2024-01-15", "author": "John Doe", "tags": ["markdown", "tutorial", "web"] } --- # Article Content ``` #### Hugo[^5] [^5]: https://gohugo.io/content-management/front-matter/ ```md { "title": "My Article Title", "date": "2024-01-15", "author": "John Doe", "tags": ["markdown", "tutorial", "web"] } # Article Content ``` #### Hexo[^6] [^6]: https://www.npmjs.com/package/hexo-front-matter ```md ;;; "title": "My Article Title", "date": "2024-01-15", "author": "John Doe", "tags": ["markdown", "tutorial", "web"] ;;; # Article Content ``` --------- Co-authored-by: azu <[email protected]>
1 parent 901abea commit f14e25a

12 files changed

Lines changed: 457 additions & 3 deletions

File tree

packages/@textlint/markdown-to-ast/src/mapping/markdown-syntax-map.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ export const SyntaxMap = {
2626
// remark(markdown) extension
2727
// Following type is not in @textlint/ast-node-types
2828
yaml: "Yaml",
29+
toml: "Toml",
30+
json: "Json",
2931
table: "Table",
3032
tableRow: "TableRow",
3133
tableCell: "TableCell",

packages/@textlint/markdown-to-ast/src/parse-markdown.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,22 @@ import frontmatter from "remark-frontmatter";
1212
import footnotes from "remark-footnotes";
1313
import type { Node } from "unist";
1414

15-
const remark = unified().use(remarkParse).use(frontmatter, ["yaml"]).use(remarkGfm).use(footnotes, {
16-
inlineNotes: true
17-
});
15+
const remark = unified()
16+
.use(remarkParse)
17+
.use(frontmatter, [
18+
"yaml",
19+
"toml",
20+
// Hexo style
21+
{ type: "json", fence: { open: ";;;", close: ";;;" } },
22+
// 11ty style
23+
{ type: "json", fence: { open: "---json", close: "---" } },
24+
// Hugo style
25+
{ type: "json", fence: { open: "{", close: "}" } }
26+
])
27+
.use(remarkGfm)
28+
.use(footnotes, {
29+
inlineNotes: true
30+
});
1831
export const parseMarkdown = (text: string): Node => {
1932
return remark.parse(text);
2033
};
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---json
2+
{
3+
"title": "My Article Title",
4+
"date": "2024-01-15",
5+
"author": "John Doe",
6+
"tags": ["markdown", "tutorial", "web"]
7+
}
8+
---
9+
10+
# Article Content
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
{
2+
"type": "Document",
3+
"children": [
4+
{
5+
"type": "Json",
6+
"value": "{\n \"title\": \"My Article Title\",\n \"date\": \"2024-01-15\",\n \"author\": \"John Doe\",\n \"tags\": [\"markdown\", \"tutorial\", \"web\"]\n}",
7+
"loc": {
8+
"start": {
9+
"line": 1,
10+
"column": 0
11+
},
12+
"end": {
13+
"line": 8,
14+
"column": 3
15+
}
16+
},
17+
"range": [
18+
0,
19+
144
20+
],
21+
"raw": "---json\n{\n \"title\": \"My Article Title\",\n \"date\": \"2024-01-15\",\n \"author\": \"John Doe\",\n \"tags\": [\"markdown\", \"tutorial\", \"web\"]\n}\n---"
22+
},
23+
{
24+
"type": "Header",
25+
"depth": 1,
26+
"children": [
27+
{
28+
"type": "Str",
29+
"value": "Article Content",
30+
"loc": {
31+
"start": {
32+
"line": 10,
33+
"column": 2
34+
},
35+
"end": {
36+
"line": 10,
37+
"column": 17
38+
}
39+
},
40+
"range": [
41+
148,
42+
163
43+
],
44+
"raw": "Article Content"
45+
}
46+
],
47+
"loc": {
48+
"start": {
49+
"line": 10,
50+
"column": 0
51+
},
52+
"end": {
53+
"line": 10,
54+
"column": 17
55+
}
56+
},
57+
"range": [
58+
146,
59+
163
60+
],
61+
"raw": "# Article Content"
62+
}
63+
],
64+
"loc": {
65+
"start": {
66+
"line": 1,
67+
"column": 0
68+
},
69+
"end": {
70+
"line": 11,
71+
"column": 0
72+
}
73+
},
74+
"range": [
75+
0,
76+
164
77+
],
78+
"raw": "---json\n{\n \"title\": \"My Article Title\",\n \"date\": \"2024-01-15\",\n \"author\": \"John Doe\",\n \"tags\": [\"markdown\", \"tutorial\", \"web\"]\n}\n---\n\n# Article Content\n"
79+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
;;;
2+
"title": "My Article Title",
3+
"date": "2024-01-15",
4+
"author": "John Doe",
5+
"tags": ["markdown", "tutorial", "web"]
6+
;;;
7+
8+
# Article Content
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
{
2+
"type": "Document",
3+
"children": [
4+
{
5+
"type": "Json",
6+
"value": "\"title\": \"My Article Title\",\n\"date\": \"2024-01-15\",\n\"author\": \"John Doe\",\n\"tags\": [\"markdown\", \"tutorial\", \"web\"]",
7+
"loc": {
8+
"start": {
9+
"line": 1,
10+
"column": 0
11+
},
12+
"end": {
13+
"line": 6,
14+
"column": 3
15+
}
16+
},
17+
"range": [
18+
0,
19+
120
20+
],
21+
"raw": ";;;\n\"title\": \"My Article Title\",\n\"date\": \"2024-01-15\",\n\"author\": \"John Doe\",\n\"tags\": [\"markdown\", \"tutorial\", \"web\"]\n;;;"
22+
},
23+
{
24+
"type": "Header",
25+
"depth": 1,
26+
"children": [
27+
{
28+
"type": "Str",
29+
"value": "Article Content",
30+
"loc": {
31+
"start": {
32+
"line": 8,
33+
"column": 2
34+
},
35+
"end": {
36+
"line": 8,
37+
"column": 17
38+
}
39+
},
40+
"range": [
41+
124,
42+
139
43+
],
44+
"raw": "Article Content"
45+
}
46+
],
47+
"loc": {
48+
"start": {
49+
"line": 8,
50+
"column": 0
51+
},
52+
"end": {
53+
"line": 8,
54+
"column": 17
55+
}
56+
},
57+
"range": [
58+
122,
59+
139
60+
],
61+
"raw": "# Article Content"
62+
}
63+
],
64+
"loc": {
65+
"start": {
66+
"line": 1,
67+
"column": 0
68+
},
69+
"end": {
70+
"line": 9,
71+
"column": 0
72+
}
73+
},
74+
"range": [
75+
0,
76+
140
77+
],
78+
"raw": ";;;\n\"title\": \"My Article Title\",\n\"date\": \"2024-01-15\",\n\"author\": \"John Doe\",\n\"tags\": [\"markdown\", \"tutorial\", \"web\"]\n;;;\n\n# Article Content\n"
79+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"title": "My Article Title",
3+
"date": "2024-01-15",
4+
"author": "John Doe",
5+
"tags": ["markdown", "tutorial", "web"]
6+
}
7+
8+
# Article Content
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
{
2+
"type": "Document",
3+
"children": [
4+
{
5+
"type": "Json",
6+
"value": " \"title\": \"My Article Title\",\n \"date\": \"2024-01-15\",\n \"author\": \"John Doe\",\n \"tags\": [\"markdown\", \"tutorial\", \"web\"]",
7+
"loc": {
8+
"start": {
9+
"line": 1,
10+
"column": 0
11+
},
12+
"end": {
13+
"line": 6,
14+
"column": 1
15+
}
16+
},
17+
"range": [
18+
0,
19+
132
20+
],
21+
"raw": "{\n \"title\": \"My Article Title\",\n \"date\": \"2024-01-15\",\n \"author\": \"John Doe\",\n \"tags\": [\"markdown\", \"tutorial\", \"web\"]\n}"
22+
},
23+
{
24+
"type": "Header",
25+
"depth": 1,
26+
"children": [
27+
{
28+
"type": "Str",
29+
"value": "Article Content",
30+
"loc": {
31+
"start": {
32+
"line": 8,
33+
"column": 2
34+
},
35+
"end": {
36+
"line": 8,
37+
"column": 17
38+
}
39+
},
40+
"range": [
41+
136,
42+
151
43+
],
44+
"raw": "Article Content"
45+
}
46+
],
47+
"loc": {
48+
"start": {
49+
"line": 8,
50+
"column": 0
51+
},
52+
"end": {
53+
"line": 8,
54+
"column": 17
55+
}
56+
},
57+
"range": [
58+
134,
59+
151
60+
],
61+
"raw": "# Article Content"
62+
}
63+
],
64+
"loc": {
65+
"start": {
66+
"line": 1,
67+
"column": 0
68+
},
69+
"end": {
70+
"line": 9,
71+
"column": 0
72+
}
73+
},
74+
"range": [
75+
0,
76+
152
77+
],
78+
"raw": "{\n \"title\": \"My Article Title\",\n \"date\": \"2024-01-15\",\n \"author\": \"John Doe\",\n \"tags\": [\"markdown\", \"tutorial\", \"web\"]\n}\n\n# Article Content\n"
79+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
{
3+
"title": "My Article Title",
4+
"date": "2024-01-15",
5+
"author": "John Doe",
6+
"tags": ["markdown", "tutorial", "web"]
7+
}
8+
---
9+
10+
# Article Content
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
{
2+
"type": "Document",
3+
"children": [
4+
{
5+
"type": "Yaml",
6+
"value": "{\n \"title\": \"My Article Title\",\n \"date\": \"2024-01-15\",\n \"author\": \"John Doe\",\n \"tags\": [\"markdown\", \"tutorial\", \"web\"]\n}",
7+
"loc": {
8+
"start": {
9+
"line": 1,
10+
"column": 0
11+
},
12+
"end": {
13+
"line": 8,
14+
"column": 3
15+
}
16+
},
17+
"range": [
18+
0,
19+
140
20+
],
21+
"raw": "---\n{\n \"title\": \"My Article Title\",\n \"date\": \"2024-01-15\",\n \"author\": \"John Doe\",\n \"tags\": [\"markdown\", \"tutorial\", \"web\"]\n}\n---"
22+
},
23+
{
24+
"type": "Header",
25+
"depth": 1,
26+
"children": [
27+
{
28+
"type": "Str",
29+
"value": "Article Content",
30+
"loc": {
31+
"start": {
32+
"line": 10,
33+
"column": 2
34+
},
35+
"end": {
36+
"line": 10,
37+
"column": 17
38+
}
39+
},
40+
"range": [
41+
144,
42+
159
43+
],
44+
"raw": "Article Content"
45+
}
46+
],
47+
"loc": {
48+
"start": {
49+
"line": 10,
50+
"column": 0
51+
},
52+
"end": {
53+
"line": 10,
54+
"column": 17
55+
}
56+
},
57+
"range": [
58+
142,
59+
159
60+
],
61+
"raw": "# Article Content"
62+
}
63+
],
64+
"loc": {
65+
"start": {
66+
"line": 1,
67+
"column": 0
68+
},
69+
"end": {
70+
"line": 11,
71+
"column": 0
72+
}
73+
},
74+
"range": [
75+
0,
76+
160
77+
],
78+
"raw": "---\n{\n \"title\": \"My Article Title\",\n \"date\": \"2024-01-15\",\n \"author\": \"John Doe\",\n \"tags\": [\"markdown\", \"tutorial\", \"web\"]\n}\n---\n\n# Article Content\n"
79+
}

0 commit comments

Comments
 (0)