Avatar for the webpack user
webpack
enhanced-resolve
BlogDocsChangelog

Performance History

Latest Results

perf: short-circuit parseIdentifier FILE_REG_EXP by first-char charCode `parseIdentifier` runs on every resolve via `Resolver.parse`. The `/file:/i` scan it runs unconditionally is pure overhead for the overwhelming majority of inputs (relative paths, module specifiers, absolute paths) — none of which can start with "file:". Short-circuit with a cheap `charCodeAt(0)` check against 'f'/'F' before running the regex. Real file URLs always begin with "file:", so the guard preserves every case the downstream `fileURLToPath` actually handles; the behavior change is limited to pathological inputs like `"./file:x"` which never produced a valid file URL anyway (fileURLToPath would throw). No tests cover `file:` URL inputs and no other code path inside lib/ touches file:// identifiers. Tests (983) still green.
claude/search-perf-improvements-GXYWT
12 hours ago
perf: revert createInnerContext + findMatch changes — CodSpeed regressions CodSpeed flagged three significant regressions: cache-predicate -47.96% exports-field -37.93% main-files -23.74% The common path across all three is `doResolve` → `createInnerContext`, which I had refactored to take the outer context + stack + message directly (avoiding the intermediate 6-field object literal). On paper this saved one allocation per call; under cachegrind simulation the extra polymorphic `outer.X` reads on the user-supplied resolveContext appear to outweigh the saved allocation, regressing every benchmark that goes through the resolve pipeline. The exports-field regression also points at `findMatch`, where the state-tracking refactor (precomputed `bestMatchPatternIndex` threaded into `patternKeyCompare`, plus `bestMatchIsPattern`/`bestMatchIsSubpath` to skip the two trailing `bestMatch.endsWith("/")` / `bestMatch.includes("*")` scans) added per-iteration store overhead in a measurably hot loop. Revert both back to upstream shape: - lib/createInnerContext.js + lib/Resolver.js doResolve: original intermediate-object form. - lib/util/entrypoints.js patternKeyCompare + findMatch: original 2-arg signature, `lastIndexOf("*")` confirmation, trailing `endsWith`/`includes` scans, `key[len-1]` indexed access. Kept (each one is either skipping a real call or eliminating real work, with no per-call accounting risk): - lib/util/path.js isSubPath — charCode-based last-char check, skips `normalize()` on the common branch. - lib/util/entrypoints.js conditionalMapping — local `top` reference for the lookup-stack tip; saves two `lookup[lookup.length - 1]` walks per iteration. - lib/ExportsFieldPlugin.js / lib/ImportsFieldPlugin.js — hoist the duplicate `relativePath.slice(2)` / `path_.slice(2)`. - lib/ModulesUtils.js — nested `for` loops instead of `.paths.map(...).reduce(spread)`; removes intermediate sub-arrays. - lib/ParsePlugin.js — hoist `this.requestOptions` to a local. - benchmark/cases/is-sub-path/ + test/path.test.js — micro-bench and extended `isSubPath` coverage. Full jest suite still passes (983 tests), lint clean.
claude/search-perf-improvements-GXYWT
13 hours ago
perf: collapse doResolve intermediate context + keep prior hot-path wins Main changed `basename` to a centralized helper on Resolver, so the previous `getPaths.basename` char-scan folded away during rebase. This commit reapplies the remaining perf work on top of main and adds one more cleanup. Kept from the previous branch: - lib/util/path.js isSubPath: charCode last-char check + skip normalize() when the parent has no trailing separator. - lib/util/entrypoints.js findMatch: track isPattern/isSubpath during the scan (drops two trailing full-string rescans), use charCode for slash suffix, confirm single "*" with anchored `indexOf`, and thread a precomputed `bestMatchPatternIndex` into `patternKeyCompare` so it doesn't re-scan the same string on every call. - lib/ExportsFieldPlugin.js / lib/ImportsFieldPlugin.js: hoist the duplicate `relativePath.slice(2)` / `path_.slice(2)`. - lib/Resolver.js isDirectory: charCode comparison instead of `String#endsWith`. - lib/ModulesUtils.js: rebuild addrs with nested for-loops instead of `.paths.map(...).reduce(...)`. - lib/ParsePlugin.js: hoist `this.requestOptions` to a local. - lib/AliasUtils.js: hoist wildcard-match check and `innerRequest.slice(item.name.length)` out of `resolveWithAlias`, so array-alias entries don't recompute the same values per element. - test/path.test.js: extended isSubPath edge coverage. - benchmark/cases/is-sub-path/: new micro-bench. New in this commit: - lib/createInnerContext.js + lib/Resolver.js doResolve: the caller previously built an intermediate 6-field object just to pass it to `createInnerContext`, which returned a second fresh object with the same shape (two allocations per doResolve call). The new signature takes the outer context + stack + message directly, so only one allocation happens per call. doResolve is on the hottest path in the resolver. - lib/util/entrypoints.js conditionalMapping: keep a local reference to the stack-top tuple so the `top[2] = i + 1` writes below don't have to re-walk `lookup.length - 1` twice. 983 jest tests still pass; lint clean; wall-clock benchmarks healthy.
claude/search-perf-improvements-GXYWT
13 hours ago
perf: deeper hot-path cleanups surfaced by follow-up audit Four targeted changes along the resolve pipeline, each keeping exact existing behavior: - lib/ModulesUtils.js: rebuild the addrs list with nested `for` loops instead of `.paths.map(...).reduce((arr, sub) => (arr.push(...sub), arr), [])`. The old shape allocated one intermediate sub-array per ancestor path plus a reduce accumulator closure; the flat form allocates exactly one array and pushes directly. Runs every time `ModulesInHierarchicalDirectoriesPlugin` handles a bare request. - lib/ParsePlugin.js: hoist `this.requestOptions` to a local before the tap. Purely removes one `this`-chase per resolve step through the parse hook. Kept the three-way spread — V8's CopyDataProperties matches `Object.assign` under CodSpeed simulation, so a swap would be a wash. - lib/util/entrypoints.js patternKeyCompare / findMatch: pass the pre-computed `patternIndex` (from the outer loop) into `patternKeyCompare` instead of re-running `a.indexOf("*")` / `b.indexOf("*")` on every comparison. `findMatch` also carries `bestMatchPatternIndex` so the left-hand side skips the rescan too. For fields with N keys this removes ~N extra `indexOf` scans per match attempt. - lib/AliasUtils.js aliasResolveHandler: hoist the wildcard-prefix/suffix check plus the `innerRequest.slice( item.name.length)` out of the per-alias `resolveWithAlias` closure. Previously recomputed for every element of an array alias (`{ alias: [preferred, fallback, …] }`); now computed once per matched item. Full jest suite still green (992 tests). Wall-clock benchmarks move positively on the cases that touch these code paths (exports-field/imports-field/exports-patterns-many, alias-realistic, array-alias, prefer-relative, cache-predicate, resolve-to-context).
claude/search-perf-improvements-GXYWT
14 hours ago
test: refactor
fix-use-basename-helper-everywhere
14 hours ago

Latest Branches

CodSpeed Performance Gauge
-48%
perf: faster getPaths/basename/isSubPath + per-fs getPaths cache#535
13 hours ago
f86e4ee
claude/search-perf-improvements-GXYWT
CodSpeed Performance Gauge
+15%
Fix use basename helper everywhere#538
15 hours ago
a8b2f06
fix-use-basename-helper-everywhere
CodSpeed Performance Gauge
0%
15 hours ago
51a5ed5
claude/resolver-optional-context-JqGad
© 2026 CodSpeed Technology
Home Terms Privacy Docs