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();