Skip to content
Draft
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
4 changes: 3 additions & 1 deletion next-env.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
/// <reference types="next/navigation-types/compat/navigation" />
/// <reference path="./.next/types/routes.d.ts" />

// NOTE: This file should not be edited
// see https://nextjs.org/docs/pages/api-reference/config/typescript for more information.
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
6 changes: 6 additions & 0 deletions next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ const nextConfig = {

return config;
},
serverExternalPackages: [
'eslint',
'@babel/core',
'@babel/preset-react',
'@babel/plugin-transform-modules-commonjs',
],
};

module.exports = nextConfig;
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
"classnames": "^2.2.6",
"debounce": "^1.2.1",
"github-slugger": "^1.3.0",
"next": "15.1.11",
"next": "^15.4.0",
"next-remote-watch": "^1.0.0",
"parse-numeric-range": "^1.2.0",
"react": "^19.0.0",
Expand Down
9 changes: 9 additions & 0 deletions src/app/errors/[errorCode]/lib/fetch-error-codes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import {unstable_cache} from 'next/cache';

export const fetchReactErrorCodes = unstable_cache(async () => {
return (
await fetch(
'https://raw.githubusercontent.com/facebook/react/main/scripts/error-codes/codes.json'
)
).json() as Promise<{[key: string]: string}>;
}, ['react-error-codes']);
55 changes: 55 additions & 0 deletions src/app/errors/[errorCode]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import {unstable_cache} from 'next/cache';
import {notFound} from 'next/navigation';
import ErrorDecoderPage from '../components/error-decoder-page';
import type {Metadata, ResolvingMetadata} from 'next';

interface ErrorDecoderPageProps {
params: Promise<{errorCode: string}>;
}

export default async function ErrorPage({params}: ErrorDecoderPageProps) {
const {errorCode} = await params;
const errorCodes = await fetchReactErrorCodes();

if (errorCode && !(errorCode in errorCodes)) {
notFound();
}

return <ErrorDecoderPage errorCode={errorCode} errorCodes={errorCodes} />;
}

const fetchReactErrorCodes = unstable_cache(async () => {
return (
await fetch(
'https://raw.githubusercontent.com/facebook/react/main/scripts/error-codes/codes.json'
)
).json() as Promise<{[key: string]: string}>;
}, ['react-error-codes']);

export async function generateStaticParams(): Promise<
Array<{errorCode: string}>
> {
return Object.keys(await fetchReactErrorCodes()).map((code) => ({
errorCode: code,
}));
}

export async function generateMetadata(
{params}: ErrorDecoderPageProps,
parent: Promise<ResolvingMetadata | undefined>
): Promise<Metadata> {
const {errorCode} = await params;
const resolvedParent = await parent;
const parentOpenGraph = resolvedParent ? resolvedParent.openGraph : {};

return {
title: `Minified React error #${errorCode}`,
alternates: {
canonical: `./`,
},
openGraph: {
...parentOpenGraph,
title: `Minified React error #${errorCode}`,
},
};
}
64 changes: 64 additions & 0 deletions src/app/errors/components/error-decoder-page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import fsp from 'node:fs/promises';
import fs from 'node:fs';
import {Page} from '../../../components/Layout/Page';
import sidebarLearn from 'sidebarLearn.json';
import type {RouteItem} from '../../../components/Layout/getRouteMeta';
import {notFound} from 'next/navigation';
import {join} from 'node:path';
import compileMDX from '../../../utils/compileMDX';
import ErrorMDX from './error-mdx';

export default async function ErrorDecoderPage({
errorCode,
errorCodes,
}: {
errorCode: string | null;
errorCodes: {[key: string]: string};
}) {
if (errorCode && !errorCodes[errorCode]) {
notFound();
}

const rootDir = join(process.cwd(), 'src', 'content', 'errors');
let path = errorCode || 'index';
let mdx;
if (fs.existsSync(join(rootDir, path + '.md'))) {
mdx = await fsp.readFile(join(rootDir, path + '.md'), 'utf8');
} else {
mdx = await fsp.readFile(join(rootDir, 'generic.md'), 'utf8');
}

const {content} = await compileMDX(mdx, path, {code: errorCode, errorCodes});
const errorMessage = errorCode ? errorCodes[errorCode] : null;

return (
<Page
toc={[]}
meta={{
title: errorCode
? 'Minified React error #' + errorCode
: 'Minified Error Decoder',
}}
routeTree={sidebarLearn as RouteItem}
section="unknown"
appRouter>
<div>
<ErrorMDX
content={content}
errorCode={errorCode}
errorMessage={errorMessage}
/>
</div>
{/* <MaxWidth>
<P>
We highly recommend using the development build locally when debugging
your app since it tracks additional debug info and provides helpful
warnings about potential problems in your apps, but if you encounter
an exception while using the production build, this page will
reassemble the original error message.
</P>
<ErrorDecoder />
</MaxWidth> */}
</Page>
);
}
48 changes: 48 additions & 0 deletions src/app/errors/components/error-mdx.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
'use client';

