Last active
January 29, 2024 08:36
-
-
Save andrelandgraf/0112631dcdf6640e4bd44360d3e7a08e to your computer and use it in GitHub Desktop.
sitemap.xml generator for remix.run
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import childProcess from 'child_process'; | |
import fs from 'fs'; | |
import dotenv from 'dotenv'; | |
import prettier from 'prettier'; | |
const rootDir = process.cwd(); | |
dotenv.config({ | |
path: `${rootDir}/.env.production`, | |
}); | |
interface Route { | |
id: string; | |
path?: string; | |
file: string; | |
children?: Route[]; | |
} | |
const today = new Date().toISOString(); | |
const domain = process.env.HOST; | |
console.log(`Updating sitemap on ${today} for domain ${domain}...`); | |
const consideredRoutes: string[] = []; | |
function addPathParts(path1 = '', path2 = ''): string { | |
return path1.endsWith('/') || path2.startsWith('/') ? `${path1}${path2}` : `${path1}/${path2}`; | |
} | |
function pathToEntry(path: string): string { | |
return ` | |
<url> | |
<loc>${addPathParts(domain, path)}</loc> | |
<lastmod>${today}</lastmod> | |
<changefreq>daily</changefreq> | |
<priority>0.7</priority> | |
</url> | |
`; | |
} | |
async function depthFirstHelper(route: Route, currentPath = ''): Promise<string> { | |
let sitemapContent = ''; | |
const isLayoutRoute = !route.path; | |
const pathIncludesParam = (route.path && route.path.includes(':')) || currentPath.includes(':'); | |
if (!isLayoutRoute && !pathIncludesParam) { | |
const filePath = `${rootDir}/app/${route.file}`; | |
const routeContent = fs.readFileSync(filePath, 'utf8'); | |
// no default export means API route | |
if (routeContent.includes('export default')) { | |
const nextPath = addPathParts(currentPath, route.path); | |
const isConsidered = consideredRoutes.includes(nextPath); | |
if (!isConsidered) { | |
sitemapContent += pathToEntry(nextPath); | |
consideredRoutes.push(nextPath); | |
} | |
} | |
} | |
if (route.children) { | |
for (const childRoute of route.children) { | |
const nextPath = addPathParts(currentPath, route.path); | |
sitemapContent += await depthFirstHelper(childRoute, nextPath); | |
} | |
} | |
return sitemapContent; | |
} | |
async function routesToSitemap(routes: Route[]): Promise<string> { | |
let sitemapContent = ''; | |
for (const route of routes) { | |
sitemapContent += await depthFirstHelper(route, ''); | |
} | |
return ` | |
<?xml version="1.0" encoding="UTF-8"?> | |
<urlset | |
xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" | |
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd" | |
> | |
${sitemapContent} | |
</urlset> | |
`; | |
} | |
const formatSitemap = (sitemap: string) => prettier.format(sitemap, { parser: 'html' }); | |
async function main() { | |
const output = childProcess.execSync('npx remix routes --json'); | |
const routes: Route[] = JSON.parse(output.toString()); | |
const root = routes.find((r) => r.id === 'root'); | |
if (!root) { | |
throw new Error('Root not found'); | |
} | |
const childRoutes = root.children; | |
if (!childRoutes) { | |
throw new Error('Root has no children routes'); | |
} | |
console.log(`Found ${childRoutes.length} root children routes!`); | |
const sitemap = await routesToSitemap(childRoutes); | |
const formattedSitemap = formatSitemap(sitemap); | |
fs.writeFileSync('./public/sitemap.xml', formattedSitemap, 'utf8'); | |
console.log('sitemap.xml updated 🎉'); | |
return formattedSitemap; | |
} | |
main(); |
Uh yea, also stumbled upon his approach though not (yet) sure what the reason is, on first look seems more complicated 😳
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@CanRau yes, you will need to do that when you have dynamic content as well. Kent is doing this on his page: https://github.com/kentcdodds/kentcdodds.com/blob/main/app/other-routes.server.ts