Richard Beattie
Posted on May 3, 2021
SvelteKit came out in public beta a little over a month ago and I've finally gotten around to trying it out. I'll write up my thoughts elsewhere but I've moved r-bt.com over to SvelteKit and replaced my Notion CMS with markdown. The reason being I want to be able to use custom components. Anyway, one problem I had was creating a sitemap.xml for my static build. SvelteKit dosn't support creating sitemaps automatically although it might in the future.
Instead I made a post build step. Some notes about this:
- I'm using Node v14 if you use an earlier version you might need to change
import
torequire
- I use
@sveltejs/adapter-static
to build a static site which is stored in/build
The Script
1. Install the dependencies
npm install -D fast-glob xmlbuilder2
2. Create a new file generate-sitemap.xml
in the root of your project (e.g. beside svelte.config.cjs
, etc) and add the following:
import fs from 'fs';
import fg from 'fast-glob';
import { create } from 'xmlbuilder2';
import pkg from './package.json';
const getUrl = (url) => {
const trimmed = url.slice(6).replace('index.html', '');
return `${pkg.url}/${trimmed}`;
};
async function createSitemap() {
const sitemap = create({ version: '1.0' }).ele('urlset', {
xmlns: 'http://www.sitemaps.org/schemas/sitemap/0.9'
});
const pages = await fg(['build/**/*.html']);
pages.forEach((page) => {
const url = sitemap.ele('url');
url.ele('loc').txt(getUrl(page));
url.ele('changefreq').txt('weekly');
});
const xml = sitemap.end({ prettyPrint: true });
fs.writeFileSync('build/sitemap.xml', xml);
}
createSitemap();
3. Update your package.json
{
url: "https://your-url.com",
scripts: {
...,
"postbuild": "node --experimental-json-modules ./generate-sitemap.js",
}
}
The Explaination
To make the sitemap we're going to build the site, glob
all the .html
files, and write the xml back to the /build
directory.
Before starting install the dependecies
npm install -D fast-glob xmlbuilder2
Now create a new file generate-sitemap.xml
First, let's get the files we need:
import fg from 'fast-glob';
async function createSitemap() {
const pages = await fg(['build/**/*.html']);
console.log({ pages });
}
If you run this you should get an array with the paths of all your pages
{
pages: [
'build/index.html',
'build/blog/index.html',
'build/about/index.html',
'build/learning/index.html',
...
];
}
Next we'll use xmlbuilder
to create the xml objects
import { create } from 'xmlbuilder2';
const sitemap = create({ version: '1.0' }).ele('urlset', {
xmlns: 'http://www.sitemaps.org/schemas/sitemap/0.9'
});
and we just loop through the pages adding each as a url
object with a loc
and changefrequency
to the sitemap
pages.forEach((page) => {
const url = sitemap.ele('url');
url.ele('loc').txt(page);
url.ele('changefreq').txt('weekly');
});
Finally we turn the sitemap into a string
and write it to a file using fs.writeFileSync
import fs from 'fs';
import fg from 'fast-glob';
import { create } from 'xmlbuilder2';
async function createSitemap() {
const sitemap = create({ version: '1.0' }).ele('urlset', {
xmlns: 'http://www.sitemaps.org/schemas/sitemap/0.9'
});
const pages = await fg(['build/**/*.html']);
console.log({ pages });
pages.forEach((page) => {
const url = sitemap.ele('url');
url.ele('loc').txt(page);
url.ele('changefreq').txt('weekly');
});
const xml = sitemap.end({ prettyPrint: true });
fs.writeFileSync('build/sitemap.xml', xml);
}
createSitemap();
Except we have a problem. If you run this code:
node generate-sitemap.js
and go to build/sitemap.xml you'll see that the locs are something that looks like:
build/learning/why-is-it-so-hard-to-find-a-domain/index.html
while we want it to be:
https://r-bt.com/learning/why-is-it-so-hard-to-find-a-domain/
To fix this go to your package.json
and add
{
"url": "https://your-url.com"
}
Then in generate-sitemap.js
we'll import package.json
and append the url to the pages paths. We'll also remove the first 5 characters build/ and index.html
import pkg from './package.json';
const getUrl = (url) => {
const trimmed = url.slice(6).replace('index.html', '');
return `${pkg.url}/${trimmed}`;
};
Node.js dosn't yet importing .json files so need to run this script with the
--experimental-json-modules
flag
node --experimental-json-modules ./generate-sitemap.js
and you're sitemap should be generated and valid 🎉
To get it to run whenever you build the site go back to package.json
and in scripts
add
{
scripts:{
...,
"postbuild": "node --experimental-json-modules ./generate-sitemap.js",
}
}
Posted on May 3, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.