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/rehype-collect-headings.ts75
1 files changed, 72 insertions, 3 deletions
diff --git a/packages/markdown/remark/src/rehype-collect-headings.ts b/packages/markdown/remark/src/rehype-collect-headings.ts
index 97fe30401..a1083f609 100644
--- a/packages/markdown/remark/src/rehype-collect-headings.ts
+++ b/packages/markdown/remark/src/rehype-collect-headings.ts
@@ -1,7 +1,10 @@
+import { type Expression, type Super } from 'estree';
import Slugger from 'github-slugger';
-import { visit } from 'unist-util-visit';
+import { type MdxTextExpression } from 'mdast-util-mdx-expression';
+import { visit, type Node } from 'unist-util-visit';
-import type { MarkdownHeading, MarkdownVFile, RehypePlugin } from './types.js';
+import { InvalidAstroDataError, safelyGetAstroData } from './frontmatter-injection.js';
+import type { MarkdownAstroData, MarkdownHeading, MarkdownVFile, RehypePlugin } from './types.js';
const rawNodeTypes = new Set(['text', 'raw', 'mdxTextExpression']);
const codeTagNames = new Set(['code', 'pre']);
@@ -11,6 +14,7 @@ export function rehypeHeadingIds(): ReturnType<RehypePlugin> {
const headings: MarkdownHeading[] = [];
const slugger = new Slugger();
const isMDX = isMDXFile(file);
+ const astroData = safelyGetAstroData(file.data);
visit(tree, (node) => {
if (node.type !== 'element') return;
const { tagName } = node;
@@ -31,7 +35,17 @@ export function rehypeHeadingIds(): ReturnType<RehypePlugin> {
}
if (rawNodeTypes.has(child.type)) {
if (isMDX || codeTagNames.has(parent.tagName)) {
- text += child.value;
+ let value = child.value;
+ if (isMdxTextExpression(child) && !(astroData instanceof InvalidAstroDataError)) {
+ const frontmatterPath = getMdxFrontmatterVariablePath(child);
+ if (Array.isArray(frontmatterPath) && frontmatterPath.length > 0) {
+ const frontmatterValue = getMdxFrontmatterVariableValue(astroData, frontmatterPath);
+ if (typeof frontmatterValue === 'string') {
+ value = frontmatterValue;
+ }
+ }
+ }
+ text += value;
} else {
text += child.value.replace(/\{/g, '${');
}
@@ -57,3 +71,58 @@ export function rehypeHeadingIds(): ReturnType<RehypePlugin> {
function isMDXFile(file: MarkdownVFile) {
return Boolean(file.history[0]?.endsWith('.mdx'));
}
+
+/**
+ * Check if an ESTree entry is `frontmatter.*.VARIABLE`.
+ * If it is, return the variable path (i.e. `["*", ..., "VARIABLE"]`) minus the `frontmatter` prefix.
+ */
+function getMdxFrontmatterVariablePath(node: MdxTextExpression): string[] | Error {
+ if (!node.data?.estree || node.data.estree.body.length !== 1) return new Error();
+
+ const statement = node.data.estree.body[0];
+
+ // Check for "[ANYTHING].[ANYTHING]".
+ if (statement?.type !== 'ExpressionStatement' || statement.expression.type !== 'MemberExpression')
+ return new Error();
+
+ let expression: Expression | Super = statement.expression;
+ const expressionPath: string[] = [];
+
+ // Traverse the expression, collecting the variable path.
+ while (
+ expression.type === 'MemberExpression' &&
+ expression.property.type === (expression.computed ? 'Literal' : 'Identifier')
+ ) {
+ expressionPath.push(
+ expression.property.type === 'Literal'
+ ? String(expression.property.value)
+ : expression.property.name
+ );
+
+ expression = expression.object;
+ }
+
+ // Check for "frontmatter.[ANYTHING]".
+ if (expression.type !== 'Identifier' || expression.name !== 'frontmatter') return new Error();
+
+ return expressionPath.reverse();
+}
+
+function getMdxFrontmatterVariableValue(astroData: MarkdownAstroData, path: string[]) {
+ let value: MdxFrontmatterVariableValue = astroData.frontmatter;
+
+ for (const key of path) {
+ if (!value[key]) return undefined;
+
+ value = value[key];
+ }
+
+ return value;
+}
+
+function isMdxTextExpression(node: Node): node is MdxTextExpression {
+ return node.type === 'mdxTextExpression';
+}
+
+type MdxFrontmatterVariableValue =
+ MarkdownAstroData['frontmatter'][keyof MarkdownAstroData['frontmatter']];