// This is a development script executed in the build step of pages const fs = require('fs'); const path = require('path'); const guides = require('../content/guides'); const roadmaps = require('../content/roadmaps'); const { getPageRoutes, getGuideRoutes } = require('./path-map'); const DOMAIN = 'https://roadmap.sh'; const PAGES_DIR = path.join(__dirname, '../pages'); const STORAGE_PATH = path.join(__dirname, '../content'); const SITEMAP_PATH = 'public/sitemap.xml'; // Set the header const xmlHeader = ` `; // Wrap all pages in tags const xmlUrlWrapper = nodes => `${xmlHeader} ${nodes} `; const getSlugPriority = (pageSlug) => { if (pageSlug === '/') { return '1.0'; } const slugPriorities = [ ['/roadmaps', '/guides'], // 1.0 ['/signup'], // 0.9 ['/about'], // 0.8 ]; const foundIndex = slugPriorities.findIndex( routes => routes.some(route => pageSlug.startsWith(route)), ); if (foundIndex !== -1) { return parseFloat((10 - foundIndex) / 10) .toFixed(1); } return 0.5; }; function generateNode({ slug, basePath, fileName, priority = null, date = null, frequency = 'monthly', }) { const pagePath = path.join(basePath, fileName); let pageStats = {}; try { pageStats = fs.lstatSync(pagePath); } catch (e) { console.log(`File not found: ${pagePath}`); pageStats = { mtime: (new Date()) } } return ` ${DOMAIN}${slug} ${ frequency } ${date || pageStats.mtime.toISOString()} ${ priority || getSlugPriority(slug) } `; } function generateSiteMap() { const pageRoutes = getPageRoutes(); const pageSlugs = Object.keys(pageRoutes) .filter(route => ![ '/privacy', '/terms', ].includes(route)); const pagesChunk = pageSlugs.map(pageSlug => { return generateNode({ basePath: PAGES_DIR, fileName: `${pageRoutes[pageSlug].page}.js`, slug: pageSlug, }); }); // Chunks for each of the guides const guideRoutes = getGuideRoutes(); const guideSlugs = Object.keys(guideRoutes); const guidesChunk = guideSlugs.map(guideSlug => { const foundGuide = guides.find(guide => guide.url === guideSlug) || {}; return generateNode({ basePath: STORAGE_PATH, fileName: `${guideSlug}.md`, slug: guideSlug, date: foundGuide.updatedAt, priority: '1.0', }); }); // Chunks for each of the roadmaps const roadmapsChunk = roadmaps.reduce((roadmapsNodes, roadmap) => { return [ ...roadmapsNodes, generateNode({ basePath: STORAGE_PATH, fileName: roadmap.path, slug: roadmap.url, priority: '1.0', }), ...Object .values(roadmap.sidebar || {}) .reduce((pageNodes, menuPages) => { return [ ...pageNodes, ...menuPages.map(menuPage => { return generateNode({ basePath: STORAGE_PATH, fileName: menuPage.path, slug: menuPage.url, priority: menuPage.path.includes('_others') ? '0.5' : '1.0', }) }) ]; }, []) ] }, []); const nodes = [ ...roadmapsChunk, ...guidesChunk, ...pagesChunk, ]; const sitemap = `${xmlUrlWrapper(nodes.join('\n'))}`; fs.writeFileSync(SITEMAP_PATH, sitemap); console.log(`sitemap.xml with ${nodes.length} entries was written to ${SITEMAP_PATH}`); } generateSiteMap();