fix(vercel): handle ISR requests with passQuery: true#3851
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Caution Review failedThe pull request is closed. 📝 WalkthroughWalkthroughAdds a named ISR URL query parameter and a helper to extract it, updates Vercel runtime files to use that helper and call getRouteRules before rewriting ISR requests, adjusts ISR route generation and tests to use the new parameter, and makes getRouteRules publicly exported (re-exported from src/runtime/app.ts). Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes
Possibly related PRs
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
commit: |
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (4)
src/presets/vercel/runtime/vercel.node.ts (1)
5-22: Node runtime ISR rewrite correctly centralized viaisrRouteRewriteThe node handler’s ISR handling now mirrors the web runtime and cleanly delegates URL extraction to
isrRouteRewrite, which is a good consolidation of logic. Thereq.url!assertion and use of thex-now-route-matchesheader align with prior behavior, so no functional regressions are apparent here.If you want to tighten the types a bit, you could consider passing
nullexplicitly when the header is missing instead of casting tostring, but that’s purely cosmetic:- const isrURL = isrRouteRewrite( - req.url!, - req.headers["x-now-route-matches"] as string - ); + const isrURL = isrRouteRewrite( + req.url!, + (req.headers["x-now-route-matches"] as string | undefined) ?? null + );src/presets/vercel/utils.ts (2)
13-239: ISR route rule generation looks consistent; double‑check behavior for non‑/**ISR routesUsing
ISR_URL_PARAMfor both the named capture and the dest query is a nice improvement and keeps the runtime and build config in sync. The transformations
- root
/rule:
src: (?<__isr_route>/)dest: /index-isr?__isr_route=$__isr_route- catch‑all ISR rules:
srcviakey.replace(/^(.*)\/\*\*/, '(?<__isr_route>$1/.*)')destvia...?__isr_route=$__isr_routeare all coherent and match the expectations encoded in the updated tests.
One corner case worth verifying: if someone defines
routeRules["/foo"]!.isrwithout a/**suffix,srcwill stay as"/foo"(no named capture), butdestwill still reference$__isr_route. That relies on Vercel’s routing engine to either (a) provide a__isr_routeentry inx-now-route-matcheseven without an explicit named group, or (b) treat$__isr_routeindestin a forgiving way. If this scenario is not supported or guaranteed, it may be safer to either:
- require
/**for ISR rules that depend on__isr_route, or- synthesize a named group for non‑
/**ISR rules as well.Please confirm this behavior against the Vercel Build Output API docs / runtime behavior for non‑
/**ISR routes that still use$__isr_routeindest.
482-487: Ensuring__isr_routeis inallowQueryis good; consider avoiding in‑place mutationThe
allowQueryaugmentation correctly guarantees that__isr_routeis forwarded when a user has explicitly setallowQuery, which is important for ISR revalidation flows.Right now this mutates the
allowQueryarray in place, which is likely the same array referenced by the original route rule config. That’s probably fine given Nitro already mutates route rules elsewhere, but if you want to keep user config more obviously immutable, you could reassign a cloned array instead:- if ( - prerenderConfig.allowQuery && - !prerenderConfig.allowQuery.includes(ISR_URL_PARAM) - ) { - prerenderConfig.allowQuery.push(ISR_URL_PARAM); - } + if (prerenderConfig.allowQuery) { + if (!prerenderConfig.allowQuery.includes(ISR_URL_PARAM)) { + prerenderConfig.allowQuery = [ + ...prerenderConfig.allowQuery, + ISR_URL_PARAM, + ]; + } + }src/presets/vercel/runtime/isr.ts (1)
1-23: Centralized ISR URL extraction is solid; avoid trailing?when no extra query remainsThe
ISR_URL_PARAMconstant andisrRouteRewritehelper nicely centralize ISR URL extraction logic for both runtimes, and the query‑string fallback correctly strips__isr_routewhile preserving other query params.There is a small edge case in the query‑based branch: if
__isr_routeis the only query parameter,params.toString()returns an empty string, so the function returns a URL like"/some/path?"instead of"/some/path". Functionally it’s usually fine, but it’s easy to avoid and keeps URLs cleaner:- const params = new URLSearchParams(reqUrl.slice(queryIndex + 1)); - const isrURL = params.get(ISR_URL_PARAM); - if (isrURL) { - params.delete(ISR_URL_PARAM); - return decodeURIComponent(isrURL) + "?" + params.toString(); - } + const params = new URLSearchParams(reqUrl.slice(queryIndex + 1)); + const isrURL = params.get(ISR_URL_PARAM); + if (isrURL) { + params.delete(ISR_URL_PARAM); + const restQuery = params.toString(); + const decoded = decodeURIComponent(isrURL); + return restQuery ? `${decoded}?${restQuery}` : decoded; + }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
src/presets/vercel/runtime/isr.ts(1 hunks)src/presets/vercel/runtime/vercel.node.ts(2 hunks)src/presets/vercel/runtime/vercel.web.ts(2 hunks)src/presets/vercel/utils.ts(4 hunks)test/presets/vercel.test.ts(3 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
src/presets/vercel/utils.ts (1)
src/presets/vercel/runtime/isr.ts (1)
ISR_URL_PARAM(1-1)
src/presets/vercel/runtime/vercel.node.ts (1)
src/presets/vercel/runtime/isr.ts (1)
isrRouteRewrite(3-23)
src/presets/vercel/runtime/vercel.web.ts (1)
src/presets/vercel/runtime/isr.ts (1)
isrRouteRewrite(3-23)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: publish-pkg-pr-new
🔇 Additional comments (2)
test/presets/vercel.test.ts (1)
121-155: Tests correctly capture new__isr_routebehavior and prerenderallowQueryThe updated snapshots for ISR routes and prerender configs line up with the new
ISR_URL_PARAM("__isr_route") and the helper logic inisrRouteRewrite. In particular:
- Route rules now consistently use
(?<__isr_route>...)insrcand?__isr_route=$__isr_routeindest.- The prerender-config expectation includes
"__isr_route"inallowQuery, validating the newwritePrerenderConfigbehavior.This provides good coverage for the new ISR URL handling across both routing and prerender metadata.
Also applies to: 366-369
src/presets/vercel/runtime/vercel.web.ts (1)
5-21: Web runtime ISR rewrite correctly delegates to shared helperThe web runtime now cleanly delegates ISR URL extraction to
isrRouteRewrite, and rewrites the request by constructing a newRequestwithnew URL(isrURL, req.url).href. This:
- Keeps ISR routing behavior in sync with the node runtime.
- Allows the helper to handle both
x-now-route-matches–based extraction and the query‑string fallback in one place.- Ensures that, when the helper includes remaining query params in the returned URL, the rewritten
Requestpreserves them.The change looks sound and improves maintainability of ISR handling across runtimes.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
src/presets/vercel/runtime/vercel.node.ts (1)
15-24: ISR rewrite logic is sound; consider a small readability tweakThe flow—derive an ISR target via
isrRouteRewrite, checkgetRouteRules("", pathname)forrouteRules?.isr, and only then rewritereq.url—is correct and safely no‑ops when there is no applicable ISR rule.For readability and to avoid magic tuple indices, you could optionally destructure the result:
- const isrURL = isrRouteRewrite( - req.url!, - req.headers["x-now-route-matches"] as string - ); - if (isrURL) { - const { routeRules } = getRouteRules("", isrURL[0]); - if (routeRules?.isr) { - req.url = isrURL[0] + (isrURL[1] ? `?${isrURL[1]}` : ""); - } - } + const isrURL = isrRouteRewrite( + req.url!, + req.headers["x-now-route-matches"] as string + ); + if (isrURL) { + const [pathname, search] = isrURL; + const { routeRules } = getRouteRules("", pathname); + if (routeRules?.isr) { + req.url = pathname + (search ? `?${search}` : ""); + } + }Behavior stays identical; the block becomes a bit clearer to future readers.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
src/presets/vercel/runtime/isr.ts(1 hunks)src/presets/vercel/runtime/vercel.node.ts(2 hunks)src/presets/vercel/runtime/vercel.web.ts(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- src/presets/vercel/runtime/vercel.web.ts
- src/presets/vercel/runtime/isr.ts
🧰 Additional context used
🧬 Code graph analysis (1)
src/presets/vercel/runtime/vercel.node.ts (2)
src/presets/vercel/runtime/isr.ts (1)
isrRouteRewrite(3-23)src/runtime/internal/app.ts (1)
getRouteRules(205-255)
🔇 Additional comments (1)
src/presets/vercel/runtime/vercel.node.ts (1)
4-5: Import wiring for ISR route rules looks correctBringing in
getRouteRulesfromnitro/appandisrRouteRewritefrom the shared ISR helper keeps this adapter thin and aligned with the new centralized ISR logic. No issues here.
Similar fix of #3539 for v3
TODO: