summaryrefslogtreecommitdiff
path: root/packages/integrations/mdx/src
diff options
context:
space:
mode:
authorGravatar Ben Holmes <hey@bholmes.dev> 2022-08-30 13:38:35 -0400
committerGravatar GitHub <noreply@github.com> 2022-08-30 13:38:35 -0400
commit8f8dff4d339a3a12ee155d81a97132032ef3b622 (patch)
tree0581df292a1003288b0dbd7a3f9246f25b5a3cce /packages/integrations/mdx/src
parente905784bf12ef45093078404d3d07f01e32638ca (diff)
downloadastro-8f8dff4d339a3a12ee155d81a97132032ef3b622.tar.gz
astro-8f8dff4d339a3a12ee155d81a97132032ef3b622.tar.zst
astro-8f8dff4d339a3a12ee155d81a97132032ef3b622.zip
[MDX] Extend Markdown plugin config, with customization options (#4504)
* test: new combined remark / rehype suite * fix: use with-plugins fixture * chore: remove old mdx plugin tests * docs: add JS docs * docs: update README with thorough example * chore: changeset * fix: add "extends" error message * fix: ignore string-based plugins in md * feat: add warning log for string plugins * docs: highlight `extendPlugins` Co-authored-by: Sarah Rainsberger <sarah@rainsberger.ca> * nit: highlight "extendPlugins" * fix: md plugins type check * chore: "defaults" -> "astroDefaults" * nit: info log when inheriting markdown plugins * refactor: one big log on new behavior * dan: dan nit Co-authored-by: Sarah Rainsberger <sarah@rainsberger.ca>
Diffstat (limited to 'packages/integrations/mdx/src')
-rw-r--r--packages/integrations/mdx/src/index.ts94
-rw-r--r--packages/integrations/mdx/src/utils.ts127
2 files changed, 157 insertions, 64 deletions
diff --git a/packages/integrations/mdx/src/index.ts b/packages/integrations/mdx/src/index.ts
index 6516e4b12..0a2ddc8d3 100644
--- a/packages/integrations/mdx/src/index.ts
+++ b/packages/integrations/mdx/src/index.ts
@@ -1,30 +1,19 @@
-import { compile as mdxCompile, nodeTypes } from '@mdx-js/mdx';
+import { compile as mdxCompile } from '@mdx-js/mdx';
import mdxPlugin, { Options as MdxRollupPluginOptions } from '@mdx-js/rollup';
-import type { AstroConfig, AstroIntegration } from 'astro';
+import type { AstroIntegration } from 'astro';
import { parse as parseESM } from 'es-module-lexer';
-import rehypeRaw from 'rehype-raw';
-import remarkGfm from 'remark-gfm';
-import remarkSmartypants from 'remark-smartypants';
import { VFile } from 'vfile';
import type { Plugin as VitePlugin } from 'vite';
-import { rehypeApplyFrontmatterExport, remarkInitializeAstroData } from './astro-data-utils.js';
-import rehypeCollectHeadings from './rehype-collect-headings.js';
-import remarkPrism from './remark-prism.js';
-import remarkShiki from './remark-shiki.js';
-import { getFileInfo, parseFrontmatter } from './utils.js';
-
-type WithExtends<T> = T | { extends: T };
-
-type MdxOptions = {
- remarkPlugins?: WithExtends<MdxRollupPluginOptions['remarkPlugins']>;
- rehypePlugins?: WithExtends<MdxRollupPluginOptions['rehypePlugins']>;
-};
-
-const DEFAULT_REMARK_PLUGINS: MdxRollupPluginOptions['remarkPlugins'] = [
- remarkGfm,
- remarkSmartypants,
-];
-const DEFAULT_REHYPE_PLUGINS: MdxRollupPluginOptions['rehypePlugins'] = [];
+import { bold, blue } from 'kleur/colors';
+import { rehypeApplyFrontmatterExport } from './astro-data-utils.js';
+import {
+ getFileInfo,
+ parseFrontmatter,
+ handleExtendsNotSupported,
+ getRehypePlugins,
+ getRemarkPlugins,
+} from './utils.js';
+import type { MdxOptions } from './utils.js';
const RAW_CONTENT_ERROR =
'MDX does not support rawContent()! If you need to read the Markdown contents to calculate values (ex. reading time), we suggest injecting frontmatter via remark plugins. Learn more on our docs: https://docs.astro.build/en/guides/integrations-guide/mdx/#inject-frontmatter-via-remark-or-rehype-plugins';
@@ -32,51 +21,32 @@ const RAW_CONTENT_ERROR =
const COMPILED_CONTENT_ERROR =
'MDX does not support compiledContent()! If you need to read the HTML contents to calculate values (ex. reading time), we suggest injecting frontmatter via rehype plugins. Learn more on our docs: https://docs.astro.build/en/guides/integrations-guide/mdx/#inject-frontmatter-via-remark-or-rehype-plugins';
-function handleExtends<T>(config: WithExtends<T[] | undefined>, defaults: T[] = []): T[] {
- if (Array.isArray(config)) return config;
-
- return [...defaults, ...(config?.extends ?? [])];
-}
-
-async function getRemarkPlugins(
- mdxOptions: MdxOptions,
- config: AstroConfig
-): Promise<MdxRollupPluginOptions['remarkPlugins']> {
- let remarkPlugins = [
- // Initialize vfile.data.astroExports before all plugins are run
- remarkInitializeAstroData,
- ...handleExtends(mdxOptions.remarkPlugins, DEFAULT_REMARK_PLUGINS),
- ];
- if (config.markdown.syntaxHighlight === 'shiki') {
- remarkPlugins.push([await remarkShiki(config.markdown.shikiConfig)]);
- }
- if (config.markdown.syntaxHighlight === 'prism') {
- remarkPlugins.push(remarkPrism);
- }
- return remarkPlugins;
-}
-
-function getRehypePlugins(
- mdxOptions: MdxOptions,
- config: AstroConfig
-): MdxRollupPluginOptions['rehypePlugins'] {
- let rehypePlugins = [
- [rehypeRaw, { passThrough: nodeTypes }] as any,
- ...handleExtends(mdxOptions.rehypePlugins, DEFAULT_REHYPE_PLUGINS),
- ];
-
- // getHeadings() is guaranteed by TS, so we can't allow user to override
- rehypePlugins.unshift(rehypeCollectHeadings);
-
- return rehypePlugins;
-}
-
export default function mdx(mdxOptions: MdxOptions = {}): AstroIntegration {
return {
name: '@astrojs/mdx',
hooks: {
'astro:config:setup': async ({ updateConfig, config, addPageExtension, command }: any) => {
addPageExtension('.mdx');
+ mdxOptions.extendPlugins ??= 'markdown';
+
+ handleExtendsNotSupported(mdxOptions.remarkPlugins);
+ handleExtendsNotSupported(mdxOptions.rehypePlugins);
+
+ // TODO: remove for 1.0. Shipping to ease migration to new minor
+ if (
+ mdxOptions.extendPlugins === 'markdown' &&
+ (config.markdown.rehypePlugins?.length || config.markdown.remarkPlugins?.length)
+ ) {
+ console.log(
+ blue(`[MDX] Now inheriting remark and rehype plugins from "markdown" config.`)
+ );
+ console.log(
+ `If you applied a plugin to both your Markdown and MDX configs, we suggest ${bold(
+ 'removing the duplicate MDX entry.'
+ )}`
+ );
+ console.log(`See "extendPlugins" option to configure this behavior.`);
+ }
const mdxPluginOpts: MdxRollupPluginOptions = {
remarkPlugins: await getRemarkPlugins(mdxOptions, config),
diff --git a/packages/integrations/mdx/src/utils.ts b/packages/integrations/mdx/src/utils.ts
index f5135ebc2..dc7879dd8 100644
--- a/packages/integrations/mdx/src/utils.ts
+++ b/packages/integrations/mdx/src/utils.ts
@@ -1,10 +1,33 @@
import type { Options as AcornOpts } from 'acorn';
-import { parse } from 'acorn';
import type { AstroConfig, SSRError } from 'astro';
import type { MdxjsEsm } from 'mdast-util-mdx';
-
+import type { PluggableList } from '@mdx-js/mdx/lib/core.js';
+import type { Options as MdxRollupPluginOptions } from '@mdx-js/rollup';
+import { bold, yellow } from 'kleur/colors';
+import { nodeTypes } from '@mdx-js/mdx';
+import { parse } from 'acorn';
+import rehypeRaw from 'rehype-raw';
+import remarkGfm from 'remark-gfm';
+import remarkSmartypants from 'remark-smartypants';
+import { remarkInitializeAstroData } from './astro-data-utils.js';
+import rehypeCollectHeadings from './rehype-collect-headings.js';
+import remarkPrism from './remark-prism.js';
+import remarkShiki from './remark-shiki.js';
import matter from 'gray-matter';
+export type MdxOptions = {
+ remarkPlugins?: PluggableList;
+ rehypePlugins?: PluggableList;
+ /**
+ * Choose which remark and rehype plugins to inherit, if any.
+ *
+ * - "markdown" (default) - inherit your project’s markdown plugin config ([see Markdown docs](https://docs.astro.build/en/guides/markdown-content/#configuring-markdown))
+ * - "astroDefaults" - inherit Astro’s default plugins only ([see defaults](https://docs.astro.build/en/reference/configuration-reference/#markdownextenddefaultplugins))
+ * - false - do not inherit any plugins
+ */
+ extendPlugins?: 'markdown' | 'astroDefaults' | false;
+};
+
function appendForwardSlash(path: string) {
return path.endsWith('/') ? path : path + '/';
}
@@ -14,6 +37,9 @@ interface FileInfo {
fileUrl: string;
}
+const DEFAULT_REMARK_PLUGINS: PluggableList = [remarkGfm, remarkSmartypants];
+const DEFAULT_REHYPE_PLUGINS: PluggableList = [];
+
/** @see 'vite-plugin-utils' for source */
export function getFileInfo(id: string, config: AstroConfig): FileInfo {
const sitePathname = appendForwardSlash(
@@ -83,3 +109,100 @@ export function jsToTreeNode(
},
};
}
+
+export async function getRemarkPlugins(
+ mdxOptions: MdxOptions,
+ config: AstroConfig
+): Promise<MdxRollupPluginOptions['remarkPlugins']> {
+ let remarkPlugins: PluggableList = [
+ // Set "vfile.data.astro" for plugins to inject frontmatter
+ remarkInitializeAstroData,
+ ];
+ switch (mdxOptions.extendPlugins) {
+ case false:
+ break;
+ case 'astroDefaults':
+ remarkPlugins = [...remarkPlugins, ...DEFAULT_REMARK_PLUGINS];
+ break;
+ default:
+ remarkPlugins = [
+ ...remarkPlugins,
+ ...(config.markdown.extendDefaultPlugins ? DEFAULT_REMARK_PLUGINS : []),
+ ...ignoreStringPlugins(config.markdown.remarkPlugins ?? []),
+ ];
+ break;
+ }
+ if (config.markdown.syntaxHighlight === 'shiki') {
+ remarkPlugins.push([await remarkShiki(config.markdown.shikiConfig)]);
+ }
+ if (config.markdown.syntaxHighlight === 'prism') {
+ remarkPlugins.push(remarkPrism);
+ }
+
+ remarkPlugins = [...remarkPlugins, ...(mdxOptions.remarkPlugins ?? [])];
+ return remarkPlugins;
+}
+
+export function getRehypePlugins(
+ mdxOptions: MdxOptions,
+ config: AstroConfig
+): MdxRollupPluginOptions['rehypePlugins'] {
+ let rehypePlugins: PluggableList = [
+ // getHeadings() is guaranteed by TS, so we can't allow user to override
+ rehypeCollectHeadings,
+ // rehypeRaw allows custom syntax highlighters to work without added config
+ [rehypeRaw, { passThrough: nodeTypes }] as any,
+ ];
+ switch (mdxOptions.extendPlugins) {
+ case false:
+ break;
+ case 'astroDefaults':
+ rehypePlugins = [...rehypePlugins, ...DEFAULT_REHYPE_PLUGINS];
+ break;
+ default:
+ rehypePlugins = [
+ ...rehypePlugins,
+ ...(config.markdown.extendDefaultPlugins ? DEFAULT_REHYPE_PLUGINS : []),
+ ...ignoreStringPlugins(config.markdown.rehypePlugins ?? []),
+ ];
+ break;
+ }
+
+ rehypePlugins = [...rehypePlugins, ...(mdxOptions.rehypePlugins ?? [])];
+ return rehypePlugins;
+}
+
+function ignoreStringPlugins(plugins: any[]) {
+ let validPlugins: PluggableList = [];
+ let hasInvalidPlugin = false;
+ for (const plugin of plugins) {
+ if (typeof plugin === 'string') {
+ console.warn(yellow(`[MDX] ${bold(plugin)} not applied.`));
+ hasInvalidPlugin = true;
+ } else if (Array.isArray(plugin) && typeof plugin[0] === 'string') {
+ console.warn(yellow(`[MDX] ${bold(plugin[0])} not applied.`));
+ hasInvalidPlugin = true;
+ } else {
+ validPlugins.push(plugin);
+ }
+ }
+ if (hasInvalidPlugin) {
+ console.warn(
+ `To inherit Markdown plugins in MDX, please use explicit imports in your config instead of "strings." See Markdown docs: https://docs.astro.build/en/guides/markdown-content/#markdown-plugins`
+ );
+ }
+ return validPlugins;
+}
+
+// TODO: remove for 1.0
+export function handleExtendsNotSupported(pluginConfig: any) {
+ if (
+ typeof pluginConfig === 'object' &&
+ pluginConfig !== null &&
+ (pluginConfig as any).hasOwnProperty('extends')
+ ) {
+ throw new Error(
+ `[MDX] The "extends" plugin option is no longer supported! Astro now extends your project's \`markdown\` plugin configuration by default. To customize this behavior, see the \`extendPlugins\` option instead: https://docs.astro.build/en/guides/integrations-guide/mdx/#extendplugins`
+ );
+ }
+}