import {Fragment} from 'react';
import {ErrorDecoderProvider} from '../../../components/_/ErrorDecoderContext';
import {MDXComponents} from '../../../components/MDX/MDXComponents';

export default function ErrorMDX({
content,
errorCode,
errorMessage,
}: {
content: string;
errorCode: string | null;
errorMessage: string | null;
}) {
return (
<ErrorDecoderProvider value={{errorMessage, errorCode}}>
{JSON.parse(content, reviveNodeOnClient)}
</ErrorDecoderProvider>
);
}

// Deserialize a client React tree from JSON.
function reviveNodeOnClient(parentPropertyName: unknown, val: any) {
if (Array.isArray(val) && val[0] == '$r') {
// Assume it's a React element.
let Type = val[1];
let key = val[2];
if (key == null) {
key = parentPropertyName; // Index within a parent.
}
let props = val[3];
if (Type === 'wrapper') {
Type = Fragment;
props = {children: props.children};
}
if (Type in MDXComponents) {
Type = MDXComponents[Type as keyof typeof MDXComponents];
}
if (!Type) {
console.error('Unknown type: ' + Type);
Type = Fragment;
}
return <Type key={key} {...props} />;
} else {
return val;
}
}
28 changes: 28 additions & 0 deletions src/app/errors/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type {Metadata, ResolvingMetadata} from 'next';
import {fetchReactErrorCodes} from './[errorCode]/lib/fetch-error-codes';
import ErrorDecoderPage from './components/error-decoder-page';

export default async function ErrorPage() {
const errorCodes = await fetchReactErrorCodes();

return <ErrorDecoderPage errorCode={null} errorCodes={errorCodes} />;
}

export async function generateMetadata(
_: unknown,
parent?: ResolvingMetadata
): Promise<Metadata> {
const resolvedParent = await parent;
const parentOpenGraph = resolvedParent ? resolvedParent.openGraph : {};

return {
title: 'Minified Error Decoder',
alternates: {
canonical: `./`,
},
openGraph: {
...parentOpenGraph,
title: 'Minified Error Decoder',
},
};
}
68 changes: 68 additions & 0 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import type {Metadata, Viewport} from 'next';
import {SharedRootBody, SharedRootHead} from '../components/_/root-layout';
import {siteConfig} from '../siteConfig';
import {preload} from 'react-dom';

import '@docsearch/css';
import '../styles/algolia.css';
import '../styles/index.css';
import '../styles/sandpack.css';

export default function RootLayout({children}: React.PropsWithChildren) {
[
'https://react.dev/fonts/Source-Code-Pro-Regular.woff2',
'https://react.dev/fonts/Source-Code-Pro-Bold.woff2',
'https://react.dev/fonts/Optimistic_Display_W_Md.woff2',
'https://react.dev/fonts/Optimistic_Display_W_SBd.woff2',
'https://react.dev/fonts/Optimistic_Display_W_Bd.woff2',
'https://react.dev/fonts/Optimistic_Text_W_Md.woff2',
'https://react.dev/fonts/Optimistic_Text_W_Bd.woff2',
'https://react.dev/fonts/Optimistic_Text_W_Rg.woff2',
'https://react.dev/fonts/Optimistic_Text_W_It.woff2',
].forEach((href) => {
preload(href, {as: 'font', type: 'font/woff2', crossOrigin: 'anonymous'});
});

return (
<html
lang={siteConfig.languageCode}
dir={siteConfig.isRTL ? 'rtl' : 'ltr'}
suppressHydrationWarning>
<head>
<SharedRootHead />
</head>
<SharedRootBody>{children}</SharedRootBody>
</html>
);
}

export const viewport: Viewport = {
width: 'device-width',
initialScale: 1,
};

export const metadata: Metadata = {
metadataBase: new URL('https://' + getDomain(siteConfig.languageCode)),
alternates: {
canonical: './',
},
openGraph: {
type: 'website',
url: './',
images: ['/images/og-default.png'],
},
twitter: {
card: 'summary_large_image',
site: '@reactjs',
creator: '@reactjs',
images: ['/images/og-default.png'],
},
facebook: {
appId: '623268441017527',
},
};

function getDomain(languageCode: string): string {
const subdomain = languageCode === 'en' ? '' : languageCode + '.';
return subdomain + 'react.dev';
}
9 changes: 5 additions & 4 deletions src/components/Layout/Feedback.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
'use client';

/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
Expand All @@ -10,14 +12,13 @@
*/

import {useState} from 'react';
import {useRouter} from 'next/router';
import {usePathname} from 'next/navigation';
import cn from 'classnames';

export function Feedback({onSubmit = () => {}}: {onSubmit?: () => void}) {
const {asPath} = useRouter();
const cleanedPath = asPath.split(/[\?\#]/)[0];
const pathname = usePathname();
// Reset on route changes.
return <SendFeedback key={cleanedPath} onSubmit={onSubmit} />;
return <SendFeedback key={pathname} onSubmit={onSubmit} />;
}

const thumbsUpIcon = (
Expand Down
2 changes: 2 additions & 0 deletions src/components/Layout/HomeContent.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
'use client';

/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
Expand Down
Loading
Loading