99 lines
3.5 KiB
JavaScript
99 lines
3.5 KiB
JavaScript
import Slugger from "github-slugger";
|
|
import { visit } from "unist-util-visit";
|
|
import { InvalidAstroDataError, safelyGetAstroData } from "./frontmatter-injection.js";
|
|
const rawNodeTypes = /* @__PURE__ */ new Set(["text", "raw", "mdxTextExpression"]);
|
|
const codeTagNames = /* @__PURE__ */ new Set(["code", "pre"]);
|
|
function rehypeHeadingIds() {
|
|
return function(tree, file) {
|
|
const headings = [];
|
|
const slugger = new Slugger();
|
|
const isMDX = isMDXFile(file);
|
|
const astroData = safelyGetAstroData(file.data);
|
|
visit(tree, (node) => {
|
|
if (node.type !== "element")
|
|
return;
|
|
const { tagName } = node;
|
|
if (tagName[0] !== "h")
|
|
return;
|
|
const [_, level] = tagName.match(/h([0-6])/) ?? [];
|
|
if (!level)
|
|
return;
|
|
const depth = Number.parseInt(level);
|
|
let text = "";
|
|
visit(node, (child, __, parent) => {
|
|
if (child.type === "element" || parent == null) {
|
|
return;
|
|
}
|
|
if (child.type === "raw") {
|
|
if (child.value.match(/^\n?<.*>\n?$/)) {
|
|
return;
|
|
}
|
|
}
|
|
if (rawNodeTypes.has(child.type)) {
|
|
if (isMDX || codeTagNames.has(parent.tagName)) {
|
|
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, "${");
|
|
}
|
|
}
|
|
});
|
|
node.properties = node.properties || {};
|
|
if (typeof node.properties.id !== "string") {
|
|
let slug = slugger.slug(text);
|
|
if (slug.endsWith("-"))
|
|
slug = slug.slice(0, -1);
|
|
node.properties.id = slug;
|
|
}
|
|
headings.push({ depth, slug: node.properties.id, text });
|
|
});
|
|
file.data.__astroHeadings = headings;
|
|
};
|
|
}
|
|
function isMDXFile(file) {
|
|
var _a;
|
|
return Boolean((_a = file.history[0]) == null ? void 0 : _a.endsWith(".mdx"));
|
|
}
|
|
function getMdxFrontmatterVariablePath(node) {
|
|
var _a;
|
|
if (!((_a = node.data) == null ? void 0 : _a.estree) || node.data.estree.body.length !== 1)
|
|
return new Error();
|
|
const statement = node.data.estree.body[0];
|
|
if ((statement == null ? void 0 : statement.type) !== "ExpressionStatement" || statement.expression.type !== "MemberExpression")
|
|
return new Error();
|
|
let expression = statement.expression;
|
|
const expressionPath = [];
|
|
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;
|
|
}
|
|
if (expression.type !== "Identifier" || expression.name !== "frontmatter")
|
|
return new Error();
|
|
return expressionPath.reverse();
|
|
}
|
|
function getMdxFrontmatterVariableValue(astroData, path) {
|
|
let value = astroData.frontmatter;
|
|
for (const key of path) {
|
|
if (!value[key])
|
|
return void 0;
|
|
value = value[key];
|
|
}
|
|
return value;
|
|
}
|
|
function isMdxTextExpression(node) {
|
|
return node.type === "mdxTextExpression";
|
|
}
|
|
export {
|
|
rehypeHeadingIds
|
|
};
|