diff options
author | 2023-09-14 20:05:38 +0800 | |
---|---|---|
committer | 2023-09-14 20:05:38 +0800 | |
commit | 74dc3edb305c49feec49c39082fa836485da8a92 (patch) | |
tree | dd7b7584627f369c462c421892978658180287f1 /packages/integrations/mdx/src/plugins.ts | |
parent | 2e8726feec2e0d6ba8bd4db941009986e8e34141 (diff) | |
download | astro-74dc3edb305c49feec49c39082fa836485da8a92.tar.gz astro-74dc3edb305c49feec49c39082fa836485da8a92.tar.zst astro-74dc3edb305c49feec49c39082fa836485da8a92.zip |
Improve MDX rendering performance (#8533)
Diffstat (limited to 'packages/integrations/mdx/src/plugins.ts')
-rw-r--r-- | packages/integrations/mdx/src/plugins.ts | 143 |
1 files changed, 31 insertions, 112 deletions
diff --git a/packages/integrations/mdx/src/plugins.ts b/packages/integrations/mdx/src/plugins.ts index a3d9e4ff3..3286a9fd8 100644 --- a/packages/integrations/mdx/src/plugins.ts +++ b/packages/integrations/mdx/src/plugins.ts @@ -4,101 +4,49 @@ import { remarkPrism, remarkShiki, } from '@astrojs/markdown-remark'; -import { - InvalidAstroDataError, - safelyGetAstroData, -} from '@astrojs/markdown-remark/dist/internal.js'; -import { nodeTypes } from '@mdx-js/mdx'; +import { createProcessor, nodeTypes } from '@mdx-js/mdx'; import type { PluggableList } from '@mdx-js/mdx/lib/core.js'; -import type { Literal, MemberExpression } from 'estree'; -import { visit as estreeVisit } from 'estree-util-visit'; import rehypeRaw from 'rehype-raw'; import remarkGfm from 'remark-gfm'; import remarkSmartypants from 'remark-smartypants'; -import type { VFile } from 'vfile'; +import { SourceMapGenerator } from 'source-map'; +import type { Processor } from 'unified'; import type { MdxOptions } from './index.js'; +import { recmaInjectImportMetaEnv } from './recma-inject-import-meta-env.js'; +import { rehypeApplyFrontmatterExport } from './rehype-apply-frontmatter-export.js'; 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 { jsToTreeNode } from './utils.js'; // Skip nonessential plugins during performance benchmark runs const isPerformanceBenchmark = Boolean(process.env.ASTRO_PERFORMANCE_BENCHMARK); -export function recmaInjectImportMetaEnvPlugin({ - importMetaEnv, -}: { +interface MdxProcessorExtraOptions { + sourcemap: boolean; importMetaEnv: Record<string, any>; -}) { - return (tree: any) => { - estreeVisit(tree, (node) => { - if (node.type === 'MemberExpression') { - // attempt to get "import.meta.env" variable name - const envVarName = getImportMetaEnvVariableName(node); - if (typeof envVarName === 'string') { - // clear object keys to replace with envVarLiteral - for (const key in node) { - delete (node as any)[key]; - } - const envVarLiteral: Literal = { - type: 'Literal', - value: importMetaEnv[envVarName], - raw: JSON.stringify(importMetaEnv[envVarName]), - }; - Object.assign(node, envVarLiteral); - } - } - }); - }; } -export function rehypeApplyFrontmatterExport() { - return function (tree: any, vfile: 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) { - // NOTE(bholmesdev) 08-22-2022 - // Using an async layout import (i.e. `const Layout = (await import...)`) - // Preserves the dev server import cache when globbing a large set of MDX files - // Full explanation: 'https://github.com/withastro/astro/pull/4428' - 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); - }; +export function createMdxProcessor( + mdxOptions: MdxOptions, + extraOptions: MdxProcessorExtraOptions +): Processor { + return createProcessor({ + remarkPlugins: getRemarkPlugins(mdxOptions), + rehypePlugins: getRehypePlugins(mdxOptions), + recmaPlugins: getRecmaPlugins(mdxOptions, extraOptions.importMetaEnv), + remarkRehypeOptions: mdxOptions.remarkRehype, + jsx: true, + jsxImportSource: 'astro', + // Note: disable `.md` (and other alternative extensions for markdown files like `.markdown`) support + format: 'mdx', + mdExtensions: [], + elementAttributeNameCase: 'html', + SourceMapGenerator: extraOptions.sourcemap ? SourceMapGenerator : undefined, + }); } -export async function getRemarkPlugins(mdxOptions: MdxOptions): Promise<PluggableList> { +function getRemarkPlugins(mdxOptions: MdxOptions): PluggableList { let remarkPlugins: PluggableList = [remarkCollectImages, remarkImageToComponent]; if (!isPerformanceBenchmark) { @@ -125,7 +73,7 @@ export async function getRemarkPlugins(mdxOptions: MdxOptions): Promise<Pluggabl return remarkPlugins; } -export function getRehypePlugins(mdxOptions: MdxOptions): PluggableList { +function getRehypePlugins(mdxOptions: MdxOptions): PluggableList { let rehypePlugins: PluggableList = [ // ensure `data.meta` is preserved in `properties.metastring` for rehype syntax highlighters rehypeMetaString, @@ -152,38 +100,9 @@ export function getRehypePlugins(mdxOptions: MdxOptions): PluggableList { return rehypePlugins; } -/** - * Check if estree entry is "import.meta.env.VARIABLE" - * If it is, return the variable name (i.e. "VARIABLE") - */ -function getImportMetaEnvVariableName(node: MemberExpression): string | Error { - try { - // check for ".[ANYTHING]" - if (node.object.type !== 'MemberExpression' || node.property.type !== 'Identifier') - return new Error(); - - const nestedExpression = node.object; - // check for ".env" - if (nestedExpression.property.type !== 'Identifier' || nestedExpression.property.name !== 'env') - return new Error(); - - const envExpression = nestedExpression.object; - // check for ".meta" - if ( - envExpression.type !== 'MetaProperty' || - envExpression.property.type !== 'Identifier' || - envExpression.property.name !== 'meta' - ) - return new Error(); - - // check for "import" - 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'); - } +function getRecmaPlugins( + mdxOptions: MdxOptions, + importMetaEnv: Record<string, any> +): PluggableList { + return [...(mdxOptions.recmaPlugins ?? []), [recmaInjectImportMetaEnv, { importMetaEnv }]]; } |