From a3e95dbeb04bb09e4253f9b690c1627b3ce7e90d Mon Sep 17 00:00:00 2001 From: Bjorn Lu Date: Tue, 23 Apr 2024 23:24:34 +0800 Subject: [PATCH] Move MDX Vite plugin to new file (#10854) --- packages/integrations/mdx/src/index.ts | 93 +------------------ .../integrations/mdx/src/vite-plugin-mdx.ts | 88 ++++++++++++++++++ 2 files changed, 93 insertions(+), 88 deletions(-) create mode 100644 packages/integrations/mdx/src/vite-plugin-mdx.ts diff --git a/packages/integrations/mdx/src/index.ts b/packages/integrations/mdx/src/index.ts index e48d0d8f19..fc1d92da48 100644 --- a/packages/integrations/mdx/src/index.ts +++ b/packages/integrations/mdx/src/index.ts @@ -1,16 +1,14 @@ import fs from 'node:fs/promises'; import { fileURLToPath } from 'node:url'; -import { markdownConfigDefaults, setVfileFrontmatter } from '@astrojs/markdown-remark'; -import type { AstroIntegration, ContentEntryType, HookParameters, SSRError } from 'astro'; +import { markdownConfigDefaults } from '@astrojs/markdown-remark'; +import type { AstroIntegration, ContentEntryType, HookParameters } from 'astro'; import astroJSXRenderer from 'astro/jsx/renderer.js'; import type { Options as RemarkRehypeOptions } from 'remark-rehype'; import type { PluggableList } from 'unified'; -import { VFile } from 'vfile'; -import type { Plugin as VitePlugin } from 'vite'; -import { createMdxProcessor } from './plugins.js'; import type { OptimizeOptions } from './rehype-optimize-static.js'; -import { getFileInfo, ignoreStringPlugins, parseFrontmatter } from './utils.js'; +import { ignoreStringPlugins, parseFrontmatter } from './utils.js'; import { vitePluginMdxPostprocess } from './vite-plugin-mdx-postprocess.js'; +import { vitePluginMdx } from './vite-plugin-mdx.js'; export type MdxOptions = Omit & { extendMarkdownConfig: boolean; @@ -70,90 +68,9 @@ export default function mdx(partialMdxOptions: Partial = {}): AstroI ), }); - let processor: ReturnType | undefined; - updateConfig({ vite: { - plugins: [ - { - name: '@mdx-js/rollup', - enforce: 'pre', - buildEnd() { - processor = undefined; - }, - configResolved(resolved) { - processor = createMdxProcessor(mdxOptions, { - sourcemap: !!resolved.build.sourcemap, - }); - - // HACK: move ourselves before Astro's JSX plugin to transform things in the right order - const jsxPluginIndex = resolved.plugins.findIndex((p) => p.name === 'astro:jsx'); - if (jsxPluginIndex !== -1) { - const myPluginIndex = resolved.plugins.findIndex( - (p) => p.name === '@mdx-js/rollup' - ); - if (myPluginIndex !== -1) { - const myPlugin = resolved.plugins[myPluginIndex]; - // @ts-ignore-error ignore readonly annotation - resolved.plugins.splice(myPluginIndex, 1); - // @ts-ignore-error ignore readonly annotation - resolved.plugins.splice(jsxPluginIndex, 0, myPlugin); - } - } - }, - async resolveId(source, importer, options) { - if (importer?.endsWith('.mdx') && source[0] !== '/') { - let resolved = await this.resolve(source, importer, options); - if (!resolved) resolved = await this.resolve('./' + source, importer, options); - return resolved; - } - }, - // Override transform to alter code before MDX compilation - // ex. inject layouts - async transform(_, id) { - if (!id.endsWith('.mdx')) return; - - // Read code from file manually to prevent Vite from parsing `import.meta.env` expressions - const { fileId } = getFileInfo(id, config); - const code = await fs.readFile(fileId, 'utf-8'); - - const { data: frontmatter, content: pageContent } = parseFrontmatter(code, id); - - const vfile = new VFile({ value: pageContent, path: id }); - // Ensure `data.astro` is available to all remark plugins - setVfileFrontmatter(vfile, frontmatter); - - // `processor` is initialized in `configResolved`, and removed in `buildEnd`. `transform` - // should be called in between those two lifecycle, so this error should never happen - if (!processor) { - return this.error( - 'MDX processor is not initialized. This is an internal error. Please file an issue.' - ); - } - - try { - const compiled = await processor.process(vfile); - - return { - code: String(compiled.value), - map: compiled.map, - }; - } catch (e: any) { - const err: SSRError = e; - - // For some reason MDX puts the error location in the error's name, not very useful for us. - err.name = 'MDXError'; - err.loc = { file: fileId, line: e.line, column: e.column }; - - // For another some reason, MDX doesn't include a stack trace. Weird - Error.captureStackTrace(err); - - throw err; - } - }, - }, - vitePluginMdxPostprocess(config), - ] as VitePlugin[], + plugins: [vitePluginMdx(config, mdxOptions), vitePluginMdxPostprocess(config)], }, }); }, diff --git a/packages/integrations/mdx/src/vite-plugin-mdx.ts b/packages/integrations/mdx/src/vite-plugin-mdx.ts new file mode 100644 index 0000000000..0185f37fb1 --- /dev/null +++ b/packages/integrations/mdx/src/vite-plugin-mdx.ts @@ -0,0 +1,88 @@ +import fs from 'node:fs/promises'; +import { setVfileFrontmatter } from '@astrojs/markdown-remark'; +import type { AstroConfig, SSRError } from 'astro'; +import { VFile } from 'vfile'; +import type { Plugin } from 'vite'; +import { createMdxProcessor } from './plugins.js'; +import { getFileInfo, parseFrontmatter } from './utils.js'; +import type { MdxOptions } from './index.js'; + +export function vitePluginMdx(astroConfig: AstroConfig, mdxOptions: MdxOptions): Plugin { + let processor: ReturnType | undefined; + + return { + name: '@mdx-js/rollup', + enforce: 'pre', + buildEnd() { + processor = undefined; + }, + configResolved(resolved) { + processor = createMdxProcessor(mdxOptions, { + sourcemap: !!resolved.build.sourcemap, + }); + + // HACK: move ourselves before Astro's JSX plugin to transform things in the right order + const jsxPluginIndex = resolved.plugins.findIndex((p) => p.name === 'astro:jsx'); + if (jsxPluginIndex !== -1) { + const myPluginIndex = resolved.plugins.findIndex((p) => p.name === '@mdx-js/rollup'); + if (myPluginIndex !== -1) { + const myPlugin = resolved.plugins[myPluginIndex]; + // @ts-ignore-error ignore readonly annotation + resolved.plugins.splice(myPluginIndex, 1); + // @ts-ignore-error ignore readonly annotation + resolved.plugins.splice(jsxPluginIndex, 0, myPlugin); + } + } + }, + async resolveId(source, importer, options) { + if (importer?.endsWith('.mdx') && source[0] !== '/') { + let resolved = await this.resolve(source, importer, options); + if (!resolved) resolved = await this.resolve('./' + source, importer, options); + return resolved; + } + }, + // Override transform to alter code before MDX compilation + // ex. inject layouts + async transform(_, id) { + if (!id.endsWith('.mdx')) return; + + // Read code from file manually to prevent Vite from parsing `import.meta.env` expressions + const { fileId } = getFileInfo(id, astroConfig); + const code = await fs.readFile(fileId, 'utf-8'); + + const { data: frontmatter, content: pageContent } = parseFrontmatter(code, id); + + const vfile = new VFile({ value: pageContent, path: id }); + // Ensure `data.astro` is available to all remark plugins + setVfileFrontmatter(vfile, frontmatter); + + // `processor` is initialized in `configResolved`, and removed in `buildEnd`. `transform` + // should be called in between those two lifecycle, so this error should never happen + if (!processor) { + return this.error( + 'MDX processor is not initialized. This is an internal error. Please file an issue.' + ); + } + + try { + const compiled = await processor.process(vfile); + + return { + code: String(compiled.value), + map: compiled.map, + }; + } catch (e: any) { + const err: SSRError = e; + + // For some reason MDX puts the error location in the error's name, not very useful for us. + err.name = 'MDXError'; + err.loc = { file: fileId, line: e.line, column: e.column }; + + // For another some reason, MDX doesn't include a stack trace. Weird + Error.captureStackTrace(err); + + throw err; + } + }, + }; +}