delucis / astro-auto-import

Auto-import components in Astro projects
95 stars 3 forks source link

Import and Export for overriding MDX HTML defaults #30

Open jonsage opened 1 year ago

jonsage commented 1 year ago

Kizu wrote this https://blog.kizu.dev/astro-mdx-components/ and I recommend he take a look at your integration to simplify things, however the part I think your integration doesn't handle when it comes to overriding default HTML elements in MDX is the export step.

I was wondering whether it would be simple to extend functionality to AutoImport to both import and re-export components for MDX and this way it would possible to provide overrides for HTML elements like <a> and <h1> etc.

delucis commented 1 year ago

Interesting idea! Might be possible I think. What do you think the API could look like? I guess we’d need a way to know what HTML elements an auto-imported component should map to.

teinett commented 1 year ago

Something like this:

integrations: [
        AutoImport({
            imports: [
                {
                   blockquote: "@/components/MDXquote.astro"
                },
            ],
        }),
        mdx(),
    ],
jonsage commented 1 year ago

Interesting idea! Might be possible I think. What do you think the API could look like? I guess we’d need a way to know what HTML elements an auto-imported component should map to.

This is a bit new to me, but something I'm an interested to see working to streamline working with MDX - AutoImport already helps a lot with that - it might even make a great "built-in" feature of Astro in the future tbh.

So, my naive ideas to avoid breaking changes:

integrations: [
    AutoImport({
        imports: [
            {
                // third item named export
                './src/components/Link.astro': [['default', 'Link', 'a']],
            },
            // OR
            {
                // second item config object
                './src/components/Link.astro': [['default', { import_as: 'Link', export_as: 'a' }]],
            },
        ]
    }),
    // OR (in a future ideal world)
    mdx({
        components: {
            a: 'Link',
            // OR (to avoid having to previously AutoImport)
            h1: './src/components/Heading.astro',
            // OR (to avoid having to previously AutoImport, and import a named export)
            p: ['./src/components/Paragraph.astro', 'default']
        }
    }),
]

Also, I found this issue https://github.com/orgs/mdx-js/discussions/2271

EDIT: just adding the link to the existing mdx() integration config object https://docs.astro.build/en/guides/integrations-guide/mdx/#options-inherited-from-markdown-config

endigo9740 commented 10 months ago

Hey @delucis, sorry for the ping, but I've been researching this issue all day and your name keeps coming up when it comes to Astro and MDX.

I'm the creator and core maintainer for Skeleton: https://www.skeleton.dev/

We're currently porting our doc site to Astro and really interested in the prospect of custom HTML components in MDX: https://docs.astro.build/en/guides/markdown-content/#assigning-custom-components-to-html-elements

The reason being, we use semantic utility classes to assign all our typography styles: https://www.skeleton.dev/elements/typography

<h1 class="h1">Skeleton H1</h1>
<a class="anchor" href="/">Anchor</a>
<blockquote class="blockquote">Skeleton</blockquote>

Because of this, we'll have dozens of doc pages that will require replacing the MDX output elements. So far, the cleanest solution I've come up with is doing something like this:

/pages/**/index.mdx

---
---

import componentSet from '@components/mdx/index'; // index.ts
export const components = componentSet;

/components/mdx/index.ts

import Blockquote from './Blockquote.astro';
// ...(repeat for every component)...

export const componentSet = {
    blockquote: Blockquote,
    // ...(repeat for every component)...
};

export default componentSet;

I've come across your astro-auto-import tool here. While this can remove one of the two lines per page, that still leave room for error if someone misses this.

So I was curious if you're aware of any movement on the proposal from @jonsage above, or have any sage advice on how I might approach this. Thanks in advance!

delucis commented 10 months ago

So I was curious if you're aware of any movement on the proposal from @jonsage above, or have any sage advice on how I might approach this. Thanks in advance!

A couple of people expressed interest in submitting PRs, but nothing has materialised just yet. But I’d be happy to guide a PR if you’re up for trying to implement it!

As a pointer, like you show, we’d need a way to generate the export const components = {}; statement and auto import any required components.

I’d suggest a config API something like:

AutoImport({
  imports: [
    // as currently supported, an array of auto-imported components
  ],
  // a map of components, more or less 1:1 with what ends up in MDX’s `components` export:
  defaultComponents: {
    // quick syntax for default exports
    p: './src/CustomParagraph.astro',
    // some kind of syntax for named exports?
    blockquote: { name: 'CustomBlockquote', from: './src/Components.ts' },
  },
});

Internally we’d then need to build this into a string like:

import p from './src/CustomParagraph.astro';
import { CustomBlockquote as blockquote } from './src/Components.ts';

export const components = {
  p,
  blockquote,
};

A corner case that would need checking would be making sure we don’t have name clashes with anything in import I guess. Or alternatively we insure against that with some namespacing like:

import AUTO_IMPORT_p from './src/CustomParagraph.astro';
import { CustomBlockquote as AUTO_IMPORT_blockquote } from './src/Components.ts';

export const components = {
  p: AUTO_IMPORT_p,
  blockquote: AUTO_IMPORT_blockquote,
};

And inject these imports much like we already do.