1*3f609562SBartosz Kaszubowskiimport fs from 'fs'; 2*3f609562SBartosz Kaszubowskiimport path from 'path'; 3*3f609562SBartosz Kaszubowskiimport { SitemapStream } from 'sitemap'; 4*3f609562SBartosz Kaszubowski 5*3f609562SBartosz Kaszubowskiconst IGNORED_PAGES = [ 6*3f609562SBartosz Kaszubowski '/404', // We don't want to add the 404 error page as sitemap entry 7*3f609562SBartosz Kaszubowski '/versions', // Skip the redirect to latest, use `/versions/latest` instead 8*3f609562SBartosz Kaszubowski]; 9*3f609562SBartosz Kaszubowski 10*3f609562SBartosz Kaszubowski/** 11*3f609562SBartosz Kaszubowski * Create a sitemap for crawlers like Algolia Docsearch. 12*3f609562SBartosz Kaszubowski * This allows crawlers to index _all_ pages, without a full page-link-chain. 13*3f609562SBartosz Kaszubowski */ 14*3f609562SBartosz Kaszubowskiexport default function createSitemap({ 15*3f609562SBartosz Kaszubowski pathMap, 16*3f609562SBartosz Kaszubowski domain, 17*3f609562SBartosz Kaszubowski output, 18*3f609562SBartosz Kaszubowski pathsPriority = [], 19*3f609562SBartosz Kaszubowski pathsHidden = [], 20*3f609562SBartosz Kaszubowski}) { 21*3f609562SBartosz Kaszubowski if (!pathMap) throw new Error(`⚠️ Couldn't generate sitemap, no 'pathMap' provided`); 22*3f609562SBartosz Kaszubowski if (!domain) throw new Error(`⚠️ Couldn't generate sitemap, no 'domain' provided`); 23*3f609562SBartosz Kaszubowski if (!output) throw new Error(`⚠️ Couldn't generate sitemap, no 'output' provided`); 24*3f609562SBartosz Kaszubowski 25*3f609562SBartosz Kaszubowski // Make sure both hidden and prioritized paths are prefixed with slash 26*3f609562SBartosz Kaszubowski pathsPriority = pathsPriority.map(pathWithStartingSlash); 27*3f609562SBartosz Kaszubowski pathsHidden = pathsHidden.map(pathWithStartingSlash); 28*3f609562SBartosz Kaszubowski 29*3f609562SBartosz Kaszubowski // Get a list of URLs from the pathMap that we can use in the sitemap 30*3f609562SBartosz Kaszubowski const urls = Object.keys(pathMap) 31*3f609562SBartosz Kaszubowski .filter( 32*3f609562SBartosz Kaszubowski url => !IGNORED_PAGES.includes(url) && !pathsHidden.find(hidden => url.startsWith(hidden)) 33*3f609562SBartosz Kaszubowski ) 34*3f609562SBartosz Kaszubowski .map(pathWithTrailingSlash) 35*3f609562SBartosz Kaszubowski .sort((a, b) => pathSortedByPriority(a, b, pathsPriority)); 36*3f609562SBartosz Kaszubowski 37*3f609562SBartosz Kaszubowski const target = fs.createWriteStream(output); 38*3f609562SBartosz Kaszubowski const sitemap = new SitemapStream({ 39*3f609562SBartosz Kaszubowski hostname: domain, 40*3f609562SBartosz Kaszubowski xmlns: { 41*3f609562SBartosz Kaszubowski news: false, 42*3f609562SBartosz Kaszubowski xhtml: false, 43*3f609562SBartosz Kaszubowski image: false, 44*3f609562SBartosz Kaszubowski video: false, 45*3f609562SBartosz Kaszubowski }, 46*3f609562SBartosz Kaszubowski }); 47*3f609562SBartosz Kaszubowski 48*3f609562SBartosz Kaszubowski sitemap.pipe(target); 49*3f609562SBartosz Kaszubowski urls.forEach(url => sitemap.write({ url })); 50*3f609562SBartosz Kaszubowski sitemap.end(); 51*3f609562SBartosz Kaszubowski 52*3f609562SBartosz Kaszubowski return urls; 53*3f609562SBartosz Kaszubowski} 54*3f609562SBartosz Kaszubowski 55*3f609562SBartosz Kaszubowskifunction pathWithTrailingSlash(url) { 56*3f609562SBartosz Kaszubowski return !path.extname(url) && !url.endsWith('/') ? `${url}/` : url; 57*3f609562SBartosz Kaszubowski} 58*3f609562SBartosz Kaszubowski 59*3f609562SBartosz Kaszubowskifunction pathWithStartingSlash(url) { 60*3f609562SBartosz Kaszubowski return url.startsWith('/') ? url : `/${url}`; 61*3f609562SBartosz Kaszubowski} 62*3f609562SBartosz Kaszubowski 63*3f609562SBartosz Kaszubowski/** 64*3f609562SBartosz Kaszubowski * This will sort the paths by their priority. 65*3f609562SBartosz Kaszubowski * It applies the following rules: 66*3f609562SBartosz Kaszubowski * - Index page is always moved to the top 67*3f609562SBartosz Kaszubowski * - Matches the order of prioritized paths using "startsWith" check 68*3f609562SBartosz Kaszubowski */ 69*3f609562SBartosz Kaszubowskifunction pathSortedByPriority(a, b, priorities = []) { 70*3f609562SBartosz Kaszubowski if (a === '/') return -1; 71*3f609562SBartosz Kaszubowski if (b === '/') return 1; 72*3f609562SBartosz Kaszubowski 73*3f609562SBartosz Kaszubowski const aPriority = priorities.findIndex(prio => a.startsWith(prio)); 74*3f609562SBartosz Kaszubowski const bPriority = priorities.findIndex(prio => b.startsWith(prio)); 75*3f609562SBartosz Kaszubowski if (aPriority >= 0 || bPriority >= 0) { 76*3f609562SBartosz Kaszubowski return aPriority - bPriority; 77*3f609562SBartosz Kaszubowski } 78*3f609562SBartosz Kaszubowski 79*3f609562SBartosz Kaszubowski return 0; 80*3f609562SBartosz Kaszubowski} 81