Skip to content

chore: upgrade to next 15 #7155

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

Merged
merged 46 commits into from
Nov 16, 2024
Merged
Show file tree
Hide file tree
Changes from 42 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
7c7e5c4
chore: upgrade to next 15
ovflowd Oct 30, 2024
790c2a2
chore: fixed vars, next-intl, etc
ovflowd Oct 31, 2024
6f1da68
chore: more build fixes
ovflowd Oct 31, 2024
029d045
chore: fix dependencies
ovflowd Oct 31, 2024
0a4c6f0
chore: some other issues on build
ovflowd Oct 31, 2024
e45a137
chore: fix unit tests
ovflowd Oct 31, 2024
a60057a
fix: Return `locale` in `i18n.tsx`, also move default there (#7157)
amannn Nov 4, 2024
12cabbc
chore: more refactor and bug fixing
ovflowd Nov 4, 2024
0c03028
chore: hydration warning
ovflowd Nov 4, 2024
6b53127
chore: revert blog post
ovflowd Nov 4, 2024
52d068f
fix: Read locale from params in layout (#7178)
amannn Nov 4, 2024
958af21
wip nextjs@15 with storybook-webpack (#7203)
bmuenzenmeyer Nov 8, 2024
fa7dd6f
chore: minor changes to fix url() on css
ovflowd Nov 8, 2024
0d69fa2
chore: thanks turbo repo!
ovflowd Nov 8, 2024
bb14619
chore: fix orama
ovflowd Nov 8, 2024
1882fa4
chore: preview head
ovflowd Nov 8, 2024
3c7ce24
chore: fix nodevu version
ovflowd Nov 8, 2024
1688da5
chore: minor cleanups
ovflowd Nov 9, 2024
0f85559
fix: minor feed fixes
ovflowd Nov 9, 2024
3e1aff9
chore: fixed tests
ovflowd Nov 9, 2024
9dc4b6c
chore: some package updates and minor code fixes
ovflowd Nov 9, 2024
0f31695
chore: minor storybook fixes
ovflowd Nov 9, 2024
f973f57
Merge branch 'main' into chore/nextjs-15
ovflowd Nov 9, 2024
a757220
test: log to vercel
ovflowd Nov 9, 2024
c0534b3
chore: attempt to make it work with default
ovflowd Nov 9, 2024
1a68031
chore: attempt to fix home
ovflowd Nov 9, 2024
d49e34a
chore: explicit JSON.parse call
ovflowd Nov 9, 2024
a55992b
Merge branch 'main' into chore/nextjs-15
ovflowd Nov 14, 2024
8a63ebe
chore: config optimization
ovflowd Nov 15, 2024
4392ff8
chore: bump nextjs
ovflowd Nov 15, 2024
e4bd6e4
chore: use otel/vercel; cache fetc; optimize build
ovflowd Nov 15, 2024
0073ffd
chore: optimize packages
ovflowd Nov 15, 2024
53bf63f
feat: partial custom mdx compiler
ovflowd Nov 15, 2024
1b2f4cc
chore: attempt to fix next.15 build
ovflowd Nov 15, 2024
7464e04
fix: fix navbar
ovflowd Nov 15, 2024
2c82b97
chore: remove duplicated paths
ovflowd Nov 15, 2024
be8be47
fix: ignore empty pathname paths
ovflowd Nov 15, 2024
12651a9
chore: remove changelog componnet, simply redirect to release page
ovflowd Nov 15, 2024
0e0eeb4
chore: added inline docs
ovflowd Nov 15, 2024
9b3290b
chore: og image default
ovflowd Nov 15, 2024
0dc4e99
remove reference to unused tool
bmuenzenmeyer Nov 15, 2024
5fbe3f9
move string to constant
bmuenzenmeyer Nov 15, 2024
53b0d0a
Update apps/site/app/[locale]/next-data/og/[category]/[title]/route.tsx
bmuenzenmeyer Nov 15, 2024
7aa6ea1
chore: remove existsSync
ovflowd Nov 15, 2024
58215c5
chore: simplify fragments
ovflowd Nov 15, 2024
1135f0b
infra: use sval to parse javascript instead of mdx's `evaluate` (#7233)
ovflowd Nov 16, 2024
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: 0 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,4 @@ cache
# TypeScript
tsconfig.tsbuildinfo

# Sentry Config File
.sentryclirc

dist/
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"
}
1 change: 0 additions & 1 deletion COLLABORATOR_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,6 @@ The Website also uses several other Open Source libraries (not limited to) liste
- We use [Rehype](https://github.com/rehypejs/rehype) and [Remark](https://github.com/remarkjs/remark) to extend MDX functionality
- We use [Storybook](https://storybook.js.org/) for Manual Testing and Visual Regression Tests of our React Components
- Storybook also provides a sandboxed environment, which is very useful whilst for crafting React Components
- We use [Sentry](https://sentry.io/about) for reporting Exceptions and monitoring the performance and reliability of the application

## Code Editing

Expand Down
51 changes: 31 additions & 20 deletions apps/site/.storybook/main.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,38 @@
import type { StorybookConfig } from '@storybook/nextjs';
import classNames from 'classnames';
import { join } from 'node:path';

const rootClasses = classNames(
// note: this is hard-coded sadly as next/font can only be loaded within next.js context
'__variable_open-sans-normal',
// note: this is hard-coded sadly as next/font can only be loaded within next.js context
'__variable_ibm-plex-mono-normal'
);
import type { StorybookConfig } from '@storybook/react-webpack5';

const mocksFolder = join(__dirname, '../components/__mocks__');

const config: StorybookConfig = {
stories: ['../components/**/*.stories.tsx'],
logLevel: 'error',
staticDirs: ['../public'],
typescript: { reactDocgen: false, check: false },
core: { disableTelemetry: true, disableWhatsNewNotifications: true },
framework: {
name: '@storybook/nextjs',
options: { builder: { useSWC: true } },
},
previewBody:
// This `<style>` is necessary to simulate what `next-themes` (ThemeProvider) does on real applications
// `next-theme` automatically injects the color-scheme based on the system preference or the current applied theme
// on Storybook we don't use `next-theme` as we want to simulate themes
'<style>:root { color-scheme: light; } html[data-theme="dark"] { color-scheme: dark; }</style>' +
// This adds the base styling for dark/light themes within Storybook. This is a Storybook-only style
`<body class="${rootClasses}"></body>`,
framework: '@storybook/react-webpack5',
swc: () => ({ jsc: { transform: { react: { runtime: 'automatic' } } } }),
addons: [
'@storybook/addon-webpack5-compiler-swc',
'@storybook/addon-controls',
'@storybook/addon-interactions',
'@storybook/addon-themes',
'@storybook/addon-viewport',
{
name: '@storybook/addon-styling-webpack',
options: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
{ loader: 'css-loader', options: { url: false } },
'postcss-loader',
],
},
],
},
},
],
webpack: async config => ({
...config,
Expand All @@ -39,7 +42,15 @@ const config: StorybookConfig = {
performance: { hints: false },
// `nodevu` is a Node.js-specific package that requires Node.js modules
// this is incompatible with Storybook. So we just mock the module
resolve: { ...config.resolve, alias: { '@nodevu/core': false } },
resolve: {
...config.resolve,
alias: {
'@nodevu/core': false,
'next-intl/navigation': join(mocksFolder, './next-intl.mjs'),
'@/client-context': join(mocksFolder, './client-context.mjs'),
'@': join(__dirname, '../'),
},
},
// We need to configure `node:` APIs as Externals to WebPack
// since essentially they're not supported on the browser
externals: {
Expand Down
20 changes: 20 additions & 0 deletions apps/site/.storybook/preview-head.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />

<link
rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono&family=Open+Sans:ital,wght@0,300..800;1,300..800"
/>

<style>
:root {
color-scheme: light;

--font-ibm-plex-mono: 'IBM Plex Mono';
--font-open-sans: 'Open Sans';
}

html[data-theme='dark'] {
color-scheme: dark;
}
</style>
2 changes: 0 additions & 2 deletions apps/site/.storybook/preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,10 @@ import { NextIntlClientProvider } from 'next-intl';
import { STORYBOOK_MODES, STORYBOOK_SIZES } from '@/.storybook/constants';
import { NotificationProvider } from '@/providers/notificationProvider';

import '../next.fonts';
import '../styles/index.css';

const preview: Preview = {
parameters: {
nextjs: { router: { basePath: '' }, appDirectory: true },
chromatic: { modes: STORYBOOK_MODES },
viewport: { defaultViewport: 'large', viewports: STORYBOOK_SIZES },
},
Expand Down
50 changes: 50 additions & 0 deletions apps/site/app/[locale]/[...path]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/**
* This file extends on the `page.tsx` file, which is the default file that is used to render
* the entry points for each locale and then also reused within the [...path] route to render the
* and contains all logic for rendering our dynamic and static routes within the Node.js Website.
*
* Note: that each `page.tsx` should have its own `generateStaticParams` to prevent clash of
* dynamic params, which will lead on static export errors and other sort of issues.
*/

import * as basePage from '@/app/[locale]/page';
import { ENABLE_STATIC_EXPORT } from '@/next.constants.mjs';
import { dynamicRouter } from '@/next.dynamic.mjs';
import { availableLocaleCodes } from '@/next.locales.mjs';

// This is the default Viewport Metadata
// @see https://nextjs.org/docs/app/api-reference/functions/generate-viewport#generateviewport-function
export const generateViewport = basePage.generateViewport;

// This generates each page's HTML Metadata
// @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
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() : [];
};

// 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 = '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 = 300;

export default basePage.default;
4 changes: 1 addition & 3 deletions apps/site/app/[locale]/error.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
'use client';

import { ArrowRightIcon } from '@heroicons/react/24/solid';
import { captureException } from '@sentry/nextjs';
import { useTranslations } from 'next-intl';
import type { FC } from 'react';

import Button from '@/components/Common/Button';
import GlowingBackdropLayout from '@/layouts/GlowingBackdrop';

const ErrorPage: FC<{ error: Error }> = ({ error }) => {
captureException(error);
const ErrorPage: FC<{ error: Error }> = () => {
const t = useTranslations();

return (
Expand Down
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
12 changes: 7 additions & 5 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 @@ -29,14 +29,16 @@ export const GET = async () => {

const gitHubApiResponse = await fetch(
getGitHubApiDocsUrl(versionWithPrefix),
authorizationHeaders
{ ...authorizationHeaders, cache: 'force-cache' }
);

return gitHubApiResponse.json().then((apiDocsFiles: Array<GitHubApiFile>) => {
// maps over each api file and get the download_url, fetch the content and deflates it
const mappedApiFiles = apiDocsFiles.map(
async ({ name, path: filename, download_url }) => {
const apiFileResponse = await fetch(download_url);
const apiFileResponse = await fetch(download_url, {
cache: 'force-cache',
});

// Retrieves the content as a raw text string
const source = await apiFileResponse.text();
Expand Down Expand Up @@ -72,9 +74,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;
Loading
Loading