Now with legal policies and services pages
This commit is contained in:
99
scripts/generate-sitemap.mjs
Normal file
99
scripts/generate-sitemap.mjs
Normal file
@@ -0,0 +1,99 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Post-build: generate sitemap.xml from prerendered pages in dist/.
|
||||
* Scans for index.html (root and under each path), excludes 410 paths.
|
||||
* Run after vite build and critters, before copy-410-paths so 410 dirs don't exist yet.
|
||||
*/
|
||||
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
import { PATHS as PATHS_410 } from './410-paths.mjs';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const DIST = path.join(__dirname, '..', 'dist');
|
||||
|
||||
/** Must match src/lib/seo.ts SEO_DEFAULTS.baseUrl */
|
||||
const BASE_URL = 'https://mifi.ventures';
|
||||
|
||||
const EXCLUDE_FILES = new Set(['404.html', '410.html']);
|
||||
const excludeSet = new Set(PATHS_410);
|
||||
|
||||
/**
|
||||
* adapter-static emits path.html or path/index.html. Walk dist and collect
|
||||
* every .html that represents a page (exclude 404/410 and 410-gone paths).
|
||||
*/
|
||||
function findPages(dir, basePath = '') {
|
||||
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
||||
const pages = [];
|
||||
|
||||
for (const e of entries) {
|
||||
const rel = basePath ? `${basePath}/${e.name}` : e.name;
|
||||
|
||||
if (e.isDirectory()) {
|
||||
const indexPath = path.join(dir, e.name, 'index.html');
|
||||
if (fs.existsSync(indexPath)) {
|
||||
if (!excludeSet.has(rel)) {
|
||||
pages.push({ path: rel, indexPath });
|
||||
}
|
||||
} else {
|
||||
pages.push(...findPages(path.join(dir, e.name), rel));
|
||||
}
|
||||
} else if (e.name.endsWith('.html') && !EXCLUDE_FILES.has(e.name)) {
|
||||
const urlPath = e.name === 'index.html'
|
||||
? basePath
|
||||
: (basePath ? `${basePath}/${e.name.slice(0, -5)}` : e.name.slice(0, -5));
|
||||
if (!excludeSet.has(urlPath)) {
|
||||
pages.push({ path: urlPath, indexPath: path.join(dir, e.name) });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pages;
|
||||
}
|
||||
|
||||
function escapeXml(s) {
|
||||
return s
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
}
|
||||
|
||||
function main() {
|
||||
const indexHtml = path.join(DIST, 'index.html');
|
||||
if (!fs.existsSync(indexHtml)) {
|
||||
console.error('dist/index.html not found. Run build first.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const pages = findPages(DIST).sort((a, b) => {
|
||||
if (a.path === '') return -1;
|
||||
if (b.path === '') return 1;
|
||||
return a.path.localeCompare(b.path);
|
||||
});
|
||||
const urlElements = [];
|
||||
|
||||
for (const { path: pagePath, indexPath } of pages) {
|
||||
const loc = pagePath ? `${BASE_URL}/${pagePath}` : BASE_URL;
|
||||
const stat = fs.statSync(indexPath);
|
||||
const lastmod = stat.mtime.toISOString().slice(0, 10);
|
||||
|
||||
urlElements.push(
|
||||
` <url>\n <loc>${escapeXml(loc)}</loc>\n <lastmod>${lastmod}</lastmod>\n </url>`
|
||||
);
|
||||
}
|
||||
|
||||
const xml = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
${urlElements.join('\n')}
|
||||
</urlset>
|
||||
`;
|
||||
|
||||
fs.writeFileSync(path.join(DIST, 'sitemap.xml'), xml, 'utf8');
|
||||
console.log('✓ sitemap.xml generated with', pages.length, 'URLs.');
|
||||
}
|
||||
|
||||
main();
|
||||
Reference in New Issue
Block a user