Automatic Site Maps in Svelte

Autogenerate a sitemap

Let's update our sitemap generation code to produce clean URLs without the "/src/routes" prefix:

// src/routes/sitemap.xml/+server.js
import { readdirSync, statSync } from 'fs';
import { join, relative } from 'path';

function getRoutes(dir) {
  const baseDir = join(process.cwd(), 'src', 'routes');
  const routes = [];

  function traverse(currentDir) {
    const files = readdirSync(currentDir);

    files.forEach(file => {
      const filePath = join(currentDir, file);
      const stat = statSync(filePath);

      if (stat.isDirectory()) {
        traverse(filePath);
      } else if (file === '+page.svelte') {
        let route = '/' + relative(baseDir, currentDir)
                          .split('\\')
                          .join('/');
        
        // Handle index routes
        route = route === '/.' ? '/' : route;
        
        routes.push(route);
      }
    });
  }

  traverse(baseDir);
  return routes;
}

export async function GET() {
  const routes = getRoutes('src/routes');

  const body = `
    <?xml version="1.0" encoding="UTF-8" ?>
    <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
      ${routes.map(route => `
        <url>
          <loc>https://example.com${route}</loc>
        </url>
      `).join('')}
    </urlset>
  `.trim();

  return new Response(body, {
    headers: {
      'Content-Type': 'application/xml'
    }
  });
}

Let's break down the changes and explain how this solves the problem:

  1. We import the relative function from the path module. This will help us get the relative path from the base directory to each route.

  2. In the getRoutes function:

    • We define baseDir as the absolute path to the 'src/routes' directory.

    • We use a nested traverse function to recursively go through the directories.

    • For each '+page.svelte' file, we calculate the route by getting the relative path from baseDir to the current directory.

    • We replace backslashes with forward slashes to ensure consistent URL formatting.

    • We handle the root route ('/') separately by checking if the relative path is '.'.

  3. We've removed any hardcoded 'src/routes' parts from the URL generation process.

This improved version should generate clean URLs for your sitemap, like:

<url>
  <loc>https://example.com/</loc>
</url>
<url>
  <loc>https://example.com/about</loc>
</url>
<url>
  <loc>https://example.com/testpage</loc>
</url>

Handling Dynamic Content

If you need to include dynamic content, you can still add it as before:

async function getBlogPosts() {
  // Fetch blog posts from your API or database
  const res = await fetch('https://api.example.com/posts');
  const posts = await res.json();
  return posts.map(post => `/blog/${post.slug}`);
}

export async function GET() {
  const staticRoutes = getRoutes('src/routes');
  const blogPosts = await getBlogPosts();
  const allRoutes = [...staticRoutes, ...blogPosts];

  // Generate XML as before
}

Automating Updates

To keep your sitemap up-to-date, consider these options:

  1. Generate the sitemap during the build process.

  2. Use a serverless function to regenerate the sitemap periodically.

  3. Trigger sitemap regeneration when content is updated.

For example, add a build script to your package.json:

{
  "scripts": {
    "build": "vite build && node generate-sitemap.js"
  }
}

Then create a generate-sitemap.js file that uses this logic to create a static sitemap.xml file during builds.

By implementing these improvements, you'll have a robust, automated sitemap generation system for your SvelteKit project that produces clean URLs without the "/src/routes" prefix, ensuring search engines always have the most up-to-date and accurate information about your site structure.

Citations: [1] https://example.com/src/routes/testpage

Last updated