Skip to content

feat(performance): three more hints, and group the options before any ship - #11

Open
anurag6569201 wants to merge 1 commit into
qa/agent-webpack-webpack/pr-11-21961/basefrom
qa/agent-webpack-webpack/pr-11-21961/head
Open

anurag6569201 wants to merge 1 commit into
qa/agent-webpack-webpack/pr-11-21961/basefrom
qa/agent-webpack-webpack/pr-11-21961/head

Conversation

@anurag6569201

Copy link
Copy Markdown

Summary

Three checks for things nothing reported, one graph fix that made two of them possible, and a pass over the option surface while it is still free to change — none of the checks has been released, so merging and renaming costs nothing.

  • sourceMaps — a production devtool that writes the map into the bundle, and a loader that rewrites code without returning a map. The second leaves webpack mapping positions to the loader's output as though it were the file: a loader injecting ten lines ships a map whose sourcesContent is the loader's output and whose throw sits on line 16 where the file has it on line 6, with no error and no warning.
  • unusedAssets — an asset file emitted for an import whose binding nothing reads. With every hint enabled the build shipped unused.png and said nothing.
  • unusedModules — a module bundled although nothing uses what it exports, naming the re-export or the side-effect statement that kept it. webpack already computed the statement and buried it in an optimization bailout, which stats show only when asked and the cache does not restore.

The graph fix: experiments.futureDefaults marks asset modules side-effect-free, which correctly drops an import whose binding nothing reads — but also dropped import "./favicon.png", where emitting the file is the only thing the import can have been written for. The two are now told apart at the declaration, by a subclass rather than a flag, so the far more common bound form keeps its class and no instance grows: both carry the same twenty fields, and over a 2000-module build the dependency count is unchanged at 6400.

The option pass took 33 checks to 27. unusedConfig replaces unusedAliases, unusedDefines, unusedExternals and unusedRules — four switches for one finding, configuration nothing used, where the fix is the same each time and each check keeps its own message. sourceMaps replaces embeddedSourceMaps and missingSourceMaps. unusedModules absorbs unusedReexports, and duplicateModules absorbs entrypointOverlap, which was a strict subset of it once the bug below was fixed.

That bug: duplicateModules counted chunks per module, so a module concatenated into two entrypoints looked like it was in none and went unreported — entrypointOverlap saw it only because it expanded concatenations. It now expands them too, and names the entrypoints paying for each copy.

Refs webpack#17122

What kind of change does this PR introduce?

feat

Did you add tests for your changes?

Yes — 18 new cases under test/configCases/performance/ and test/configCases/asset-modules/, with the entrypoint-overlap* cases migrated to duplicate-modules-*. Every guard is mutation-checked, each mutation failing a named case: dropping the useSourceMap, has-loaders or SourceMapSource check fails missing-source-maps; ignoring a bare import, accepting any dependency class, or dropping the asset-type check fails unused-assets and unused-assets-inline; ignoring the re-export, the bound-import or the chunk guard fails unused-modules and unused-reexports-side-effects-free; always using the base side-effect class fails asset-modules/bare-import-side-effect-free.

Two guards were removed as unreachable after mutation showed nothing depended on them, and one of those removals was wrong and is back: a module in no chunk has no runtime, which is the only place hasOnlyUnusedExports reads usage in, so it answered "all exports unused" for a module whose export was used.

Does this PR introduce a breaking change?

No. Every check is off by default and reports nothing unless performance.hints is set, and the renamed options have never been in a release. The one behaviour change is on the experiments.futureDefaults path, where a bare import of an asset now keeps emitting its file instead of dropping it.

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

The webpack.js.org performance page needs the 27-option surface: the three new checks, and the four merges. A companion change is drafted but not yet opened, since it documents the pre-merge names.

Use of AI

