cloudflare / cloudflare/vinext
perf: emit pre-compiled regex literals for config patterns at build time
- Dominant language
- TypeScript
- Stars
- 8.8k
- Forks
- 406
- Avg merge
- 2d 6h
- Merged PRs (30d)
- 120
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:
```js
// 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:
```js
// 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
1. **Extract a `compileConfigPattern(pattern)` function** from `matchConfigPattern` that returns `{ re: RegExp; paramNames: string[] } | null` — the same logic currently cached by `_compiledPatternCache`, made callable from the code generators.
2. **`generateRscEntry` (App Router)** — call `compileConfigPattern` for each redirect/rewrite/header source at code-gen time; emit `__compiledRedirects`, `__compiledRewrites`, `__compiledHeaders` as JS regex literals alongside the existing `__configRedirects` JSON arrays.
3. **`generateServerEntry` (Pages Router)** — same: embed compiled forms into the `vinextConfig` export or as separate module-level constants.
4. **`matchRedirect` / `matchRewrite`** — add an overload (or a new `matchRedirectCompiled`) that accepts the pre-compiled array and skips to `re.exec` directly.
5. **Dev server (`index.ts` connect handler)** — the dev server calls `applyRedirects`/`applyRewrites` which call `matchRedirect`/`matchRewrite` with the live `nextConfig` arrays. Pre-compilation here would mean compiling once at plugin init time (after `resolveNextConfig`) and keeping a compiled copy alongside `nextConfig`. Worth doing for consistency, though the module-level cache in `config-matchers.ts` already covers this path.
6. **Emit regex source correctly** — `re.source` + `re.flags` can be used to reconstruct a regex literal as a string: `` `/${re.source}/${re.flags}` ``. Flags are `""` for all current patterns (no `i`, `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.get` per 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).
Contributor guide
Assessment
This issue has not been assessed yet.