diff options
author | 2023-04-13 11:54:40 +0200 | |
---|---|---|
committer | 2023-04-13 11:54:40 +0200 | |
commit | 2511d58d586af080a78e5ef8a63020b3e17770db (patch) | |
tree | 1afdd2bd6e1cffb4d321a6a27f1b7e11233f8363 /packages/integrations/mdx/src | |
parent | 948a6d7be0c76fd1dd8550270bd29821075f799c (diff) | |
download | astro-2511d58d586af080a78e5ef8a63020b3e17770db.tar.gz astro-2511d58d586af080a78e5ef8a63020b3e17770db.tar.zst astro-2511d58d586af080a78e5ef8a63020b3e17770db.zip |
feat(mdx): Add support for turning ![]() into <Image> (#6824)
Diffstat (limited to 'packages/integrations/mdx/src')
-rw-r--r-- | packages/integrations/mdx/src/plugins.ts | 5 | ||||
-rw-r--r-- | packages/integrations/mdx/src/remark-images-to-component.ts | 98 |
2 files changed, 101 insertions, 2 deletions
diff --git a/packages/integrations/mdx/src/plugins.ts b/packages/integrations/mdx/src/plugins.ts index 12b8f2bd3..56fbbf837 100644 --- a/packages/integrations/mdx/src/plugins.ts +++ b/packages/integrations/mdx/src/plugins.ts @@ -1,4 +1,4 @@ -import { rehypeHeadingIds } from '@astrojs/markdown-remark'; +import { rehypeHeadingIds, remarkCollectImages } from '@astrojs/markdown-remark'; import { InvalidAstroDataError, safelyGetAstroData, @@ -16,6 +16,7 @@ import type { VFile } from 'vfile'; import type { MdxOptions } from './index.js'; import { rehypeInjectHeadingsExport } from './rehype-collect-headings.js'; import rehypeMetaString from './rehype-meta-string.js'; +import { remarkImageToComponent } from './remark-images-to-component.js'; import remarkPrism from './remark-prism.js'; import remarkShiki from './remark-shiki.js'; import { jsToTreeNode } from './utils.js'; @@ -99,7 +100,7 @@ export async function getRemarkPlugins( mdxOptions: MdxOptions, config: AstroConfig ): Promise<MdxRollupPluginOptions['remarkPlugins']> { - let remarkPlugins: PluggableList = []; + let remarkPlugins: PluggableList = [...(config.experimental.assets ? [remarkCollectImages, remarkImageToComponent] : [])]; if (!isPerformanceBenchmark) { if (mdxOptions.gfm) { diff --git a/packages/integrations/mdx/src/remark-images-to-component.ts b/packages/integrations/mdx/src/remark-images-to-component.ts new file mode 100644 index 000000000..8a3166f49 --- /dev/null +++ b/packages/integrations/mdx/src/remark-images-to-component.ts @@ -0,0 +1,98 @@ +import type { MarkdownVFile } from '@astrojs/markdown-remark'; +import { type Image, type Parent } from 'mdast'; +import type { MdxJsxFlowElement, MdxjsEsm } from 'mdast-util-mdx'; +import { visit } from 'unist-util-visit'; +import { jsToTreeNode } from './utils.js'; + +export function remarkImageToComponent() { + return function (tree: any, file: MarkdownVFile) { + if (!file.data.imagePaths) return; + + const importsStatements: MdxjsEsm[] = []; + const importedImages = new Map<string, string>(); + + visit(tree, 'image', (node: Image, index: number | null, parent: Parent | null) => { + // Use the imagePaths set from the remark-collect-images so we don't have to duplicate the logic for + // checking if an image should be imported or not + if (file.data.imagePaths?.has(node.url)) { + let importName = importedImages.get(node.url); + + // If we haven't already imported this image, add an import statement + if (!importName) { + importName = `__${importedImages.size}_${node.url.replace(/\W/g, '_')}__`; + + importsStatements.push({ + type: 'mdxjsEsm', + value: '', + data: { + estree: { + type: 'Program', + sourceType: 'module', + body: [ + { + type: 'ImportDeclaration', + source: { type: 'Literal', value: node.url, raw: JSON.stringify(node.url) }, + specifiers: [ + { + type: 'ImportDefaultSpecifier', + local: { type: 'Identifier', name: importName }, + }, + ], + }, + ], + }, + }, + }); + importedImages.set(node.url, importName); + } + + // Build a component that's equivalent to <Image src={importName} alt={node.alt} title={node.title} /> + const componentElement: MdxJsxFlowElement = { + name: '__AstroImage__', + type: 'mdxJsxFlowElement', + attributes: [ + { + name: 'src', + type: 'mdxJsxAttribute', + value: { + type: 'mdxJsxAttributeValueExpression', + value: importName, + data: { + estree: { + type: 'Program', + sourceType: 'module', + comments: [], + body: [ + { + type: 'ExpressionStatement', + expression: { type: 'Identifier', name: importName }, + }, + ], + }, + }, + }, + }, + { name: 'alt', type: 'mdxJsxAttribute', value: node.alt || '' }, + ], + children: [], + }; + + if (node.title) { + componentElement.attributes.push({ + type: 'mdxJsxAttribute', + name: 'title', + value: node.title, + }); + } + + parent!.children.splice(index!, 1, componentElement); + } + }); + + // Add all the import statements to the top of the file for the images + tree.children.unshift(...importsStatements); + + // Add an import statement for the Astro Image component, we rename it to avoid conflicts + tree.children.unshift(jsToTreeNode(`import { Image as __AstroImage__ } from "astro:assets";`)); + }; +} |