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

Variables doesn't work as expected with Markdoc #9132

Open alexbouchardd opened 9 months ago

alexbouchardd commented 9 months ago

Astro Info

Astro                    v3.4.3
Node                     v18.16.0
System                   macOS (arm64)
Package Manager          yarn
Output                   hybrid
Adapter                  none
Integrations             @astrojs/react
                         @astrojs/markdoc

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

No response

Describe the Bug

I've noticed that the variables in Markdoc only render as expected when used in plain text. Fence, code, headers, etc do not render the variable value.

The fence one is especially curious since the variable doesn't render ONLY when shiki markdoc plugin is enabled

markdoc.config.mjs

import { defineMarkdocConfig, component, nodes } from "@astrojs/markdoc/config";
import shiki from "@astrojs/markdoc/shiki";

export default defineMarkdocConfig({
  extends: [
    shiki({
      // https://github.com/shikijs/shiki/blob/main/docs/themes.md
      theme: "css-variables",
      wrap: true,
      langs: [],
    }),
  ],
  variables: { test: 'hello-world' } ,
});

some-template.mdoc

Screenshot 2023-11-18 at 5 40 32 PM

Added as a screenshot cause I can't do code block in a code block in Github

Given the above, the first {% $test %} renders hello-world the second a pre with {% $test %}.

If I comment out shiki() it both renders hello-world as expected

// shiki({
//   // https://github.com/shikijs/shiki/blob/main/docs/themes.md
//   theme: "css-variables",
//   wrap: true,
//   langs: [],
// });

Additionally, no variables render if used in a other nodes


Doesn't work in a code <code>

`{% $test %}`

Doesn't work as heading either

# `{% $test %}`

What's the expected result?

Using a variable a in Markdoc renders the variable value regardless of where the variable is. Fence renders variables when Shiki is enabled.

Link to Minimal Reproducible Example

https://stackblitz.com/edit/github-osnyth?file=src%2Fcontent%2Ftemp%2Fexample.mdoc

Participation

Work Around?

In the meantime, this is a significant blocker, is there any workaround to recommend?

I've tried the following but no luck, then render() seems to ignore the updated body. The string interpolation work thought, if I console.log(entry.body) the string has the variables properly replaced.

for (const key of Object.keys(VARIABLES)) {
    entry.body = entry.body.replaceAll(`{% $${key} %}`, VARIABLES[key]);
}

Thank you 🙏 
delucis commented 9 months ago

While not specifically mentioned in the Markdoc documentation of variables, it may be the case that the format doesn’t support variables in code blocks and inline code snippets. For example they do say:

Markdoc doesn't support passing variables to certain nodes, such as the href of a link Node. Instead, pass your variable to the href attribute of a custom link Tag.

From playing around with the Markdoc playground it looks like the following should work:

Inline {% code %}{% $test %}{% /code %} variable.

```js
{% $test %}


But regular inline code backticks do not render variables.

Playground example: https://markdoc.dev/sandbox?mode=preview&c=LS0tCnRpdGxlOiBUZXN0Ci0tLQoKSW5saW5lIHslIGNvZGUgJX17JSAkbWFya2RvYy5mcm9udG1hdHRlci50aXRsZSAlfXslIC9jb2RlICV9IHZhcmlhYmxlLgoKYGBganMKeyUgJG1hcmtkb2MuZnJvbnRtYXR0ZXIudGl0bGUgJX0KYGBgCgpJbmxpbmUgYHslICRtYXJrZG9jLmZyb250bWF0dGVyLnRpdGxlICV9YCB2YXJpYWJsZS4%253D
alexbouchardd commented 9 months ago

Good catch! Although I do know from the previous project (I'm porting my docs over to Astro) that the variables are supposed to render in fences, and the playground link also supports that.

Do you know why the workaround or replaceAll on entry.body doesn't work? That would also be the "solution" for code backticks not interpolating.

delucis commented 9 months ago

I don’t think render() depends on entry.body at all, so mutating it won’t have any impact.

Maybe one option would be to provide an Astro component to render the code blocks?

I’m not that familiar with Markdoc, but if this is supported you could do the find/replace in the Astro component maybe?

---
// src/components/MarkdocFence.astro

const VARIABLES = {
  // ...
};

let html = await Astro.slots.render('default');
for (const key of Object.keys(VARIABLES)) {
    html = html.replaceAll(`{% $${key} %}`, VARIABLES[key]);
}
---

<Fragment set:html={html} />
alexbouchardd commented 9 months ago

I don’t think render() depends on entry.body at all, so mutating it won’t have any impact.

Right, that makes sense and is consistent with what I've seen. That's a good approach. I'll validate if it works and update here. Nonetheless, I think this should be considered a workaround.

alexbouchardd commented 9 months ago

That worked as a workaround. For reference, instead of providing a component for the render block, I wrapped the whole rendered Markdoc output.

---
const entry = await getEntryBySlug("api", section);
const { Content } = await entry.render();
---
<Markdoc>
   <Content  />
</Markdoc>
---
import VARIABLES from "@/variables";

let html = await Astro.slots.render('default');
for (const key of Object.keys(VARIABLES)) {
    html = html.replaceAll(`{% $${key} %}`, VARIABLES[key]);
}
---

<Fragment set:html={html} />

Thanks @delucis !

lilnasy commented 9 months ago

Is this a limitation in markdoc?

alexbouchardd commented 9 months ago

No it is not, as per their playground and reproduction it's an issue with Astro Markdoc / shiki

lilnasy commented 9 months ago

Thanks for clarifying! I couldn't make it out from the previous comments.