From c88117373819fdd08894b1980b37123b9eeea10d Mon Sep 17 00:00:00 2001 From: Nicholas Chiang Date: Fri, 28 Jul 2023 19:58:19 -0600 Subject: [PATCH] feat: add sitemap and all show routes to map This patch adds a sitemap that is dynamically generated by `remix-sitemap`. This allows us to ensure that all the shows in our database are properly added to it. Ref: https://github.com/fedeya/remix-sitemap Ref: https://stackoverflow.com/a/31354426 Ref: https://developers.google.com/search/docs/crawling-indexing/sitemaps/build-sitemap Closes: NC-681 --- app/entry.server.tsx | 9 ++ .../route.tsx | 12 ++ app/utils.ts | 4 +- package.json | 1 + pnpm-lock.yaml | 106 ++++++++++-------- 5 files changed, 86 insertions(+), 46 deletions(-) diff --git a/app/entry.server.tsx b/app/entry.server.tsx index 7535587f..189d217c 100644 --- a/app/entry.server.tsx +++ b/app/entry.server.tsx @@ -1,6 +1,14 @@ import { RemixServer } from '@remix-run/react' import type { EntryContext } from '@vercel/remix' import { handleRequest } from '@vercel/remix' +import { createSitemapGenerator } from 'remix-sitemap' + +import { BASE_URL } from 'utils' + +const { isSitemapUrl, sitemap } = createSitemapGenerator({ + siteUrl: BASE_URL, + generateRobotsTxt: true, +}) export default function entry( request: Request, @@ -8,6 +16,7 @@ export default function entry( responseHeaders: Headers, remixContext: EntryContext, ) { + if (isSitemapUrl(request)) return sitemap(request, remixContext) responseHeaders.set('Cache-Control', 's-maxage=1, stale-while-revalidate=59') return handleRequest( request, diff --git a/app/routes/shows.$seasonYear.$seasonName.($sex).$brandSlug/route.tsx b/app/routes/shows.$seasonYear.$seasonName.($sex).$brandSlug/route.tsx index c58d58aa..59d9d734 100644 --- a/app/routes/shows.$seasonYear.$seasonName.($sex).$brandSlug/route.tsx +++ b/app/routes/shows.$seasonYear.$seasonName.($sex).$brandSlug/route.tsx @@ -4,6 +4,7 @@ import { type SerializeFrom, type V2_MetaFunction, } from '@vercel/remix' +import { type SitemapFunction } from 'remix-sitemap' import { prisma } from 'db.server' import { log } from 'log.server' @@ -48,6 +49,17 @@ export const meta: V2_MetaFunction = ({ data }) => { ] } +export const sitemap: SitemapFunction = async () => { + const shows = await prisma.show.findMany({ + include: { season: true, brand: true }, + orderBy: { name: 'asc' }, + }) + return shows.map((show) => ({ + loc: getShowPath(show), + lastmod: show.updatedAt.toISOString(), + })) +} + export const handle: Handle = { breadcrumb: (match) => { const data = match.data as SerializeFrom | undefined diff --git a/app/utils.ts b/app/utils.ts index 80deaddd..4c3ecb44 100644 --- a/app/utils.ts +++ b/app/utils.ts @@ -6,8 +6,8 @@ import type { User } from 'models/user.server' import type { loader } from 'root' -const DEFAULT_REDIRECT = '/' -const BASE_URL = 'https://nicholas.engineering' +export const DEFAULT_REDIRECT = '/' +export const BASE_URL = 'https://nicholas.engineering' export const clone = rfdc() diff --git a/package.json b/package.json index 7ccb0401..eb92122f 100644 --- a/package.json +++ b/package.json @@ -83,6 +83,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-hotkeys-hook": "^4.4.1", + "remix-sitemap": "^2.2.0", "rfdc": "^1.3.0", "schema-dts": "^1.1.2", "sharp": "^0.32.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 41f63dfb..49771691 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -115,6 +115,9 @@ dependencies: react-hotkeys-hook: specifier: ^4.4.1 version: 4.4.1(react-dom@18.2.0)(react@18.2.0) + remix-sitemap: + specifier: ^2.2.0 + version: 2.2.0(@remix-run/server-runtime@1.19.1)(typescript@4.9.5) rfdc: specifier: ^1.3.0 version: 1.3.0 @@ -2073,13 +2076,27 @@ packages: resolution: {integrity: sha512-14FtKiHhy2QoPIzdTcvh//8OyBlknNs2nXRwIhG904opCby3l+9Xaf/wuPvICBF0rc1ZCNBd3nKe9cd2mecVkQ==} dev: true + /@esbuild-plugins/tsconfig-paths@0.1.2(esbuild@0.17.19)(typescript@4.9.5): + resolution: {integrity: sha512-TusFR26Y+Ze+Zm+NdfqZTSG4XyrXKxIaAfYCL3jASEI/gHjSdoCujATjzNWaaXs6Sk6Bv2D7NLr4Jdz1gysy/Q==} + peerDependencies: + esbuild: '*' + typescript: '*' + dependencies: + debug: 4.3.4(supports-color@8.1.1) + esbuild: 0.17.19 + find-up: 5.0.0 + strip-json-comments: 3.1.1 + typescript: 4.9.5 + transitivePeerDependencies: + - supports-color + dev: false + /@esbuild/android-arm64@0.17.19: resolution: {integrity: sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==} engines: {node: '>=12'} cpu: [arm64] os: [android] requiresBuild: true - dev: true optional: true /@esbuild/android-arm64@0.17.6: @@ -2106,7 +2123,6 @@ packages: cpu: [arm] os: [android] requiresBuild: true - dev: true optional: true /@esbuild/android-arm@0.17.6: @@ -2133,7 +2149,6 @@ packages: cpu: [x64] os: [android] requiresBuild: true - dev: true optional: true /@esbuild/android-x64@0.17.6: @@ -2160,7 +2175,6 @@ packages: cpu: [arm64] os: [darwin] requiresBuild: true - dev: true optional: true /@esbuild/darwin-arm64@0.17.6: @@ -2187,7 +2201,6 @@ packages: cpu: [x64] os: [darwin] requiresBuild: true - dev: true optional: true /@esbuild/darwin-x64@0.17.6: @@ -2214,7 +2227,6 @@ packages: cpu: [arm64] os: [freebsd] requiresBuild: true - dev: true optional: true /@esbuild/freebsd-arm64@0.17.6: @@ -2241,7 +2253,6 @@ packages: cpu: [x64] os: [freebsd] requiresBuild: true - dev: true optional: true /@esbuild/freebsd-x64@0.17.6: @@ -2268,7 +2279,6 @@ packages: cpu: [arm64] os: [linux] requiresBuild: true - dev: true optional: true /@esbuild/linux-arm64@0.17.6: @@ -2295,7 +2305,6 @@ packages: cpu: [arm] os: [linux] requiresBuild: true - dev: true optional: true /@esbuild/linux-arm@0.17.6: @@ -2322,7 +2331,6 @@ packages: cpu: [ia32] os: [linux] requiresBuild: true - dev: true optional: true /@esbuild/linux-ia32@0.17.6: @@ -2349,7 +2357,6 @@ packages: cpu: [loong64] os: [linux] requiresBuild: true - dev: true optional: true /@esbuild/linux-loong64@0.17.6: @@ -2376,7 +2383,6 @@ packages: cpu: [mips64el] os: [linux] requiresBuild: true - dev: true optional: true /@esbuild/linux-mips64el@0.17.6: @@ -2403,7 +2409,6 @@ packages: cpu: [ppc64] os: [linux] requiresBuild: true - dev: true optional: true /@esbuild/linux-ppc64@0.17.6: @@ -2430,7 +2435,6 @@ packages: cpu: [riscv64] os: [linux] requiresBuild: true - dev: true optional: true /@esbuild/linux-riscv64@0.17.6: @@ -2457,7 +2461,6 @@ packages: cpu: [s390x] os: [linux] requiresBuild: true - dev: true optional: true /@esbuild/linux-s390x@0.17.6: @@ -2484,7 +2487,6 @@ packages: cpu: [x64] os: [linux] requiresBuild: true - dev: true optional: true /@esbuild/linux-x64@0.17.6: @@ -2511,7 +2513,6 @@ packages: cpu: [x64] os: [netbsd] requiresBuild: true - dev: true optional: true /@esbuild/netbsd-x64@0.17.6: @@ -2538,7 +2539,6 @@ packages: cpu: [x64] os: [openbsd] requiresBuild: true - dev: true optional: true /@esbuild/openbsd-x64@0.17.6: @@ -2565,7 +2565,6 @@ packages: cpu: [x64] os: [sunos] requiresBuild: true - dev: true optional: true /@esbuild/sunos-x64@0.17.6: @@ -2592,7 +2591,6 @@ packages: cpu: [arm64] os: [win32] requiresBuild: true - dev: true optional: true /@esbuild/win32-arm64@0.17.6: @@ -2619,7 +2617,6 @@ packages: cpu: [ia32] os: [win32] requiresBuild: true - dev: true optional: true /@esbuild/win32-ia32@0.17.6: @@ -2646,7 +2643,6 @@ packages: cpu: [x64] os: [win32] requiresBuild: true - dev: true optional: true /@esbuild/win32-x64@0.17.6: @@ -6944,7 +6940,6 @@ packages: dependencies: ms: 2.1.2 supports-color: 8.1.1 - dev: true /decamelize-keys@1.1.1: resolution: {integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==} @@ -7654,7 +7649,6 @@ packages: '@esbuild/win32-arm64': 0.17.19 '@esbuild/win32-ia32': 0.17.19 '@esbuild/win32-x64': 0.17.19 - dev: true /esbuild@0.17.6: resolution: {integrity: sha512-TKFRp9TxrJDdRWfSsSERKEovm6v30iHnrjlcGhLBOtReE28Yp1VSBRfO3GTaOFMoxsNerx4TjrhzSuma9ha83Q==} @@ -8539,6 +8533,13 @@ packages: resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} dev: false + /fast-xml-parser@4.2.6: + resolution: {integrity: sha512-Xo1qV++h/Y3Ng8dphjahnYe+rGHaaNdsYOBWL9Y9GCPKpNKilJtilvWkLcI9f9X2DoKTLsZsGYAls5+JL5jfLA==} + hasBin: true + dependencies: + strnum: 1.0.5 + dev: false + /fastq@1.15.0: resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} dependencies: @@ -8643,7 +8644,6 @@ packages: dependencies: locate-path: 6.0.0 path-exists: 4.0.0 - dev: true /find-up@6.3.0: resolution: {integrity: sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==} @@ -9229,7 +9229,6 @@ packages: /has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} - dev: true /has-property-descriptors@1.0.0: resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==} @@ -9948,6 +9947,15 @@ packages: engines: {node: '>=0.10.0'} dev: true + /isomorphic-fetch@3.0.0: + resolution: {integrity: sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA==} + dependencies: + node-fetch: 2.6.12 + whatwg-fetch: 3.6.17 + transitivePeerDependencies: + - encoding + dev: false + /isstream@0.1.2: resolution: {integrity: sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==} dev: true @@ -10371,7 +10379,6 @@ packages: engines: {node: '>=10'} dependencies: p-locate: 5.0.0 - dev: true /locate-path@7.2.0: resolution: {integrity: sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==} @@ -11207,7 +11214,7 @@ packages: acorn: 8.10.0 pathe: 1.1.1 pkg-types: 1.0.2 - ufo: 1.1.1 + ufo: 1.1.2 dev: true /mlly@1.3.0: @@ -11250,7 +11257,6 @@ packages: /ms@2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} - dev: true /ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -11399,7 +11405,6 @@ packages: optional: true dependencies: whatwg-url: 5.0.0 - dev: true /node-fetch@2.6.7: resolution: {integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==} @@ -11858,7 +11863,6 @@ packages: engines: {node: '>=10'} dependencies: yocto-queue: 0.1.0 - dev: true /p-limit@4.0.0: resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} @@ -11886,7 +11890,6 @@ packages: engines: {node: '>=10'} dependencies: p-limit: 3.1.0 - dev: true /p-locate@6.0.0: resolution: {integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==} @@ -12042,7 +12045,6 @@ packages: /path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} - dev: true /path-exists@5.0.0: resolution: {integrity: sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==} @@ -13305,6 +13307,25 @@ packages: unified: 10.1.2 dev: true + /remix-sitemap@2.2.0(@remix-run/server-runtime@1.19.1)(typescript@4.9.5): + resolution: {integrity: sha512-Wb5/7DMOsqVbne/aTTRJgDdPNltXBMUEenI0Wd3o4HQ/QwBCziDgCi+0R4fhN4xDVLxyYLDpbqb2gBy0FEJYmw==} + hasBin: true + peerDependencies: + '@remix-run/server-runtime': ^1.15.0 + dependencies: + '@esbuild-plugins/tsconfig-paths': 0.1.2(esbuild@0.17.19)(typescript@4.9.5) + '@remix-run/server-runtime': 1.19.1 + esbuild: 0.17.19 + fast-xml-parser: 4.2.6 + isomorphic-fetch: 3.0.0 + require-from-string: 2.0.2 + ufo: 1.1.2 + transitivePeerDependencies: + - encoding + - supports-color + - typescript + dev: false + /replace-in-file@6.3.5: resolution: {integrity: sha512-arB9d3ENdKva2fxRnSjwBEXfK1npgyci7ZZuwysgAp7ORjHSyxz6oqIjTEv8R0Ydl4Ll7uOAZXL4vbkhGIizCg==} engines: {node: '>=10'} @@ -13339,7 +13360,6 @@ packages: /require-from-string@2.0.2: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} - dev: true /require-like@0.1.2: resolution: {integrity: sha512-oyrU88skkMtDdauHDuKVrgR+zuItqr6/c//FXzvmxRGMexSDc6hNvJInGW3LL46n+8b50RykrvwSUIIQH2LQ5A==} @@ -14178,6 +14198,10 @@ packages: acorn: 8.8.2 dev: true + /strnum@1.0.5: + resolution: {integrity: sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==} + dev: false + /style-to-object@0.4.1: resolution: {integrity: sha512-HFpbb5gr2ypci7Qw+IOhnP2zOU7e77b+rzM+wTzXzfi1PrtBCX0E7Pk4wL4iTLnhzZ+JgEGAhX81ebTg/aYjQw==} dependencies: @@ -14216,7 +14240,6 @@ packages: engines: {node: '>=10'} dependencies: has-flag: 4.0.0 - dev: true /supports-hyperlinks@2.3.0: resolution: {integrity: sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==} @@ -14494,7 +14517,6 @@ packages: /tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} - dev: true /traverse@0.6.7: resolution: {integrity: sha512-/y956gpUo9ZNCb99YjxG7OaslxZWHfCHAUUfshwqOXmxUIvqLjVO581BT+gM59+QV9tFe6/CGG53tsA1Y7RSdg==} @@ -14731,13 +14753,8 @@ packages: engines: {node: '>=4.2.0'} hasBin: true - /ufo@1.1.1: - resolution: {integrity: sha512-MvlCc4GHrmZdAllBc0iUDowff36Q9Ndw/UzqmEKyrfSzokTd9ZCy1i+IIk5hrYKkjoYVQyNbrw7/F8XJ2rEwTg==} - dev: true - /ufo@1.1.2: resolution: {integrity: sha512-TrY6DsjTQQgyS3E3dBaOXf0TpPD8u9FVrVYmKVegJuFw51n/YB9XPt+U6ydzFG5ZIN7+DIjPbNmXoBj9esYhgQ==} - dev: true /uglify-js@3.17.4: resolution: {integrity: sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==} @@ -15318,7 +15335,6 @@ packages: /webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} - dev: true /webidl-conversions@7.0.0: resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} @@ -15332,6 +15348,10 @@ packages: iconv-lite: 0.6.3 dev: true + /whatwg-fetch@3.6.17: + resolution: {integrity: sha512-c4ghIvG6th0eudYwKZY5keb81wtFz9/WeAHAoy8+r18kcWlitUIrmGFQ2rWEl4UCKUilD3zCLHOIPheHx5ypRQ==} + dev: false + /whatwg-mimetype@3.0.0: resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==} engines: {node: '>=12'} @@ -15342,7 +15362,6 @@ packages: dependencies: tr46: 0.0.3 webidl-conversions: 3.0.1 - dev: true /which-boxed-primitive@1.0.2: resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} @@ -15597,7 +15616,6 @@ packages: /yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} - dev: true /yocto-queue@1.0.0: resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==}