withastro / astro

The web framework for content-driven websites. ⭐️ Star to support our work!
https://astro.build
Other
45.3k stars 2.37k forks source link

Markdoc `transform()` function overrides custom Astro component #9708

Open tysian opened 7 months ago

tysian commented 7 months ago

Astro Info

Astro                    v4.1.2
Node                     v18.18.0
System                   Linux (x64)
Package Manager          unknown
Output                   static
Adapter                  none
Integrations             @astrojs/markdoc (v0.8.2)

If this issue only occurs in one browser, which browser is a problem?

N/A

Describe the Bug

If transform() function is used inside Markdoc config, it is rendered instead of custom component provided in render property.

I found out that custom component does not work with fence and code nodes.

I have followed this guide, but if I pass ...nodes.fence or ...nodes.code- it doesn't work, because they have transform() function inside.

Also when I pass my own transform function, it overrides custom component too, no matter if I return html using unescapeHTML function or Markdoc Tag instance.

What's the expected result?

Render Astro custom component with transformed data inside.

Link to Minimal Reproducible Example

https://stackblitz.com/edit/github-r2tgyt?file=markdoc.config.mjs

Participation

tysian commented 7 months ago

Hey!

I've done some research and debugging in Markdoc integration. I have found the possible reason why this is happening.

The Renderer.astro component is using Markdoc.transform() function and probably this is why render property is overriden by transform() (this requires some deep dive into Markdoc codebase, and I didn't have much time to do this right now).

render property is a string in Markdoc. In Astro it is an object, that contains information about component that has to be rendered.

I was trying to fix it by using Markdoc.transform() twice. Firstly, I set render property to undefined to run transform() function. Then for the second time, without transform(), but reverting render property from original config.

Unfortunately, this can't be done, because Markdoc.transform() returns a renderable node tree, that can't be transformed for the second time. Also this is probably not the best solution, because we would run this twice, especially that this code runs on runtime.

Maybe there is some way to do this with using different Markdoc methods, but I'm not that familiar with this package.

bholmesdev commented 5 months ago

Hi @tysian! Thanks for reporting. First, I agree that spreading nodes.fence and nodes.code according to the documentation. We should add a check for when render and transform() are used together to ensure the render attribute wins out.

As for running transform and render simultaneously, I don't think this would be possible. render uses a Markdoc transform under-the-hood as you described. Since you cannot transform a node twice, you would need to either use a transform or a render property. Luckily, it's possible to render an Astro component from a transform function manually without using render. This is a more advanced use case for when children need to be transformed before being passed to a component. Our image component uses this capability.