From 80af3e128840f80302fd3eb8f4102069fb034a78 Mon Sep 17 00:00:00 2001 From: Claudio W Date: Thu, 30 Nov 2023 18:42:17 +0000 Subject: [PATCH] fix: we should invalidate data over time (#6153) --- app/[locale]/[[...path]]/page.tsx | 1 + .../next-data/blog-data/[category]/route.ts | 6 +++ app/[locale]/next-data/release-data/route.ts | 8 +++- next-data/blogData.ts | 9 ++++- next-data/releaseData.ts | 9 ++++- next.constants.mjs | 28 +++++++++++--- next.dynamic.mjs | 38 +++++++++++++++---- 7 files changed, 81 insertions(+), 18 deletions(-) diff --git a/app/[locale]/[[...path]]/page.tsx b/app/[locale]/[[...path]]/page.tsx index 34f9e544f7402..fea31f53c0565 100644 --- a/app/[locale]/[[...path]]/page.tsx +++ b/app/[locale]/[[...path]]/page.tsx @@ -120,6 +120,7 @@ const getPage: FC = async ({ params }) => { export const dynamicParams = true; // Enforces that this route is used as static rendering +// Except whenever on the Development mode as we want instant-refresh when making changes // @see https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#dynamic export const dynamic = 'error'; diff --git a/app/[locale]/next-data/blog-data/[category]/route.ts b/app/[locale]/next-data/blog-data/[category]/route.ts index 1c99211c46696..b0cd326bec155 100644 --- a/app/[locale]/next-data/blog-data/[category]/route.ts +++ b/app/[locale]/next-data/blog-data/[category]/route.ts @@ -1,4 +1,5 @@ import provideBlogData from '@/next-data/providers/blogData'; +import { VERCEL_EVALIDATE_TIME } from '@/next.constants.mjs'; import { defaultLocale } from '@/next.locales.mjs'; // We only support fetching these pages from the /en/ locale code @@ -40,3 +41,8 @@ export const dynamicParams = false; // Enforces that this route is cached and static as much as possible // @see https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#dynamic export const dynamic = 'error'; + +// Ensures that this endpoint is invalidated and re-executed every X minutes +// so that when new deployments happen, the data is refreshed +// @see https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#revalidate +export const revalidate = VERCEL_EVALIDATE_TIME; diff --git a/app/[locale]/next-data/release-data/route.ts b/app/[locale]/next-data/release-data/route.ts index f8026bcc0adbf..0bd37dd5ab8ba 100644 --- a/app/[locale]/next-data/release-data/route.ts +++ b/app/[locale]/next-data/release-data/route.ts @@ -1,4 +1,5 @@ import provideReleaseData from '@/next-data/providers/releaseData'; +import { VERCEL_EVALIDATE_TIME } from '@/next.constants.mjs'; import { defaultLocale } from '@/next.locales.mjs'; // We only support fetching these pages from the /en/ locale code @@ -18,10 +19,15 @@ export const GET = async () => { // This is used for ISR static validation and generation export const generateStaticParams = async () => [{ locale }]; -// Forces that only the paths from `generateStaticParams` are allowed, giving 404 on the contrary +// Enforces that only the paths from `generateStaticParams` are allowed, giving 404 on the contrary // @see https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#dynamicparams export const dynamicParams = false; // Enforces that this route is used as static rendering // @see https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#dynamic export const dynamic = 'error'; + +// Ensures that this endpoint is invalidated and re-executed every X minutes +// so that when new deployments happen, the data is refreshed +// @see https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#revalidate +export const revalidate = VERCEL_EVALIDATE_TIME; diff --git a/next-data/blogData.ts b/next-data/blogData.ts index 0c2db1df774b0..067da3b1248e0 100644 --- a/next-data/blogData.ts +++ b/next-data/blogData.ts @@ -1,4 +1,9 @@ -import { ENABLE_STATIC_EXPORT, NEXT_DATA_URL } from '@/next.constants.mjs'; +import { + ENABLE_STATIC_EXPORT, + IS_DEVELOPMENT, + NEXT_DATA_URL, + VERCEL_ENV, +} from '@/next.constants.mjs'; import type { BlogDataRSC } from '@/types'; const getBlogData = (category: string): Promise => { @@ -6,7 +11,7 @@ const getBlogData = (category: string): Promise => { // hence the self-ingestion APIs will not be available. In this case we want to load // the data directly within the current thread, which will anyways be loaded only once // We use lazy-imports to prevent `provideBlogData` from executing on import - if (ENABLE_STATIC_EXPORT) { + if (ENABLE_STATIC_EXPORT || (!IS_DEVELOPMENT && !VERCEL_ENV)) { return import('@/next-data/providers/blogData').then( ({ default: provideBlogData }) => provideBlogData(category) ); diff --git a/next-data/releaseData.ts b/next-data/releaseData.ts index b2c0050513169..71a3cb3516ee2 100644 --- a/next-data/releaseData.ts +++ b/next-data/releaseData.ts @@ -1,4 +1,9 @@ -import { ENABLE_STATIC_EXPORT, NEXT_DATA_URL } from '@/next.constants.mjs'; +import { + ENABLE_STATIC_EXPORT, + IS_DEVELOPMENT, + NEXT_DATA_URL, + VERCEL_ENV, +} from '@/next.constants.mjs'; import type { NodeRelease } from '@/types'; const getReleaseData = (): Promise => { @@ -6,7 +11,7 @@ const getReleaseData = (): Promise => { // hence the self-ingestion APIs will not be available. In this case we want to load // the data directly within the current thread, which will anyways be loaded only once // We use lazy-imports to prevent `provideBlogData` from executing on import - if (ENABLE_STATIC_EXPORT) { + if (ENABLE_STATIC_EXPORT || (!IS_DEVELOPMENT && !VERCEL_ENV)) { return import('@/next-data/providers/releaseData').then( ({ default: provideReleaseData }) => provideReleaseData() ); diff --git a/next.constants.mjs b/next.constants.mjs index 985b9f571410d..934aee0c1b963 100644 --- a/next.constants.mjs +++ b/next.constants.mjs @@ -21,6 +21,16 @@ export const IS_DEVELOPMENT = process.env.NODE_ENV === 'development'; */ export const VERCEL_ENV = process.env.NEXT_PUBLIC_VERCEL_ENV || undefined; +/** + * This is used for defining a default time of when `next-data` and other dynamically generated + * but static-enabled pages should be regenerated. + * + * Note that this is a custom Environment Variable that can be defined by us when necessary + */ +export const VERCEL_EVALIDATE_TIME = Number( + process.env.NEXT_PUBLIC_VERCEL_REVALIDATE_TIME || 300 +); + /** * This is used for telling Next.js to to a Static Export Build of the Website * @@ -50,7 +60,7 @@ export const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL /** * This is used for any place that requires the Node.js distribution URL (which by default is nodejs.org/dist) * - * Note that this is a manual Environment Variable defined by us if necessary. + * Note that this is a custom Environment Variable that can be defined by us when necessary */ export const DIST_URL = process.env.NEXT_PUBLIC_DIST_URL || 'https://nodejs.org/dist/'; @@ -58,7 +68,7 @@ export const DIST_URL = /** * This is used for any place that requires the Node.js API Docs URL (which by default is nodejs.org/docs) * - * Note that this is a manual Environment Variable defined by us if necessary. + * Note that this is a custom Environment Variable that can be defined by us when necessary */ export const DOCS_URL = process.env.NEXT_PUBLIC_DOCS_URL || 'https://nodejs.org/docs/'; @@ -69,7 +79,7 @@ export const DOCS_URL = * This is useful when running the deployment on a subdirectory * of a domain, such as when hosted on GitHub Pages. * - * Note that this is a manual Environment Variable defined by us if necessary. + * Note that this is a custom Environment Variable that can be defined by us when necessary */ export const BASE_PATH = process.env.NEXT_PUBLIC_BASE_PATH || ''; @@ -112,6 +122,8 @@ export const EXTERNAL_LINKS_SITEMAP = [ /** * The `localStorage` key to store the theme choice of `next-themes` + * + * This is what allows us to store user preference for theming */ export const THEME_STORAGE_KEY = 'theme'; @@ -123,19 +135,25 @@ export const SENTRY_DSN = /** * This states if Sentry should be enabled and bundled within our App + * + * We enable sentry by default if we're om development mode or deployed + * on Vercel (either production or preview branches) */ export const SENTRY_ENABLE = IS_DEVELOPMENT || !!VERCEL_ENV; /** * This configures the sampling rate for Sentry * - * @note we always want to capture 100% on preview branches and development mode + * We always want to capture 100% on Vercel Preview Branches + * and not when it's on Production Mode (nodejs.org) */ export const SENTRY_CAPTURE_RATE = - SENTRY_ENABLE && BASE_URL !== 'https://nodejs.org' ? 1.0 : 0.01; + SENTRY_ENABLE && VERCEL_ENV && BASE_URL !== 'https://nodejs.org' ? 1.0 : 0.01; /** * Provides the Route for Sentry's Server-Side Tunnel + * + * This is a `@sentry/nextjs` specific feature */ export const SENTRY_TUNNEL = (components = '') => SENTRY_ENABLE ? `/monitoring${components}` : undefined; diff --git a/next.dynamic.mjs b/next.dynamic.mjs index 16135d6899787..cee5be95e82cf 100644 --- a/next.dynamic.mjs +++ b/next.dynamic.mjs @@ -8,7 +8,12 @@ import matter from 'gray-matter'; import { cache } from 'react'; import { VFile } from 'vfile'; -import { MD_EXTENSION_REGEX, BASE_URL, BASE_PATH } from './next.constants.mjs'; +import { + MD_EXTENSION_REGEX, + BASE_URL, + BASE_PATH, + IS_DEVELOPMENT, +} from './next.constants.mjs'; import { DYNAMIC_ROUTES_IGNORES, DYNAMIC_ROUTES_REWRITES, @@ -47,8 +52,25 @@ const mapPathToRoute = (locale = defaultLocale.code, path = '') => ({ path: path.split(sep), }); +// Provides an in-memory Map that lasts the whole build process +// and disabled when on development mode (stubbed) +const createCachedMarkdownCache = () => { + if (IS_DEVELOPMENT) { + return { + has: () => false, + set: () => {}, + get: () => null, + }; + } + + return new Map(); +}; + const getDynamicRouter = async () => { - const cachedMarkdownFiles = new Map(); + // Creates a Cache System that is disabled during development mode + const cachedMarkdownFiles = createCachedMarkdownCache(); + + // Keeps the map of pathnames to filenames const pathnameToFilename = new Map(); const websitePages = await getMarkdownFiles( @@ -131,7 +153,7 @@ const getDynamicRouter = async () => { // We then attempt to retrieve the source version of the file as there is no localised version // of the file and we set it on the cache to prevent future checks of the same locale for this file - const { source: fileContent } = getMarkdownFile( + const { source: fileContent } = _getMarkdownFile( defaultLocale.code, pathname ); @@ -217,13 +239,13 @@ const getDynamicRouter = async () => { }); return { - getRoutesByLanguage, - getMarkdownFile, - getMDXContent, - getPathname, + mapPathToRoute, shouldIgnoreRoute, + getPathname, getRouteRewrite, - mapPathToRoute, + getRoutesByLanguage, + getMDXContent, + getMarkdownFile, getPageMetadata, }; };