sveltejs / kit

web development, streamlined
https://kit.svelte.dev
MIT License
18.12k stars 1.84k forks source link

Programmatic approach to use `@sveltejs/enhanced-img` in markdown #11132

Open LukaHarambasic opened 7 months ago

LukaHarambasic commented 7 months ago

Describe the problem

I would like to also improve images in markdown, therefore I use remark/rehype and inject a custom plugin to replace the img tag with <enhanced:img /> (see here). The HTML generated that way than gets used via {@html html} (see here).

The modified version (<enhanced:img />) than gets added to the DOM, so it sveltekit doesn't know that it has to do some magic.

Describe the proposed solution

I see two options:

  1. A programmatic approach to do the transformation manually during the markdown to HTML process.
  2. Somehow tell svelte that the HTML injected this way needs some special treatment.

Alternatives considered

No response

Importance

would make my life easier

Additional Information

PS: When I use it as described in the docu for the more basic use case it works as expected and I love it!!

benmccann commented 7 months ago

I don't see the @html call you're referring to

If you wrote a plugin for mdsvex it would probably work because it'd get inserted into a Svelte component that could then be preprocessed

ryansheehan commented 7 months ago

I've been looking for a workaround on this very issue. I have a working solution for images within the content of a markdown file, and I am currently looking for a solution for images specified in the frontmatter.

For some context, I am using mdsvex with some plugins. I wanted to have my posts contained within a folder, and all assets for a post within that folder. I am using several remark/rehype plugins for help.

Remark

Rehype

Modifying svelte.config.js to use rehype-rewrite

import { mdsvex, escapeSvelte } from 'mdsvex';
import remarkUnwrapImages from 'remark-unwrap-images';
import relativeImages from 'mdsvex-relative-images';
import rehypeRewrite from 'rehype-rewrite';

/** @type {import('mdsvex').MdsvexOptions} */
const mdsvexOptions = {
    ...,
    remarkPlugins: [
        remarkUnwrapImages,
        relativeImages,
    ],
    rehypePlugins: [
        [rehypeRewrite, {
            rewrite: (node) => {
                // replace img tags with enhanced:img
                if (node.type == 'element' && node.tagName == 'img') {
                    node.tagName = 'enhanced:img';
                    // add a css class "post-img" for styling purposes
                    node.properties['class'] = `post-img ${node.properties['class'] ?? ''}`.trimEnd();
                }

                // change the image import lines to have the "?enhanced" query param and carry
                // over any additional vite image transformations specified from the markdown source
                if (node.type == 'raw' && node.value?.startsWith("<script>")) {
                    const re = /"\.[\\\/].*\.(apng|avif|gif|jpg|jpeg|jfif|pjpeg|pjp|png|svg|webp)\??.*"/gm;
                    let newValue = node.value;
                    Array.from(node.value.match(re)).forEach(m => {
                        const relativePath = m.split('"')[1];
                        // using URL to help parse the src properties
                        // this allows us to specify vite image transformations on the src string in markdown
                        // the "c://" is just needed to help make parse a valid URL path, it's ultimately ignored
                        const url = new URL(relativePath, "c://");
                        // make sure the enhanced attribute is added so we don't have to specify it in markdown
                        url.searchParams.append('enhanced', '');
                        const updatedPath = `".${url.pathname}${url.search}"`;
                        newValue = newValue.replace(m, updatedPath);
                    })
                    node.value = newValue;
                }
            }
        }],
    ]
}

/** @type {import('@sveltejs/kit').Config} */
const config = {
        ...,
    extensions: ['.svelte', '.svx', '.md'],
        preprocess: [
        vitePreprocess(),
        mdsvex(mdsvexOptions),
    ],
}
LukaHarambasic commented 7 months ago

@benmccann

I don't see the @html call you're referring to

Updated in the issue and it's here: https://github.com/LukaHarambasic/harambasic.de/blob/main/src/routes/posts/%5Bslug%5D/Entry.svelte#L38

If you wrote a plugin for mdsvex it would probably work because it'd get inserted into a Svelte component that could then be preprocessed

mdsvex seems limiting for me as I have some other uses cases as well, where I would need another md to HTML conversion as well.

@ryansheehan That's what i did, just without mdsvex, I also hooked into the process and provided my own plugin to do this.