Skip to content

Commit 2f3144e

Browse files
authored
feat(nextjs): Support cacheComponents on turbopack (#18304)
With this PR we stop breaking builds by simply re-exporting APIs that make use of synchronous random APIs: - `startSpan` - `startSpanManual` - `startInactiveSpan` We do this with a naive approach of detecting a prod build phase and returning a noop span in cases where we call these APIs. We hope to get a more solid API for this from Vercel at some point so we can make this more bulletproof. The current version will also still emit a warning in webpack as the behaviour seems to change there. This will only work for Turbopack as of now, as we still need to get rid of build time instrumentation in webpack in order for this to work. closes #17895
1 parent ebb8eed commit 2f3144e

30 files changed

+534
-0
lines changed
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2+
3+
# dependencies
4+
/node_modules
5+
/.pnp
6+
.pnp.*
7+
.yarn/*
8+
!.yarn/patches
9+
!.yarn/plugins
10+
!.yarn/releases
11+
!.yarn/versions
12+
13+
# testing
14+
/coverage
15+
16+
# next.js
17+
/.next/
18+
/out/
19+
20+
# production
21+
/build
22+
23+
# misc
24+
.DS_Store
25+
*.pem
26+
27+
# debug
28+
npm-debug.log*
29+
yarn-debug.log*
30+
yarn-error.log*
31+
.pnpm-debug.log*
32+
33+
# env files (can opt-in for committing if needed)
34+
.env*
35+
36+
# vercel
37+
.vercel
38+
39+
# typescript
40+
*.tsbuildinfo
41+
next-env.d.ts
42+
43+
# Sentry Config File
44+
.env.sentry-build-plugin
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
@sentry:registry=http://127.0.0.1:4873
2+
@sentry-internal:registry=http://127.0.0.1:4873
3+
public-hoist-pattern[]=*import-in-the-middle*
4+
public-hoist-pattern[]=*require-in-the-middle*
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { Suspense } from 'react';
2+
import * as Sentry from '@sentry/nextjs';
3+
4+
export default function Page() {
5+
return (
6+
<>
7+
<h1>This will be pre-rendered</h1>
8+
<DynamicContent />
9+
</>
10+
);
11+
}
12+
13+
async function DynamicContent() {
14+
const getTodos = async () => {
15+
return Sentry.startSpan({ name: 'getTodos', op: 'get.todos' }, async () => {
16+
'use cache';
17+
await new Promise(resolve => setTimeout(resolve, 100));
18+
return [1, 2, 3, 4, 5];
19+
});
20+
};
21+
22+
const todos = await getTodos();
23+
24+
return <div id="todos-fetched">Todos fetched: {todos.length}</div>;
25+
}
Binary file not shown.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
'use client';
2+
3+
import * as Sentry from '@sentry/nextjs';
4+
import NextError from 'next/error';
5+
import { useEffect } from 'react';
6+
7+
export default function GlobalError({ error }: { error: Error & { digest?: string } }) {
8+
useEffect(() => {
9+
Sentry.captureException(error);
10+
}, [error]);
11+
12+
return (
13+
<html>
14+
<body>
15+
{/* `NextError` is the default Next.js error page component. Its type
16+
definition requires a `statusCode` prop. However, since the App Router
17+
does not expose status codes for errors, we simply pass 0 to render a
18+
generic error message. */}
19+
<NextError statusCode={0} />
20+
</body>
21+
</html>
22+
);
23+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export default function Layout({ children }: { children: React.ReactNode }) {
2+
return (
3+
<html lang="en">
4+
<body>{children}</body>
5+
</html>
6+
);
7+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default function Page() {
2+
return <p>Next 16 CacheComponents test app</p>;
3+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { Suspense } from 'react';
2+
import * as Sentry from '@sentry/nextjs';
3+
4+
export default function Page() {
5+
return (
6+
<>
7+
<h1>This will be pre-rendered</h1>
8+
<Suspense fallback={<div>Loading...</div>}>
9+
<DynamicContent />
10+
</Suspense>
11+
</>
12+
);
13+
}
14+
15+
async function DynamicContent() {
16+
const getTodos = async () => {
17+
return Sentry.startSpan({ name: 'getTodos', op: 'get.todos' }, async () => {
18+
await new Promise(resolve => setTimeout(resolve, 100));
19+
return [1, 2, 3, 4, 5];
20+
});
21+
};
22+
23+
const todos = await getTodos();
24+
25+
return <div id="todos-fetched">Todos fetched: {todos.length}</div>;
26+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { dirname } from 'path';
2+
import { fileURLToPath } from 'url';
3+
import { FlatCompat } from '@eslint/eslintrc';
4+
5+
const __filename = fileURLToPath(import.meta.url);
6+
const __dirname = dirname(__filename);
7+
8+
const compat = new FlatCompat({
9+
baseDirectory: __dirname,
10+
});
11+
12+
const eslintConfig = [
13+
...compat.extends('next/core-web-vitals', 'next/typescript'),
14+
{
15+
ignores: ['node_modules/**', '.next/**', 'out/**', 'build/**', 'next-env.d.ts'],
16+
},
17+
];
18+
19+
export default eslintConfig;
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import * as Sentry from '@sentry/nextjs';
2+
3+
Sentry.init({
4+
environment: 'qa', // dynamic sampling bias to keep transactions
5+
dsn: process.env.NEXT_PUBLIC_E2E_TEST_DSN,
6+
tunnel: `http://localhost:3031/`, // proxy server
7+
tracesSampleRate: 1.0,
8+
sendDefaultPii: true,
9+
});
10+
11+
export const onRouterTransitionStart = Sentry.captureRouterTransitionStart;

0 commit comments

Comments
 (0)