0.10.5 • Published 8 months ago

sk-sitemap v0.10.5

Weekly downloads
-
License
MIT
Repository
github
Last release
8 months ago

Table of Contents

Features

  • 🤓 Supports any rendering method.
  • 🪄 Automatically collects routes from /src/routes using Vite + data for route parameters provided by you.
  • 🧠 Easy maintenance–accidental omission of data for parameterized routes throws an error and requires the developer to either explicitly exclude the route pattern or provide an array of data for that param value.
  • 👻 Exclude specific routes or patterns using regex patterns (e.g. ^/dashboard.*, paginated URLs, etc).
  • 🚀 Defaults to 1h CDN cache, no browser cache.
  • 💆 Set custom headers to override default headers: sitemap.response({ headers: {'cache-control: '...'}, ...}).
  • 🫡 Uses SvelteKit's recommended sitemap XML structure.
  • 💡 Google, and other modern search engines, ignore priority and changefreq and use their own heuristics to determine when to crawl pages on your site. As such, these properties are not included by default to minimize KB size and enable faster crawling. Optionally, you can enable them like so: sitemap.response({changefreq:'daily', priority: 0.7, ...}).
  • 🧪 Well tested.
  • 🫶 Built with TypeScript.

Limitations

  • A future version could build a Sitemap Index when total URLs exceed >50,000, which is the max quantity Google will read in a single sitemap.xml file.
  • Excludes lastmod from each item, but a future version could include it for parameterized data items. Obviously, lastmod would be indeterminate for non-parameterized routes, such as /about. Due to this, Google would likely ignore lastmod anyway since they only respect if it's "consistently and verifiably accurate".
  • Image or video sitemap extensions.

Installation

npm i -D sk-sitemap

or

bun add -d sk-sitemap

Usage

Basic example

JavaScript:

// /src/routes/sitemap.xml/+server.js
import * as sitemap from 'sk-sitemap';

export const GET = async () => {
  return await sitemap.response({
    origin: 'https://example.com'
  });
};

TypeScript:

// /src/routes/sitemap.xml/+server.ts
import * as sitemap from 'sk-sitemap';
import type { RequestHandler } from '@sveltejs/kit';

export const GET: RequestHandler = async () => {
  return await sitemap.response({
    origin: 'https://example.com'
  });
};

The "everything" example

All aspects of the below example are optional, except for origin and paramValues to provide data for parameterized routes.

JavaScript:

// /src/routes/sitemap.xml/+server.js
import * as sitemap from 'sk-sitemap';
import * as blog from '$lib/data/blog';

export const prerender = true; // optional

export const GET = async () => {
  // Get data for parameterized routes
  let blogSlugs, blogTags;
  try {
    [blogSlugs, blogTags] = await Promise.all([blog.getSlugs(), blog.getTags()]);
  } catch (err) {
    throw error(500, 'Could not load data for param values.');
  }

  return await sitemap.response({
    origin: 'https://example.com',
    excludePatterns: [
      '^/dashboard.*', // e.g. routes starting with `/dashboard`
      `.*\\[page=integer\\].*` // e.g. routes containing `[page=integer]`–e.g. `/blog/2`
    ],
    paramValues: {
      '/blog/[slug]': blogSlugs, // e.g. ['hello-world', 'another-post']
      '/blog/tag/[tag]': blogTags, // e.g. ['red', 'green', 'blue']
      '/campsites/[country]/[state]': [
        ['usa', 'new-york'],
        ['usa', 'california'],
        ['canada', 'toronto']
      ]
    },
    headers: {
      'custom-header': 'foo' // case insensitive; xml content type & 1h CDN cache by default
    },
    additionalPaths: [
      '/foo.pdf' // e.g. to a file in your static dir
    ],
    changefreq: 'daily', // excluded by default b/c ignored by modern search engines
    priority: 0.7 // excluded by default b/c ignored by modern search engines
  });
};

TypeScript:

// /src/routes/sitemap.xml/+server.ts
import * as sitemap from 'sk-sitemap';
import * as blog from '$lib/data/blog';
import type { RequestHandler } from '@sveltejs/kit';

export const prerender = true; // optional

export const GET: RequestHandler = async () => {
  // Get data for parameterized routes
  let blogSlugs, blogTags;
  try {
    [blogSlugs, blogTags] = await Promise.all([blog.getSlugs(), blog.getTags()]);
  } catch (err) {
    throw error(500, 'Could not load data for param values.');
  }

  return await sitemap.response({
    origin: 'https://example.com',
    excludePatterns: [
      '^/dashboard.*', // e.g. routes starting with `/dashboard`
      `.*\\[page=integer\\].*` // e.g. routes containing `[page=integer]`–e.g. `/blog/2`
    ],
    paramValues: {
      '/blog/[slug]': blogSlugs, // e.g. ['hello-world', 'another-post']
      '/blog/tag/[tag]': blogTags, // e.g. ['red', 'green', 'blue']
      '/campsites/[country]/[state]': [
        ['usa', 'new-york'],
        ['usa', 'california'],
        ['canada', 'toronto']
      ]
    },
    headers: {
      'custom-header': 'foo' // case insensitive; xml content type & 1h CDN cache by default
    },
    additionalPaths: [
      '/foo.pdf' // e.g. to a file in your static dir
    ],
    changefreq: 'daily', // excluded by default b/c ignored by modern search engines
    priority: 0.7 // excluded by default b/c ignored by modern search engines
  });
};

Recommended robots.txt

Create a robots.txt so search engines know where to find your sitemap.

You can either create a file at /static/robots.txt or a route at /src/routes/robots.txt/+server.ts if you want to get the origin from your env, which is common.

# /static/robots.txt
User-agent: *
Allow: /

Sitemap: https://example.com/sitemap.xml

or

// /src/routes/robots.txt/+server.ts
// A static file is not used because this allows access to env.ORIGIN

export const prerender = true;

export async function GET(): Promise<Response> {
  // prettier-ignore
  const body = [
		'User-agent: *',
		'Allow: /',
		'',
		`Sitemap: ${process.env.ORIGIN}/sitemap.xml`
	].join('\n').trim();

  const headers = {
    'Content-Type': 'text/plain'
  };

  return new Response(body, { headers });
}

Note on prerendering

  • 💡 If you set export const prerender = true; within your /src/routes/sitemap.xml/+server.ts file, you can find sitemap.xml is generated in your .svelte-kit build dir ✅. But when you run npm run preview, you will notice the SvelteKit preview server sets an HTML content type on the response 😱. This is due to the preview server's limitations, because it's the web server's responsibility to set the content type response header when serving static files.

    However, production hosts like Cloudflare, Vercel, Netlify, & others are smarter and set 'content-type': 'application/xml' when serving your prerendered sitemap.xml file 😅. Or if not prerendering your sitemap, 'content-type': 'application/xml' is set by SK Sitemap's default response headers 👌.

    The above is also true for robots.txt, which uses a text/plain mime type.

Example output

<urlset
    xmlns="https://www.sitemaps.org/schemas/sitemap/0.9"
    xmlns:news="https://www.google.com/schemas/sitemap-news/0.9"
    xmlns:xhtml="https://www.w3.org/1999/xhtml"
    xmlns:mobile="https://www.google.com/schemas/sitemap-mobile/1.0"
    xmlns:image="https://www.google.com/schemas/sitemap-image/1.1"
    xmlns:video="https://www.google.com/schemas/sitemap-video/1.1">
    <url>
        <loc>https://example/</loc>
        <changefreq>daily</changefreq>
        <priority>0.7</priority>
    </url>
    <url>
        <loc>https://example/about</loc>
        <changefreq>daily</changefreq>
        <priority>0.7</priority>
    </url>
    <url>
        <loc>https://example/blog</loc>
        <changefreq>daily</changefreq>
        <priority>0.7</priority>
    </url>
    <url>
        <loc>https://example/login</loc>
        <changefreq>daily</changefreq>
        <priority>0.7</priority>
    </url>
    <url>
        <loc>https://example/pricing</loc>
        <changefreq>daily</changefreq>
        <priority>0.7</priority>
    </url>
    <url>
        <loc>https://example/privacy</loc>
        <changefreq>daily</changefreq>
        <priority>0.7</priority>
    </url>
    <url>
        <loc>https://example/signup</loc>
        <changefreq>daily</changefreq>
        <priority>0.7</priority>
    </url>
    <url>
        <loc>https://example/support</loc>
        <changefreq>daily</changefreq>
        <priority>0.7</priority>
    </url>
    <url>
        <loc>https://example/terms</loc>
        <changefreq>daily</changefreq>
        <priority>0.7</priority>
    </url>
    <url>
        <loc>https://example/blog/hello-world</loc>
        <changefreq>daily</changefreq>
        <priority>0.7</priority>
    </url>
    <url>
        <loc>https://example/blog/another-post</loc>
        <changefreq>daily</changefreq>
        <priority>0.7</priority>
    </url>
    <url>
        <loc>https://example/blog/tag/red</loc>
        <changefreq>daily</changefreq>
        <priority>0.7</priority>
    </url>
    <url>
        <loc>https://example/blog/tag/green</loc>
        <changefreq>daily</changefreq>
        <priority>0.7</priority>
    </url>
    <url>
        <loc>https://example/blog/tag/blue</loc>
        <changefreq>daily</changefreq>
        <priority>0.7</priority>
    </url>
    <url>
        <loc>https://example/campsites/usa/new-york</loc>
        <changefreq>daily</changefreq>
        <priority>0.7</priority>
    </url>
    <url>
        <loc>https://example/campsites/usa/california</loc>
        <changefreq>daily</changefreq>
        <priority>0.7</priority>
    </url>
    <url>
        <loc>https://example/campsites/canada/toronto</loc>
        <changefreq>daily</changefreq>
        <priority>0.7</priority>
    </url>
    <url>
        <loc>https://example/foo.pdf</loc>
        <changefreq>daily</changefreq>
        <priority>0.7</priority>
    </url>
</urlset>

Changelog

  • 0.10.0 - Adds ability to use unlimited dynamic params per route! 🎉
  • 0.9.0 - BREAKING CHANGE. Adds configurable changefreq and priority and excludes these by default. See the README's features list for why.
  • 0.8.0 - Adds ability to specify additionalPaths that live outside /src/routes, such as /foo.pdf located at /static/foo.pdf.

Developing

git clone https://github.com/jasongitmail/sk-sitemap.git
bun install
# Then edit files in `/src/lib`

Publishing

A new version of this NPM package is automatically published when the semver version within package.json is incremented.

0.10.5

8 months ago

0.10.4

8 months ago

0.10.3

8 months ago

0.10.1

8 months ago

0.10.0

8 months ago

0.9.3

8 months ago

0.9.1

8 months ago

0.9.0

8 months ago

0.7.0

8 months ago

0.6.0

8 months ago

0.5.0

8 months ago

0.4.0

8 months ago

0.3.0

8 months ago

0.2.0

8 months ago

0.1.0

8 months ago