mirror of
https://github.com/cloudflare/vinext.git
synced 2026-05-09 08:25:34 +02:00
[PR #387] [MERGED] perf: cache compiled regex patterns in matchConfigPattern #532
Labels
No labels
enhancement
enhancement
good first issue
help wanted
nextjs-tracking
nextjs-tracking
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
starred/vinext#532
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
📋 Pull Request Information
Original PR: https://github.com/cloudflare/vinext/pull/387
Author: @james-elicx
Created: 3/9/2026
Status: ✅ Merged
Merged: 3/9/2026
Merged by: @james-elicx
Base:
main← Head:perf/cache-compiled-config-patterns📝 Commits (4)
f376632perf: cache compiled regex patterns in matchConfigPattern4651974fmtff5971aperf: cache compiled regexes in matchHeaders and checkSingleConditiond26652bfmt📊 Changes
2 files changed (+292 additions, -41 deletions)
View changed files
📝
packages/vinext/src/config/config-matchers.ts(+113 -41)📝
tests/shims.test.ts(+179 -0)📄 Description
Problem
Profiling identified per-request regex recompilation across three functions in `config-matchers.ts`:
1. `matchConfigPattern` — 2.4 s self-time, 53% of CPU (dominant bottleneck)
Every rule whose `source` contains `(`, `\`, or certain `:param` suffix patterns enters the regex branch. For each such rule, on every request: full tokeniser walk → `isSafeRegex` scan → `new RegExp()`. Locale-prefixed rules like `/:locale(en|es|fr|...)?/security` all contain `(`, so 88 rules × 3 passes per request.
2. `matchHeaders` — same pattern
`escapeHeaderSource(rule.source)` + `safeRegExp()` called for every header rule on every request. Both are pure functions of `rule.source`.
3. `checkSingleCondition` — same pattern
`safeRegExp(condition.value)` called on every request for every `has`/`missing` condition (header, cookie, query, host branches). `condition.value` comes from `next.config.js` and never changes.
All three functions receive data that is static — sourced from `next.config.js`, resolved once at Vite plugin init, and frozen into the bundle.
Fix
Three module-level `Map` caches, one per call site:
A private `_cachedConditionRegex(value)` helper centralises the lookup for the four `checkSingleCondition` branches. `null` is stored for patterns rejected by `safeRegExp` so `isSafeRegex` also runs at most once per pattern.
All changes are contained in `packages/vinext/src/config/config-matchers.ts`. No changes to generated entries or the middleware code-gen path.
Tests
Six new `describe` blocks in `tests/shims.test.ts` covering:
All 48 tests in the affected suites pass.
Follow-up
Issue #389 tracks moving compilation fully to build time (emitting regex literals into generated virtual modules), which would eliminate even the first-request compile cost per isolate. The module-level cache is sufficient for warm isolates — Workers reuses isolates aggressively for popular routes.
🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.