Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: upgrade to next 15 #7155

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"javascript.updateImportsOnFileMove.enabled": "always",
"typescript.updateImportsOnFileMove.enabled": "always"
"typescript.updateImportsOnFileMove.enabled": "always",
"typescript.tsdk": "node_modules/typescript/lib"
}
10 changes: 5 additions & 5 deletions CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,18 @@
.husky @nodejs/web-infra

# Framework
app/site/next.config.mjs @nodejs/web-infra
apps/site/next.config.mjs @nodejs/web-infra
app/site/next.dynamic.mjs @nodejs/web-infra

# Node.js Release Blog Posts
app/site/pages/en/blog/release @nodejs/releasers
app/site/pages/en/blog/announcements @nodejs/releasers
apps/site/pages/en/blog/release @nodejs/releasers
apps/site/pages/en/blog/announcements @nodejs/releasers

# Package Ecosystem
package.json @nodejs/nodejs-website
turbo.json @nodejs/nodejs-website @nodejs/web-infra

# Web Infrastructure
crowdin.yml @nodejs/web-infra
app/site/redirects.json @nodejs/web-infra
app/site/site.json @nodejs/web-infra
apps/site/redirects.json @nodejs/web-infra
apps/site/site.json @nodejs/web-infra
25 changes: 10 additions & 15 deletions apps/site/app/[locale]/[[...path]]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { setContext, setTags } from '@sentry/nextjs';
import { notFound, redirect } from 'next/navigation';
import { unstable_setRequestLocale } from 'next-intl/server';
import { setRequestLocale } from 'next-intl/server';
import type { FC } from 'react';

