vite-plugin-svelte-entries-generator

Vite Plugin Svelte Entries Generator

A vite plugin to generate sveltekit prerender entries for paths that can't be scraped automatically

This plugin is no longer required! Its functionality is now apart of sveltekit!

vite-plugin-svelte-entries-generator

Doesn't sveltekit auto-discover all pages to pre-render?

Mostly yes, but under certain circumstances, it is unable to discover all dynamic paths to prerender.

For example, you have a dynamic route posts/[slug] where you want all the posts to be pre-rendered. But you have some form of client side pagination which means only a handful of the posts will be caught be sveltekit's <a> tag scanning.

Because sveltekit currently lacks something similar to NextJS's getStaticPaths, you have to fall back to using the prerender.entries array in the svelte config to tell sveltekit about your dynamic paths.

But this in inflexible and isn't simple to automate. Hence, this plugin. With this plugin, you can pass an array of base api paths and a transformer function to auto-generate the entries array upon vite build.

Usage

Ensure you have kit.prerender in svelte.config.js somewhere, like so

const config = {
    extensions: ['.svelte'],
    preprocess: [
        preprocess({
            postcss: true,
        })],
    kit: {
        adapter: vercel(),
        prerender: {},
    },
};

The prerender object can have other data in it, just make sure it exists.

Arguments

  • paths: Path[] - An array of Paths
  • repoRoot?: string - The absolute path of the current repo. If this isn't passed it will be derived from the vite config root prop or if that's undefined process.cwd()
  • svelteConfigPath?: string - Absolute path to your svelte.config.js file. If this isn't passed, it will be assumed it is located at <repoRoot>/svelte.config.js.

Paths

  • apiPath: string - The actual route/url this content is found at, including all dynamic parameters, I.E. /posts/[slug]
  • transform: (apiPath: string, repoRoot: string) => Promise<string | string[]> | string | string[] - A function that provides the apiPath being processed and the absolute path of the repo. This function generates the entry for sveltekit to discover. I'd recommend you .replace() on the apiPath, replacing the dynamic parameters with your slug/id/whatever. See below for examples.
import entriesGenerator from 'vite-plugin-svelte-entries-generator';
import postTransformer from './transformers/postTransformer'

import {sveltekit} from '@sveltejs/kit/vite';

/** @type {import('vite').UserConfig} */
const config = {
    plugins: [
        sveltekit(),
        entriesGenerator({
            paths: [
                {
                    transform: postTransformer,
                    apiPath: '/posts/[slug]'
                }
            ]
        })
    ],
};

export default config;

Transform examples

import path from 'path';
import glob from 'glob';
import fs from 'fs/promises';
import matter from 'gray-matter';
import type {EntriesTransformFunction} from 'vite-plugin-svelte-entries-generator';

const CONTENT_PATH = 'src/content/posts/*.svx';

const getFilePaths = async (path: string) => {
    try {
        return await glob(path);
    } catch (e) {
        console.error(`Failed to read ${path}`);
        return [];
    }
};

// The path being processed here is /posts/[slug]

// Reads the filename without the extension of some md/svx files and uses that filename as the slug
export const transformByFilename: EntriesTransformFunction = async (apiPath, repoRoot) => {
    const fullPath = path.resolve(repoRoot, CONTENT_PATH);

    const fullPaths = await getFilePaths(fullPath);

    return fullPaths.map((filePath) => {
        const extension = path.extname(filePath);
        const fileName = path.basename(filePath, extension);
        return apiPath.replace(`[slug]`, fileName);
    });
};

// Reads each md/svx file and parses the frontmatter data to get the posts id and uses that as the slug 
export const transformById: EntriesTransformFunction = async (apiPath, repoRoot) => {
    const fullPath = path.resolve(repoRoot, CONTENT_PATH);

    const fullPaths = await getFilePaths(fullPath);

    return await Promise.all(
        fullPaths.map(async (filePath) => {
            const content = await fs.readFile(filePath, 'utf-8');

            const matterData = matter(content);

            return apiPath.replace(`[slug]`, matterData.data.id);
        }),
    );
};

You could also call your db to get ids and so on

Top categories

Loading Svelte Themes