diff --git a/.changeset/slimy-cobras-end.md b/.changeset/slimy-cobras-end.md new file mode 100644 index 000000000000..e87571c2ee11 --- /dev/null +++ b/.changeset/slimy-cobras-end.md @@ -0,0 +1,7 @@ +--- +"@astrojs/mdx": major +--- + +Allows integrations after the MDX integration to update `markdown.remarkPlugins` and `markdown.rehypePlugins`, and have the plugins work in MDX too. + +If you rely on the ordering before to not add remark/rehype plugins for MDX, you need to configure `@astrojs/mdx` with `extendMarkdownConfig: false` and explicitly specify the `remarkPlugins` and `rehypePlugins` options instead. diff --git a/packages/integrations/mdx/src/index.ts b/packages/integrations/mdx/src/index.ts index cc831704ff9e..3aaed8787585 100644 --- a/packages/integrations/mdx/src/index.ts +++ b/packages/integrations/mdx/src/index.ts @@ -29,6 +29,10 @@ type SetupHookParams = HookParameters<'astro:config:setup'> & { }; export default function mdx(partialMdxOptions: Partial = {}): AstroIntegration { + // @ts-expect-error Temporarily assign an empty object here, which will be re-assigned by the + // `astro:config:done` hook later. This is so that `vitePluginMdx` can get hold of a reference earlier. + let mdxOptions: MdxOptions = {}; + return { name: '@astrojs/mdx', hooks: { @@ -58,21 +62,30 @@ export default function mdx(partialMdxOptions: Partial = {}): AstroI handlePropagation: true, }); + updateConfig({ + vite: { + plugins: [vitePluginMdx(mdxOptions), vitePluginMdxPostprocess(config)], + }, + }); + }, + 'astro:config:done': ({ config }) => { + // We resolve the final MDX options here so that other integrations have a chance to modify + // `config.markdown` before we access it const extendMarkdownConfig = partialMdxOptions.extendMarkdownConfig ?? defaultMdxOptions.extendMarkdownConfig; - const mdxOptions = applyDefaultOptions({ + const resolvedMdxOptions = applyDefaultOptions({ options: partialMdxOptions, defaults: markdownConfigToMdxOptions( extendMarkdownConfig ? config.markdown : markdownConfigDefaults ), }); - updateConfig({ - vite: { - plugins: [vitePluginMdx(mdxOptions), vitePluginMdxPostprocess(config)], - }, - }); + // Mutate `mdxOptions` so that `vitePluginMdx` can reference the actual options + Object.assign(mdxOptions, resolvedMdxOptions); + // @ts-expect-error After we assign, we don't need to reference `mdxOptions` in this context anymore. + // Re-assign it so that the garbage can be collected later. + mdxOptions = {}; }, }, }; @@ -81,7 +94,8 @@ export default function mdx(partialMdxOptions: Partial = {}): AstroI const defaultMdxOptions = { extendMarkdownConfig: true, recmaPlugins: [], -}; + optimize: false, +} satisfies Partial; function markdownConfigToMdxOptions(markdownConfig: typeof markdownConfigDefaults): MdxOptions { return { @@ -90,7 +104,6 @@ function markdownConfigToMdxOptions(markdownConfig: typeof markdownConfigDefault remarkPlugins: ignoreStringPlugins(markdownConfig.remarkPlugins), rehypePlugins: ignoreStringPlugins(markdownConfig.rehypePlugins), remarkRehype: (markdownConfig.remarkRehype as any) ?? {}, - optimize: false, }; } diff --git a/packages/integrations/mdx/src/vite-plugin-mdx.ts b/packages/integrations/mdx/src/vite-plugin-mdx.ts index 4d3479129238..1b966ecd2a30 100644 --- a/packages/integrations/mdx/src/vite-plugin-mdx.ts +++ b/packages/integrations/mdx/src/vite-plugin-mdx.ts @@ -17,6 +17,10 @@ export function vitePluginMdx(mdxOptions: MdxOptions): Plugin { processor = undefined; }, configResolved(resolved) { + // `mdxOptions` should be populated at this point, but `astro sync` doesn't call `astro:config:done` :( + // Workaround this for now by skipping here. `astro sync` shouldn't call the `transform()` hook here anyways. + if (Object.keys(mdxOptions).length === 0) return; + processor = createMdxProcessor(mdxOptions, { sourcemap: !!resolved.build.sourcemap, }); diff --git a/packages/integrations/mdx/test/mdx-plugins.test.js b/packages/integrations/mdx/test/mdx-plugins.test.js index 6b15884fb712..06e4cc3ad654 100644 --- a/packages/integrations/mdx/test/mdx-plugins.test.js +++ b/packages/integrations/mdx/test/mdx-plugins.test.js @@ -64,6 +64,30 @@ describe('MDX plugins', () => { assert.notEqual(selectRehypeExample(document), null); }); + it('supports custom rehype plugins from integrations', async () => { + const fixture = await buildFixture({ + integrations: [ + mdx(), + { + name: 'test', + hooks: { + 'astro:config:setup': ({ updateConfig }) => { + updateConfig({ + markdown: { + rehypePlugins: [rehypeExamplePlugin], + } + }); + } + } + } + ], + }); + const html = await fixture.readFile(FILE); + const { document } = parseHTML(html); + + assert.notEqual(selectRehypeExample(document), null); + }); + it('supports custom rehype plugins with namespaced attributes', async () => { const fixture = await buildFixture({ integrations: [