first commit
Some checks failed
Test examples / Test Examples (20) (push) Has been cancelled
Test examples / Test Examples (22) (push) Has been cancelled
Lock Threads / action (push) Has been cancelled
Trigger Release / start (push) Has been cancelled
Stale issue handler / stale (push) Has been cancelled
Update Font Data / create-pull-request (push) Has been cancelled
build-and-deploy / deploy-target (push) Has been cancelled
build-and-deploy / build (push) Has been cancelled
build-and-deploy / stable - aarch64-unknown-linux-musl - node@16 (push) Has been cancelled
build-and-deploy / stable - x86_64-unknown-linux-musl - node@16 (push) Has been cancelled
build-and-deploy / stable - aarch64-unknown-linux-gnu - node@16 (push) Has been cancelled
build-and-deploy / stable - x86_64-unknown-linux-gnu - node@16 (push) Has been cancelled
build-and-deploy / stable - aarch64-pc-windows-msvc - node@16 (push) Has been cancelled
build-and-deploy / stable - x86_64-pc-windows-msvc - node@16 (push) Has been cancelled
build-and-deploy / stable - aarch64-apple-darwin - node@16 (push) Has been cancelled
build-and-deploy / stable - x86_64-apple-darwin - node@16 (push) Has been cancelled
build-and-deploy / build-wasm (nodejs) (push) Has been cancelled
build-and-deploy / build-wasm (web) (push) Has been cancelled
build-and-deploy / Deploy preview tarball (push) Has been cancelled
build-and-deploy / Potentially publish release (push) Has been cancelled
build-and-deploy / publish-turbopack-npm-packages (push) Has been cancelled
build-and-deploy / Deploy examples (push) Has been cancelled
build-and-deploy / thank you, build (push) Has been cancelled
build-and-deploy / Upload Turbopack Bytesize metrics to Datadog (push) Has been cancelled
Rspack Next.js development integration tests / Rspack integration tests (push) Has been cancelled
Rspack Next.js production integration tests / Rspack integration tests (push) Has been cancelled
Turbopack Next.js development integration tests / Next.js integration tests (push) Has been cancelled
Turbopack Next.js production integration tests / Next.js integration tests (push) Has been cancelled
Update Rspack test manifest / Update and upload Rspack development test manifest (push) Has been cancelled
Update Rspack test manifest / Update and upload Rspack production test manifest (push) Has been cancelled
Upload bundler test manifests to areweturboyet.com / Upload test results (push) Has been cancelled
Update React / create-pull-request (push) Has been cancelled
test-e2e-project-reset-cron / reset-test-project (push) Has been cancelled
Notify about the top 15 issues/PRs/feature requests (most reacted) in the last 90 days / run (push) Has been cancelled

This commit is contained in:
Arian Tron
2026-03-10 19:37:31 +03:30
commit 61f56f997c
27684 changed files with 2784175 additions and 0 deletions

View File

