Skip to content

fix: inherit sideEffects past type-only nested package.json - #21686

Open
xiaoxiaojx wants to merge 1 commit into
mainfrom
fix/nested-package-json-sideEffects
Open

fix: inherit sideEffects past type-only nested package.json#21686
xiaoxiaojx wants to merge 1 commit into
mainfrom
fix/nested-package-json-sideEffects

Conversation

@xiaoxiaojx

@xiaoxiaojx xiaoxiaojx commented Aug 11, 2026

Copy link
Copy Markdown
Member

Summary

Nested type-only package.json files (e.g. {"type":"module"}) were shadowing a parent sideEffects flag, so unused modules from packages like entities stayed in the graph. Inherit the ancestor flag within compiler.context, rebase globs to the declaring package root, and stop at named packages / node_modules directory boundaries.

What kind of change does this PR introduce?

fix

Did you add tests for your changes?

Yes — test/configCases/side-effects/nested-package-json-sideEffects and nested-package-json-sideEffects-outside-context.

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

Yes — AI assisted implementation, tests, and PR drafting; changes were reviewed and verified locally.

Made with Cursor

Summary by CodeRabbit

  • Bug Fixes

    • Improved handling of inherited sideEffects settings in nested packages.
    • More accurately removes unused modules while retaining modules that may have side effects.
    • Preserved deferred-import re-export modules during optimization.
    • Improved behavior across package boundaries and configured build contexts.
    • Improved reliability when resolving package metadata from nested directories.
  • Chores

    • Added a patch release entry documenting the updated side-effects behavior.

@changeset-bot

changeset-bot Bot commented Aug 11, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 4ba77ec

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 added area: parser JavaScript parsing and dependency analysis (lib/javascript, lib/dependencies) area: optimization Tree-shaking, splitChunks, concatenation, ids (lib/optimize, lib/ids) labels Aug 11, 2026
@xiaoxiaojx
xiaoxiaojx marked this pull request as draft August 11, 2026 17:25
@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

.coderabbit.yaml has unrecognized properties

CodeRabbit is using all valid settings from your configuration. Unrecognized properties (listed below) have been ignored and may indicate typos or deprecated fields that can be removed.

⚠️ Parsing warnings (1)
Validation error: Unrecognized key: "tools"
⚙️ Configuration instructions
  • Please see the configuration documentation for more information.
  • You can also validate your configuration using the online YAML validator.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json
📝 Walkthrough

Walkthrough

Changes

The SideEffectsFlagPlugin now inherits sideEffects metadata from ancestor package.json files within the compiler context. It caches resolved metadata per module creation and applies it during module creation. New configuration cases cover nested packages and context boundaries.

Suggested reviewers: alexander-akait, hai-x

Side-effects metadata inheritance

