perf(plugins): skip dynamic-request AST parse for static-import-only modules — 5000-route build −21%#2392
Merged
Conversation
9640731 to
c0161e7
Compare
c0161e7 to
d9bd689
Compare
Contributor
Performance benchmarksCompared 0 improved · 0 regressed · 6 within ±1.5%
View detailed results and traces 🟢 improvement · 🔴 regression · ⚫ change below 1.5% · paired base/head |
james-elicx
approved these changes
Jun 28, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Extracts a shared, documented pre-parse gate —
DYNAMIC_IMPORT_PRESCAN/mayContainDynamicImportinast-utils— and uses it as the "might this module contain a dynamicimport(...)?" check in:ignore-dynamic-requests(in-handler prescan), andextensionless-dynamic-import(Rolldowntransform.filter.code, which already used the same idea inline).Why
ignore-dynamic-requestspre-filtered with:The
importsubstring matches almost every ESM module (every staticimport x from "…"), so the plugin ranparseAst+ a full AST walk on essentially the entire module graph — then found nothing to do in the vast majority. It was the only one of vinext's AST-walking transform plugins without a tight code filter; the siblings already gate natively (require-context→/\brequire\b…\.context/,extensionless-dynamic-import→import(,typeof-window→typeof window,import-meta-url→import.meta…).Static imports never put a
((or a comment leading to one) right after theimportkeyword, so narrowing the import side to dynamic-call syntax skips static-import-only modules entirely. The require side stays a broad substring check (it must keep covering aliasing and comment-separatedrequire/* … */().Pulling the pattern into a named, documented helper makes the intent explicit — this is a deliberate, measured performance gate, not an incidental regex — and lets both plugins share one source of truth.
extensionless-dynamic-importadditionally gains correct handling of comment-separated dynamic imports (import/* … */("…")), which its old\bimport\s*\(filter missed.Measured
On the 5000-route stress fixture from #2388 (same machine, clean builds, median of 3):
CPU profile of the build:
jsonParseAst1536 → 510 ms,forEachAstChild1199 ms → off the chart, with matching GC relief — i.e. ~5000 redundant module parses removed. The saving is in AST parsing and is orthogonal to #2389 (the route-graph read cache); they stack.Correctness
\s*[(/]tolerates whitespace and block/line comments betweenimportand(, andrequirestays broad.dynamic-requests-build,extensionless-dynamic-import,import-meta-url: 100 tests.vp lintclean.