Skip to content

Instantly share code, notes, and snippets.

@multipliedtwice
Last active November 20, 2021 13:49
Show Gist options
  • Save multipliedtwice/da72201afa07460095cbbfbdd8fa524b to your computer and use it in GitHub Desktop.
Save multipliedtwice/da72201afa07460095cbbfbdd8fa524b to your computer and use it in GitHub Desktop.
Multilingual Sapper sitemap.xml with day.js
import dayjs from "dayjs"
import { supportedLanguages } from "../config"
const fs = require("fs")
const path = require("path")
const cwd = process.cwd()
const matter = require("gray-matter")
const util = require("util")
const glob = require("glob")
const stat = util.promisify(fs.stat)
const BASE_URL = "https://rememo.io" // Change this with ur domain
const pages = [""]
const POSTS_DIR = path.join(cwd, "src/")
let svelteRoutes = []
const i18nFolder = "[lang]"
const notPages = [`/${i18nFolder}`, "/p"] // Urls to exclude from sitemap
const extraFilter = (file) => !notPages.includes(file)
const directory = path.join(cwd, "src/routes")
const unixStyleDirectory = directory.replace(/\\/gi, "/")
const getRoutes = async (files) => {
// get all the routes in the directory and index files stats (last modified)
const mapped = await Promise.all(
files.map(async (file) => ({
last_modified: dayjs(await stat(file).mtime).format("YYYY-MM-DD"),
filepath: file.replace(unixStyleDirectory, "").replace(/\/index.svelte/gi, ""),
}))
)
// remove every route that has ID in it - those will be rendered after API call
const filtered = await Promise.all(
mapped.filter(
({ filepath }) => filepath.length && !filepath?.includes("id") && extraFilter(filepath)
)
)
// replace [lang] with available languages
const reduced = await Promise.all(
filtered.reduce((acc = [], current) => {
if (current.filepath.includes(i18nFolder)) {
const i18nRoutes = supportedLanguages.map((lang) =>
current.filepath.replace("[lang]", lang)
)
acc.push(
i18nRoutes.map((route) => ({ filepath: route, last_modified: current.last_modified }))
)
} else {
acc.push(current)
}
return acc
}, [])
)
/*
Output will look something like this:
[
[
{ filepath: '/ru/shortcuts', last_modified: '2021-11-20' },
{ filepath: '/en/shortcuts', last_modified: '2021-11-20' },
{ filepath: '/th/shortcuts', last_modified: '2021-11-20' }
],
{ last_modified: '2021-11-20', filepath: '/changelog' },
{ last_modified: '2021-11-20', filepath: '/privacy' },
]
*/
return reduced
}
glob(directory + "/**/!(_)*.svelte", {}, async (err, files) => {
svelteRoutes = await getRoutes(files)
})
const render = (pages, posts) => `<?xml version="1.0" encoding="UTF-8" ?>
<urlset
xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
xmlns:xhtml="http://www.w3.org/1999/xhtml"
>
${svelteRoutes
.map((route) => {
if (Array.isArray(route)) {
return `<url>
${route
.map((r, i) => {
if (i === 0) {
return `<loc>${BASE_URL}${r.filepath}</loc>`
} else {
return `<xhtml:link rel="alternate" hreflang="${
r.filepath.split("/")[1]
}" href="${BASE_URL}${r.filepath}"/>`
}
})
.join("\n")}
<lastmod>${route[0].last_modified}</lastmod>
</url>`
} else {
return `<url>
<loc>
${BASE_URL}${route.filepath}
</loc>
<lastmod>${route.last_modified}</lastmod>
</url>`
}
})
.join("\n")}
</urlset>
`
export function get(req, res, next) {
res.setHeader("Cache-Control", `max-age=0, s-max-age=${600}`) // 10 minutes
res.setHeader("Content-Type", "application/xml")
const sitemap = render(pages, posts)
res.end(sitemap)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment