Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

action: support not found #49209

Merged
merged 2 commits into from
May 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 19 additions & 13 deletions packages/next/src/server/app-render/action-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ export async function handleAction({
generateFlight: (options: {
actionResult: ActionResult
skipFlight: boolean
asNotFound?: boolean
}) => Promise<RenderResult>
staticGenerationStore: StaticGenerationStore
}): Promise<undefined | RenderResult | 'not-found'> {
Expand Down Expand Up @@ -363,28 +364,33 @@ export async function handleAction({
res.statusCode = 303
return new RenderResult('')
} else if (isNotFoundError(err)) {
res.statusCode = 404

await Promise.all(staticGenerationStore.pendingRevalidates || [])
if (isFetchAction) {
throw new Error('Invariant: not implemented.')
const promise = Promise.reject(err)
try {
await promise
} catch (_) {}
return generateFlight({
skipFlight: false,
actionResult: promise,
asNotFound: true,
})
}
await Promise.all(staticGenerationStore.pendingRevalidates || [])
res.statusCode = 404
return 'not-found'
}

if (isFetchAction) {
res.statusCode = 500
const rejectedPromise = Promise.reject(err)
await Promise.all(staticGenerationStore.pendingRevalidates || [])
const promise = Promise.reject(err)
try {
// we need to await the promise to trigger the rejection early
// so that it's already handled by the time we call
// the RSC runtime. Otherwise, it will throw an unhandled
// promise rejection error in the renderer.
await rejectedPromise
} catch (_) {
// swallow error, it's gonna be handled on the client
}
await promise
} catch (_) {}

return generateFlight({
actionResult: rejectedPromise,
actionResult: promise,
// if the page was not revalidated, we can skip the rendering the flight tree
skipFlight: !staticGenerationStore.pathWasRevalidated,
})
Expand Down
3 changes: 2 additions & 1 deletion packages/next/src/server/app-render/app-render.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -939,6 +939,7 @@ export async function renderToHTMLOrFlight(
const generateFlight = async (options?: {
actionResult: ActionResult
skipFlight: boolean
asNotFound?: boolean
}): Promise<RenderResult> => {
/**
* Use router state to decide at what common layout to render the page.
Expand Down Expand Up @@ -1176,7 +1177,7 @@ export async function renderToHTMLOrFlight(
injectedCSS: new Set(),
injectedFontPreloadTags: new Set(),
rootLayoutIncluded: false,
asNotFound: pathname === '/404',
asNotFound: pathname === '/404' || options?.asNotFound,
})
).map((path) => path.slice(1)) // remove the '' (root) segment

Expand Down
12 changes: 11 additions & 1 deletion test/e2e/app-dir/actions/app-action.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ createNextDescribe(
}, '/header?name=test&constructor=FormData')
})

it('should support notFound', async () => {
it('should support notFound (javascript disabled)', async () => {
const browser = await next.browser('/server', {
// TODO we should also test this with javascript on but not-found is not implemented yet.
disableJavaScript: true,
Expand All @@ -91,6 +91,16 @@ createNextDescribe(
}, 'my-not-found')
})

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

await browser.elementByCss('#nowhere').click()

await check(() => {
return browser.elementByCss('h1').text()
}, 'my-not-found')
})

it('should support uploading files', async () => {
const logs: string[] = []
next.on('stdout', (log) => {
Expand Down