summaryrefslogtreecommitdiff
path: root/packages/markdown/remark/src
diff options
context:
space:
mode:
Diffstat (limited to 'packages/markdown/remark/src')
-rw-r--r--packages/markdown/remark/src/frontmatter.ts65
-rw-r--r--packages/markdown/remark/src/index.ts8
2 files changed, 72 insertions, 1 deletions
diff --git a/packages/markdown/remark/src/frontmatter.ts b/packages/markdown/remark/src/frontmatter.ts
index d8828a6fe..60ae0b5da 100644
--- a/packages/markdown/remark/src/frontmatter.ts
+++ b/packages/markdown/remark/src/frontmatter.ts
@@ -1,3 +1,5 @@
+import yaml from 'js-yaml';
+
export function isFrontmatterValid(frontmatter: Record<string, any>) {
try {
// ensure frontmatter is JSON-serializable
@@ -7,3 +9,66 @@ export function isFrontmatterValid(frontmatter: Record<string, any>) {
}
return typeof frontmatter === 'object' && frontmatter !== null;
}
+
+const frontmatterRE = /^---(.*?)^---/ms;
+export function extractFrontmatter(code: string): string | undefined {
+ return frontmatterRE.exec(code)?.[1];
+}
+
+export interface ParseFrontmatterOptions {
+ /**
+ * How the frontmatter should be handled in the returned `content` string.
+ * - `preserve`: Keep the frontmatter.
+ * - `remove`: Remove the frontmatter.
+ * - `empty-with-spaces`: Replace the frontmatter with empty spaces. (preserves sourcemap line/col/offset)
+ * - `empty-with-lines`: Replace the frontmatter with empty line breaks. (preserves sourcemap line/col)
+ *
+ * @default 'remove'
+ */
+ frontmatter: 'preserve' | 'remove' | 'empty-with-spaces' | 'empty-with-lines';
+}
+
+export interface ParseFrontmatterResult {
+ frontmatter: Record<string, any>;
+ rawFrontmatter: string;
+ content: string;
+}
+
+export function parseFrontmatter(
+ code: string,
+ options?: ParseFrontmatterOptions,
+): ParseFrontmatterResult {
+ const rawFrontmatter = extractFrontmatter(code);
+
+ if (rawFrontmatter == null) {
+ return { frontmatter: {}, rawFrontmatter: '', content: code };
+ }
+
+ const parsed = yaml.load(rawFrontmatter);
+ const frontmatter = (parsed && typeof parsed === 'object' ? parsed : {}) as Record<string, any>;
+
+ let content: string;
+ switch (options?.frontmatter ?? 'remove') {
+ case 'preserve':
+ content = code;
+ break;
+ case 'remove':
+ content = code.replace(`---${rawFrontmatter}---`, '');
+ break;
+ case 'empty-with-spaces':
+ content = code.replace(
+ `---${rawFrontmatter}---`,
+ ` ${rawFrontmatter.replace(/[^\r\n]/g, ' ')} `,
+ );
+ break;
+ case 'empty-with-lines':
+ content = code.replace(`---${rawFrontmatter}---`, rawFrontmatter.replace(/[^\r\n]/g, ''));
+ break;
+ }
+
+ return {
+ frontmatter,
+ rawFrontmatter,
+ content,
+ };
+}
diff --git a/packages/markdown/remark/src/index.ts b/packages/markdown/remark/src/index.ts
index ad52dc16f..dd7d0444a 100644
--- a/packages/markdown/remark/src/index.ts
+++ b/packages/markdown/remark/src/index.ts
@@ -20,7 +20,13 @@ export { rehypeHeadingIds } from './rehype-collect-headings.js';
export { remarkCollectImages } from './remark-collect-images.js';
export { rehypePrism } from './rehype-prism.js';
export { rehypeShiki } from './rehype-shiki.js';
-export { isFrontmatterValid } from './frontmatter.js';
+export {
+ isFrontmatterValid,
+ extractFrontmatter,
+ parseFrontmatter,
+ type ParseFrontmatterOptions,
+ type ParseFrontmatterResult,
+} from './frontmatter.js';
export {
createShikiHighlighter,
type ShikiHighlighter,