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 Dynamically Load Markdown Files #285

Open
thecodeclimber opened this issue Jul 26, 2021 · 4 comments
Open

Can't Dynamically Load Markdown Files #285

thecodeclimber opened this issue Jul 26, 2021 · 4 comments

Comments

@thecodeclimber
Copy link

I am trying to use mdsvex to create a markdown blog. I have installed the package using npx svelte-add@latest mdsvex. I then create a [slug].svelte file so that I can test if it works:

// src/routes/posts/[slug].svelte

<script context="module">
	export async function load({ page }) {
		const Hello = (await import('../../posts/hello.md')).default;

		return {
			props: { Hello }
		};
	}
</script>

<script>
	export let Hello;
</script>


<Hello />

This works exactly as I want it to work. If I navigate to localhost:3000/posts/hello, it loads up the src/posts/hello.md file and displays it properly.

Now I want to make the [slug].svelte file dynamic. So I change it as follows:

// src/routes/posts/[slug].svelte

<script context="module">
	export async function load({ page }) {
		const Post = (await import(`../../posts/${page.params.slug}.md`)).default;

		return {
			props: { Post }
		};
	}
</script>

<script>
	export let Post;
</script>

<Post />

This, however, does NOT work. I get the following error if I refresh the localhost:3000/posts/hello page:

failed to load module for ssr: posts/hello.md

Error: failed to load module for ssr: posts/hello.md
    at instantiateModule (C:\code\learn\svelte\blog-2\node_modules\vite\dist\node\chunks\dep-f2b4ca46.js:73353:15)

Any idea why this does not work and what I can do to fix it?

Thanks.

@jcayzac
Copy link

jcayzac commented Aug 2, 2021

@babichjacob has a POC here, which I tried to built upon (without success) here.

My findings so far:

  • It seems our code might be missing some integration with Vite, so that importing a .svelte layout could work?
  • I think MDSveX might have a bug: the dependencies field of the preprocessor result isn't populated at all. Shouldn't the layout be a dependency? @pngwn

@pngwn
Copy link
Owner

pngwn commented Sep 10, 2021

@jcayzac It definitely should.

@pngwn
Copy link
Owner

pngwn commented Sep 10, 2021

There reason this doesn't work:

await import(`../../posts/${page.params.slug}.md`)

Is because the bundler has no way of knowing what the variable could be at build time, so it won't dynamically compile the relevant files for you.

Another solution to this is to precompile those files manually (via a build script), turning them into svelte components that have been compiled to JS. Then putting them in the 'correct' place on the server to ensure they exists when you hit the relevant routes. You'll need some kind of error handling fo course because not every route will work.

I believe there are rollup plugins that will help with supporting dynamic imports with variables but I've investigated this extensively.

@khromov
Copy link

khromov commented Jun 15, 2022

There seems to have been some improvements recently and it appears that rollup can now handle this dynamic import case for example, the code below works fine on both static and node adapter exports.

<script context="module" type="ts">
import type { LoadEvent, LoadOutput } from '@sveltejs/kit';

export async function load(input: LoadEvent): Promise<LoadOutput> {
	let BlogPost = null;
	try {
		BlogPost = await import(`../../posts/${input?.params?.slug}.md`);
	} catch (e) {}

	return {
		props: {
			BlogPost: BlogPost?.default,
		}
	};
}
</script>

<script type="ts">
	export let BlogPost;
</script>

<div>
	<svelte:component this={BlogPost}/>
</div>

When looking at the generated output the preprocessor seems to enumerate all the files in the posts/ directory and add a switch case for them.

// ... note: compiled code
function I(s) {
    switch (s) {
    case "../../posts/hello.md":
        return i(()=>import("../../chunks/hello-49317b17.js"), ["chunks/hello-49317b17.js", "chunks/index-d6f189e3.js"]);
    case "../../posts/post-1.md":
        return i(()=>import("../../chunks/post-1-3fa5182a.js"), ["chunks/post-1-3fa5182a.js", "chunks/index-d6f189e3.js"]);
    case "../../posts/world.md":
        return i(()=>import("../../chunks/world-dc36d26f.js"), ["chunks/world-dc36d26f.js", "chunks/index-d6f189e3.js"]);
    default:
        return new Promise(function(e, t) {
            (typeof queueMicrotask == "function" ? queueMicrotask : setTimeout)(t.bind(null, new Error("Unknown variable dynamic import: " + s)))
        }
        )
    }
}

However this code does not work for me on the dev server (npm run dev), where I get the error:
"Failed to load module script: Expected a JavaScript module script but the server responded with a MIME type of "text/markdown". Strict MIME type checking is enforced for module scripts per HTML spec."

This is caused by the browser literally trying to load the .md file, it is not preprocessed:

Screenshot 2022-06-16 at 00 01 46

@pngwn Is this an issue in my configuration, or is this an issue with MDsveX?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
Status: Triage + Refine
Development

No branches or pull requests

3 participants