pngwn/MDsveX

`mdsvex.compile(content, options)` adds extra `{@html` and `}` wrapper for no reason

swyxio opened this issue ยท 12 comments

in here: https://github.com/sw-yx/swyxkit

this section https://swyxkit.netlify.app/welcome#setup

image

this {@html seems to be injected by mdsvex. i presume its how mdsvex injects itself in normal operation, but the usecase for .compile users seems to be overlooked.

right now i'm thinking of just using regex to take it out but ofc would be nice to fix at source

simple repro

run

npm i mdsvex
node

inside the node env, do:

  compile(`
  \`\`\`js
  let foo = 123
  \`\`\`
  `).then(console.log)

the result is

'<pre class="language-js">{@html let foo = 123}</pre>\n'

which has the extra {@html stuff

alright this works:

  compile(`
  \`\`\`js
  let foo = 123
  \`\`\`
  `).then(x => console.log(x.code
			.replace(/>{@html `<code class="language-/g, '><code class="language-')
			.replace(/<\/code>`}<\/pre>/g, '</code></pre>');
  ))

but its ugly af of course

pngwn commented

compile still returns a Svelte component, there is plenty more that could break when using mdsvex as a markdown -> html tool but appreciate this usecase.

Another way to work around this would be to use a custom highlight function, it may be possible to expose the internal highlight function that produces the correct markup without the @html wrapper.

Or maybe I could expose a to_html version of compile that avoids any svelte specific insertions, there would still be lots that could break however.

ah i see. if compile returns a Svelte component its not very obvious to me how to consume it from within a svelte app. do i use svelte:component?

hmm.. when i try <svelte:component this={content} /> with the raw output of compile, the error says <svelte:component this={...}> is not a valid SSR component. You may need to review your build config to ensure that dependencies are compiled, rather than imported as pre-compiled modules

pngwn commented

Yeah this is trickier. It also returns an uncompiled svelte component so needs to be compiled by svelte too (and any imports would need to be resolved), you'd also need some and SSR versions of the component.

I think using vites glob imports might work in this case, as I think you get both the SSR and dom mode components that way. But not sure how nicely that would play with kit routes.

And the final option would be to just generate an SSR version of the component (if it is all static markdown) and then you could inject the html that is returned from render. If you are doing your markdown parsing/ conversion in an endpoint of some description and this is a statically generated site then you could do this work there without any issues.

pngwn commented

Of course this all far too complicated for a pretty common use case, so I need to improve this story a bit here.

There are some technical details that can't be avoided but I'm certain there are DX improvements that can be made. I just haven't had time to explore this thoroughly enough so far.

ok gotcha thank you for understanding. i think i should be able to compile it with svelte serverside. will try another day

agree that this is a common enough usecase that it should be addressed first class eventually

@felixsanz see my solution in swyxkit

Thanks @sw-yx for the temporary solution, feels dirty though. Any edge cases where this might not work?

Useful but ugly. Thanks @sw-yx

@williamviktorsson you tell me haha

@williamviktorsson you tell me haha

So far so good lol ๐Ÿ˜‚

Adding this as a comment as I've tried to use compile and found myself in this issue. Happy to open a new question-type issue if this is off-topic.

When I use @swyxio's solution then render the generated code (I'm trying to do this while using custom components) I get a whole <layout_mdsvex_default {...$$props}> ... in there, which I assume is what you mean by "compile still returns a Svelte component", @pngwn?

The use case is I'm writing a blog that has accompanying code snippets which are used to

  1. render a p5.js canvas; and
  2. display the source in a "Code" tab.

My approach to this has been to dynamically import the p5.js sketch's .ts file and accompanying description.md in +page.ts:

// +page.ts
  // in load
  sketch = await import(`../sketches/${params.slug}.ts`);
  description = await import(`../sketches/${params.slug}.md`);

  return {
    sketch: sketch.default,
    Description: description.default,
  };

Which I then render in my page as <Description /> and <SketchComponent {sketch} />. I can't quite figure out how to read the source from the .ts file, parse it as a Markdown code block, and prettify it using my MDsveX setup (with rehype-pretty-code), but so far I'm doing it like this:

// +page.server.ts
  // in load
  const formattedSource = await prettier.format(fs.readFileSync(filePath, "utf-8"));

  // Transpile to JS so it's easier for readers to paste into p5.js web editor and run it.
  const jsSource = await esbuild.transform(strippedSource, {
    loader: 'ts',
  });

  const mdx = await compile('```js\n' + jsSource.code + '\n```', mdsvexConfig);  // imported from mdsvex.config.js.

  return mdx.code  // @swyxio's workaround.
      .replace(/>{@html `<code class="language-/g, '><code class="language-')
      .replace(/<\/code>`}<\/pre>/g, '</code></pre>');

Any idea if there's a simpler solution to this or how to further compile it into a proper Svelte component? I tried remark-code-import but got errors when trying to load files.