Skip to content

Commit

Permalink
Fix bundling of Server Actions (#51367)
Browse files Browse the repository at this point in the history
There're 3 layers in the RSC module graph: server → client → action. "Action" means that a Client Component re-enters the server layer by importing a file with `"use server"`, and it should behave the same as the server layer but you can't enter the client layer again (hence we have a 3rd layer name).

Since the action layer has the same behavior and module resolution rules, it should be bundled just like the server layer.

Closes #50658. Originally the issue was that `auth/next` isn't being bundled on the action layer, and it has the async local storage imported. Because of that, that storage comes from node_modules instead of the server bundle.
fix NEXT-1290
  • Loading branch information
shuding committed Jun 16, 2023
1 parent 3cac097 commit 4698138
Show file tree
Hide file tree
Showing 4 changed files with 24 additions and 2 deletions.
4 changes: 2 additions & 2 deletions packages/next/src/build/webpack-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1385,7 +1385,7 @@ export default async function getBaseWebpackConfig(
// so that the DefinePlugin can inject process.env values.

// Treat next internals as non-external for server layer
if (layer === WEBPACK_LAYERS.server) {
if (layer === WEBPACK_LAYERS.server || layer === WEBPACK_LAYERS.action) {
return
}

Expand Down Expand Up @@ -1520,7 +1520,7 @@ export default async function getBaseWebpackConfig(
(isEsm && isAppLayer)

if (/node_modules[/\\].*\.[mc]?js$/.test(res)) {
if (layer === WEBPACK_LAYERS.server) {
if (layer === WEBPACK_LAYERS.server || layer === WEBPACK_LAYERS.action) {
// All packages should be bundled for the server layer if they're not opted out.
// This option takes priority over the transpilePackages option.

Expand Down
9 changes: 9 additions & 0 deletions test/e2e/app-dir/actions/app-action.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ createNextDescribe(
files: __dirname,
dependencies: {
react: 'latest',
nanoid: 'latest',
'react-dom': 'latest',
'server-only': 'latest',
},
Expand Down Expand Up @@ -281,6 +282,14 @@ createNextDescribe(
}
})
})

it('should bundle external libraries if they are on the action layer', async () => {
await next.fetch('/client')
const pageBundle = await fs.readFile(
join(next.testDir, '.next', 'server', 'app', 'client', 'page.js')
)
expect(pageBundle.toString()).toContain('node_modules/nanoid/index.js')
})
}

describe('Edge SSR', () => {
Expand Down
9 changes: 9 additions & 0 deletions test/e2e/app-dir/actions/app/client/actions-lib.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
'use server'

// Any arbitrary library just to ensure it's bundled.
// https://github.com/vercel/next.js/pull/51367
import nanoid from 'nanoid'

export async function test() {
console.log(nanoid)
}
4 changes: 4 additions & 0 deletions test/e2e/app-dir/actions/app/client/page.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import { useState } from 'react'

import double, { inc, dec, redirectAction, getHeaders } from './actions'
import { test } from './actions-lib'

export default function Counter() {
const [count, setCount] = useState(0)
Expand Down Expand Up @@ -58,6 +59,9 @@ export default function Counter() {
submit
</button>
</form>
<form action={test}>
<button>test</button>
</form>
</div>
)
}

0 comments on commit 4698138

Please sign in to comment.