diff --git a/package.json b/package.json
index e771569de..2dae90b34 100644
--- a/package.json
+++ b/package.json
@@ -7,7 +7,9 @@
"build": "next build",
"start": "next start",
"lint": "next lint",
- "meta:roadmaps": "node ./scripts/roadmaps-meta.js"
+ "meta:sitemap": "node scripts/sitemap.js",
+ "meta:roadmaps": "node ./scripts/roadmaps-meta.js",
+ "meta": "npm run meta:roadmaps && npm run meta:sitemap"
},
"dependencies": {
"@chakra-ui/icons": "^1.0.14",
@@ -34,6 +36,7 @@
"eslint": "7.32.0",
"eslint-config-next": "11.0.1",
"eslint-config-prettier": "^8.3.0",
+ "glob": "^7.1.7",
"husky": "^7.0.1",
"pretty-quick": "^3.1.1",
"typescript": "4.3.5"
diff --git a/public/sitemap.xml b/public/sitemap.xml
new file mode 100644
index 000000000..02b67b017
--- /dev/null
+++ b/public/sitemap.xml
@@ -0,0 +1,237 @@
+
+
+
+ https://roadmap.sh/frontend
+ monthly
+ 2021-09-04T17:09:59.269Z
+ 1.0
+
+
+ https://roadmap.sh/backend
+ monthly
+ 2021-09-04T17:10:19.270Z
+ 1.0
+
+
+ https://roadmap.sh/devops
+ monthly
+ 2021-09-04T17:10:44.437Z
+ 1.0
+
+
+ https://roadmap.sh/react
+ monthly
+ 2021-09-04T17:11:05.241Z
+ 1.0
+
+
+ https://roadmap.sh/postgresql-dba
+ monthly
+ 2021-09-04T17:04:01.161Z
+ 1.0
+
+
+ https://roadmap.sh/android
+ monthly
+ 2021-09-04T17:04:18.383Z
+ 1.0
+
+
+ https://roadmap.sh/qa
+ monthly
+ 2021-09-04T17:01:41.946Z
+ 1.0
+
+
+ https://roadmap.sh/guides/ci-cd
+ monthly
+ 2020-07-09T19:59:14.191Z
+ 1.0
+
+
+ https://roadmap.sh/guides/sso
+ monthly
+ 2020-07-01T19:59:14.191Z
+ 1.0
+
+
+ https://roadmap.sh/guides/oauth
+ monthly
+ 2020-06-28T19:59:14.191Z
+ 1.0
+
+
+ https://roadmap.sh/guides/jwt-authentication
+ monthly
+ 2020-06-20T19:59:14.191Z
+ 1.0
+
+
+ https://roadmap.sh/guides/token-authentication
+ monthly
+ 2020-06-02T20:59:14.191Z
+ 1.0
+
+
+ https://roadmap.sh/guides/session-authentication
+ monthly
+ 2020-05-26T20:59:14.191Z
+ 1.0
+
+
+ https://roadmap.sh/guides/basic-authentication
+ monthly
+ 2020-05-19T20:59:14.191Z
+ 1.0
+
+
+ https://roadmap.sh/guides/character-encodings
+ monthly
+ 2020-05-14T20:59:14.191Z
+ 1.0
+
+
+ https://roadmap.sh/guides/unfamiliar-codebase
+ monthly
+ 2020-05-04T20:59:14.191Z
+ 1.0
+
+
+ https://roadmap.sh/guides/why-build-it-and-they-will-come-wont-work-anymore
+ monthly
+ 2020-05-04T12:59:14.191Z
+ 1.0
+
+
+ https://roadmap.sh/guides/dhcp-in-one-picture
+ monthly
+ 2020-04-28T15:48:21.191Z
+ 1.0
+
+
+ https://roadmap.sh/guides/ssl-tls-https-ssh
+ monthly
+ 2020-04-22T15:48:21.191Z
+ 1.0
+
+
+ https://roadmap.sh/guides/asymptotic-notation
+ monthly
+ 2020-04-03T15:48:21.191Z
+ 1.0
+
+
+ https://roadmap.sh/guides/big-o-notation
+ monthly
+ 2020-03-15T15:48:21.191Z
+ 1.0
+
+
+ https://roadmap.sh/guides/random-numbers
+ monthly
+ 2020-03-14T15:48:21.191Z
+ 1.0
+
+
+ https://roadmap.sh/guides/scaling-databases
+ monthly
+ 2020-02-18T15:48:21.191Z
+ 1.0
+
+
+ https://roadmap.sh/guides/what-is-internet
+ monthly
+ 2020-02-29T15:48:21.191Z
+ 1.0
+
+
+ https://roadmap.sh/guides/torrent-client
+ monthly
+ 2020-01-17T15:48:21.191Z
+ 1.0
+
+
+ https://roadmap.sh/guides/levels-of-seniority
+ monthly
+ 2019-12-03T12:13:00.860Z
+ 1.0
+
+
+ https://roadmap.sh/guides/design-patterns-for-humans
+ monthly
+ 2019-10-09T12:00:00.860Z
+ 1.0
+
+
+ https://roadmap.sh/guides/journey-to-http2
+ monthly
+ 2018-12-04T12:00:00.860Z
+ 1.0
+
+
+ https://roadmap.sh/guides/dns-in-one-picture
+ monthly
+ 2018-12-04T12:00:00.860Z
+ 1.0
+
+
+ https://roadmap.sh/guides/http-caching
+ monthly
+ 2018-11-29T17:00:00.860Z
+ 1.0
+
+
+ https://roadmap.sh/guides/history-of-javascript
+ monthly
+ 2017-10-28T17:00:00.860Z
+ 1.0
+
+
+ https://roadmap.sh/guides/proxy-servers
+ monthly
+ 2020-07-24T12:40:18
+ 1.0
+
+
+ https://roadmap.sh/about
+ monthly
+ 2021-09-04T18:03:52.068Z
+ 0.8
+
+
+ https://roadmap.sh/guides
+ monthly
+ 2021-09-04T20:56:05.377Z
+ 1.0
+
+
+ https://roadmap.sh/
+ monthly
+ 2021-09-04T20:58:19.326Z
+ 1.0
+
+
+ https://roadmap.sh/roadmaps
+ monthly
+ 2021-09-04T18:05:27.986Z
+ 1.0
+
+
+ https://roadmap.sh/signup
+ monthly
+ 2021-09-04T18:05:54.681Z
+ 0.9
+
+
+ https://roadmap.sh/thanks
+ monthly
+ 2021-09-04T17:38:08.556Z
+ 0.5
+
+
+ https://roadmap.sh/watch
+ monthly
+ 2021-09-04T18:06:47.417Z
+ 1.0
+
+
\ No newline at end of file
diff --git a/scripts/sitemap.js b/scripts/sitemap.js
new file mode 100644
index 000000000..6af264ffe
--- /dev/null
+++ b/scripts/sitemap.js
@@ -0,0 +1,149 @@
+// This is a development script executed in the build step of pages
+const fs = require('fs');
+const path = require('path');
+const glob = require('glob');
+const guides = require('../content/guides.json');
+const roadmaps = require('../content/roadmaps.json');
+
+const DOMAIN = 'https://roadmap.sh';
+const PAGES_DIR = path.join(__dirname, '../pages');
+const STORAGE_PATH = path.join(__dirname, '../content');
+const SITEMAP_PATH = path.join(__dirname, '../public/sitemap.xml');
+
+const PAGES_PATH = path.join(__dirname, '../pages');
+const ROADMAPS_PATH = path.join(__dirname, '../content/roadmaps');
+const GUIDES_PATH = path.join(__dirname, '../content/guides');
+
+// Set the header
+const xmlHeader = `
+`;
+
+// Wrap all pages in tags
+const xmlUrlWrapper = nodes => `${xmlHeader}
+${nodes}
+`;
+
+function getSlugPriority(pageSlug) {
+ if (pageSlug === '/') {
+ return '1.0';
+ }
+
+ const slugPriorities = [
+ [
+ '/roadmaps',
+ '/guides',
+ '/watch',
+ '/podcasts'
+ ], // 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 getPageRoutes() {
+ const files = glob.sync(`${PAGES_PATH}/**/*.tsx`, {
+ ignore: [
+ '**/_*.tsx', // private non-page files e.g. _document.js
+ '**/[[]*[]].tsx', // Ignore dynamic pages i.e. `page/[something].js` files
+ '**/[[]*[]]/*.tsx', // Ignore files inside dynamic pages i.e. `[something]/abc.js`
+ '**/components/*.tsx' // Ignore the component files
+ ]
+ });
+
+ const pageRoutes = {};
+ files.forEach(file => {
+ const pageName = file.replace(PAGES_PATH, '').replace('.tsx', '');
+ const pagePath = pageName.replace('/index', '') || '/';
+
+ pageRoutes[pagePath] = { page: `${pageName}` };
+ });
+
+ return pageRoutes;
+}
+
+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}.tsx`,
+ slug: pageSlug
+ });
+ });
+
+ const guidesChunk = guides.map(guide => {
+ return generateNode({
+ basePath: GUIDES_PATH,
+ fileName: `${guide.id}.md`,
+ slug: `/guides/${guide.id}`,
+ date: guide.updatedAt,
+ priority: '1.0'
+ });
+ });
+
+ const roadmapsChunk = roadmaps.map((roadmap, roadmapCounter) => {
+ return generateNode({
+ basePath: ROADMAPS_PATH,
+ fileName: `${roadmapCounter + 1}-${roadmap.id}/meta.json`,
+ slug: `/${roadmap.id}`,
+ date: roadmap.updatedAt,
+ priority: '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();