I am continuing to explore Svelte, SvelteKit and GitHub Pages. It’s pretty easy to create a blog and use mdsvex to process markdown files. But I think that the imposed folder structure is limiting.

In SvelteKit the blog pages must be in a blog folder (or post, or any other name). The post url will look something like:

blog.stranianelli.com/2021/2021-07-26-sveltekit-routing-from-other-folders

Instead I prefer a simpler url:

blog.stranianelli.com/sveltekit-routing-from-other-folders

Partly because I write a few posts, partly for personal taste. I know that it is not a widespread practice but mine sine qua non condition to switch from Jekyll to Svelte is the possibility of use the same url structure.

There is another important condition: I want to keep post images in the same folder with the .md file. I prefer to keep everything together, it makes my life easier when I write.

I had to search a bit, but combining various pieces and trying and trying again I managed to get something working.

Before starting a few links:

The trick is to “map” all blog posts and keep them as “modules”. Then I use Vite to dynamically import the modules into the blog. To test I don’t create a new repository. I continue modifying the MEMENTO SvelteKit & GitHub Pages: I add a src/news folder to insert the various posts.

I use a structure similar to the one I will need in my blog. So the posts will be divided by year but not by month. Each post will stay in a folder whose first few characters represent the publication date. Inside the folder I will put the images I will need and an index.md file with the post. I think in the future I will have to add the ability to use markdown files with other names. That’s enough for the moment.

Every post will be in a path similar to:

root: /src/news/2021/2021-07-26-sveltekit-routing-from-other-folders/index.md

Also, I want to index the various posts. I change the src/routes/index.svelte file.

index.svelte

First, I need the list of all markdown files contained in the news folder. I use a function of Vite, globEager:

const allPosts = import.meta.globEager(`../news/**/*.md`);

With this code I get all the md files as modules. Then I go through every post I’ve just found and I extract some data

for (let path in allPosts) {
    // ...
}

I need the post module

const post = allPosts[path];

And I need the metadatas (extracted from the markdown frontmatter):

const metadata = post.metadata;

I put all in an array (body = []). Then I pass body to the page with a load() function:

<script context="module">
    const allPosts = import.meta.globEager(`../news/**/*.md`);
    let body = [];
    for (let path in allPosts) {
        const post = allPosts[path];
        const metadata = post.metadata;
        const p = {
            path, metadata
        }
        body.push(p); 
    }

    export const load = async () => {
        return { props: {posts: body} }
    }
</script>

Now I add the html side in index.svelte:

<script lang="ts">
    import { base } from '$app/paths';
    export let posts;
</script>

<h1>News</h1>

<ul>
    {#each posts as {slugPage, metadata: {title, slug}} }
        <li>
            <a href={`${base}/${slug}`} >{title}</a>
        </li>
    {/each}
</ul>

I can also play with frontmatter. I can use the folder name if I don’t define a slug in the frontmatter. I extract the name of the folder minus the date:

    for (let path in allPosts) {
      //...
        const namePage = path.split('/');
        const slugPage = namePage[namePage.length-2].slice(11);
        const p = {
            path, metadata, slugPage
        }
        body.push(p); 
    }

Then I define the helper function:

function linkSlug(s:string | undefined, p: string): string {
    let result = "";
    if ( !s) {
        result = p
    } else {
        result = s;
    }
    return result;
}

And I change the html code:

<a href={`${base}/${linkSlug(slug, slugPage)}`} sveltekit:prefetch >{title}</a>

[slug].svelte

Once I fix the home page, I have to understand how to access the various posts using a simplified slug. I need a dynamic parameter. So I create the src/routes/[slug].svelte component.

I have to import the various posts. But this time I also import the modules of the various posts:

    export const ssr = false;
    const allPosts = import.meta.globEager(`../news/**/*.md`);
    let body = [];

    for (let path in allPosts) {
        const post = allPosts[path];
        const metadata = post.metadata;
        const pathArray = path.split('/');
        const slugPage = pathArray[pathArray.length-2].slice(11);

        const p = {post, slugPage, metadata };

        body.push(p);
    }

Now I filter the various posts to extract what I want to show:

export const load = ({page}) => {
    const posts = body;
    const { slug } = page.params;

    const filteredPosts = posts.filter( (p) => {
        const slugPost = p.metadata.slug;
        const slugToCompare = !slugPost ? p.slugPage : slugPost;
        return slugToCompare.toLowerCase() === slug.toLowerCase();
    } );

    return {
        props: {
            page: filteredPosts[0].post.default,
        }
    }
}

The component itself is very simple:

<script>
    export let page;
</script>

<svelte:component this={page}/>

After that, I can build the static site with:

npm run build

Now I can upload to GitHub:

npm run deploy

That’s all for today. The code is available on GitHub:

The blog is visible at the address: el3um4s.github.io/memento-sveltekit-and-github-pages.

And this is my Patreon: