Skip to content

Commit 08b32ce

Browse files
authored
Merge branch 'canary' into add-example
2 parents 9057356 + e951e89 commit 08b32ce

File tree

39 files changed

+841
-24
lines changed

39 files changed

+841
-24
lines changed

packages/next/src/build/webpack/plugins/flight-client-entry-plugin.ts

Lines changed: 187 additions & 24 deletions
Large diffs are not rendered by default.
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import { type NextInstance } from 'e2e-utils'
2+
3+
async function getActionsMappingByRuntime(
4+
next: NextInstance,
5+
runtime: 'node' | 'edge'
6+
) {
7+
const manifest = JSON.parse(
8+
await next.readFile('.next/server/server-reference-manifest.json')
9+
)
10+
11+
return manifest[runtime]
12+
}
13+
14+
export function markLayoutAsEdge(next: NextInstance) {
15+
beforeAll(async () => {
16+
await next.stop()
17+
const layoutContent = await next.readFile('app/layout.js')
18+
await next.patchFile(
19+
'app/layout.js',
20+
layoutContent + `\nexport const runtime = 'edge'`
21+
)
22+
await next.start()
23+
})
24+
}
25+
26+
/*
27+
{
28+
[route path]: { [layer]: Set<workerId> ]
29+
}
30+
*/
31+
type ActionsMappingOfRuntime = {
32+
[actionId: string]: {
33+
workers: {
34+
[route: string]: string
35+
}
36+
layer: {
37+
[route: string]: string
38+
}
39+
}
40+
}
41+
type ActionState = {
42+
[route: string]: {
43+
[layer: string]: number
44+
}
45+
}
46+
47+
function getActionsRoutesState(
48+
actionsMappingOfRuntime: ActionsMappingOfRuntime
49+
): ActionState {
50+
const state: ActionState = {}
51+
Object.keys(actionsMappingOfRuntime).forEach((actionId) => {
52+
const action = actionsMappingOfRuntime[actionId]
53+
const routePaths = Object.keys(action.workers)
54+
55+
routePaths.forEach((routePath) => {
56+
if (!state[routePath]) {
57+
state[routePath] = {}
58+
}
59+
const layer = action.layer[routePath]
60+
61+
if (!state[routePath][layer]) {
62+
state[routePath][layer] = 0
63+
}
64+
65+
state[routePath][layer]++
66+
})
67+
})
68+
69+
return state
70+
}
71+
72+
export async function getActionsRoutesStateByRuntime(next: NextInstance) {
73+
const actionsMappingOfRuntime = await getActionsMappingByRuntime(
74+
next,
75+
process.env.TEST_EDGE ? 'edge' : 'node'
76+
)
77+
return getActionsRoutesState(actionsMappingOfRuntime)
78+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
'use server'
2+
3+
export async function serverComponentAction() {
4+
return 'server-action'
5+
}
6+
7+
export async function clientComponentAction() {
8+
return 'client-action'
9+
}
10+
11+
export async function unusedExportedAction() {
12+
return 'unused-exported-action'
13+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
'use client'
2+
3+
import { useState } from 'react'
4+
import { clientComponentAction } from '../actions'
5+
6+
export default function Page() {
7+
const [text, setText] = useState('initial')
8+
return (
9+
<div>
10+
<button
11+
id="action-1"
12+
onClick={async () => {
13+
setText(await clientComponentAction())
14+
}}
15+
>
16+
Action 1
17+
</button>
18+
<span>{text}</span>
19+
</div>
20+
)
21+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
export default function Page() {
2+
// Inline Server Action
3+
async function inlineServerAction() {
4+
'use server'
5+
return 'inline-server-action'
6+
}
7+
8+
return (
9+
<form action={inlineServerAction}>
10+
<button type="submit">Submit</button>
11+
</form>
12+
)
13+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export default function Layout({ children }) {
2+
return (
3+
<html>
4+
<body>{children}</body>
5+
</html>
6+
)
7+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { serverComponentAction } from '../actions'
2+
3+
export default function Page() {
4+
return (
5+
<form>
6+
<input type="text" placeholder="input" />
7+
<button formAction={serverComponentAction}>submit</button>
8+
</form>
9+
)
10+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
process.env.TEST_EDGE = '1'
2+
3+
require('./basic.test')
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { nextTestSetup } from 'e2e-utils'
2+
import {
3+
getActionsRoutesStateByRuntime,
4+
markLayoutAsEdge,
5+
} from '../_testing/utils'
6+
7+
describe('actions-tree-shaking - basic', () => {
8+
const { next } = nextTestSetup({
9+
files: __dirname,
10+
})
11+
12+
if (process.env.TEST_EDGE) {
13+
markLayoutAsEdge(next)
14+
}
15+
16+
it('should not have the unused action in the manifest', async () => {
17+
const actionsRoutesState = await getActionsRoutesStateByRuntime(next)
18+
19+
expect(actionsRoutesState).toMatchObject({
20+
// only one server layer action
21+
'app/server/page': {
22+
rsc: 1,
23+
},
24+
// only one browser layer action
25+
'app/client/page': {
26+
'action-browser': 1,
27+
},
28+
'app/inline/page': {
29+
rsc: 1,
30+
},
31+
})
32+
})
33+
})
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export default function Layout({ children }) {
2+
return (
3+
<html>
4+
<body>{children}</body>
5+
</html>
6+
)
7+
}

0 commit comments

Comments
 (0)