Layer / File(s) Summary
Package metadata resolution
lib/optimize/SideEffectsFlagPlugin.js, lib/util/identifier.js, lib/dependencies/ImportMetaGlobHelpers.js
The plugin resolves inherited sideEffects values while traversing ancestor package metadata. Shared request normalization is exported and reused.
Module creation integration
lib/optimize/SideEffectsFlagPlugin.js
afterResolve caches resolved metadata, and module creation applies the cached value.
Nested package regression coverage
test/configCases/side-effects/nested-package-json-sideEffects/*, test/configCases/side-effects/nested-package-json-sideEffects-outside-context/*, .changeset/047-inherit-nested-sideEffects.md
Configuration cases verify nested declarations, glob rebasing, local package metadata, node_modules boundaries, and paths outside the compiler context. The changeset records the patch release.
🚥 Pre-merge checks | ✅ 3 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title uses valid Conventional Commit syntax and describes the sideEffects inheritance fix, but the branch prefix is not provided for verification. Provide the branch name or branch prefix to verify that fix matches it.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

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

Install it locally:

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

@xiaoxiaojx
xiaoxiaojx requested a review from hai-x August 11, 2026 17:27
@codecov

codecov Bot commented Aug 11, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 89.33333% with 8 lines in your changes missing coverage. Please review.
✅ Project coverage is 94.53%. Comparing base (638ce71) to head (4ba77ec).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
lib/optimize/SideEffectsFlagPlugin.js 89.04% 8 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #21686      +/-   ##
==========================================
- Coverage   94.53%   94.53%   -0.01%     
==========================================
  Files         631      631              
  Lines       82362    82420      +58     
  Branches    24405    24424      +19     
==========================================
+ Hits        77861    77912      +51     
- Misses       4501     4508       +7     
Flag Coverage Δ
css-parsing 26.58% <25.33%> (-0.01%) ⬇️
html5lib 30.98% <25.33%> (-0.01%) ⬇️
integration 88.26% <89.33%> (-0.01%) ⬇️
test262 51.69% <41.33%> (-0.02%) ⬇️
unit 54.49% <41.33%> (-0.02%) ⬇️

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.

@github-actions

github-actions Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Generated code size

Comparing 4ba77ec merged into 638ce71 against 638ce71. Merging this PR will change the code webpack generates.

Changed New Deleted Unchanged Raw change
Cases 2 2 0 1569 🔴 ↑ +13.43 KiB
Assets 2 2 0 6155 🔴 ↑ +13.43 KiB
Runtimes 0 2 0 1933
4 asset(s) changed size
Asset Before After Change Gzip (9) Brotli (11) Zstd (19)
side-effects/nested-package-json-sideEffects bundle0.js 8.03 KiB +8.03 KiB (new) new new new
side-effects/nested-package-json-sideEffects-outside-context bundle0.js 5.40 KiB +5.40 KiB (new) new new new
🟢 ↓ externals/externals-array 0/bundle0.js 414 B 414 B 0 B (—) -0.34%
🟢 ↓ externals/node-require bundle0.js 502 B 502 B 0 B (—) -0.29%
2 runtime(s) changed which runtime modules they carry
Runtime Modules Added Removed
side-effects/nested-package-json-sideEffects main 0 → 2 define property getters, hasOwnProperty shorthand
side-effects/nested-package-json-sideEffects-outside-context main 0 → 2 define property getters, hasOwnProperty shorthand

Built test/configCases with the defaults a user gets: 1573 case(s), 6159 asset(s), 40 emitted nothing.

@xiaoxiaojx
xiaoxiaojx force-pushed the fix/nested-package-json-sideEffects branch from 475a3e0 to 4ba77ec Compare August 11, 2026 17:38
@codspeed-hq

codspeed-hq Bot commented Aug 11, 2026

Copy link
Copy Markdown

Merging this PR will degrade performance by 36.21%

⚠️ 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
❌ 2 regressed benchmarks
✅ 307 untouched benchmarks

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Mode Benchmark BASE HEAD Efficiency
Memory benchmark "many-modules-esm", scenario '{"name":"mode-development","mode":"development"}' 882.6 KB 2,299.2 KB -61.61%
Memory benchmark "devtool-eval", scenario '{"name":"mode-development-rebuild","mode":"development","watch":true}' 479.2 KB 942.9 KB -49.18%
Memory benchmark "css-modules", scenario '{"name":"mode-development-rebuild","mode":"development","watch":true}' 1,233.4 KB 927.1 KB +33.05%

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing fix/nested-package-json-sideEffects (4ba77ec) with main (638ce71)

Open in CodSpeed

@github-actions

Copy link
Copy Markdown
Contributor

Types Coverage

Coverage after merging fix/nested-package-json-sideEffects into main will be
99.25%
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%214, 234
   CodeGenerationResults.js100%100%100%100%
   CompatibilityPlugin.js100%100%100%100%
   Compilation.js98.44%100%100%98.44%1673, 1992, 1999, 2007, 2029, 2032, 2971, 3450–3451, 3483, 4192, 4222, 4275–4276, 4280, 4285, 4301–4302, 4316–4317, 4322–4323, 4843, 4869, 549, 554, 5679, 5711, 5728, 5746, 5762, 5777, 5802–5803, 5805, 6140, 6145, 6151, 6154, 6161, 6173, 6175, 6179, 6197, 6212, 6246, 6302, 6326, 6442, 801–802
   Compiler.js99.56%100%100%99.56%1168–1169, 1177
   ConcatenationScope.js99.12%100%100%99.12%300
   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%1080, 176–177, 193, 212, 286
   DependenciesBlock.js100%100%100%100%
   Dependency.js98.51%100%100%98.51%480, 527
   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.67%100%100%98.67%1220, 1223, 519–523, 525, 671
   ExternalModuleFactoryPlugin.js100%100%100%100%
   ExternalsPlugin.js100%100%100%100%
   FileSystemInfo.js99.53%100%100%99.53%186, 2454–2455, 2458, 2469, 2480, 2491, 284, 3928, 3943, 3967
   FlagAllModulesAsUsedPlugin.js100%100%100%100%
   FlagDependencyExportsPlugin.js98.36%100%100%98.36%504, 513, 516, 520, 532
   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.51%100%100%98.51%1303, 1308, 1368, 1382, 1444, 1453
   ModuleFactory.js100%100%100%100%
   ModuleFilenameHelpers.js98.90%100%100%98.90%111, 113
   ModuleGraph.js99.78%100%100%99.78%1170
   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.72%100%100%99.72%721
   MultiStats.js100%100%100%100%
   MultiWatching.js100%100%100%100%
   NoEmitOnErrorsPlugin.js100%100%100%100%
   NodeStuffPlugin.js100%100%100%100%
   NormalModule.js97.99%100%100%97.99%1027, 1030, 1047, 1064, 1312, 1346, 1362, 1817, 2114, 2119–2129, 29
   NormalModuleFactory.js98.77%100%100%98.77%1142, 1410, 1421, 1431, 1482–1484, 1491, 536, 548
   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%690
   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.js95.69%100%100%95.69%310, 366, 375, 378, 402, 420, 441–442, 465, 485–486, 522–523, 546, 559–560, 632, 645, 666, 685
   RuntimeTemplate.js99.91%100%100%99.91%123
   SelfModuleFactory.js100%100%100%100%
   SingleEntryPlugin.js100%100%100%100%
   SourceMapDevToolModuleOptionsPlugin.js100%100%100%100%
   SourceMapDevToolPlugin.js98.63%100%100%98.63%220, 224, 226, 420, 431, 890
   Stats.js100%100%100%100%
   Template.js100%100%100%100%
   TemplatedPathPlugin.js99.48%100%100%99.48%364–365
   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%376
   cli.js98.63%100%100%98.63%10, 119, 549, 581, 631, 905
   index.js99.73%100%100%99.73%184
   validateSchema.js94.67%100%100%94.67%100, 87, 89, 98
   webpack.js97.12%100%100%97.12%10, 267, 289, 291
lib/asset
   AssetBytesGenerator.js100%100%100%100%
   AssetBytesParser.js100%100%100%100%
   AssetGenerator.js100%100%100%100%
   AssetModule.js100%100%100%100%
   AssetModulesPlugin.js98.15%100%100%98.15%330, 354, 357, 487, 49, 54
   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.js92%100%100%92%34, 43
   MemoryWithGcCachePlugin.js93.42%100%100%93.42%108, 122–123, 132, 90
   PackFileCacheStrategy.js96.52%100%100%96.52%1310, 1410, 1414, 1476, 1712, 1796, 1819, 1851, 675, 694, 704–706, 708, 724–725, 730, 733, 735, 740, 745, 770, 776, 810,

@xiaoxiaojx
xiaoxiaojx marked this pull request as ready for review August 11, 2026 17:59

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
lib/optimize/SideEffectsFlagPlugin.js (2)

278-286: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Read compiler.inputFileSystem inside the hook, not at apply time.

compiler.inputFileSystem is reassigned during the compiler lifecycle, for example by NodeEnvironmentPlugin on watch invalidation and on close. The captured reference can become stale, and the !inputFileSystem guard then silently disables the whole resolution path for the rest of the run.

♻️ Proposed change
-		const inputFileSystem = compiler.inputFileSystem;
 		compiler.hooks.compilation.tap(
 			PLUGIN_NAME,
 			(compilation, { normalModuleFactory }) => {
 				const moduleGraph = compilation.moduleGraph;
 				normalModuleFactory.hooks.afterResolve.tapAsync(
 					PLUGIN_NAME,
 					(resolveData, callback) => {
+						const inputFileSystem = compiler.inputFileSystem;
 						if (!inputFileSystem) return callback();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/optimize/SideEffectsFlagPlugin.js` around lines 278 - 286, Update the
compilation hook around `normalModuleFactory.hooks.afterResolve` to read
`compiler.inputFileSystem` when the `afterResolve` callback executes, rather
than capturing it before `compiler.hooks.compilation.tap`. Apply the existing
missing-filesystem guard to this freshly read value so resolution continues
using the current compiler filesystem after lifecycle reassignment.

170-226: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Memoize the ancestor walk per directory.

afterResolve runs for every resolved module. Every module inside a node_modules package that declares no sideEffects repeats the full ancestor walk, including one readJson per ancestor directory. The result depends only on descriptionFileRoot, not on relativePath, so the walk can be cached per directory and reused.

Store the resolved owning package root plus its sideEffects value in a Map keyed by descriptionFileRoot (scoped to compiler.root, like globToRegexpCache), then compute relativePath per module from that cached root. This also removes repeated work when the input filesystem is not a caching one.

As per coding guidelines: "Evaluate time and memory costs of changes, especially on hot paths."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/optimize/SideEffectsFlagPlugin.js` around lines 170 - 226, Memoize the
ancestor walk in the afterResolve path using a Map scoped to compiler.root,
keyed by descriptionFileRoot, alongside globToRegexpCache. Cache both the owning
package root and its sideEffects value, including no-result cases, then compute
each module’s relativePath from the cached root instead of repeating readJson
traversal. Preserve existing boundary and error behavior while avoiding
unbounded per-module cache growth.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@lib/optimize/SideEffectsFlagPlugin.js`:
- Around line 197-208: Update the ancestor package.json error handling in the
readJson callback to continue walking via next() for missing or unreadable paths
(ENOENT, ENOTDIR, EISDIR, ENAMETOOLONG) and JSON parse SyntaxError, while
preserving callback(err) for other I/O failures. Keep the existing
parent-directory traversal behavior unchanged.

---

Nitpick comments:
In `@lib/optimize/SideEffectsFlagPlugin.js`:
- Around line 278-286: Update the compilation hook around
`normalModuleFactory.hooks.afterResolve` to read `compiler.inputFileSystem` when
the `afterResolve` callback executes, rather than capturing it before
`compiler.hooks.compilation.tap`. Apply the existing missing-filesystem guard to
this freshly read value so resolution continues using the current compiler
filesystem after lifecycle reassignment.
- Around line 170-226: Memoize the ancestor walk in the afterResolve path using
a Map scoped to compiler.root, keyed by descriptionFileRoot, alongside
globToRegexpCache. Cache both the owning package root and its sideEffects value,
including no-result cases, then compute each module’s relativePath from the
cached root instead of repeating readJson traversal. Preserve existing boundary
and error behavior while avoiding unbounded per-module cache growth.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: f1d2eb66-2173-428c-83f0-e3ca40f61717

📥 Commits

Reviewing files that changed from the base of the PR and between 475a3e0 and 4ba77ec.

📒 Files selected for processing (3)
  • .changeset/047-inherit-nested-sideEffects.md
  • lib/optimize/SideEffectsFlagPlugin.js
  • test/configCases/side-effects/nested-package-json-sideEffects/index.js
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • webpack/schema-utils (auto-detected)
  • webpack/tapable (auto-detected)
🚧 Files skipped from review as they are similar to previous changes (2)
  • .changeset/047-inherit-nested-sideEffects.md
  • test/configCases/side-effects/nested-package-json-sideEffects/index.js

Comment on lines +197 to +208
readJson(fs, join(fs, dir, "package.json"), (err, data) => {
if (err) {
if ("code" in err && err.code === "ENOENT") {
dir = parent;
return next();
}
return callback(err);
}
if (!data || typeof data !== "object" || Array.isArray(data)) {
dir = parent;
return next();
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Do not fail resolution on a malformed or non-readable ancestor package.json.

Only ENOENT is tolerated. A SyntaxError from JSON.parse inside readJson carries no code, so it reaches callback(err) and fails module creation. ENOTDIR and EISDIR behave the same way.

These ancestor files were never read before this change. A broken package.json in any ancestor directory inside compiler.context now breaks builds of unrelated modules. Treat an unreadable or unparsable ancestor file as "no flag here" and continue the walk.

🐛 Proposed fix
 		readJson(fs, join(fs, dir, "package.json"), (err, data) => {
 			if (err) {
-				if ("code" in err && err.code === "ENOENT") {
-					dir = parent;
-					return next();
-				}
-				return callback(err);
+				// A broken ancestor package.json must not fail unrelated modules.
+				dir = parent;
+				return next();
 			}

If you want to keep hard failures for genuine I/O faults, restrict the skip list explicitly to ENOENT, ENOTDIR, EISDIR, ENAMETOOLONG plus SyntaxError.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
readJson(fs, join(fs, dir, "package.json"), (err, data) => {
if (err) {
if ("code" in err && err.code === "ENOENT") {
dir = parent;
return next();
}
return callback(err);
}
if (!data || typeof data !== "object" || Array.isArray(data)) {
dir = parent;
return next();
}
readJson(fs, join(fs, dir, "package.json"), (err, data) => {
if (err) {
// A broken ancestor package.json must not fail unrelated modules.
dir = parent;
return next();
}
if (!data || typeof data !== "object" || Array.isArray(data)) {
dir = parent;
return next();
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/optimize/SideEffectsFlagPlugin.js` around lines 197 - 208, Update the
ancestor package.json error handling in the readJson callback to continue
walking via next() for missing or unreadable paths (ENOENT, ENOTDIR, EISDIR,
ENAMETOOLONG) and JSON parse SyntaxError, while preserving callback(err) for
other I/O failures. Keep the existing parent-directory traversal behavior
unchanged.

@alexander-akait alexander-akait left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is interesting, initially we don’t inherit this specially, yes this idea was too, but as I remember we declined it in testing, let’s take a look how other bundlers do it

@xiaoxiaojx

Copy link
Copy Markdown
Member Author

@alexander-akait Thanks. I checked other bundlers: Rspack/esbuild/Parcel/Rolldown all use the nearest enclosing package.json only (Rolldown explicitly declined walking up for a sideEffects field — rolldown#3336). I noticed this gap while reviewing production bundle output with AI: root sideEffects: false often gets shadowed by a nested type-only package.json under node_modules, which looks like either a packaging omission or an expectation that bundlers would inherit. This PR treats that as a reasonable optimization (scoped to node_modules, with context / named-package stops so app-local nests still shadow). If there was a clearer historical reason not to support this, we’re happy to reconsider.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: optimization Tree-shaking, splitChunks, concatenation, ids (lib/optimize, lib/ids) area: parser JavaScript parsing and dependency analysis (lib/javascript, lib/dependencies)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants