UNPKG

@astrojs/mdx

Version:

Add support for MDX pages in your Astro site

153 lines (152 loc) 5.93 kB
import { pathToFileURL } from "node:url"; import { isFrontmatterValid } from "@astrojs/internal-helpers/frontmatter"; import { satteriCollectImagesPlugin, satteriCreateHighlightFn, satteriHeadingIdsPlugin, satteriHighlightPlugin } from "@astrojs/markdown-satteri"; import { createDefaultAstroMetadata } from "astro/markdown"; import { mdxToJs } from "satteri"; import { ASTRO_IMAGE_IMPORT, USES_ASTRO_IMAGE_FLAG } from "../image-constants.js"; import { shouldAddCharset } from "./charset.js"; import { createAstroMetadataPlugin } from "./hast-astro-metadata.js"; import { createImageToComponentPlugin } from "./hast-images-to-component.js"; function createMdxProcessor(mdxOptions, satteriOptions, ctx) { let highlightFn; let initPromise; function initHighlighter() { initPromise = satteriCreateHighlightFn(mdxOptions.syntaxHighlight, mdxOptions.shikiConfig).then( (fn) => { highlightFn = fn; } ); } return { async process(content, filePath, frontmatter) { if (!highlightFn && !initPromise) { initHighlighter(); } if (initPromise) await initPromise; const astroData = { frontmatter, headings: [], localImagePaths: /* @__PURE__ */ new Set(), remoteImagePaths: /* @__PURE__ */ new Set() }; const collectImages = satteriCollectImagesPlugin(); const headingIds = satteriHeadingIdsPlugin(); const astroMeta = createAstroMetadataPlugin(filePath); const imageImportInfo = { importedImages: /* @__PURE__ */ new Map(), hasImages: false }; const imageToComponent = createImageToComponentPlugin(imageImportInfo); const syntaxHighlight = mdxOptions.syntaxHighlight; const excludeLangs = typeof syntaxHighlight === "object" ? syntaxHighlight.excludeLangs : void 0; const allMdastPlugins = [ ...satteriOptions.mdastPlugins, collectImages ]; const hastPlugins = []; if (highlightFn) { hastPlugins.push(satteriHighlightPlugin(highlightFn, excludeLangs)); } if (satteriOptions.hastPlugins.length) { hastPlugins.push(...satteriOptions.hastPlugins); } hastPlugins.push(imageToComponent, headingIds, astroMeta); let optimizeStatic; if (mdxOptions.optimize) { const ignoreElements = typeof mdxOptions.optimize === "object" ? mdxOptions.optimize.ignoreElementNames : void 0; optimizeStatic = { component: "Fragment", prop: "set:html", ...ignoreElements && { ignoreElements } }; } const mdxResult = await mdxToJs(content, { mdastPlugins: allMdastPlugins, hastPlugins, optimizeStatic, features: { ...satteriOptions.features, // `mdxOptions.gfm`/`smartypants` are always boolean-shaped; skip the override when // satteri's feature is an object so granular config isn't clobbered. ...typeof satteriOptions.features.gfm === "object" ? {} : { gfm: mdxOptions.gfm !== false }, ...typeof satteriOptions.features.smartPunctuation === "object" ? {} : { smartPunctuation: mdxOptions.smartypants !== false } }, fileURL: pathToFileURL(filePath), jsxImportSource: "astro", data: { astro: astroData } }); let compiled = mdxResult.code; const astro = mdxResult.data.astro; const headings = astro?.headings ?? []; const astroMetadata = mdxResult.data.__astroMetadata ?? createDefaultAstroMetadata(); const resolvedFrontmatter = astro?.frontmatter; if (!resolvedFrontmatter || !isFrontmatterValid(resolvedFrontmatter)) { throw new Error( "[MDX] A S\xE4tteri plugin attempted to inject invalid frontmatter. Ensure `ctx.data.astro.frontmatter` is a valid object that is not `null` or `undefined`." ); } compiled = compiled.replace(/^export default MDXContent;\s*$/m, ""); if (imageImportInfo.hasImages) { compiled += ` import { Image as ${ASTRO_IMAGE_IMPORT} } from "astro:assets";`; for (const [src, importName] of imageImportInfo.importedImages) { compiled += ` import ${importName} from ${JSON.stringify(src)};`; } compiled += ` export const ${USES_ASTRO_IMAGE_FLAG} = true;`; } compiled += ` export const frontmatter = ${JSON.stringify(resolvedFrontmatter)};`; compiled += ` export function getHeadings() { return ${JSON.stringify(headings)}; }`; if (resolvedFrontmatter.layout) { compiled = compiled.replace(/^function MDXContent\(/m, "function __OriginalMDXContent__("); compiled += ` import { jsx as __astro_layout_jsx__ } from 'astro/jsx-runtime'; import __astro_layout_component__ from ${JSON.stringify(resolvedFrontmatter.layout)}; export default function MDXContent(props) { const content = __OriginalMDXContent__(props); const { layout, ...frontmatterContent } = frontmatter; frontmatterContent.file = file; frontmatterContent.url = url; return __astro_layout_jsx__(__astro_layout_component__, { file, url, content: frontmatterContent, frontmatter: frontmatterContent, headings: getHeadings(), 'server:root': true, children: content, }); }`; } else if (shouldAddCharset(content, filePath, ctx.srcDir)) { compiled = compiled.replace(/^function MDXContent\(/m, "function __OriginalMDXContent__("); compiled += ` import { jsx as __astro_charset_jsx__, jsxs as __astro_charset_jsxs__, Fragment as __astro_charset_Fragment__ } from 'astro/jsx-runtime'; export default function MDXContent(props) { return __astro_charset_jsxs__(__astro_charset_Fragment__, { children: [ __astro_charset_jsx__('meta', { charset: 'utf-8' }), __OriginalMDXContent__(props), ], }); }`; } return { code: compiled, astroMetadata }; } }; } export { createMdxProcessor };