Claude Code was used to survey the gap and implement this, under review at each step. Several proposals were built and then dropped because measurement contradicted them: a hint for chunks always loaded together (a branch picking one of two import()s is indistinguishable from Promise.all of both — both give parent set [main]), one for resolve.extensions ordering (.ts leads webpack's own default, so it would fire out of the box on any project without .ts files), and one for a package lying in sideEffects: false (the danger did not reproduce). unusedModules shipped narrower than first written, because the test suite showed it reporting every import "./polyfill" — deliberate, and a line unusedReexports had already drawn. Memory was measured within one tree rather than across two checkouts, after a cross-checkout comparison proved confounded: the per-module field costs ~60 bytes and is written only when the hint is on, so with it off no module carries it and retained heap is unchanged over a 2000-module build where every module has a side effect.


🤖 Generated with Claude Code

https://claude.ai/code/session_01FxbjLoCKW57MoXMHEzmruX


Generated by Claude Code

Summary by CodeRabbit

  • New Features
    • Added performance diagnostics for missing source maps, unused assets, unused modules, and duplicate modules across chunks.
    • Added clearer diagnostic details, including affected entrypoints and source locations.
    • Bare side-effect imports preserve required emitted asset files.
    • Added support for "all" boolean-attribute collapsing and URL-attribute normalization in HTML minimization.
  • Configuration
    • Consolidated unused checks under performance.unusedConfig.
    • Grouped source-map checks under performance.sourceMaps.
    • Added performance.unusedAssets and expanded performance.unusedModules reporting.
  • Bug Fixes
    • Improved duplicate-module detection for scope-hoisted and multi-entry builds.

Source merge-base: ea21f562d9712d81798d551f34015524873ce7ef
Source head: af3b149ebd0380866df0e40ddc9201ba61a1cc34

@shipwright-agent

Copy link
Copy Markdown

⛔ Shipwright · Blocked

Recommendation: do not merge PR #11 · Tier T3
Checks: 0 total · 0 needing attention

Next step: resolve the blocking findings before merge.

Findings (7)

  • CRITICAL The new HarmonyImportBareSideEffectDependency.getCondition returns true when refModule is falsy, which keeps the connection active for unresolved modules. · lib/dependencies/HarmonyImportBareSideEffectDependency.js:35
    • Fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.
  • CRITICAL The diff removes the entrypointOverlap and embeddedSourceMaps performance options and their plugin registrations, but the truncated diff does not show removal of all references. · lib/WebpackOptionsApply.js:974
    • Fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.
  • HIGH The new performance option names (sourceMaps, unusedAssets, unusedConfig, unusedModules) are not backward compatible with the removed names (embeddedSourceMaps, entrypointOverlap, · declarations/WebpackOptions.d.ts:3472
    • Fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.
  • HIGH DuplicateModulesWarning now assumes module.entrypoints is always an array with a length property. · lib/errors/DuplicateModulesWarning.js:27
    • Fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.
  • HIGH SideEffectsFlagPlugin now writes sideEffectStatement onto parser.state.module.buildInfo during parsing. · lib/optimize/SideEffectsFlagPlugin.js:551
    • Fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.
  • HIGH The new warning messages interpolate module names, loader names, and statement locations directly into user-facing output without escaping. · lib/errors/MissingSourceMapsWarning.js:24
    • Fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.
  • HIGH UnusedAssetsWarning and UnusedModulesWarning report file sizes and statement locations derived from build internals. · lib/errors/UnusedAssetsWarning.js:24
    • Fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.

Fireworks usage: 65,101 input · 720 output · 65,821 total tokens · $0.0148 · 18s · 0 fix iteration(s)

Open the Shipwright check for full evidence and the audit bundle. Use /shipwright rerun to verify again.

/**
* Returns function to determine if the connection is active.
* @param {ModuleGraph} moduleGraph module graph
* @returns {null | false | GetConditionFn} function to determine if the connection is active

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Shipwright · CRITICAL

The new HarmonyImportBareSideEffectDependency.getCondition returns true when refModule is falsy, which keeps the connection active for unresolved modules.

Impact: The new HarmonyImportBareSideEffectDependency.getCondition returns true when refModule is falsy, which keeps the connection active for unresolved modules. This can retain broken/optional imports that previously would have been pruned, changing bundle output and potentially masking resolution errors.

Suggested fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.

module.wasted
)} extra)`
)
.map((module) => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Shipwright · HIGH

DuplicateModulesWarning now assumes module.entrypoints is always an array with a length property.

Impact: DuplicateModulesWarning now assumes module.entrypoints is always an array with a length property. The typedef says entrypoints is string[] only when more than one, but the code accesses module.entrypoints.length unconditionally, which will throw if entrypoints is undefined for single-entrypoint modules.

Suggested fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.

*/
constructor(modules, total) {
const list = modules
.map((it) => `\n ${it.name} (${it.loaders.join(", ")})`)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Shipwright · HIGH

The new warning messages interpolate module names, loader names, and statement locations directly into user-facing output without escaping.

Impact: The new warning messages interpolate module names, loader names, and statement locations directly into user-facing output without escaping. A malicious package name or loader identifier containing control characters could inject terminal escape sequences or spoof log output.

Suggested fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.

*/
constructor(assets, total) {
const list = assets
.map((it) => `\n ${it.name} (${it.size} bytes)`)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Shipwright · HIGH

UnusedAssetsWarning and UnusedModulesWarning report file sizes and statement locations derived from build internals.

Impact: UnusedAssetsWarning and UnusedModulesWarning report file sizes and statement locations derived from build internals. If these values are attacker-influenced via package metadata or source content, they could be used to exfiltrate build details or craft misleading diagnostics.

Suggested fix: Review the cited evidence, fix the risk if confirmed, and rerun Shipwright.

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