diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4c1d071d7e3b6..5ac1b393d69a7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -114,11 +114,29 @@ jobs: # Used for API requests that require GitHub API scopes NEXT_GITHUB_API_KEY: ${{ secrets.GITHUB_TOKEN }} - - name: Build Next.js (Static) - # We only run full static builds within Pull Requests. As they're not needed on `merge_group` or `push` events + - name: Build Next.js (Static All Locales) + # We only run full static builds within Pull Requests. This step is also used to export + # static output in all languages, and it only works on `push` events. + if: github.event_name == 'push' + # We want to enforce that the actual `turbo@latest` package is used instead of a possible hijack from the user + # the `${{ steps.turborepo_arguments.outputs.turbo_args }}` is a string substitution coming from a previous step + run: npx --package=turbo@latest -- turbo deploy ${{ steps.turborepo_arguments.outputs.turbo_args }} + env: + # We want to ensure we have enough RAM allocated to the Node.js process + # this should be a last resort in case by any chances the build memory gets too high + # but in general this should never happen + NODE_OPTIONS: '--max_old_space_size=4096' + # Used for API requests that require GitHub API scopes + NEXT_GITHUB_API_KEY: ${{ secrets.GITHUB_TOKEN }} + # We want to ensure that static exports for all locales are triggered only on `push` events to save resources + # and time. + NEXT_PUBLIC_STATIC_EXPORT_LOCALE: true + + - name: Build Next.js (Static Default Locale) + # We want to generate static output in the default language within Pull Requests + # in order to reduce source wastages and build times. # Note that we skip full static builds on Crowdin-based Pull Requests as these PRs should only contain translation changes if: | - (github.event_name == 'push') || (github.event_name == 'pull_request_target' && github.event.pull_request.head.ref != 'chore/crowdin') # We want to enforce that the actual `turbo@latest` package is used instead of a possible hijack from the user @@ -131,6 +149,8 @@ jobs: NODE_OPTIONS: '--max_old_space_size=4096' # Used for API requests that require GitHub API scopes NEXT_GITHUB_API_KEY: ${{ secrets.GITHUB_TOKEN }} + # We want to ensure that static exports for all locales do not occur on `pull_request_target` events + NEXT_PUBLIC_STATIC_EXPORT_LOCALE: false - name: Sync Orama Cloud # We only want to sync the Orama Cloud production indexes on `push` events. diff --git a/apps/site/app/[locale]/[...path]/page.tsx b/apps/site/app/[locale]/[...path]/page.tsx index 89bb335d8f427..13e6342fd58be 100644 --- a/apps/site/app/[locale]/[...path]/page.tsx +++ b/apps/site/app/[locale]/[...path]/page.tsx @@ -8,9 +8,12 @@ */ import * as basePage from '@/app/[locale]/page'; -import { ENABLE_STATIC_EXPORT } from '@/next.constants.mjs'; +import { + ENABLE_STATIC_EXPORT_LOCALE, + ENABLE_STATIC_EXPORT, +} from '@/next.constants.mjs'; import { dynamicRouter } from '@/next.dynamic.mjs'; -import { availableLocaleCodes } from '@/next.locales.mjs'; +import { availableLocaleCodes, defaultLocale } from '@/next.locales.mjs'; // This is the default Viewport Metadata // @see https://nextjs.org/docs/app/api-reference/functions/generate-viewport#generateviewport-function @@ -20,21 +23,35 @@ export const generateViewport = basePage.generateViewport; // @see https://nextjs.org/docs/app/api-reference/functions/generate-metadata export const generateMetadata = basePage.generateMetadata; -// This provides all the possible paths that can be generated statically -// + provides all the paths that we support on the Node.js Website +// Generates all possible static paths based on the locales and environment configuration +// - Returns an empty array if static export is disabled (`ENABLE_STATIC_EXPORT` is false) +// - If `ENABLE_STATIC_EXPORT_LOCALE` is true, generates paths for all available locales +// - Otherwise, generates paths only for the default locale +// @see https://nextjs.org/docs/app/api-reference/functions/generate-static-params export const generateStaticParams = async () => { - const allAvailableRoutes = await Promise.all( - // Gets all mapped routes to the Next.js Routing Engine by Locale - availableLocaleCodes.map(async (locale: string) => { - const routesForLanguage = await dynamicRouter.getRoutesByLanguage(locale); - - return routesForLanguage.map(pathname => - dynamicRouter.mapPathToRoute(locale, pathname) - ); - }) - ); - - return ENABLE_STATIC_EXPORT ? allAvailableRoutes.flat().sort() : []; + // Return an empty array if static export is disabled + if (!ENABLE_STATIC_EXPORT) { + return []; + } + + // Helper function to fetch and map routes for a specific locale + const getRoutesForLocale = async (locale: string) => { + const routes = await dynamicRouter.getRoutesByLanguage(locale); + + return routes.map(pathname => + dynamicRouter.mapPathToRoute(locale, pathname) + ); + }; + + // Determine which locales to include in the static export + const locales = ENABLE_STATIC_EXPORT_LOCALE + ? availableLocaleCodes + : [defaultLocale.code]; + + // Generates all possible routes for all available locales + const routes = await Promise.all(locales.map(getRoutesForLocale)); + + return routes.flat().sort(); }; // Enforces that this route is used as static rendering diff --git a/apps/site/app/[locale]/page.tsx b/apps/site/app/[locale]/page.tsx index 75d023daf9cf7..123644759baf6 100644 --- a/apps/site/app/[locale]/page.tsx +++ b/apps/site/app/[locale]/page.tsx @@ -13,7 +13,10 @@ import type { FC } from 'react'; import { setClientContext } from '@/client-context'; import WithLayout from '@/components/withLayout'; -import { ENABLE_STATIC_EXPORT } from '@/next.constants.mjs'; +import { + ENABLE_STATIC_EXPORT_LOCALE, + ENABLE_STATIC_EXPORT, +} from '@/next.constants.mjs'; import { PAGE_VIEWPORT, DYNAMIC_ROUTES } from '@/next.dynamic.constants.mjs'; import { dynamicRouter } from '@/next.dynamic.mjs'; import { allLocaleCodes, availableLocaleCodes } from '@/next.locales.mjs'; @@ -37,15 +40,28 @@ export const generateMetadata = async (props: DynamicParams) => { return dynamicRouter.getPageMetadata(locale, pathname); }; -// This provides all the possible paths that can be generated statically -// + provides all the paths that we support on the Node.js Website +// Generates all possible static paths based on the locales and environment configuration +// - Returns an empty array if static export is disabled (`ENABLE_STATIC_EXPORT` is false) +// - If `ENABLE_STATIC_EXPORT_LOCALE` is true, generates paths for all available locales +// - Otherwise, generates paths only for the default locale +// @see https://nextjs.org/docs/app/api-reference/functions/generate-static-params export const generateStaticParams = async () => { - const allAvailableRoutes = await Promise.all( + // Return an empty array if static export is disabled + if (!ENABLE_STATIC_EXPORT) { + return []; + } + + // Determine which locales to include in the static export + const locales = ENABLE_STATIC_EXPORT_LOCALE + ? availableLocaleCodes + : [defaultLocale.code]; + + const routes = await Promise.all( // Gets all mapped routes to the Next.js Routing Engine by Locale - availableLocaleCodes.map((locale: string) => ({ locale })) + locales.map((locale: string) => ({ locale })) ); - return ENABLE_STATIC_EXPORT ? allAvailableRoutes.flat().sort() : []; + return routes.flat().sort(); }; // This method parses the current pathname and does any sort of modifications needed on the route diff --git a/apps/site/next.constants.mjs b/apps/site/next.constants.mjs index 3df5797431b16..a371029829248 100644 --- a/apps/site/next.constants.mjs +++ b/apps/site/next.constants.mjs @@ -35,6 +35,17 @@ export const ENABLE_STATIC_EXPORT = process.env.NEXT_PUBLIC_STATIC_EXPORT === 'true' || process.env.NEXT_PUBLIC_STATIC_EXPORT === true; +/** + * This is used to ensure that pages are Static Export for all locales or only + * in the default (`en`) locale. + * + * Note that this is a manual Environment Variable defined by us during the + * build process in CI. + */ +export const ENABLE_STATIC_EXPORT_LOCALE = + process.env.NEXT_PUBLIC_STATIC_EXPORT_LOCALE === 'true' || + process.env.NEXT_PUBLIC_STATIC_EXPORT_LOCALE === true; + /** * This is used for any place that requires the full canonical URL path for the Node.js Website (and its deployment), such as for example, the Node.js RSS Feed. * diff --git a/apps/site/turbo.json b/apps/site/turbo.json index a940f23dc590a..ade8cb87f6e87 100644 --- a/apps/site/turbo.json +++ b/apps/site/turbo.json @@ -11,6 +11,7 @@ "VERCEL_URL", "VERCEL_REGION", "NEXT_PUBLIC_STATIC_EXPORT", + "NEXT_PUBLIC_STATIC_EXPORT_LOCALE", "NEXT_PUBLIC_BASE_URL", "NEXT_PUBLIC_DIST_URL", "NEXT_PUBLIC_DOCS_URL", @@ -37,6 +38,7 @@ "VERCEL_URL", "VERCEL_REGION", "NEXT_PUBLIC_STATIC_EXPORT", + "NEXT_PUBLIC_STATIC_EXPORT_LOCALE", "NEXT_PUBLIC_BASE_URL", "NEXT_PUBLIC_DIST_URL", "NEXT_PUBLIC_DOCS_URL", @@ -56,6 +58,7 @@ "VERCEL_URL", "VERCEL_REGION", "NEXT_PUBLIC_STATIC_EXPORT", + "NEXT_PUBLIC_STATIC_EXPORT_LOCALE", "NEXT_PUBLIC_BASE_URL", "NEXT_PUBLIC_DIST_URL", "NEXT_PUBLIC_DOCS_URL", @@ -81,6 +84,7 @@ "VERCEL_URL", "VERCEL_REGION", "NEXT_PUBLIC_STATIC_EXPORT", + "NEXT_PUBLIC_STATIC_EXPORT_LOCALE", "NEXT_PUBLIC_BASE_URL", "NEXT_PUBLIC_DIST_URL", "NEXT_PUBLIC_DOCS_URL", diff --git a/packages/i18n/turbo.json b/packages/i18n/turbo.json index 345419cd21dbd..642d71d8af575 100644 --- a/packages/i18n/turbo.json +++ b/packages/i18n/turbo.json @@ -14,6 +14,7 @@ "VERCEL_URL", "VERCEL_REGION", "NEXT_PUBLIC_STATIC_EXPORT", + "NEXT_PUBLIC_STATIC_EXPORT_LOCALE", "NEXT_PUBLIC_BASE_URL", "NEXT_PUBLIC_DIST_URL", "NEXT_PUBLIC_DOCS_URL",