@@ -0,0 +1,28 @@
The main purpose of this end-to-end test app is to allow manual testing of
Server Component and server action source mapping in DevTools.
## Server Components
To check the source mapping of server components, follow these steps:
1. Run `pnpm next dev test/development/app-dir/source-mapping`.
2. Go to [http://localhost:3000]().
3. Open the Console panel of the Chrome DevTools.
4. Look at the component stack of the replayed console warning. It should
contain the server source filenames.
5. Clicking on any filename for a component stack frame should open the actual
server sources in the DevTools Sources panel.
## Server Actions
To check the source mapping of server actions, follow these steps:
1. Run `pnpm next dev test/development/app-dir/source-mapping`.
2. Go to [http://localhost:3000]() or [http://localhost:3000/client]().
3. Open the Components panel of the React DevTools.
4. Select the `Form` element.
5. In the props section, right-click on the `action` prop and select "Go to
definition" (sometimes it needs two tries).
6. You should end up in the Chrome DevTools Sources panel with the `actions.ts`
file open and the cursor either at `foo()` (for `/`), or `bar()` (for
`/client`).

View File

@@ -0,0 +1,21 @@
'use server'
export const foo = async () => {
return 'exported exported arrow function expression'
}
export async function bar() {
return 'exported named function declaration'
}
export const baz = async function () {
return 'exported anonymous function expression'
}
export const qux = async function quux() {
return 'exported named function expression'
}
export default async () => {
return 'default exported arrow function expression'
}

View File

@@ -0,0 +1,5 @@
'use server'
export default async function () {
return 'default exported anonymous function expression'
}

View File

@@ -0,0 +1,5 @@
'use server'
export default async function someName() {
return 'default exported named function expression'
}

View File

@@ -0,0 +1,23 @@
'use client'
import { Form } from '../form'
import defaultAction1, { foo, bar, baz, qux } from '../actions1'
import defaultAction2 from '../actions2'
import defaultAction3 from '../actions3'
import Link from 'next/link'
export default function Page() {
return (
<main>
<h1>client component page</h1>
<Form id="form-1" action={defaultAction1} />
<Form id="form-2" action={defaultAction2} />
<Form id="form-3" action={defaultAction3} />
<Form id="form-4" action={foo} />
<Form id="form-5" action={bar} />
<Form id="form-6" action={baz} />
<Form id="form-7" action={qux} />
<Link href="/">server component page</Link>
</main>
)
}

View File

@@ -0,0 +1,20 @@
'use client'
import { useActionState } from 'react'
export function Form({
action,
id,
}: {
action: () => Promise<string>
id: string
}) {
const [result, formAction] = useActionState(action, 'initial')
return (
<form id={id} action={formAction}>
<button>Submit</button>
<p>{result}</p>
</form>
)
}

View File

@@ -0,0 +1,8 @@
import { ReactNode } from 'react'
export default function Root({ children }: { children: ReactNode }) {
return (
<html>
<body>{children}</body>
</html>
)
}

View File

@@ -0,0 +1,60 @@
import { Form } from './form'
import defaultAction1, { foo, bar, baz, qux } from './actions1'
import defaultAction2 from './actions2'
import defaultAction3 from './actions3'
import Link from 'next/link'
import { ServerComponent } from './server-component'
export default function Page() {
const action1 = async () => {
'use server'
return 'declarator arrow function expression'
}
async function action2() {
'use server'
return 'function declaration'
}
return (
<main>
<ServerComponent />
<Form id="form-1" action={defaultAction1} />
<Form id="form-2" action={defaultAction2} />
<Form id="form-3" action={defaultAction3} />
<Form id="form-4" action={foo} />
<Form id="form-5" action={bar} />
<Form id="form-6" action={baz} />
<Form id="form-7" action={qux} />
<Form id="form-8" action={action1} />
<Form id="form-9" action={action2} />
<Form
id="form-10"
action={async () => {
'use server'
return 'arrow function expression'
}}
/>
<Form
id="form-11"
action={async function () {
'use server'
return 'anonymous function expression'
}}
/>
<Form
id="form-12"
action={async function myAction() {
'use server'
return 'named function expression'
}}
/>
<Link href="/client">client component page</Link>
</main>
)
}

View File

@@ -0,0 +1,5 @@
'use client'
export function useClient() {
return 'client function'
}

View File

@@ -0,0 +1,15 @@
import { Suspense } from 'react'
import { useClient } from './client'
function Component() {
useClient()
return <p>Hello, Dave</p>
}
export default function Page() {
return (
<Suspense>
<Component />
</Suspense>
)
}

View File

@@ -0,0 +1,5 @@
export function ServerComponent() {
console.warn('foo')
return <h1>server component</h1>
}

View File

@@ -0,0 +1,20 @@
/**
* @type {import('next').NextConfig}
*/
const nextConfig = {
assetPrefix: '/assets',
rewrites() {
return {
beforeFiles: [
{
source: '/assets/:path*',
destination: '/:path*',
},
],
}
},
cacheComponents: true,
}
module.exports = nextConfig

View File

@@ -0,0 +1,189 @@
import { nextTestSetup } from 'e2e-utils'
import { retry } from 'next-test-utils'
const isCacheComponentsEnabled = process.env.__NEXT_CACHE_COMPONENTS === 'true'
// TODO(NAR-423): Migrate to Cache Components.
describe.skip('source-mapping', () => {
const { next } = nextTestSetup({
files: __dirname,
})
it('should work with server actions passed to client components', async () => {
const browser = await next.browser('/')
expect(await browser.elementByCss('#form-1 p').text()).toBe('initial')
await browser.elementByCss('#form-1 button').click()
await retry(async () => {
expect(await browser.elementByCss('#form-1 p').text()).toBe(
'default exported arrow function expression'
)
})
expect(await browser.elementByCss('#form-2 p').text()).toBe('initial')
await browser.elementByCss('#form-2 button').click()
await retry(async () => {
expect(await browser.elementByCss('#form-2 p').text()).toBe(
'default exported anonymous function expression'
)
})
expect(await browser.elementByCss('#form-3 p').text()).toBe('initial')
await browser.elementByCss('#form-3 button').click()
await retry(async () => {
expect(await browser.elementByCss('#form-3 p').text()).toBe(
'default exported named function expression'
)
})
expect(await browser.elementByCss('#form-4 p').text()).toBe('initial')
await browser.elementByCss('#form-4 button').click()
await retry(async () => {
expect(await browser.elementByCss('#form-4 p').text()).toBe(
'exported exported arrow function expression'
)
})
expect(await browser.elementByCss('#form-5 p').text()).toBe('initial')
await browser.elementByCss('#form-5 button').click()
await retry(async () => {
expect(await browser.elementByCss('#form-5 p').text()).toBe(
'exported named function declaration'
)
})
expect(await browser.elementByCss('#form-6 p').text()).toBe('initial')
await browser.elementByCss('#form-6 button').click()
await retry(async () => {
expect(await browser.elementByCss('#form-6 p').text()).toBe(
'exported anonymous function expression'
)
})
expect(await browser.elementByCss('#form-7 p').text()).toBe('initial')
await browser.elementByCss('#form-7 button').click()
await retry(async () => {
expect(await browser.elementByCss('#form-7 p').text()).toBe(
'exported named function expression'
)
})
expect(await browser.elementByCss('#form-8 p').text()).toBe('initial')
await browser.elementByCss('#form-8 button').click()
await retry(async () => {
expect(await browser.elementByCss('#form-8 p').text()).toBe(
'declarator arrow function expression'
)
})
expect(await browser.elementByCss('#form-9 p').text()).toBe('initial')
await browser.elementByCss('#form-9 button').click()
await retry(async () => {
expect(await browser.elementByCss('#form-9 p').text()).toBe(
'function declaration'
)
})
expect(await browser.elementByCss('#form-10 p').text()).toBe('initial')
await browser.elementByCss('#form-10 button').click()
await retry(async () => {
expect(await browser.elementByCss('#form-10 p').text()).toBe(
'arrow function expression'
)
})
expect(await browser.elementByCss('#form-11 p').text()).toBe('initial')
await browser.elementByCss('#form-11 button').click()
await retry(async () => {
expect(await browser.elementByCss('#form-11 p').text()).toBe(
'anonymous function expression'
)
})
expect(await browser.elementByCss('#form-12 p').text()).toBe('initial')
await browser.elementByCss('#form-12 button').click()
await retry(async () => {
expect(await browser.elementByCss('#form-12 p').text()).toBe(
'named function expression'
)
})
})
it('should work with server actions imported from client components', async () => {
const browser = await next.browser('/client')
expect(await browser.elementByCss('#form-1 p').text()).toBe('initial')
await browser.elementByCss('#form-1 button').click()
await retry(async () => {
expect(await browser.elementByCss('#form-1 p').text()).toBe(
'default exported arrow function expression'
)
})
expect(await browser.elementByCss('#form-2 p').text()).toBe('initial')
await browser.elementByCss('#form-2 button').click()
await retry(async () => {
expect(await browser.elementByCss('#form-2 p').text()).toBe(
'default exported anonymous function expression'
)
})
expect(await browser.elementByCss('#form-3 p').text()).toBe('initial')
await browser.elementByCss('#form-3 button').click()
await retry(async () => {
expect(await browser.elementByCss('#form-3 p').text()).toBe(
'default exported named function expression'
)
})
expect(await browser.elementByCss('#form-4 p').text()).toBe('initial')
await browser.elementByCss('#form-4 button').click()
await retry(async () => {
expect(await browser.elementByCss('#form-4 p').text()).toBe(
'exported exported arrow function expression'
)
})
expect(await browser.elementByCss('#form-5 p').text()).toBe('initial')
await browser.elementByCss('#form-5 button').click()
await retry(async () => {
expect(await browser.elementByCss('#form-5 p').text()).toBe(
'exported named function declaration'
)
})
expect(await browser.elementByCss('#form-6 p').text()).toBe('initial')
await browser.elementByCss('#form-6 button').click()
await retry(async () => {
expect(await browser.elementByCss('#form-6 p').text()).toBe(
'exported anonymous function expression'
)
})
expect(await browser.elementByCss('#form-7 p').text()).toBe('initial')
await browser.elementByCss('#form-7 button').click()
await retry(async () => {
expect(await browser.elementByCss('#form-7 p').text()).toBe(
'exported named function expression'
)
})
})
it('should show an error when client functions are called from server components', async () => {
const browser = await next.browser('/server-client')
await expect(browser).toDisplayRedbox(`
{
"description": "Attempted to call useClient() from the server but useClient is on the client. It's not possible to invoke a client function from the server, it can only be rendered as a Component or passed to props of a Client Component.",
"environmentLabel": "${isCacheComponentsEnabled ? 'Prerender' : 'Server'}",
"label": "Runtime Error",
"source": "app/server-client/page.js (5:12) @ Component
> 5 | useClient()
| ^",
"stack": [
"Component app/server-client/page.js (5:12)",
],
}
`)
})
})