summaryrefslogtreecommitdiff
path: root/packages/integrations/mdx/src
diff options
context:
space:
mode:
authorGravatar Erika <3019731+Princesseuh@users.noreply.github.com> 2023-04-13 11:54:40 +0200
committerGravatar GitHub <noreply@github.com> 2023-04-13 11:54:40 +0200
commit2511d58d586af080a78e5ef8a63020b3e17770db (patch)
tree1afdd2bd6e1cffb4d321a6a27f1b7e11233f8363 /packages/integrations/mdx/src
parent948a6d7be0c76fd1dd8550270bd29821075f799c (diff)
downloadastro-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.ts5
-rw-r--r--packages/integrations/mdx/src/remark-images-to-component.ts98
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";`));
+ };
+}