Created
September 7, 2020 08:28
-
-
Save arvigeus/100c732019dff5619b32cc41cad2791e to your computer and use it in GitHub Desktop.
Next.js sitemap
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
const { promises: fs } = require("fs"); | |
const path = require("path"); | |
interface FileInfo { | |
page: string; | |
lastModified: Date; | |
} | |
const pagesDir = `${process.cwd()}/src/pages/`; | |
async function getAllFiles(): Promise<FileInfo[]> { | |
const files: FileInfo[] = []; | |
for await (const file of getFiles(pagesDir)) files.push(file); | |
return files; | |
} | |
async function* getFiles(dir: string): AsyncGenerator<FileInfo> { | |
// Get all files of the current directory & iterate over them | |
const files = await fs.readdir(dir); | |
for (const file of files) { | |
// Construct whole file-path & retrieve file's stats | |
const filePath = `${dir}${file}`; | |
const fileStat = await fs.stat(filePath); | |
if (!isValid(file, dir, fileStat)) continue; | |
if (fileStat.isDirectory()) { | |
// Recurse one folder deeper | |
for await (const innerFile of getFiles(`${filePath}/`)) yield innerFile; | |
} else { | |
// Check if page is dynamic route | |
if (/\[.*\]\.(jsx?|tsx)/.test(file)) { | |
const page = require(filePath); | |
// Dynamic paths are generated at build time | |
if (page.getStaticPaths) { | |
const { | |
paths, | |
}: { | |
paths: { params: { [key: string]: string } }[]; | |
} = page.getStaticPaths(); | |
for (const { params } of paths) { | |
let dynamicPath = file; | |
for (const [param, value] of Object.entries(params)) | |
dynamicPath = dynamicPath.replace(`[${param}]`, value); | |
yield { | |
page: `${cleanFileName(`${dir}${dynamicPath}`)}`, | |
lastModified: fileStat.mtime, | |
}; | |
} | |
} else { | |
const page = `/${cleanFileName(filePath)}`; | |
console.info( | |
`\`/${page}\` is dynamic page and must be obtained at runtime` | |
); | |
} | |
} | |
// Page is normal route | |
else { | |
yield { | |
page: `/${cleanFileName(filePath)}`, | |
lastModified: fileStat.mtime, | |
}; | |
} | |
} | |
} | |
} | |
const isValid = (file: string, parentDir: string, fileStat): boolean => { | |
const isRoot = parentDir === pagesDir; | |
const isDir = fileStat.isDirectory(); | |
if (isRoot && isDir && file === "api") return false; | |
if (isRoot && !isDir && /^404\.(jsx?|tsx)$/.test(file)) return false; | |
if (file.startsWith("_")) return false; | |
if (file) | |
if ( | |
!file.endsWith(".js") && | |
!file.endsWith(".jsx") && | |
!file.endsWith(".tsx") && | |
!file.endsWith(".md") && | |
!file.endsWith(".mdx") | |
) | |
return false; | |
return true; | |
}; | |
const cleanFileName = (fileName: string): string => { | |
const fixed = fileName | |
.substr(0, fileName.lastIndexOf(".")) | |
.replace(pagesDir, ""); | |
return fixed.endsWith("/index") || fixed === "index" | |
? fixed.substr(0, fixed.lastIndexOf("/") + 1) | |
: fixed; | |
}; | |
const formatDate = (date: Date): string => { | |
let month = String(date.getMonth() + 1), | |
day = String(date.getDate()), | |
year = date.getFullYear(); | |
if (month.length < 2) month = `0${month}`; | |
if (day.length < 2) day = `0${day}`; | |
return [year, month, day].join("-"); | |
}; | |
export function createSiteMap(items: FileInfo[]): string { | |
const lines = [ | |
'<?xml version="1.0" encoding="UTF-8"?>', | |
'<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> ', | |
]; | |
for (const { page, lastModified } of items) { | |
lines.push("<url>"); | |
lines.push(`<loc>${page}</loc>`); | |
lines.push(`<lastmod>${formatDate(lastModified)}</lastmod>`); | |
lines.push("</url>"); | |
} | |
lines.push("</urlset>"); | |
return lines.join(""); | |
} | |
export const writeSiteMap = (sitemap: string) => { | |
const parentDir = path.basename(path.dirname(pagesDir)); | |
const publicDir = `${parentDir}public/`; | |
if (!fs.existsSync(publicDir)) fs.mkdirSync(publicDir); | |
fs.writeFileSync(`${publicDir}sitemap.xml`, sitemap); | |
}; | |
export default getAllFiles; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment