Skip to content

fix: track CommonJS build dependencies without require.cache children; run more suites on Bun/Deno - #21531

Merged
alexander-akait merged 7 commits into
mainfrom
claude/liar-skipped-tests-deno-bun-w127yo
Jul 27, 2026
Merged

fix: track CommonJS build dependencies without require.cache children; run more suites on Bun/Deno#21531
alexander-akait merged 7 commits into
mainfrom
claude/liar-skipped-tests-deno-bun-w127yo

Conversation

@alexander-akait

@alexander-akait alexander-akait commented Jul 27, 2026

Copy link
Copy Markdown
Member

Summary

Several test suites were silently skipped on Bun/Deno — and a few were effectively dead on Node too. This PR investigates each skip, fixes the ones that were webpack-side or over-broad, and keeps only the genuine engine limitations skipped (with accurate comments).

Includes one runtime-correctness fix: webpack's persistent-cache build-dependency capture relied on require.cache module.children, which Bun doesn't populate, so transitive CommonJS build dependencies went untracked and the cache could go stale. When module.children is empty on Bun, webpack now parses the source (acorn) for static require() specifiers. Node/Deno keep using the populated children (gated, zero cost).

The rest is test enablement, verified on Node, Bun 1.3.11 and Deno 2.9.4:

  • WebpackDevServer.longtest was dead on every runtime: the top-level require("puppeteer-core") throws Must use import to load ES Module under Jest (puppeteer-core@25 is ESM-only). Load it via dynamic import() in beforeAll (as the ProfilingPlugin Chrome test already does) — now runs wherever a launchable Chrome exists.
  • statsCases/dynamic-import and context-independence dropped their ESM-only @babel/core@8 dependency (JSX → React.createElement; a local identity loader) so they run on all runtimes.
  • LazyCompilationBackend.unittest replaced jest fake timers (need @sinonjs/fake-timers, uninstallable on Bun) with a runtime-agnostic timer harness.
  • BinaryMiddleware version-guard case now builds its payload by hand instead of via v8.serialize, so it runs on Bun/Deno.
  • internalSerializables per-entry require checks were skipped for an OOM that doesn't reproduce (loading all 143 costs ~5 MB under bun --smol); only the prettier-based generator check stays skipped on Bun.

What kind of change does this PR introduce?

fix (a persistent-cache correctness fix, plus substantial test-suite enablement across Node/Bun/Deno).

Did you add tests for your changes?

Yes — this PR is largely test changes. The build-dependency fix is covered by BuildDependencies.longtest (now un-skipped on Bun) and verified on Node by forcing the source-parse fallback path.

Does this PR introduce a breaking change?

No.

If relevant, what needs to be documented once your changes are merged or what have you already documented?

n/a

Use of AI

AI-assisted: I used Claude Code to investigate each skipped test, reproduce failures on Node/Bun/Deno, implement the fixes, and verify them (empirical measurements and targeted runs, not guesses). I reviewed every change before committing.


Generated by Claude Code


Note

Medium Risk
The production change touches persistent-cache build-dependency resolution (best-effort static require parsing on Bun only); incorrect or incomplete dependency edges could affect cache invalidation on Bun, while Node/Deno behavior is unchanged.

Overview
Fixes persistent-cache build-dependency capture on Bun by adding a Bun-only fallback when require.cache entries have an empty module.children list: webpack reads the file and uses acorn to collect static require("literal") specifiers (Node/Deno keep the existing children walk with no extra cost).

The rest of the PR re-enables or hardens tests that were skipped or broken on alternate runtimes:

  • BuildDependencies.longtest runs on Bun again (no it.skip); LazyCompilationBackend.unittest uses a custom timer harness instead of Jest fake timers.
  • WebpackDevServer.longtest and ProfilingPlugin.unittest load puppeteer-core via dynamic import() and use visible it.skip when Chrome/Node prerequisites are missing.
  • BinaryMiddleware V8 version test builds a minimal 0xff + version payload so it works across Node/Deno/Bun.
  • Stats cases dynamic-import and context-independence drop babel-loader / @babel/core@8 in favor of React.createElement and a local identity-loader; related snapshots and filters are updated.
  • internalSerializables per-entry load tests run on Bun; only the prettier generator check stays Node-only.

