xref: /expo/docs/scripts/create-sitemap.js (revision 3f609562)
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