-
-
Notifications
You must be signed in to change notification settings - Fork 49
Expand file tree
/
Copy pathparse.js
More file actions
121 lines (103 loc) · 2.61 KB
/
parse.js
File metadata and controls
121 lines (103 loc) · 2.61 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
import replaceNonLineBreaksWithSpace from './replace-non-line-breaks-with-space.js';
import { FRONT_MATTER_MARK } from './constants.js';
const DELIMITER_LENGTH = 3;
/**
@typedef {{
index: number,
// 1-based line number
line: number,
// 0-based column number
column: number,
}} Position
@typedef {{
language: string,
explicitLanguage: string | null,
value: string,
startDelimiter: string,
endDelimiter: string,
raw: string,
start: Position,
end: Position,
[FRONT_MATTER_MARK]: true,
}} FrontMatter
*/
/**
@param {string} text
@returns {FrontMatter | undefined}
*/
function getFrontMatter(text) {
const startDelimiter = text.slice(0, DELIMITER_LENGTH);
if (startDelimiter !== '---' && startDelimiter !== '+++') {
return;
}
const firstLineBreakIndex = text.indexOf('\n', DELIMITER_LENGTH);
if (firstLineBreakIndex === -1) {
return;
}
const explicitLanguage = text
.slice(DELIMITER_LENGTH, firstLineBreakIndex)
.trim();
let endDelimiterIndex = text.indexOf(
`\n${startDelimiter}`,
firstLineBreakIndex,
);
let language = explicitLanguage;
if (!language) {
language = startDelimiter === '+++' ? 'toml' : 'yaml';
}
if (
endDelimiterIndex === -1 &&
startDelimiter === '---' &&
language === 'yaml'
) {
// In some markdown processors such as pandoc,
// "..." can be used as the end delimiter for YAML front-matter.
endDelimiterIndex = text.indexOf('\n...', firstLineBreakIndex);
}
if (endDelimiterIndex === -1) {
return;
}
const frontMatterEndIndex = endDelimiterIndex + 1 + DELIMITER_LENGTH;
const nextCharacter = text.charAt(frontMatterEndIndex + 1);
if (!/\s?/.test(nextCharacter)) {
return;
}
const raw = text.slice(0, frontMatterEndIndex);
/** @type {string[]} */
let lines;
return {
language,
explicitLanguage: explicitLanguage || null,
value: text.slice(firstLineBreakIndex + 1, endDelimiterIndex),
startDelimiter,
endDelimiter: raw.slice(-DELIMITER_LENGTH),
raw,
start: { line: 1, column: 0, index: 0 },
end: {
index: raw.length,
get line() {
lines ??= raw.split('\n');
return lines.length;
},
get column() {
lines ??= raw.split('\n');
return lines.at(-1).length;
},
},
[FRONT_MATTER_MARK]: true,
};
}
function parse(text) {
const frontMatter = getFrontMatter(text);
if (!frontMatter) {
return { content: text };
}
return {
frontMatter,
get content() {
const { raw } = frontMatter;
return replaceNonLineBreaksWithSpace(raw) + text.slice(raw.length);
},
};
}
export default parse;