Reviewed by Cursor Bugbot for commit f7b974e. Bugbot is set up for automated code reviews on this repo. Configure here.

… visibly

The BinaryMiddleware "newer V8 format version" case built its fixture with
v8.serialize, whose wire format differs on Bun, so it silently returned. The
guard it covers is pure byte logic, so build the payload by hand and derive the
version from the runtime's own v8.serialize(null)[1] — it now runs on Node,
Deno and Bun instead of being skipped.

For cases that need a capability a runtime lacks (a launchable Chrome for the
ProfilingPlugin/WebpackDevServer suites, require(esm) for LoaderRunner),
convert the silent early-return into it.skip so they report as skipped rather
than falsely passing.
…platforms

Both cases were filtered out on Deno, Bun and Node < 24.9 because they loaded
ESM-only @babel/core@8, which cannot load under Jest's vm there. Drop the babel
dependency so the cases run everywhere:

- dynamic-import: replace JSX (@babel/preset-react) with React.createElement so
  webpack parses the sources natively; the case still exercises vendor splitting
  and the pages/[request] dynamic-import chunk name.
- context-independence: replace the empty-options babel-loader with a local
  pass-through identity-loader, keeping a loader-processed module in the graph.

Remove the now-obsolete test.filter.js gates and babel.config.js, and refresh
the snapshots (only deterministic chunk ids and react module grouping change).
…e unavailable

Build-dependency capture reconstructs a CJS file's dependencies from its
require.cache children. Bun doesn't populate module.children, so the walk
silently misses transitive requires made from inside node_modules — build
dependencies aren't tracked and the persistent cache can go stale.

On Bun only, when module.children is empty, parse the source with acorn and
enqueue its static require() specifiers (best effort, unresolved ones
tolerated). The fallback is gated behind a runtime check so Node and Deno,
which populate module.children, pay nothing for it.

This un-skips the BuildDependencies suite on Bun.
The idle-timer cases used jest.useFakeTimers/advanceTimersByTime, which need
@sinonjs/fake-timers — uninstallable on Bun, so they were skipped there.
Replace them with a runtime-agnostic harness that patches global
setTimeout/clearTimeout, so the cases run on Node, Deno and Bun alike.
…runs

puppeteer-core is ESM-only (v25+); the top-level require() throws
"Must use import to load ES Module" under Jest's vm, so puppeteer was always
undefined and the whole WebpackDevServer suite silently self-skipped on every
runtime (Node included), despite its existing jest-on-Bun workarounds.

Load puppeteer-core with dynamic import() in beforeAll (as ProfilingPlugin's
Chrome test already does) and gate itChrome on Node support + webpack-dev-server
instead of the failed require. The three cases now actually run wherever a
launchable Chrome is present — Node and Bun alike (verified on Node); they still
self-skip cleanly when no browser can launch.
The 143 per-entry "should load" checks were skipped on Bun for an alleged
OOM that doesn't reproduce: loading all 143 serializables costs only a few MB
(~5MB heap under bun --smol) and the checks all pass under jest-on-Bun. Only
the generator check genuinely fails on Bun (prettier trips Bun's module
builtin), so keep just that one skipped and correct the comment.
@changeset-bot

changeset-bot Bot commented Jul 27, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: f7b974e

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
webpack Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions

github-actions Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

This PR is packaged and the instant preview is available (6926396).

Install it locally:

  • npm
npm i -D webpack@https://pkg.pr.new/webpack@6926396
  • yarn
yarn add -D webpack@https://pkg.pr.new/webpack@6926396
  • pnpm
pnpm add -D webpack@https://pkg.pr.new/webpack@6926396

@codecov

codecov Bot commented Jul 27, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 93.80%. Comparing base (bcb1abd) to head (f7b974e).

Additional details and impacted files
@@           Coverage Diff           @@
##             main   #21531   +/-   ##
=======================================
  Coverage   93.79%   93.80%           
=======================================
  Files         620      620           
  Lines       73327    73329    +2     
  Branches    21202    21202           
=======================================
+ Hits        68776    68784    +8     
+ Misses       4551     4545    -6     
Flag Coverage Δ
css-parsing 25.59% <100.00%> (+<0.01%) ⬆️
html5lib 27.02% <100.00%> (+<0.01%) ⬆️
integration 89.61% <100.00%> (+<0.01%) ⬆️
test262 43.34% <100.00%> (+<0.01%) ⬆️
unit 47.22% <100.00%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

- eslint: allow dynamic import() in WebpackDevServer.longtest (ecmaVersion 2020),
  fixing the "Unexpected token import" parse error.
- prettier: reformat ProfilingPlugin.unittest with Prettier 3 (the itChrome call).
- lazyCompilationBackend test: iterate the timer Set directly (no useless spread).
- FileSystemInfo: mark the Bun-only require()-parsing fallback with istanbul
  ignore — it's exercised by BuildDependencies.longtest on the Bun CI job, which
  doesn't upload coverage, so the Node coverage run can't reach it.
@codspeed-hq

codspeed-hq Bot commented Jul 27, 2026

Copy link
Copy Markdown

Merging this PR will improve performance by ×2.4

⚠️ Different runtime environments detected

Some benchmarks with significant performance changes were compared across different runtime environments,
which may affect the accuracy of the results.

Open the report in CodSpeed to investigate

⚡ 1 improved benchmark
✅ 215 untouched benchmarks

Performance Changes

Mode Benchmark BASE HEAD Efficiency
Memory benchmark "devtool-eval", scenario '{"name":"mode-development-rebuild","mode":"development","watch":true}' 3.1 MB 1.3 MB ×2.4

Tip

Curious why this is faster? Comment @codspeedbot explain why this is faster on this PR, or directly use the CodSpeed MCP with your agent.


Comparing claude/liar-skipped-tests-deno-bun-w127yo (f7b974e) with main (bcb1abd)

Open in CodSpeed

@github-actions

Copy link
Copy Markdown
Contributor

Types Coverage

Coverage after merging claude/liar-skipped-tests-deno-bun-w127yo into main will be
99.32%
Coverage Report
FileStmtsBranchesFuncsLinesUncovered Lines
bin
   webpack.js98.82%100%100%98.82%103
examples
   build-common.js100%100%100%100%
   buildAll.js100%100%100%100%
   examples.js100%100%100%100%
   template-common.js98.21%100%100%98.21%72
examples/custom-javascript-parser
   test.filter.js100%100%100%100%
examples/custom-javascript-parser/internals
   acorn-parse.js100%100%100%100%
   meriyah-parse.js100%100%100%100%
   oxc-parse.js100%100%100%100%
examples/markdown
   webpack.config.mjs100%100%100%100%
examples/module-federation
   test.filter.js100%100%100%100%
examples/reexport-components
   test.filter.js100%100%100%100%
examples/typescript
   test.filter.js100%100%100%100%
examples/typescript-non-erasable
   test.filter.js50%100%100%50%5
examples/virtual-modules
   test.filter.js100%100%100%100%
examples/wasm-bindgen-esm
   test.filter.js100%100%100%100%
examples/wasm-complex
   test.filter.js100%100%100%100%
examples/wasm-emscripten
   test.filter.js100%100%100%100%
examples/wasm-simple
   test.filter.js100%100%100%100%
examples/wasm-simple-source-phase
   test.filter.js100%100%100%100%
