152 lines
5.3 KiB
JavaScript
152 lines
5.3 KiB
JavaScript
![]() |
import { rehypeHeadingIds, remarkCollectImages } from "@astrojs/markdown-remark";
|
||
|
import {
|
||
|
InvalidAstroDataError,
|
||
|
safelyGetAstroData
|
||
|
} from "@astrojs/markdown-remark/dist/internal.js";
|
||
|
import { nodeTypes } from "@mdx-js/mdx";
|
||
|
import { visit as estreeVisit } from "estree-util-visit";
|
||
|
import rehypeRaw from "rehype-raw";
|
||
|
import remarkGfm from "remark-gfm";
|
||
|
import remarkSmartypants from "remark-smartypants";
|
||
|
import { rehypeInjectHeadingsExport } from "./rehype-collect-headings.js";
|
||
|
import rehypeMetaString from "./rehype-meta-string.js";
|
||
|
import { rehypeOptimizeStatic } from "./rehype-optimize-static.js";
|
||
|
import { remarkImageToComponent } from "./remark-images-to-component.js";
|
||
|
import remarkPrism from "./remark-prism.js";
|
||
|
import remarkShiki from "./remark-shiki.js";
|
||
|
import { jsToTreeNode } from "./utils.js";
|
||
|
const isPerformanceBenchmark = Boolean(process.env.ASTRO_PERFORMANCE_BENCHMARK);
|
||
|
function recmaInjectImportMetaEnvPlugin({
|
||
|
importMetaEnv
|
||
|
}) {
|
||
|
return (tree) => {
|
||
|
estreeVisit(tree, (node) => {
|
||
|
if (node.type === "MemberExpression") {
|
||
|
const envVarName = getImportMetaEnvVariableName(node);
|
||
|
if (typeof envVarName === "string") {
|
||
|
for (const key in node) {
|
||
|
delete node[key];
|
||
|
}
|
||
|
const envVarLiteral = {
|
||
|
type: "Literal",
|
||
|
value: importMetaEnv[envVarName],
|
||
|
raw: JSON.stringify(importMetaEnv[envVarName])
|
||
|
};
|
||
|
Object.assign(node, envVarLiteral);
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
};
|
||
|
}
|
||
|
function rehypeApplyFrontmatterExport() {
|
||
|
return function(tree, vfile) {
|
||
|
const astroData = safelyGetAstroData(vfile.data);
|
||
|
if (astroData instanceof InvalidAstroDataError)
|
||
|
throw new Error(
|
||
|
// Copied from Astro core `errors-data`
|
||
|
// TODO: find way to import error data from core
|
||
|
'[MDX] A remark or rehype plugin attempted to inject invalid frontmatter. Ensure "astro.frontmatter" is set to a valid JSON object that is not `null` or `undefined`.'
|
||
|
);
|
||
|
const { frontmatter } = astroData;
|
||
|
const exportNodes = [
|
||
|
jsToTreeNode(`export const frontmatter = ${JSON.stringify(frontmatter)};`)
|
||
|
];
|
||
|
if (frontmatter.layout) {
|
||
|
exportNodes.unshift(
|
||
|
jsToTreeNode(
|
||
|
/** @see 'vite-plugin-markdown' for layout props reference */
|
||
|
`import { jsx as layoutJsx } from 'astro/jsx-runtime';
|
||
|
|
||
|
export default async function ({ children }) {
|
||
|
const Layout = (await import(${JSON.stringify(frontmatter.layout)})).default;
|
||
|
const { layout, ...content } = frontmatter;
|
||
|
content.file = file;
|
||
|
content.url = url;
|
||
|
return layoutJsx(Layout, {
|
||
|
file,
|
||
|
url,
|
||
|
content,
|
||
|
frontmatter: content,
|
||
|
headings: getHeadings(),
|
||
|
'server:root': true,
|
||
|
children,
|
||
|
});
|
||
|
};`
|
||
|
)
|
||
|
);
|
||
|
}
|
||
|
tree.children = exportNodes.concat(tree.children);
|
||
|
};
|
||
|
}
|
||
|
async function getRemarkPlugins(mdxOptions, config) {
|
||
|
let remarkPlugins = [
|
||
|
...config.experimental.assets ? [remarkCollectImages, remarkImageToComponent] : []
|
||
|
];
|
||
|
if (!isPerformanceBenchmark) {
|
||
|
if (mdxOptions.gfm) {
|
||
|
remarkPlugins.push(remarkGfm);
|
||
|
}
|
||
|
if (mdxOptions.smartypants) {
|
||
|
remarkPlugins.push(remarkSmartypants);
|
||
|
}
|
||
|
}
|
||
|
remarkPlugins = [...remarkPlugins, ...mdxOptions.remarkPlugins];
|
||
|
if (!isPerformanceBenchmark) {
|
||
|
if (mdxOptions.syntaxHighlight === "shiki") {
|
||
|
remarkPlugins.push([await remarkShiki(mdxOptions.shikiConfig)]);
|
||
|
}
|
||
|
if (mdxOptions.syntaxHighlight === "prism") {
|
||
|
remarkPlugins.push(remarkPrism);
|
||
|
}
|
||
|
}
|
||
|
return remarkPlugins;
|
||
|
}
|
||
|
function getRehypePlugins(mdxOptions) {
|
||
|
let rehypePlugins = [
|
||
|
// ensure `data.meta` is preserved in `properties.metastring` for rehype syntax highlighters
|
||
|
rehypeMetaString,
|
||
|
// rehypeRaw allows custom syntax highlighters to work without added config
|
||
|
[rehypeRaw, { passThrough: nodeTypes }]
|
||
|
];
|
||
|
rehypePlugins = [
|
||
|
...rehypePlugins,
|
||
|
...mdxOptions.rehypePlugins,
|
||
|
// getHeadings() is guaranteed by TS, so this must be included.
|
||
|
// We run `rehypeHeadingIds` _last_ to respect any custom IDs set by user plugins.
|
||
|
...isPerformanceBenchmark ? [] : [rehypeHeadingIds, rehypeInjectHeadingsExport],
|
||
|
// computed from `astro.data.frontmatter` in VFile data
|
||
|
rehypeApplyFrontmatterExport
|
||
|
];
|
||
|
if (mdxOptions.optimize) {
|
||
|
const options = mdxOptions.optimize === true ? void 0 : mdxOptions.optimize;
|
||
|
rehypePlugins.push([rehypeOptimizeStatic, options]);
|
||
|
}
|
||
|
return rehypePlugins;
|
||
|
}
|
||
|
function getImportMetaEnvVariableName(node) {
|
||
|
try {
|
||
|
if (node.object.type !== "MemberExpression" || node.property.type !== "Identifier")
|
||
|
return new Error();
|
||
|
const nestedExpression = node.object;
|
||
|
if (nestedExpression.property.type !== "Identifier" || nestedExpression.property.name !== "env")
|
||
|
return new Error();
|
||
|
const envExpression = nestedExpression.object;
|
||
|
if (envExpression.type !== "MetaProperty" || envExpression.property.type !== "Identifier" || envExpression.property.name !== "meta")
|
||
|
return new Error();
|
||
|
if (envExpression.meta.name !== "import")
|
||
|
return new Error();
|
||
|
return node.property.name;
|
||
|
} catch (e) {
|
||
|
if (e instanceof Error) {
|
||
|
return e;
|
||
|
}
|
||
|
return new Error("Unknown parsing error");
|
||
|
}
|
||
|
}
|
||
|
export {
|
||
|
getRehypePlugins,
|
||
|
getRemarkPlugins,
|
||
|
recmaInjectImportMetaEnvPlugin,
|
||
|
rehypeApplyFrontmatterExport
|
||
|
};
|