Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can't substitute <pre> elements for components #100

Open
Tracked by #587
illright opened this issue Jul 10, 2020 · 5 comments
Open
Tracked by #587

Can't substitute <pre> elements for components #100

illright opened this issue Jul 10, 2020 · 5 comments
Labels
assigned Whether or not this bug has been assigned some to some other issues as a subtask or pre-req enhancement New feature or request
Milestone

Comments

@illright
Copy link
Contributor

illright commented Jul 10, 2020

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
@pngwn
Copy link
Owner

pngwn commented Jul 12, 2020

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 pngwn added the enhancement New feature or request label Jul 12, 2020
@TheComputerM
Copy link
Contributor

TheComputerM commented Aug 9, 2020

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

@pngwn
Copy link
Owner

pngwn commented Aug 14, 2020

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.

@Kyza
Copy link

Kyza commented Apr 22, 2021

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}\`} />`;
	},
}

@pngwn pngwn added this to mdsvex Oct 16, 2021
@pngwn pngwn moved this to Refine in mdsvex Oct 16, 2021
@pngwn pngwn added this to the 1.0 milestone Oct 16, 2021
@pngwn pngwn removed v1 labels Feb 23, 2024
@pngwn pngwn mentioned this issue Feb 23, 2024
6 tasks
@pngwn pngwn added the assigned Whether or not this bug has been assigned some to some other issues as a subtask or pre-req label Feb 23, 2024
@joekrump
Copy link

I ended up hacking together a svelte preprocessor for this and that worked out pretty well. I'm not sure if it would scale too well for hundreds or thousands of .md files but has worked out pretty well for my case which has a ~50 .md files. There are some improvements that can be made to avoid using regular expressions as well, however, this was the quick and dirty solution that worked for me. Hope this helps others!

const mdClipboardCodeblocks = () => {
  return {
    name: "md-clipboard-codeblocks",
    /**
     * @param {object} options
     * @param {string} options.content
     * @param {string} options.filename
     */
    markup: ({ content, filename }) => {
      if (filename.endsWith(".md")) {
        // Add the copy to clipboard import to every .md file has access to it automatically without needing to add an import statement in <script> tags
        content = content.replace(/<script>([\s\S]*?)<\/script>/, `<script>\nimport { CopyToClipboard } from "$lib/components";\n$1</script>`);

        // Wrap codeblocks with copy to clipboard
        if (content.includes("<pre class=\"language-")) {
          content = content.replace(/(<pre class=")(language-[^"]*">)([\s\S]*?)(<\/pre>)/g, '$1 relative inline-flex $2<CopyToClipboard class="absolute top-1.5 right-1.5">$3</CopyToClipboard>$4')
        }

        return { code: content };
      }
    },
  };
};

export default mdClipboardCodeblocks;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
assigned Whether or not this bug has been assigned some to some other issues as a subtask or pre-req enhancement New feature or request
Projects
No open projects
Status: Refine
Development

No branches or pull requests

5 participants