import { setClientContext } from '@/client-context';
import { MDXRenderer } from '@/components/mdxRenderer';
import WithLayout from '@/components/withLayout';
import { ENABLE_STATIC_EXPORT, VERCEL_REVALIDATE } from '@/next.constants.mjs';
import { ENABLE_STATIC_EXPORT } from '@/next.constants.mjs';
import { PAGE_VIEWPORT, DYNAMIC_ROUTES } from '@/next.dynamic.constants.mjs';
import { dynamicRouter } from '@/next.dynamic.mjs';
import {
Expand All @@ -17,15 +17,17 @@ import {
import { MatterProvider } from '@/providers/matterProvider';

type DynamicStaticPaths = { path: Array<string>; locale: string };
type DynamicParams = { params: DynamicStaticPaths };
type DynamicParams = { params: Promise<DynamicStaticPaths> };

// This is the default Viewport Metadata
// @see https://nextjs.org/docs/app/api-reference/functions/generate-viewport#generateviewport-function
export const generateViewport = async () => ({ ...PAGE_VIEWPORT });

// This generates each page's HTML Metadata
// @see https://nextjs.org/docs/app/api-reference/functions/generate-metadata
export const generateMetadata = async ({ params }: DynamicParams) => {
export const generateMetadata = async (props: DynamicParams) => {
const params = await props.params;

const { path = [], locale = defaultLocale.code } = params;

const pathname = dynamicRouter.getPathname(path);
Expand Down Expand Up @@ -64,13 +66,11 @@ export const generateStaticParams = async () => {
// then it proceeds to retrieve the Markdown file and parse the MDX Content into a React Component
// finally it returns (if the locale and route are valid) the React Component with the relevant context
// and attached context providers for rendering the current page
const getPage: FC<DynamicParams> = async ({ params }) => {
const getPage: FC<DynamicParams> = async props => {
const params = await props.params;
const { path = [], locale = defaultLocale.code } = params;

if (!availableLocaleCodes.includes(locale)) {
// Forces the current locale to be the Default Locale
unstable_setRequestLocale(defaultLocale.code);

if (!allLocaleCodes.includes(locale)) {
// when the locale is not listed in the locales, return NotFound
return notFound();
Expand All @@ -82,7 +82,7 @@ const getPage: FC<DynamicParams> = async ({ params }) => {
}

// Configures the current Locale to be the given Locale of the Request
unstable_setRequestLocale(locale);
setRequestLocale(locale);

// Gets the current full pathname for a given path
const pathname = dynamicRouter.getPathname(path);
Expand Down Expand Up @@ -165,11 +165,6 @@ const getPage: FC<DynamicParams> = async ({ params }) => {
return notFound();
};

// In this case we want to catch-all possible pages even to this page. This ensures that we use our 404
// and that all pages including existing ones are handled here and provide `next-intl` locale also
// @see https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#dynamicparams
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
Expand All @@ -178,6 +173,6 @@ export const dynamic = 'force-static';
// 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_REVALIDATE;
export const revalidate = 300;

export default getPage;
15 changes: 7 additions & 8 deletions apps/site/app/[locale]/feed/[feed]/route.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
import { NextResponse } from 'next/server';

import provideWebsiteFeeds from '@/next-data/providers/websiteFeeds';
import { VERCEL_REVALIDATE } from '@/next.constants.mjs';
import { siteConfig } from '@/next.json.mjs';
import { defaultLocale } from '@/next.locales.mjs';

// We only support fetching these pages from the /en/ locale code
const locale = defaultLocale.code;

type StaticParams = { params: { feed: string; locale: string } };
type StaticParams = { params: Promise<{ feed: string; locale: string }> };

// This is the Route Handler for the `GET` method which handles the request
// for the Node.js Website Blog Feeds (RSS)
// @see https://nextjs.org/docs/app/building-your-application/routing/router-handlers
export const GET = async (_: Request, { params }: StaticParams) => {
export const GET = async (_: Request, props: StaticParams) => {
const params = await props.params;

// Generate the Feed for the given feed type (blog, releases, etc)
const websiteFeed = provideWebsiteFeeds(params.feed);

Expand All @@ -29,11 +30,9 @@ export const GET = async (_: Request, { params }: StaticParams) => {
export const generateStaticParams = async () =>
siteConfig.rssFeeds.map(feed => ({ feed: feed.file, locale }));

// In this case we want to catch-all possible requests. This is so that if a non defined feed is
// requested we can manually return a 404 response for it instead of having Next.js handle it
// and return our top level custom 404 html page instead
// 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 = true;
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
Expand All @@ -42,4 +41,4 @@ export const dynamic = 'force-static';
// 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_REVALIDATE;
export const revalidate = 300;
14 changes: 10 additions & 4 deletions apps/site/app/[locale]/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { Analytics } from '@vercel/analytics/react';
import { SpeedInsights } from '@vercel/speed-insights/next';
import classNames from 'classnames';
import { getLocale } from 'next-intl/server';
import type { FC, PropsWithChildren } from 'react';

import BaseLayout from '@/layouts/Base';
Expand All @@ -15,13 +14,20 @@ import '@/styles/index.css';

const fontClasses = classNames(IBM_PLEX_MONO.variable, OPEN_SANS.variable);

const RootLayout: FC<PropsWithChildren> = async ({ children }) => {
const locale = await getLocale();
type RotLayoutProps = PropsWithChildren<{ params: { locale: string } }>;

const RootLayout: FC<RotLayoutProps> = async ({ children, params }) => {
const { locale } = await params;

const { langDir, hrefLang } = availableLocalesMap[locale] || defaultLocale;

return (
<html className={fontClasses} dir={langDir} lang={hrefLang}>
<html
className={fontClasses}
dir={langDir}
lang={hrefLang}
suppressHydrationWarning
>
<body suppressHydrationWarning>
<LocaleProvider>
<ThemeProvider>
Expand Down
6 changes: 3 additions & 3 deletions apps/site/app/[locale]/next-data/api-data/route.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { deflateSync } from 'node:zlib';

import provideReleaseData from '@/next-data/providers/releaseData';
import { GITHUB_API_KEY, VERCEL_REVALIDATE } from '@/next.constants.mjs';
import { GITHUB_API_KEY } from '@/next.constants.mjs';
import { defaultLocale } from '@/next.locales.mjs';
import type { GitHubApiFile } from '@/types';
import { getGitHubApiDocsUrl } from '@/util/gitHubUtils';
Expand Down Expand Up @@ -72,9 +72,9 @@ 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';
export const dynamic = 'force-static';

// 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_REVALIDATE;
export const revalidate = 300;
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import {
provideBlogCategories,
provideBlogPosts,
providePaginatedBlogPosts,
} from '@/next-data/providers/blogData';
import { VERCEL_REVALIDATE } from '@/next.constants.mjs';
import { defaultLocale } from '@/next.locales.mjs';

type StaticParams = {
params: { locale: string; category: string; page: string };
params: Promise<{ locale: string; category: string; page: string }>;
};

// This is the Route Handler for the `GET` method which handles the request
// for providing Blog Posts for Blog Categories and Pagination Metadata
// @see https://nextjs.org/docs/app/building-your-application/routing/router-handlers
export const GET = async (_: Request, { params }: StaticParams) => {
export const GET = async (_: Request, props: StaticParams) => {
const params = await props.params;

const requestedPage = Number(params.page);

const data =
Expand All @@ -27,41 +27,18 @@ export const GET = async (_: Request, { params }: StaticParams) => {
};

// This function generates the static paths that come from the dynamic segments
// `[locale]/next-data/blog-data/[category]` and returns an array of all available static paths
// This is used for ISR static validation and generation
export const generateStaticParams = async () => {
// This metadata is the original list of all available categories and all available years
// within the Node.js Website Blog Posts (2011, 2012...)
const categories = provideBlogCategories();

const mappedCategories = categories.map(category => {
// gets the current pagination meta for a given category
const { pagination } = provideBlogPosts(category);

// creates a sequential array containing each page number
const pages = [...Array(pagination.pages).keys()].map((_, key) => key + 1);

// maps the data into valid Next.js Route Engine routes with all required params
// notice that we add an extra 0 in the beginning in case we want a non-paginated route
return [0, ...pages].map(page => ({
locale: defaultLocale.code,
page: String(page),
category,
}));
});

return mappedCategories.flat();
};

// 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;
// `[locale]/next-data/blog-data/[category]/[page]` this will return a default value as we don't want to
// statically generate this route as it is compute-expensive.
// Hence we generate a "fake" OG image during build just to satisfy Next.js requirements.
export const generateStaticParams = async () => [
{ locale: defaultLocale.code, category: 'all', page: '0' },
];

// 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';
export const dynamic = 'force-static';

// 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_REVALIDATE;
export const revalidate = 300;
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import { provideChangelogData } from '@/next-data/providers/changelogData';
import provideReleaseData from '@/next-data/providers/releaseData';
import { VERCEL_REVALIDATE } from '@/next.constants.mjs';
import { defaultLocale } from '@/next.locales.mjs';

type StaticParams = {
params: { version: string };
params: Promise<{ version: string }>;
};

// This is the Route Handler for the `GET` method which handles the request
// for generating static data related to the Node.js Changelog Data
// @see https://nextjs.org/docs/app/building-your-application/routing/router-handlers
export const GET = async (_: Request, { params }: StaticParams) => {
export const GET = async (_: Request, props: StaticParams) => {
const params = await props.params;

const changelogData = await provideChangelogData(params.version);

return Response.json(changelogData);
Expand All @@ -34,11 +35,11 @@ export const generateStaticParams = async () => {
// @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
// 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';
export const dynamic = 'force-static';

// 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_REVALIDATE;
export const revalidate = 300;
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { ImageResponse } from 'next/og';

import HexagonGrid from '@/components/Icons/HexagonGrid';
import JsIconWhite from '@/components/Icons/Logos/JsIconWhite';
import { DEFAULT_CATEGORY_OG_TYPE } from '@/next.constants.mjs';
import { defaultLocale } from '@/next.locales.mjs';
import tailwindConfig from '@/tailwind.config';
import { hexToRGBA } from '@/util/hexToRGBA';

const CATEGORY_TO_THEME_COLOUR_MAP = {
announcement: tailwindConfig.theme.colors.green['700'],
release: tailwindConfig.theme.colors.info['600'],
vulnerability: tailwindConfig.theme.colors.warning['600'],
};

type StaticParams = {
params: Promise<{
locale: string;
category: keyof typeof CATEGORY_TO_THEME_COLOUR_MAP;
title: string;
}>;
};

// This is the Route Handler for the `GET` method which handles the request
// for generating OpenGrapgh images for Blog Posts and Pages
// @see https://nextjs.org/docs/app/building-your-application/routing/router-handlers
export const GET = async (_: Request, props: StaticParams) => {
const params = await props.params;

const categoryColour =
params.category in CATEGORY_TO_THEME_COLOUR_MAP
? CATEGORY_TO_THEME_COLOUR_MAP[params.category]
: CATEGORY_TO_THEME_COLOUR_MAP[DEFAULT_CATEGORY_OG_TYPE];

const gridBackground = `radial-gradient(circle, ${hexToRGBA(categoryColour)}, transparent)`;

return new ImageResponse(
(
<div tw="relative flex items-center justify-center bg-black w-[1200px] h-[600px]">
<HexagonGrid style={{ background: gridBackground }} />

<div tw="absolute mx-auto flex max-w-xl flex-col text-center text-3xl font-semibold text-white">
<JsIconWhite width={71} height={80} tw="mx-auto" />

<h2>{params.title.slice(0, 100)}</h2>
</div>
</div>
),
{ width: 1200, height: 600 }
);
};

// This function generates the static paths that come from the dynamic segments
// `[locale]/next-data/og/[category]/[title]` this will return a default value as we don't want to
// statically generate this route as it is compute-expensive.
// Hence we generate a "fake" OG image during build just to satisfy Next.js requirements.
export const generateStaticParams = async () => [
{ locale: defaultLocale.code, category: 'default', title: 'default' },
];

// 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 = 'force-static';
Loading
Loading