lib
   APIPlugin.js100%100%100%100%
   AsyncDependenciesBlock.js100%100%100%100%
   AutomaticPrefetchPlugin.js100%100%100%100%
   BannerPlugin.js100%100%100%100%
   Cache.js98.21%100%100%98.21%101
   CacheFacade.js100%100%100%100%
   Chunk.js99.72%100%100%99.72%39
   ChunkGraph.js100%100%100%100%
   ChunkGroup.js100%100%100%100%
   ChunkTemplate.js100%100%100%100%
   CircularModulesPlugin.js98.81%100%100%98.81%136
   CleanPlugin.js99.12%100%100%99.12%207, 227
   CodeGenerationResults.js100%100%100%100%
   CompatibilityPlugin.js100%100%100%100%
   Compilation.js98.42%100%100%98.42%1639, 1958, 1965, 1973, 1995, 1998, 2937, 3416–3417, 3449, 4153, 4183, 4236–4237, 4241, 4246, 4262–4263, 4277–4278, 4283–4284, 4764, 4790, 527, 532, 5598, 5630, 5647, 5663, 5679, 5694, 5719–5720, 5722, 6052, 6057, 6063, 6066, 6073, 6085, 6087, 6091, 6107, 6122, 6154, 6208, 6232, 6347, 778–779
   Compiler.js99.56%100%100%99.56%1147–1148, 1156
   ConcatenationScope.js98.65%100%100%98.65%195
   ConditionalInitFragment.js100%100%100%100%
   ConstPlugin.js100%100%100%100%
   ContextExclusionPlugin.js100%100%100%100%
   ContextModule.js99.88%100%100%99.88%1461
   ContextModuleFactory.js97.20%100%100%97.20%266, 435, 456, 461, 501, 512, 514, 518, 527–528
   ContextReplacementPlugin.js100%100%100%100%
   DefinePlugin.js99.08%100%100%99.08%1074, 176–177, 193, 212, 286
   DependenciesBlock.js100%100%100%100%
   Dependency.js98.51%100%100%98.51%479, 525
   DependencyTemplate.js100%100%100%100%
   DependencyTemplates.js100%100%100%100%
   DotenvPlugin.js98.41%100%100%98.41%378, 391–392
   DynamicEntryPlugin.js100%100%100%100%
   EntryOptionPlugin.js100%100%100%100%
   EntryPlugin.js100%100%100%100%
   Entrypoint.js100%100%100%100%
   EnvironmentPlugin.js97.14%100%100%97.14%49
   ErrorHelpers.js100%100%100%100%
   EvalDevToolModulePlugin.js100%100%100%100%
   EvalSourceMapDevToolPlugin.js100%100%100%100%
   ExportsInfo.js100%100%100%100%
   ExportsInfoApiPlugin.js100%100%100%100%
   ExternalModule.js98.65%100%100%98.65%1196, 1199, 514–518, 520, 666
   ExternalModuleFactoryPlugin.js100%100%100%100%
   ExternalsPlugin.js100%100%100%100%
   FileSystemInfo.js99.16%100%100%99.16%1267, 1269–1274, 1281, 1284, 182, 2502–2503, 2506, 2517, 2528, 2539, 280, 3976, 3991, 4015
   FlagAllModulesAsUsedPlugin.js100%100%100%100%
   FlagDependencyExportsPlugin.js98.21%100%100%98.21%448, 457, 460, 464, 476
   FlagDependencyUsagePlugin.js100%100%100%100%
   FlagEntryExportAsUsedPlugin.js100%100%100%100%
   Generator.js100%100%100%100%
   HotModuleReplacementPlugin.js100%100%100%100%
   HotUpdateChunk.js100%100%100%100%
   IgnorePlugin.js100%100%100%100%
   IgnoreWarningsPlugin.js100%100%100%100%
   InitFragment.js100%100%100%100%
   JavascriptMetaInfoPlugin.js100%100%100%100%
   LazyBarrel.js100%100%100%100%
   LibraryTemplatePlugin.js100%100%100%100%
   LoaderOptionsPlugin.js100%100%100%100%
   LoaderTargetPlugin.js100%100%100%100%
   MainTemplate.js100%100%100%100%
   ManifestPlugin.js100%100%100%100%
   Module.js98.50%100%100%98.50%1288, 1293, 1353, 1367, 1429, 1438
   ModuleFactory.js100%100%100%100%
   ModuleFilenameHelpers.js98.85%100%100%98.85%106, 108
   ModuleGraph.js99.73%100%100%99.73%1005
   ModuleGraphConnection.js100%100%100%100%
   ModuleInfoHeaderPlugin.js100%100%100%100%
   ModuleNotFoundError.js100%100%100%100%
   ModuleProfile.js100%100%100%100%
   ModuleSourceTypeConstants.js100%100%100%100%
   ModuleTemplate.js100%100%100%100%
   ModuleTypeConstants.js100%100%100%100%
   MultiCompiler.js99.70%100%100%99.70%663
   MultiStats.js100%100%100%100%
   MultiWatching.js100%100%100%100%
   NoEmitOnErrorsPlugin.js100%100%100%100%
   NodeStuffPlugin.js100%100%100%100%
   NormalModule.js97.98%100%100%97.98%1014, 1031, 1279, 1313, 1329, 1776, 2073, 2078–2088, 34, 994, 997
   NormalModuleFactory.js98.72%100%100%98.72%1117, 1385, 1396, 1406, 1457–1459, 1466, 520, 532
   NormalModuleReplacementPlugin.js100%100%100%100%
   NullFactory.js100%100%100%100%
   OptimizationStages.js100%100%100%100%
   OptionsApply.js100%100%100%100%
   Parser.js100%100%100%100%
   PlatformPlugin.js100%100%100%100%
   PrefetchPlugin.js100%100%100%100%
   ProgressPlugin.js99.80%100%100%99.80%691
   ProvidePlugin.js100%100%100%100%
   RawModule.js100%100%100%100%
   RecordIdsPlugin.js100%100%100%100%
   RequestShortener.js100%100%100%100%
   ResolverFactory.js100%100%100%100%
   RuntimeGlobals.js100%100%100%100%
   RuntimeModule.js100%100%100%100%
   RuntimePlugin.js100%100%100%100%
   RuntimeTemplate.js100%100%100%100%
   SelfModuleFactory.js100%100%100%100%
   SingleEntryPlugin.js100%100%100%100%
   SourceMapDevToolModuleOptionsPlugin.js100%100%100%100%
   SourceMapDevToolPlugin.js98.62%100%100%98.62%220, 224, 226, 419, 430, 889
   Stats.js100%100%100%100%
   Template.js100%100%100%100%
   TemplatedPathPlugin.js99.43%100%100%99.43%308–309
   UseStrictPlugin.js100%100%100%100%
   WarnCaseSensitiveModulesPlugin.js100%100%100%100%
   WarnDeprecatedOptionPlugin.js100%100%100%100%
   WarnNoModeSetPlugin.js100%100%100%100%
   WatchIgnorePlugin.js100%100%100%100%
   Watching.js100%100%100%100%
   WebpackError.js100%100%100%100%
   WebpackIsIncludedPlugin.js100%100%100%100%
   WebpackOptionsApply.js100%100%100%100%
   WebpackOptionsDefaulter.js100%100%100%100%
   buildChunkGraph.js99.87%100%100%99.87%371
   cli.js98.63%100%100%98.63%10, 119, 549, 581, 631, 905
   index.js99.72%100%100%99.72%184
   validateSchema.js94.67%100%100%94.67%100, 87, 89, 98
   webpack.js97.10%100%100%97.10%10, 263, 285, 287
lib/asset
   AssetBytesGenerator.js100%100%100%100%
   AssetBytesParser.js100%100%100%100%
   AssetGenerator.js100%100%100%100%
   AssetModule.js100%100%100%100%
   AssetModulesPlugin.js97.95%100%100%97.95%295, 319, 322, 42, 452, 47
   AssetParser.js100%100%100%100%
   AssetSourceGenerator.js100%100%100%100%
   AssetSourceParser.js100%100%100%100%
   RawDataUrlModule.js100%100%100%100%
   WebManifestGenerator.js100%100%100%100%
   WebManifestParser.js100%100%100%100%
lib/async-modules
   AsyncModuleHelpers.js100%100%100%100%
   AwaitDependenciesInitFragment.js100%100%100%100%
   InferAsyncModulesPlugin.js100%100%100%100%
   isGeneratorLowered.js100%100%100%100%
lib/bun
   BunTargetPlugin.js100%100%100%100%
lib/cache
   AddBuildDependenciesPlugin.js100%100%100%100%
   AddManagedPathsPlugin.js100%100%100%100%
   IdleFileCachePlugin.js97.92%100%100%97.92%75, 87, 95
   MemoryCachePlugin.js95.83%100%100%95.83%33
   MemoryWithGcCachePlugin.js93.15%100%100%93.15%107, 114–115, 123, 90
   PackFileCacheStrategy.js96.41%100%100%96.41%1257, 1357, 1361, 1423, 628, 647, 657–659, 661, 677–678, 683, 686, 688, 693, 698, 723, 729, 763, 769, 775, 780, 791, 800, 805–806, 808, 825, 831–832, 834
   ResolverCachePlugin.js100%100%100%100%
   getLazyHashedEtag.js100%100%100%100%
   mergeEtags.js100%100%100%100%
lib/config
   browserslistTargetHandler.js100%100%100%100%

@alexander-akait
alexander-akait merged commit 6926396 into main Jul 27, 2026
63 checks passed
@alexander-akait
alexander-akait deleted the claude/liar-skipped-tests-deno-bun-w127yo branch July 27, 2026 11:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant