Skip to content

Commit d1a65c7

Browse files
committed
copy frontmatter parsing code from prettier base code
1 parent eb2a82a commit d1a65c7

4 files changed

Lines changed: 148 additions & 0 deletions

File tree

src/utils/frontmatter/constants.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/** @type {unique symbol} */
2+
export const FRONT_MATTER_MARK = Symbol.for("PRETTIER_IS_FRONT_MATTER");
3+
export const FRONT_MATTER_VISITOR_KEYS = [];

src/utils/frontmatter/parse.js

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

src/utils/frontmatter/print.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
function printFrontMatter({ node }) {
2+
return node.raw;
3+
}
4+
5+
export default printFrontMatter;
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// import * as assert from "#universal/assert";
2+
3+
/**
4+
Replaces all characters in the input string except line breaks `\n` with a space.
5+
6+
@param {string} string - The input string to process.
7+
@returns {string}
8+
*/
9+
function replaceNonLineBreaksWithSpace(string) {
10+
const replaced = string.replaceAll(/[^\n]/g, ' ');
11+
12+
if (process.env.NODE_ENV !== 'production') {
13+
// assert.equal(replaced.length, string.length);
14+
}
15+
16+
return replaced;
17+
}
18+
19+
export default replaceNonLineBreaksWithSpace;

0 commit comments

Comments
 (0)