-
Notifications
You must be signed in to change notification settings - Fork 177
Fix fallback false #858
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
Fix fallback false #858
Changes from 5 commits
dbe3198
fd85949
ab8a3c1
851af13
e087cb6
54a8b34
ccdb493
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "@opennextjs/aws": patch | ||
| --- | ||
|
|
||
| fix 404 with fallback false on dynamic route |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| import type { InferGetServerSidePropsType } from "next"; | ||
|
|
||
| export function getServerSideProps() { | ||
| return { | ||
| props: { | ||
| message: "This is a dynamic fallback page.", | ||
| }, | ||
| }; | ||
| } | ||
|
|
||
| export default function Page({ | ||
| message, | ||
| }: InferGetServerSidePropsType<typeof getServerSideProps>) { | ||
| return ( | ||
| <div> | ||
| <h1>Dynamic Fallback Page</h1> | ||
| <p data-testid="message">{message}</p> | ||
| </div> | ||
| ); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| import type { InferGetStaticPropsType } from "next"; | ||
|
|
||
| export function getStaticPaths() { | ||
| return { | ||
| paths: [ | ||
| { | ||
| params: { | ||
| slug: "fallback", | ||
| }, | ||
| }, | ||
| ], | ||
| fallback: false, | ||
| }; | ||
| } | ||
|
|
||
| export function getStaticProps() { | ||
| return { | ||
| props: { | ||
| message: "This is a static fallback page.", | ||
| }, | ||
| }; | ||
| } | ||
|
|
||
| export default function Page({ | ||
| message, | ||
| }: InferGetStaticPropsType<typeof getStaticProps>) { | ||
| return ( | ||
| <div> | ||
| <h1>Static Fallback Page</h1> | ||
| <p data-testid="message">{message}</p> | ||
| </div> | ||
| ); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| import type { InferGetStaticPropsType } from "next"; | ||
|
|
||
| export function getStaticProps() { | ||
| return { | ||
| props: { | ||
| message: "This is a static ssg page.", | ||
| }, | ||
| }; | ||
| } | ||
|
|
||
| export default function Page({ | ||
| message, | ||
| }: InferGetStaticPropsType<typeof getStaticProps>) { | ||
| return ( | ||
| <div> | ||
| <h1>Static Fallback Page</h1> | ||
| <p data-testid="message">{message}</p> | ||
| </div> | ||
| ); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| export default function Page() { | ||
| return ( | ||
| <div> | ||
| <h1>Static Fallback Page</h1> | ||
| <p data-testid="message">This is a fully static page.</p> | ||
| </div> | ||
| ); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| import type { InferGetStaticPropsType } from "next"; | ||
|
|
||
| export function getStaticPaths() { | ||
| return { | ||
| paths: [ | ||
| { | ||
| params: { | ||
| slug: "fallback", | ||
| }, | ||
| }, | ||
| ], | ||
| fallback: false, | ||
| }; | ||
| } | ||
|
|
||
| export function getStaticProps() { | ||
| return { | ||
| props: { | ||
| message: "This is a static fallback page.", | ||
| }, | ||
| }; | ||
| } | ||
|
|
||
| export default function Page({ | ||
| message, | ||
| }: InferGetStaticPropsType<typeof getStaticProps>) { | ||
| return ( | ||
| <div> | ||
| <h1>Static Fallback Page</h1> | ||
| <p data-testid="message">{message}</p> | ||
| </div> | ||
| ); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -221,43 +221,73 @@ async function processRequest( | |
| // https://github.com/dougmoscrop/serverless-http/issues/227 | ||
| delete req.body; | ||
|
|
||
| // Here we try to apply as much request metadata as possible | ||
| // We apply every metadata from `resolve-routes` https://github.com/vercel/next.js/blob/canary/packages/next/src/server/lib/router-utils/resolve-routes.ts | ||
| // and `router-server` https://github.com/vercel/next.js/blob/canary/packages/next/src/server/lib/router-server.ts | ||
| const initialURL = new URL(routingResult.initialURL); | ||
| let invokeStatus: number | undefined; | ||
| if (routingResult.internalEvent.rawPath === "/500") { | ||
| invokeStatus = 500; | ||
| } else if (routingResult.internalEvent.rawPath === "/404") { | ||
| invokeStatus = 404; | ||
| } | ||
| const requestMetadata = { | ||
| isNextDataReq: routingResult.internalEvent.query.__nextDataReq === "1", | ||
| initURL: routingResult.initialURL, | ||
| initQuery: convertToQuery(initialURL.search), | ||
| initProtocol: initialURL.protocol, | ||
| defaultLocale: NextConfig.i18n?.defaultLocale, | ||
| locale: routingResult.locale, | ||
| middlewareInvoke: false, | ||
| // By setting invokePath and invokeQuery we can bypass some of the routing logic in Next.js | ||
| invokePath: routingResult.internalEvent.rawPath, | ||
| invokeQuery: routingResult.internalEvent.query, | ||
| // invokeStatus is only used for error pages | ||
| invokeStatus, | ||
| }; | ||
|
|
||
| try { | ||
| //#override applyNextjsPrebundledReact | ||
| setNextjsPrebundledReact(routingResult.internalEvent.rawPath); | ||
| //#endOverride | ||
|
|
||
| // Here we try to apply as much request metadata as possible | ||
| // We apply every metadata from `resolve-routes` https://github.com/vercel/next.js/blob/canary/packages/next/src/server/lib/router-utils/resolve-routes.ts | ||
| // and `router-server` https://github.com/vercel/next.js/blob/canary/packages/next/src/server/lib/router-server.ts | ||
| const initialURL = new URL(routingResult.initialURL); | ||
| let invokeStatus: number | undefined; | ||
| if (routingResult.internalEvent.rawPath === "/500") { | ||
| invokeStatus = 500; | ||
| } else if (routingResult.internalEvent.rawPath === "/404") { | ||
| invokeStatus = 404; | ||
| } | ||
| const requestMetadata = { | ||
| isNextDataReq: routingResult.internalEvent.query.__nextDataReq === "1", | ||
| initURL: routingResult.initialURL, | ||
| initQuery: convertToQuery(initialURL.search), | ||
| initProtocol: initialURL.protocol, | ||
| defaultLocale: NextConfig.i18n?.defaultLocale, | ||
| locale: routingResult.locale, | ||
| middlewareInvoke: false, | ||
| // By setting invokePath and invokeQuery we can bypass some of the routing logic in Next.js | ||
| invokePath: routingResult.internalEvent.rawPath, | ||
| invokeQuery: routingResult.internalEvent.query, | ||
| // invokeStatus is only used for error pages | ||
| invokeStatus, | ||
| }; | ||
| // Next Server | ||
| await requestHandler(requestMetadata)(req, res); | ||
| } catch (e: any) { | ||
| // This might fail when using bundled next, importing won't do the trick either | ||
| if (e.constructor.name === "NoFallbackError") { | ||
| // Do we need to handle _not-found | ||
| // Ideally this should never get triggered and be intercepted by the routing handler | ||
| await tryRenderError("404", res, routingResult.internalEvent); | ||
| await handleNoFallbackError(req, res, routingResult, requestMetadata); | ||
| } else { | ||
| error("NextJS request failed.", e); | ||
| await tryRenderError("500", res, routingResult.internalEvent); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| async function handleNoFallbackError( | ||
| req: IncomingMessage, | ||
| res: OpenNextNodeResponse, | ||
| routingResult: RoutingResult, | ||
| metadata: Record<string, unknown>, | ||
| index = 1, | ||
| ) { | ||
| if (index >= 5) { | ||
| await tryRenderError("500", res, routingResult.internalEvent); | ||
| return; | ||
| } | ||
| if (index >= routingResult.resolvedRoutes.length) { | ||
| await tryRenderError("404", res, routingResult.internalEvent); | ||
| return; | ||
| } | ||
| try { | ||
| await requestHandler({ | ||
| ...routingResult, | ||
| invokeOutput: routingResult.resolvedRoutes[index].route, | ||
| ...metadata, | ||
| })(req, res); | ||
| } catch (e: any) { | ||
| if (e.constructor.name === "NoFallbackError") { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. could that cause issue with minification? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll need to test it. Not sure what we can use if we can't use that, i don't think we can use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It does work in cloudflare in preview There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If this is a concern, there is a |
||
| await handleNoFallbackError(req, res, routingResult, metadata, index + 1); | ||
| } else { | ||
| error("NextJS request failed.", e); | ||
| await tryRenderError("500", res, routingResult.internalEvent); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| import { expect, test } from "@playwright/test"; | ||
|
|
||
| test.describe("fallback", () => { | ||
| test("should work with fully static fallback", async ({ page }) => { | ||
| await page.goto("/fallback-intercepted/static/"); | ||
| const h1 = page.locator("h1"); | ||
| await expect(h1).toHaveText("Static Fallback Page"); | ||
| const p = page.getByTestId("message"); | ||
| await expect(p).toHaveText("This is a fully static page."); | ||
| }); | ||
|
|
||
| test("should work with static fallback", async ({ page }) => { | ||
| await page.goto("/fallback-intercepted/ssg/"); | ||
| const h1 = page.locator("h1"); | ||
| await expect(h1).toHaveText("Static Fallback Page"); | ||
| const p = page.getByTestId("message"); | ||
| await expect(p).toHaveText("This is a static ssg page."); | ||
| }); | ||
|
|
||
| test("should work with fallback intercepted by dynamic route", async ({ | ||
| page, | ||
| }) => { | ||
| await page.goto("/fallback-intercepted/something/"); | ||
| const h1 = page.locator("h1"); | ||
| await expect(h1).toHaveText("Dynamic Fallback Page"); | ||
| const p = page.getByTestId("message"); | ||
| await expect(p).toHaveText("This is a dynamic fallback page."); | ||
| }); | ||
|
|
||
| test("should work with fallback page pregenerated", async ({ page }) => { | ||
| await page.goto("/fallback-intercepted/fallback/"); | ||
| const h1 = page.locator("h1"); | ||
| await expect(h1).toHaveText("Static Fallback Page"); | ||
| const p = page.getByTestId("message"); | ||
| await expect(p).toHaveText("This is a static fallback page."); | ||
| }); | ||
|
|
||
| test("should 404 on page not pregenerated", async ({ request }) => { | ||
| const res = await request.get("/fallback/not-generated"); | ||
| expect(res.status()).toBe(404); | ||
| }); | ||
| }); |
Uh oh!
There was an error while loading. Please reload this page.