Skip to content

perf: emit pre-compiled regex literals for config patterns at build time #389

@james-elicx

Description

@james-elicx

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

  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 correctlyre.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).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions