-
Notifications
You must be signed in to change notification settings - Fork 231
Description
Background
PR #387 fixed the dominant per-request CPU bottleneck in matchConfigPattern by adding a module-level Map cache so the tokeniser, isSafeRegex scan, and new RegExp() call run at most once per unique pattern string per isolate lifetime.
That fix is sufficient for long-lived isolates (Cloudflare Workers reuses isolates aggressively for popular routes). The remaining opportunity is to move pattern compilation entirely to build time so even the first request after a cold start pays zero compilation cost.
What this would look like
Redirect/rewrite source patterns are already baked into the bundle as JSON-serialized arrays:
// Current output in the generated virtual module
const __configRedirects = [
{ source: "/:locale(en|es|fr|id|ja|ko|pt-br|pt|ro|ta|tr|uk|zh-cn|zh-tw)?/security",
destination: "/security", permanent: true },
// ...87 more rules
];With build-time compilation, the code generator would also emit a parallel compiled array as raw JS regex literals, completely bypassing matchConfigPattern's regex branch at runtime:
// Proposed: emitted alongside __configRedirects
const __compiledRedirects = [
{ re: /^\/(en|es|fr|id|ja|ko|pt-br|pt|ro|ta|tr|uk|zh-cn|zh-tw)?\/security$/,
paramNames: ["locale"], destination: "/security", permanent: true },
// null for simple segment rules that don't use the regex branch
null,
// ...
];matchRedirect would accept an optional pre-compiled array and skip matchConfigPattern entirely when it's present.
What needs to change
-
Extract a
compileConfigPattern(pattern)function frommatchConfigPatternthat returns{ re: RegExp; paramNames: string[] } | null— the same logic currently cached by_compiledPatternCache, made callable from the code generators. -
generateRscEntry(App Router) — callcompileConfigPatternfor each redirect/rewrite/header source at code-gen time; emit__compiledRedirects,__compiledRewrites,__compiledHeadersas JS regex literals alongside the existing__configRedirectsJSON arrays. -
generateServerEntry(Pages Router) — same: embed compiled forms into thevinextConfigexport or as separate module-level constants. -
matchRedirect/matchRewrite— add an overload (or a newmatchRedirectCompiled) that accepts the pre-compiled array and skips tore.execdirectly. -
Dev server (
index.tsconnect handler) — the dev server callsapplyRedirects/applyRewriteswhich callmatchRedirect/matchRewritewith the livenextConfigarrays. Pre-compilation here would mean compiling once at plugin init time (afterresolveNextConfig) and keeping a compiled copy alongsidenextConfig. Worth doing for consistency, though the module-level cache inconfig-matchers.tsalready covers this path. -
Emit regex source correctly —
re.source+re.flagscan be used to reconstruct a regex literal as a string:`/${re.source}/${re.flags}`. Flags are""for all current patterns (noi,g, etc.), so the output is simply/${re.source}/.
Why the current cache is sufficient for most cases
The module-level Map cache (PR #387) means:
- Steady-state (warm isolate): O(1)
Map.getper rule — identical to reading a module constant - Cold start / first request: each unique pattern compiled once, amortised over all subsequent requests
Build-time emission only helps the first request after a cold start on low-traffic routes where isolates are recycled frequently. For high-traffic routes Workers keeps isolates alive long enough that the cache is always warm.
When to prioritise this
Worth revisiting if profiling shows measurable p99 cold-start latency from pattern compilation, or if the codebase moves toward a model where isolates are intentionally short-lived (e.g. per-request isolate mode).