magidoc-org / magidoc

Autogenerate static GraphQL API documentation
https://magidoc.js.org
MIT License
242 stars 19 forks source link

Markdown rendering for Svelte does not work with content nested in html tags like <details> #391

Open anly2 opened 1 month ago

anly2 commented 1 month ago

Describe the bug

The rendering of markdown contents breaks when there is nested content in html tags like <details>

Example:

<details>
  <summary>Examples</summary>

// Amazing code

</details>

Marked parses the contents correctly, but probably not in the expected way.
The resulting tokens for the above example are (abbreviated):

[
  {"type": "html", "text": "<details>\n  <summary>Examples</summary>"},
  {"type": "code", "code": "// Amazing code" }
  {"type": "html", "text": "</details>"}
]

Notice how the opening <details> and its corresponding closing tag </details> are in separate tokens.

Then the rendering - the recursive component instantiation - does its thing.
But the MarkdownHtml component, relies on the svelte {@html} directive, and messes things up (because of the unexpected token structure).
The {@html} directive implicitly closes unterminated tags. Which is the root cause of the problem.

Technically, marked parsed things correctly, because if the html it would produce was just joined together and rendered as a whole, things would work fine.

Reproduction

Try to render the following in svelte:

<details>
  <summary>Examples</summary>

// Amazing code

</details>

Logs

No response

System Info

"svelte": "^4.2.8",
"@sveltejs/kit": "^1.30.4",

Any browser, though tried with Firefox.

Severity

Serious, but I can work around it

pelletier197 commented 1 month ago

Thank you for raising this and for investing. Ir this is a bug in Marked itself, this may be difficult to work around.

I'll have a look into it when I have some free time, but it might take a little while. Contributions are always welcome if this is urgent. 🙂

anly2 commented 1 month ago

I am currently trying to use the container extension as a workaround.
(Unrelated, but I am having trouble getting the extension picked up at all)

Because the container extension seems nice in that it already tracks the opening and closing blocks, I believe something like this would work:


    marked.use({
      hooks: {
        preprocess: (md) => md.replace(/<(\/?)(details|summary)(\s[^>]*)?>/gi, (whole, closing, tag, options) =>
            closing?.length ? `\n:::\n` : `\n:::html-${tag}${options}\n`)
      },
      extensions: [
        extensions.containerExtension((params: TokenExtractionParameters) => {
          if (!params.type.startsWith("html-")) return null;
          return {
            type: params.type,
            raw: params.raw,
            props: params.options,
            tokens: []
          };
        })
      ]
    });

I specifically look for just details and summary tags here, but it should work with all other tags too (from the limited set that are fine in markdown)