MDsveX icon indicating copy to clipboard operation
MDsveX copied to clipboard

Can't substitute <pre> elements for components

Open illright opened this issue 4 years ago • 5 comments

Layouts allow providing Svelte components to be rendered instead of certain HTML elements that markdown generates, but that doesn't seem to apply to <pre> or <code> elements.

Perhaps this has something to do with the fact that they undergo special treatment with the default syntax highlighter, but I think this deserves a note in the docs. Or maybe it is possible to substitute them, which would be great!

Steps to reproduce

  • Clone the Svelte template
  • Install mdsvex: yarn add -D mdsvex
  • Set up MDsveX in rollup.config.js:
// ...
import { mdsvex } from 'mdsvex';

export default {
  // ...
  plugins: [
    // ...
    svelte({
      extensions: ['.svelte', '.svx'],
      preprocess: mdsvex({
        layout: './src/layout.svelte',
      }),
    }),
// ...
  • Add an MDsveX file (src/test.svx) with the following content:
```js
alert()
```
  • Add a layout file (src/layout.svelte) exporting the pre element (also create src/pre.svelte with content .<pre><slot /></pre> to see a dot before every <pre> element):
<script context="module">
  import pre from './pre.svelte';
  export { pre };
</script>
<slot />
  • Import the SVX into src/App.svelte

illright avatar Jul 10 '20 18:07 illright

Yeah, this doesn't work which is expected but perhaps undesirable.

This is simply because of how the markup for code blocks is generated, which is slightly different to other markdown elements.

It should be possible to make this work, I'll take a look at it.

pngwn avatar Jul 12 '20 22:07 pngwn

Found a workaround:

Make a custom highlighter function and change the pre tags to Components.pre; Mine looks like this:

highlight: {
  highlighter: (code, lang) => {
    if (lang && Prism.languages[lang]) {
      const parsed = Prism.highlight(code, Prism.languages[lang]);
      const escaped = parsed.replace(/{/g, '&#123;').replace(/}/g, '&#125;');
      const langTag = 'language-' + lang;
     // This thing below ↓↓↓↓
      return `<Components.pre class=${langTag}><code class=${langTag}>${escaped}</code></Components.pre>`;
    } else {
      const escaped = code.replace(/{/g, '&#123;').replace(/}/g, '&#125;');
      return `<Components.pre><code>${escaped}</code></Components.pre>`;
    }
  },
},
<script context="module">
  import { pre } from "./defaults";
  export { pre };
</script>

MDsveX replaces all html tags with Components.{tag} so we basically tell it to do the same with pre

TheComputerM avatar Aug 09 '20 07:08 TheComputerM

This isn't public API and could change at any time, so I wouldn't recommend this approach (although it would work fine as a workaround). I'll have a real fix for this soon.

pngwn avatar Aug 14 '20 20:08 pngwn

I managed to make my own Svelte component receive the direct props from the highlighter this way. This lets me make a single Svelte component for both MDsveX and normal Svelte code.

highlight: {
	highlighter: (code, lang) => {
		return `<Components.pre code={\`${escape(code)}\`} lang={\`${lang}\`} />`;
	},
}

Kyza avatar Apr 22 '21 22:04 Kyza