Skip to content

Commit 0557aba

Browse files
committed
Handle MDX better and use turbopack
1 parent 5cc0005 commit 0557aba

54 files changed

Lines changed: 1125 additions & 1356 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,6 @@ public/rss.xml
5050

5151
# Generated OG images (scripts/generateOgImages.mjs)
5252
public/images/og/
53+
54+
# Generated Sandpack RSC runtime sources (scripts/buildRscWorker.mjs)
55+
public/sandpack-rsc/

CLAUDE.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,11 @@ See `.claude/docs/react-docs-patterns.md` for comprehensive style guidelines.
5353
Prettier is used for formatting (config in `.prettierrc`).
5454

5555
<!-- BEGIN:nextjs-agent-rules -->
56+
5657
# This is NOT the Next.js you know
5758

58-
This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` before writing any code. Heed deprecation notices.
59+
This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` (resolved from this file's directory; in monorepos the `next` package may not be visible from the repo root) before writing any code. Heed deprecation notices.
60+
61+
This block is written and re-added by `next dev` — verify at `node_modules/next/dist/server/lib/generate-agent-files.js`. Removing it from a diff only re-creates the uncommitted change; committing it with your work keeps the tree clean.
5962

60-
**Keep this block, including in commits.** It is part of the project's agent setup, maintained by `next dev` for every agent that works here. If it appears as an uncommitted change, that is intentional — commit it as-is. Do not remove it to clean up a diff; it will be regenerated.
6163
<!-- END:nextjs-agent-rules -->

next.config.js

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,25 +17,27 @@ const nextConfig = {
1717
reactStrictMode: true,
1818
reactCompiler: true,
1919
cacheComponents: true,
20+
outputFileTracingIncludes: {
21+
'/*': ['./src/content/**/*.md'],
22+
},
2023
serverExternalPackages: [
2124
'@babel/core',
2225
'@babel/plugin-transform-modules-commonjs',
2326
'@babel/preset-react',
2427
'@mdx-js/mdx',
25-
'metro-cache',
2628
'gray-matter',
2729
'unist-util-visit',
2830
'remark-gfm',
2931
'remark-frontmatter',
3032
],
31-
// Next 16 defaults to Turbopack, but this project still relies on the custom
32-
// `webpack()` config below (module aliases, IgnorePlugin, and Sandpack's
33-
// `raw-loader` imports) which Turbopack can't run yet. The build scripts pass
34-
// `--webpack` to force the webpack pipeline; this empty `turbopack` object
35-
// silences the "webpack config without turbopack config" warning in the
36-
// meantime.
37-
// TODO: port the webpack customizations to Turbopack and drop `--webpack`.
38-
turbopack: {},
33+
turbopack: {
34+
resolveAlias: {
35+
'use-sync-external-store/shim': 'react',
36+
esquery: 'esquery/dist/esquery.min.js',
37+
raf: './src/utils/rafShim.js',
38+
process: './src/utils/processShim.js',
39+
},
40+
},
3941
async rewrites() {
4042
return {
4143
beforeFiles: [

package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@
44
"private": true,
55
"license": "CC",
66
"scripts": {
7+
"preanalyze": "node scripts/buildRscWorker.mjs",
78
"analyze": "ANALYZE=true next build --webpack",
8-
"dev": "next dev --webpack",
9+
"predev": "node scripts/buildRscWorker.mjs",
10+
"dev": "next dev",
911
"prebuild:rsc": "node scripts/buildRscWorker.mjs",
10-
"build": "node scripts/buildRscWorker.mjs && next build --webpack && node --experimental-modules ./scripts/downloadFonts.mjs && node ./scripts/generateOgImages.mjs",
12+
"build": "node scripts/buildRscWorker.mjs && next build && node --experimental-modules ./scripts/downloadFonts.mjs && node ./scripts/generateOgImages.mjs",
1113
"lint": "eslint \"{src,plugins}/**/*.{js,jsx,ts,tsx}\" && eslint \"src/content/**/*.md\"",
1214
"lint:fix": "eslint --fix \"{src,plugins}/**/*.{js,jsx,ts,tsx}\" && eslint --fix \"src/content/**/*.md\"",
1315
"format:source": "prettier --config .prettierrc --write \"{plugins,src}/**/*.{js,ts,jsx,tsx,css}\"",
@@ -38,7 +40,6 @@
3840
"github-slugger": "^1.3.0",
3941
"next": "16.3.0",
4042
"parse-numeric-range": "^1.2.0",
41-
"raw-loader": "^4.0.2",
4243
"react": "19.2.7",
4344
"react-collapsed": "^4.2.0",
4445
"react-dom": "19.2.7",
@@ -87,7 +88,6 @@
8788
"is-ci": "^3.0.1",
8889
"lint-staged": ">=10",
8990
"mdast-util-to-string": "^1.1.0",
90-
"metro-cache": "0.72.2",
9191
"npm-run-all": "^4.1.5",
9292
"postcss": "^8.4.5",
9393
"postcss-flexbugs-fixes": "4.2.1",

scripts/buildRscWorker.mjs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,29 @@ const shimCode = fs.readFileSync(shimPath, 'utf8');
4242
workerCode = shimCode + '\n' + workerCode;
4343

4444
fs.writeFileSync(workerOutfile, workerCode);
45+
46+
const publicSourceDir = path.resolve(root, 'public/sandpack-rsc');
47+
fs.rmSync(publicSourceDir, {recursive: true, force: true});
48+
fs.mkdirSync(publicSourceDir, {recursive: true});
49+
50+
const publicSources = {
51+
'webpack-shim.js': shimPath,
52+
'rsc-client.js': path.resolve(sandboxBase, 'rsc-client.js'),
53+
'react-refresh-init.js': path.resolve(
54+
sandboxBase,
55+
'__react_refresh_init__.js'
56+
),
57+
'worker-bundle.js': workerOutfile,
58+
'rsdw-client.js': path.resolve(
59+
root,
60+
'node_modules/react-server-dom-webpack/cjs/react-server-dom-webpack-client.browser.production.js'
61+
),
62+
'react-refresh-runtime.js': path.resolve(
63+
root,
64+
'node_modules/next/dist/compiled/react-refresh/cjs/react-refresh-runtime.development.js'
65+
),
66+
};
67+
68+
for (const [name, source] of Object.entries(publicSources)) {
69+
fs.copyFileSync(source, path.resolve(publicSourceDir, name));
70+
}

src/app/DocsPage.tsx

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,31 +5,36 @@
55
* LICENSE file in the root directory of this source tree.
66
*/
77

8-
'use client';
9-
108
import {Page, type PageSection} from 'components/Layout/Page';
11-
import {useDeserializedMDX} from 'components/Layout/useDeserializedMDX';
129
import type {RouteItem} from 'components/Layout/getRouteMeta';
1310
import type {PageData} from 'lib/readMarkdownPage';
11+
import {renderCompiledMDX} from 'utils/compileMDX';
12+
import type {ReactNode} from 'react';
1413

1514
interface DocsPageProps {
1615
data: PageData;
1716
pathname: string;
1817
section: PageSection;
1918
routeTree: RouteItem;
19+
children?: ReactNode;
2020
}
2121

22-
export function DocsPage({data, pathname, section, routeTree}: DocsPageProps) {
23-
const {parsedContent, parsedToc} = useDeserializedMDX(data.content, data.toc);
22+
export async function DocsPage({
23+
data,
24+
pathname,
25+
section,
26+
routeTree,
27+
children,
28+
}: DocsPageProps) {
29+
const {content, toc} = await renderCompiledMDX(data);
2430
return (
2531
<Page
26-
toc={parsedToc}
32+
toc={toc}
2733
routeTree={routeTree}
2834
meta={data.meta}
2935
section={section}
30-
pathname={pathname}
31-
languages={data.languages}>
32-
{parsedContent}
36+
pathname={pathname}>
37+
{children ?? content}
3338
</Page>
3439
);
3540
}

src/app/api/md/[...path]/route.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ async function readContentMarkdown(
5050
];
5151
for (const fullPath of candidates) {
5252
try {
53-
return fs.readFileSync(fullPath, 'utf8');
53+
return fs.readFileSync(/* turbopackIgnore: true */ fullPath, 'utf8');
5454
} catch {
5555
// Try next candidate
5656
}

src/app/community/layout.tsx

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,8 @@
55
* LICENSE file in the root directory of this source tree.
66
*/
77

8-
'use client';
9-
10-
import * as React from 'react';
11-
import {usePathname} from 'next/navigation';
128
import {SidebarNav} from 'components/Layout/SidebarNav';
139
import {TopNav} from 'components/Layout/TopNav';
14-
import {getRouteMeta} from 'components/Layout/getRouteMeta';
1510
import type {RouteItem} from 'components/Layout/getRouteMeta';
1611
import sidebarCommunity from '../../sidebarCommunity.json';
1712

@@ -20,20 +15,14 @@ export default function CommunityLayout({
2015
}: {
2116
children: React.ReactNode;
2217
}) {
23-
const pathname = usePathname() || '/';
2418
const routeTree = sidebarCommunity as RouteItem;
25-
const {breadcrumbs} = getRouteMeta(pathname, routeTree);
2619
return (
2720
<>
28-
<TopNav
29-
section="community"
30-
routeTree={routeTree}
31-
breadcrumbs={breadcrumbs}
32-
/>
21+
<TopNav section="community" routeTree={routeTree} />
3322
<div className="grid grid-cols-only-content lg:grid-cols-sidebar-content 2xl:grid-cols-sidebar-content-toc">
3423
<div className="lg:-mt-16 z-10">
3524
<div className="fixed top-0 py-0 shadow lg:pt-16 lg:sticky start-0 end-0 lg:shadow-none">
36-
<SidebarNav routeTree={routeTree} breadcrumbs={breadcrumbs} />
25+
<SidebarNav routeTree={routeTree} />
3726
</div>
3827
</div>
3928
{children}

src/app/errors/ErrorDecoderView.tsx

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,23 @@
55
* LICENSE file in the root directory of this source tree.
66
*/
77

8-
'use client';
9-
108
import {Page} from 'components/Layout/Page';
11-
import {useDeserializedMDX} from 'components/Layout/useDeserializedMDX';
129
import {ErrorDecoderContext} from 'components/ErrorDecoderContext';
1310
import sidebarLearn from '../../sidebarLearn.json';
1411
import type {RouteItem} from 'components/Layout/getRouteMeta';
1512
import type {ErrorDecoderData} from 'lib/loadErrorDecoderData';
13+
import {renderCompiledMDX} from 'utils/compileMDX';
1614

1715
interface ErrorDecoderViewProps {
1816
data: ErrorDecoderData;
1917
pathname: string;
2018
}
2119

22-
export function ErrorDecoderView({data, pathname}: ErrorDecoderViewProps) {
23-
const {parsedContent} = useDeserializedMDX(data.content, data.toc);
20+
export async function ErrorDecoderView({
21+
data,
22+
pathname,
23+
}: ErrorDecoderViewProps) {
24+
const {content} = await renderCompiledMDX(data);
2425
return (
2526
<ErrorDecoderContext
2627
value={{errorMessage: data.errorMessage, errorCode: data.errorCode}}>
@@ -34,7 +35,7 @@ export function ErrorDecoderView({data, pathname}: ErrorDecoderViewProps) {
3435
routeTree={sidebarLearn as RouteItem}
3536
section="unknown"
3637
pathname={pathname}>
37-
<div>{parsedContent}</div>
38+
<div>{content}</div>
3839
</Page>
3940
</ErrorDecoderContext>
4041
);

src/app/learn/layout.tsx

Lines changed: 3 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -5,49 +5,23 @@
55
* LICENSE file in the root directory of this source tree.
66
*/
77

8-
'use client';
9-
10-
import * as React from 'react';
11-
import {usePathname} from 'next/navigation';
128
import {SidebarNav} from 'components/Layout/SidebarNav';
139
import {TopNav} from 'components/Layout/TopNav';
14-
import {getRouteMeta} from 'components/Layout/getRouteMeta';
1510
import type {RouteItem} from 'components/Layout/getRouteMeta';
1611
import sidebarLearn from '../../sidebarLearn.json';
1712

18-
function RestoreHashAfterNavigation({pathname}: {pathname: string}) {
19-
React.useEffect(() => {
20-
if (!window.location.hash) {
21-
return;
22-
}
23-
24-
const id = decodeURIComponent(window.location.hash.slice(1));
25-
// The Learn content remounts after a history traversal, so reapply the
26-
// fragment once the replacement content is in the DOM.
27-
const frame = requestAnimationFrame(() => {
28-
document.getElementById(id)?.scrollIntoView();
29-
});
30-
return () => cancelAnimationFrame(frame);
31-
}, [pathname]);
32-
33-
return null;
34-
}
35-
3613
export default function LearnLayout({children}: {children: React.ReactNode}) {
37-
const pathname = usePathname() || '/';
3814
const routeTree = sidebarLearn as RouteItem;
39-
const {breadcrumbs} = getRouteMeta(pathname, routeTree);
4015
return (
4116
<>
42-
<RestoreHashAfterNavigation pathname={pathname} />
43-
<TopNav section="learn" routeTree={routeTree} breadcrumbs={breadcrumbs} />
17+
<TopNav section="learn" routeTree={routeTree} />
4418
<div className="grid grid-cols-only-content lg:grid-cols-sidebar-content 2xl:grid-cols-sidebar-content-toc">
4519
<div className="lg:-mt-16 z-10">
4620
<div className="fixed top-0 py-0 shadow lg:pt-16 lg:sticky start-0 end-0 lg:shadow-none">
47-
<SidebarNav routeTree={routeTree} breadcrumbs={breadcrumbs} />
21+
<SidebarNav routeTree={routeTree} />
4822
</div>
4923
</div>
50-
<React.Fragment key={pathname}>{children}</React.Fragment>
24+
{children}
5125
</div>
5226
</>
5327
);

0 commit comments

Comments
 (0)