Skip to content

Commit

Permalink
Merge branch 'canary' into shuding/next-762-compilation-detect-typos-…
Browse files Browse the repository at this point in the history
…and-obvious
  • Loading branch information
kodiakhq[bot] authored Mar 13, 2023
2 parents a8cc6c9 + baa2225 commit 569881d
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 18 deletions.
4 changes: 1 addition & 3 deletions packages/next/src/client/app-index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -152,8 +152,6 @@ function useInitialServerResponse(cacheKey: string): Promise<JSX.Element> {

const newResponse = createFromReadableStream(readable, {
async callServer(id: string, args: any[]) {
console.log('callServer', id, args)

const actionId = id

// Fetching the current url with the action header.
Expand All @@ -169,7 +167,7 @@ function useInitialServerResponse(cacheKey: string): Promise<JSX.Element> {
}),
})

return res.json()
return (await res.json())[0]
},
})

Expand Down
53 changes: 39 additions & 14 deletions packages/next/src/server/app-render.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,10 @@ import { isClientReference } from '../build/is-client-reference'
import { getLayoutOrPageModule, LoaderTree } from './lib/app-dir-module'
import { warnOnce } from '../shared/lib/utils/warn-once'
import { isNotFoundError } from '../client/components/not-found'
import { isRedirectError } from '../client/components/redirect'
import {
getURLFromRedirectError,
isRedirectError,
} from '../client/components/redirect'
import { NEXT_DYNAMIC_NO_SSR_CODE } from '../shared/lib/lazy-dynamic/no-ssr-error'
import { patchFetch } from './lib/patch-fetch'
import { AppRenderSpan } from './lib/trace/constants'
Expand Down Expand Up @@ -806,11 +809,6 @@ export async function renderToHTMLOrFlight(
renderOpts: RenderOpts
): Promise<RenderResult | null> {
const isFlight = req.headers[RSC.toLowerCase()] !== undefined
const actionId = req.headers[ACTION.toLowerCase()]
const isAction =
actionId !== undefined &&
typeof actionId === 'string' &&
req.method === 'POST'

const {
buildManifest,
Expand Down Expand Up @@ -1757,27 +1755,54 @@ export async function renderToHTMLOrFlight(
}

// For action requests, we handle them differently with a sepcial render result.
if (isAction) {
let actionId = req.headers[ACTION.toLowerCase()] as string
const isFormAction =
req.method === 'POST' &&
req.headers['content-type'] === 'application/x-www-form-urlencoded'
const isFetchAction =
actionId !== undefined &&
typeof actionId === 'string' &&
req.method === 'POST'

if (isFetchAction || isFormAction) {
if (process.env.NEXT_RUNTIME !== 'edge') {
const workerName = 'app' + renderOpts.pathname
const actionModId = serverActionsManifest[actionId].workers[workerName]

const { parseBody } =
require('./api-utils/node') as typeof import('./api-utils/node')
const actionData = (await parseBody(req, '1mb')) || {}
let bound = []

if (isFormAction) {
actionId = actionData.$$id as string
if (!actionId) {
throw new Error('Invariant: missing action ID.')
}
const formData = new URLSearchParams(actionData)
formData.delete('$$id')
bound = [formData]
} else {
bound = actionData.bound || []
}

const workerName = 'app' + renderOpts.pathname
const actionModId = serverActionsManifest[actionId].workers[workerName]

const actionHandler =
ComponentMod.__next_app_webpack_require__(actionModId)

try {
return new ActionRenderResult(
JSON.stringify(
await actionHandler(actionId, actionData.bound || [])
)
JSON.stringify([await actionHandler(actionId, bound)])
)
} catch (err) {
if (isRedirectError(err)) {
throw new Error('Invariant: not implemented.')
if (isFetchAction) {
throw new Error('Invariant: not implemented.')
} else {
const redirectUrl = getURLFromRedirectError(err)
res.statusCode = 303
res.setHeader('Location', redirectUrl)
return new ActionRenderResult(JSON.stringify({}))
}
}
throw err
}
Expand Down
11 changes: 11 additions & 0 deletions test/e2e/app-dir/actions/app-action.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,16 @@ createNextDescribe(
return res.includes('Mozilla') ? 'UA' : ''
}, 'UA')
})

it('should support formData and redirect', async () => {
const browser = await next.browser('/server')

await browser.eval(`document.getElementById('name').value = 'test'`)
await browser.elementByCss('#submit').click()

await check(() => {
return browser.eval('window.location.pathname + window.location.search')
}, '/header?name=test')
})
}
)
21 changes: 21 additions & 0 deletions test/e2e/app-dir/actions/app/server/form.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { redirect } from 'next/navigation'

async function action(formData) {
'use server'
redirect('/header?name=' + formData.get('name'))
}

export default function Form() {
return (
<>
<hr />
<form action="" method="POST">
<input type="text" name="name" id="name" required />
<input type="text" name="$$id" value={action.$$id} hidden readOnly />
<button type="submit" id="submit">
Submit
</button>
</form>
</>
)
}
8 changes: 7 additions & 1 deletion test/e2e/app-dir/actions/app/server/page.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import Counter from './counter'
import Form from './form'

import double, { inc, dec } from './actions'

export default function Page() {
return <Counter inc={inc} dec={dec} double={double} />
return (
<>
<Counter inc={inc} dec={dec} double={double} />
<Form />
</>
)
}

0 comments on commit 569881d

Please sign in to comment.