summaryrefslogtreecommitdiff
path: root/packages/integrations/mdx/src/remark-images-to-component.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/integrations/mdx/src/remark-images-to-component.ts')
-rw-r--r--packages/integrations/mdx/src/remark-images-to-component.ts98
1 files changed, 98 insertions, 0 deletions
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";`));
+ };
+}