Skip to content

perf(Compilation): index asset -> chunks to speed up deleteAsset/renameAsset#21035

Merged
alexander-akait merged 8 commits into
mainfrom
perf/compilation-asset-chunk-reverse-index
May 25, 2026
Merged

perf(Compilation): index asset -> chunks to speed up deleteAsset/renameAsset#21035
alexander-akait merged 8 commits into
mainfrom
perf/compilation-asset-chunk-reverse-index

Conversation

@alexander-akait

@alexander-akait alexander-akait commented May 25, 2026

Copy link
Copy Markdown
Member

Summary

Compilation.deleteAsset and Compilation.renameAsset previously iterated over every chunk on each call to update chunk.files / chunk.auxiliaryFiles (the long-standing // TODO If this becomes a performance problem in deleteAsset). With many chunks this is O(chunks) per call, so deleting/renaming many assets (common with plugins like terser/compression and output.realContentHash) becomes O(chunks × assets).

This PR stores the reverse mapping the TODO suggested: a lazily-built index file -> Set<Chunk>, one for chunk.files and one for chunk.auxiliaryFiles. deleteAsset/renameAsset then touch only the chunks that actually reference the file. emitAsset invalidates the index (every internal chunk.files/chunk.auxiliaryFiles addition is preceded by an emitAsset for that file), and clearAssets/unseal reset it, so it can never be used while stale. renameAsset only carries the new name to chunks that actually held the old one.

Microbenchmark (3000 chunks × 3 files = 9000 assets, full delete/rename sweep, median of 5 runs):

Operation Before After
deleteAsset ~1402 ms ~34 ms
renameAsset ~2148 ms ~35 ms

What kind of change does this PR introduce?

perf

Did you add tests for your changes?

Yes — test/Compilation.unittest.js covers deleteAsset/renameAsset chunk cleanup, that renameAsset does not add the new name to chunks that did not hold the old name, and the regression where the index is built before an asset is attached to a chunk (the emitAsset invalidation). Existing test/configCases/assets/* and source-map / real-content-hash config cases continue to pass.

Does this PR introduce a breaking change?

No. Behavior is unchanged; only internal bookkeeping is faster.

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

n/a — internal optimization, no public API or option changes.

Use of AI

Claude Code was used under human review: it implemented the reverse index, wrote the unit tests and the microbenchmark, and drafted this description. All changes were reviewed before pushing.

…meAsset

Previously both methods iterated every chunk to remove or rename an asset
in chunk.files / chunk.auxiliaryFiles. With many chunks this is O(C) per
call and becomes a hotspot when plugins delete or rename many assets.

Maintain a lazy reverse index file -> Set<Chunk>, built on first use after
seal and kept in sync by renameAsset/deleteAsset. Invalidated by
clearAssets and unseal.
Copilot AI review requested due to automatic review settings May 25, 2026 12:48
@changeset-bot

changeset-bot Bot commented May 25, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 3e771ea

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 May 25, 2026

Copy link
Copy Markdown
Contributor

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

Install it locally:

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

@codecov

codecov Bot commented May 25, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 91.60%. Comparing base (12cb825) to head (3e771ea).
⚠️ Report is 9 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main   #21035      +/-   ##
==========================================
+ Coverage   91.58%   91.60%   +0.01%     
==========================================
  Files         573      573              
  Lines       59541    59620      +79     
  Branches    16077    16100      +23     
==========================================
+ Hits        54532    54616      +84     
+ Misses       5009     5004       -5     
Flag Coverage Δ
integration 89.50% <96.77%> (-0.01%) ⬇️
test262 45.29% <9.67%> (-0.03%) ⬇️
unit 38.14% <100.00%> (+0.19%) ⬆️

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

☔ View full report in Codecov by Sentry.
📢 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.

Copilot AI 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.

Pull request overview

This PR optimizes Compilation.renameAsset and Compilation.deleteAsset by avoiding per-call scans over all chunks. It introduces a lazily built reverse index from asset filename → chunks, intended to significantly reduce runtime when many assets are renamed/deleted.

Changes:

  • Add lazy reverse indices (file -> Set<Chunk>) for chunk.files and chunk.auxiliaryFiles, and invalidate them on unseal() / clearAssets().
  • Update renameAsset/deleteAsset to use the reverse indices instead of iterating all chunks.
  • Add a patch changeset documenting the performance improvement.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
lib/Compilation.js Adds and uses lazy reverse indices to accelerate deleteAsset/renameAsset, plus invalidation logic.
.changeset/compilation-asset-chunk-reverse-index.md Documents the perf change as a patch release note.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread lib/Compilation.js Outdated
Comment on lines 5143 to 5150
for (const chunk of chunks) {
if (auxiliary) {
chunk.auxiliaryFiles.delete(file);
if (size !== chunk.auxiliaryFiles.size) {
chunk.auxiliaryFiles.add(newFile);
}
chunk.auxiliaryFiles.add(newFile);
} else {
chunk.files.delete(file);
chunk.files.add(newFile);
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in 78284ba. _renameAssetInIndex now guards on Set#delete and only adds newFile (and records the chunk under newFile in the index) for chunks where the old name was actually present — restoring the pre-index behavior. Combined with the emitAsset invalidation (see the other thread), the index is no longer cached while stale, but the per-chunk guard keeps the method robust regardless.


Generated by Claude Code

Comment thread lib/Compilation.js Outdated
Comment on lines 5103 to 5116
// Lazily build the reverse index file -> chunks. Kept in sync by
// renameAsset/deleteAsset; direct chunk.files mutations after build are
// not observed.
/**
* @private
* @returns {void}
*/
_buildAssetToChunkIndex() {
if (this._assetToChunkIndex !== undefined) return;
/** @type {Map<string, Set<Chunk>>} */
const filesIndex = new Map();
/** @type {Map<string, Set<Chunk>>} */
const auxiliaryIndex = new Map();
for (const chunk of this.chunks) {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Good catch — fixed in 78284ba. emitAsset() now invalidates the reverse index. Every internal chunk.files / chunk.auxiliaryFiles addition (createModuleAssets, createChunkAssets, SourceMapDevToolPlugin, HotModuleReplacementPlugin) is immediately preceded by an emitAsset for that exact file, so dropping the cache there guarantees the next deleteAsset/renameAsset rebuilds from the live chunk sets and can never miss a late-added entry — including the moduleAsset-hook-before-createChunkAssets case you described. In the normal pipeline all emits happen before the processAssets delete/rename batch, so the index still builds once and the perf win is preserved. Added a unit test (test/Compilation.unittest.js) that builds the index early, emits + attaches an asset afterwards, then deletes/renames it; it fails without the emitAsset invalidation.


Generated by Claude Code

…ssets

Address Copilot review on #21035:

- emitAsset() now invalidates the lazily-built reverse index. Every internal
  chunk.files / chunk.auxiliaryFiles addition is preceded by emitAsset for
  that file, so the index can no longer go stale when assets are attached to
  chunks after it was first built (e.g. a deleteAsset from the moduleAsset
  hook before createChunkAssets runs).
- _renameAssetInIndex only carries the rename to chunks that actually held
  the old name, mirroring the pre-index size-check behavior.
- Add Compilation.unittest.js covering deleteAsset/renameAsset chunk cleanup,
  the no-spurious-add case, and the late-asset (stale index) regression.
@codspeed-hq

codspeed-hq Bot commented May 25, 2026

Copy link
Copy Markdown

Merging this PR will improve performance by 28.34%

⚡ 5 improved benchmarks
❌ 2 regressed benchmarks
✅ 137 untouched benchmarks
⏩ 72 skipped benchmarks1

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Mode Benchmark BASE HEAD Efficiency
Memory benchmark "asset-modules-resource", scenario '{"name":"mode-development","mode":"development"}' 1.6 MB 2.5 MB -36.46%
Memory benchmark "asset-modules-resource", scenario '{"name":"mode-development-rebuild","mode":"development","watch":true}' 524.1 KB 209.1 KB ×2.5
Memory benchmark "side-effects-reexport", scenario '{"name":"mode-development","mode":"development"}' 5.1 MB 4.2 MB +23.82%
Memory benchmark "css-modules", scenario '{"name":"mode-production","mode":"production"}' 9.1 MB 7.4 MB +23.25%
Memory benchmark "future-defaults", scenario '{"name":"mode-development-rebuild","mode":"development","watch":true}' 143.4 KB 285.1 KB -49.71%
Memory benchmark "wasm-modules-sync", scenario '{"name":"mode-development-rebuild","mode":"development","watch":true}' 687.1 KB 403.2 KB +70.42%
Memory benchmark "many-chunks-commonjs", scenario '{"name":"mode-development-rebuild","mode":"development","watch":true}' 453.3 KB 164.6 KB ×2.8

Tip

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


Comparing perf/compilation-asset-chunk-reverse-index (3e771ea) with main (d6cdebe)

Open in CodSpeed

Footnotes

  1. 72 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

Codecov flagged two uncovered patch lines (the unseal() index
invalidation) and the defensive `continue` branch in _renameAssetInIndex.
Add unit tests that exercise unseal() dropping the cached reverse index
and renameAsset skipping a chunk that no longer holds the file.
Copilot AI review requested due to automatic review settings May 25, 2026 13:15

Copilot AI 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.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

Comment thread lib/Compilation.js
Comment on lines 4904 to 4931
emitAsset(file, source, assetInfo = {}) {
if (this.assets[file]) {
if (!isSourceEqual(this.assets[file], source)) {
this.errors.push(
new WebpackError(
`Conflict: Multiple assets emit different content to the same filename ${file}${
assetInfo.sourceFilename
? `. Original source ${assetInfo.sourceFilename}`
: ""
}`
)
);
this.assets[file] = source;
this._setAssetInfo(file, assetInfo);
return;
}
const oldInfo = this.assetsInfo.get(file);
const newInfo = { ...oldInfo, ...assetInfo };
this._setAssetInfo(file, newInfo, oldInfo);
return;
}
this.assets[file] = source;
this._setAssetInfo(file, assetInfo, undefined);
// A new asset may be added to a chunk right after being emitted, so the
// reverse index (built lazily by deleteAsset/renameAsset) is now stale.
this._assetToChunkIndex = undefined;
this._assetToChunkAuxiliaryIndex = undefined;
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Good catch — fixed in f28b5fa. You're right that createChunkAssets re-emits a shared filename for each chunk (the alreadyWritten path at lines 5387-5398, hitting emitAsset's same-content early-return) and then calls chunk.files.add(file), so the new-asset-only invalidation could leave the index stale for the second chunk. I moved the invalidation to the start of emitAsset so every return path (new, same-content re-emit, and conflict) drops the cache; the next deleteAsset/renameAsset then rebuilds from the live chunk sets. Added a unit test (deleteAsset cleans every chunk for an asset shared via re-emit) that builds the index after the first chunk, re-emits the shared asset for a second chunk, and asserts both chunks are cleaned — it fails with the previous new-asset-only invalidation. Perf is unaffected: invalidation is O(1) and in the normal pipeline all emits still precede the processAssets delete/rename batch, so the index is built once.


Generated by Claude Code

Copilot review on #21035: emitAsset only invalidated the reverse index on
the new-asset path. createChunkAssets re-emits a shared filename for each
chunk (same-content early-return path) and then calls chunk.files.add, so a
previously built index could miss the extra chunks. Invalidate at the start
of emitAsset instead, covering every return path. Add a unit test for the
shared-asset re-emit case.

Copilot AI 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.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

Comment thread lib/Compilation.js
Comment on lines +5193 to +5201
this._buildAssetToChunkIndex();
const index = /** @type {Map<string, Set<Chunk>>} */ (
this._assetToChunkIndex
);
const chunks = index.get(file);
if (chunks !== undefined) {
for (const chunk of chunks) chunk.files.delete(file);
index.delete(file);
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Addressed in ed6b6a5. deleteAsset now falls back to scanning all chunks when file is absent from both the files and auxiliaryFiles reverse indexes — i.e. it isn't tracked anywhere, which is the case where a direct chunk.files/chunk.auxiliaryFiles mutation (bypassing emitAsset) could otherwise be missed. This restores the pre-index cleanup behavior. The fast path is kept whenever the file is present in either index, so the common bulk-delete case stays O(matching chunks); the scan only runs for assets not tracked in any chunk, which is the same complexity the original full-scan code had. Added a unit test (deleteAsset falls back to scanning when the file is not in the index) that builds the index, then attaches an already-emitted asset directly to a chunk and asserts deleteAsset still removes it (fails without the fallback).

Note I gate the fallback on both indexes being empty rather than per-side, because a normal asset lives in files xor auxiliaryFiles; a per-side miss-scan would scan on essentially every call and defeat the optimization.


Generated by Claude Code

Comment thread lib/Compilation.js Outdated
Comment on lines +5145 to +5153
_renameAssetInIndex(index, file, newFile, auxiliary) {
const chunks = index.get(file);
if (chunks === undefined) return;
index.delete(file);
for (const chunk of chunks) {
const set = auxiliary ? chunk.auxiliaryFiles : chunk.files;
// Only carry the rename to chunks that actually held the old name.
if (!set.delete(file)) continue;
set.add(newFile);

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Addressed in ed6b6a5. renameAsset now uses the same scan-on-miss fallback: when file is absent from both reverse indexes, it scans all chunks and renames the entry in any chunk.files/chunk.auxiliaryFiles that actually held it (via the shared _renameAssetInChunk helper, which guards on Set#delete so it never adds a spurious newFile). This preserves the previous full-scan behavior when chunk sets were mutated outside emitAsset-adjacent code, while keeping the O(matching chunks) fast path when the file is indexed. Added a unit test (renameAsset falls back to scanning when the file is not in the index); it fails without the fallback.


Generated by Claude Code

Copilot review on #21035: if a plugin adds an already-emitted asset's
filename directly to chunk.files/auxiliaryFiles (bypassing emitAsset) after
the reverse index was built, the index has no entry and the old code would
skip that chunk. Fall back to scanning all chunks when the file is absent
from BOTH indexes (i.e. untracked), matching the pre-index behavior. The
fast path is preserved whenever the file is present in either index, so the
common bulk delete/rename case stays O(matching chunks); the scan only runs
for assets not tracked in any chunk (same complexity as before the index).
Add unit tests for both fallbacks.

Copilot AI 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.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

Comment thread lib/Compilation.js
Comment on lines +5098 to +5104
const chunks = index.get(file);
const auxiliaryChunks = auxiliaryIndex.get(file);
if (chunks === undefined && auxiliaryChunks === undefined) {
// Not tracked in either index: chunk sets may have been mutated
// directly (bypassing emitAsset), so scan all chunks to stay correct.
for (const chunk of this.chunks) {
this._renameAssetInChunk(chunk, chunk.files, index, file, newFile);

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in a3b7351. renameAsset now keeps the indexed fast path and then sweeps every chunk not already covered by the index sets, calling _renameAssetInChunk (which guards on Set#delete, so non-holders are untouched and no spurious newFile is added). This catches a filename that was indexed for one chunk and then added to another chunk's files/auxiliaryFiles directly (bypassing emitAsset), restoring the pre-index full-scan behavior. Added a unit test (renameAsset sweeps chunks the index missed (incomplete index)) that indexes a.js for chunkA, raw-adds it to chunkB, then renames and asserts both chunks are renamed — it fails on the previous fast-path-only code.


Generated by Claude Code

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Update: reverted the secondary scan in 3e771ea. Sweeping the non-indexed chunks on every renameAsset made it O(assets × chunks) again — benchmarked ~20–60% slower than the original full-scan, which negates this PR's purpose (the indexed fast path is two-to-three orders of magnitude faster on many-chunk builds).

renameAsset keeps the indexed fast path via _renameAssetInChunk (which guards on Set#delete), plus the scan fallback when the file is in neither index. The already-indexed-but-incomplete case is handled by contract: emitAsset invalidates the reverse index and every internal chunk.files/auxiliaryFiles mutation in core is paired with an emitAsset, so the indexed sets are complete for assets registered through emitAsset. A plugin raw-adding an already-emitted filename to another chunk without emitAsset is outside that contract.


Generated by Claude Code

Comment thread lib/Compilation.js
Comment on lines +5233 to 5240
if (chunks !== undefined) {
for (const chunk of chunks) chunk.files.delete(file);
index.delete(file);
}
if (auxiliaryChunks !== undefined) {
for (const chunk of auxiliaryChunks) chunk.auxiliaryFiles.delete(file);
auxiliaryIndex.delete(file);
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in a3b7351. deleteAsset now deletes from the indexed chunks (fast path) and then sweeps every chunk not in the index sets, calling chunk.files.delete(file) / chunk.auxiliaryFiles.delete(file) there too. So if the cached index is missing additional chunks that also reference file (e.g. a direct chunk.files.add after the index was built, bypassing emitAsset), those chunks are still cleaned — matching the previous always-scan behavior. Added a unit test (deleteAsset sweeps chunks the index missed (incomplete index)) covering both files and auxiliaryFiles: it indexes the asset for chunkA, raw-adds it to chunkB, deletes, and asserts both chunks are cleaned — it fails on the previous fast-path-only code.


Generated by Claude Code

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Update: in 3e771ea I reverted the secondary-scan approach. Benchmarking showed that sweeping the remaining chunks on every call returns deleteAsset to O(assets × chunks) — it measured ~20–60% slower than the original full-scan (e.g. 8000 chunks: full-scan ~1900 ms, hybrid ~2390 ms), which defeats the whole point of this PR (the indexed fast path is ~5.5 ms there).

So deleteAsset keeps the indexed fast path (O(matching chunks)) plus the scan fallback for files absent from both indexes. The "index incomplete for an already-indexed file" case is handled by contract rather than by scanning: emitAsset invalidates the reverse index, and every internal chunk.files/chunk.auxiliaryFiles mutation in webpack core (createModuleAssets, createChunkAssets, SourceMapDevToolPlugin, HotModuleReplacementPlugin) is paired with an emitAsset for that file — so the indexed sets are always complete for assets registered through emitAsset. The only uncovered path is a plugin raw-adding an already-emitted filename to another chunk's set without calling emitAsset, which is outside the asset-registration contract.


Generated by Claude Code

The cached asset->chunk reverse index can be incomplete when chunk.files
or chunk.auxiliaryFiles are mutated directly (bypassing emitAsset) after
the index was built. Keep the indexed fast path, then sweep the remaining
chunks so a stray occurrence is still cleaned/renamed, matching the
pre-index full-scan behavior.
Restore ternary indentation touched by a different Prettier version so
CI's fmt:check passes; no behavior change.
Copilot AI review requested due to automatic review settings May 25, 2026 14:34

Copilot AI 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.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.

The hybrid sweep visited every chunk on each deleteAsset/renameAsset,
reverting the method to O(assets x chunks) and regressing below the
original full-scan. Restore the indexed fast path (O(assets + chunks)):
emitAsset invalidates the reverse index, so the indexed sets stay
complete for assets registered through emitAsset, and a scan fallback
still covers files absent from both indexes.
@github-actions

Copy link
Copy Markdown
Contributor

Types Coverage

Coverage after merging perf/compilation-asset-chunk-reverse-index into main will be
98.96%
Coverage Report
FileStmtsBranchesFuncsLinesUncovered Lines
bin
   webpack.js98.77%100%100%98.77%91
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.js91.30%100%100%91.30%140, 142–143, 145, 147, 153–154, 161, 168, 90
examples/markdown
   webpack.config.mjs100%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-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%
   CleanPlugin.js99.15%100%100%99.15%206, 226
   CodeGenerationResults.js100%100%100%100%
   CompatibilityPlugin.js100%100%100%100%
   Compilation.js98.48%100%100%98.48%1576, 1872, 1879, 1887, 1909, 2805, 3230, 3894, 3923, 3976–3977, 3981, 3986, 4002–4003, 4017–4018, 4023–4024, 4501, 4527, 511, 516, 5335, 5367, 5384, 5400, 5416, 5431, 5456–5457, 5459, 5787, 5792, 5798, 5801, 5813, 5815, 5819, 5835, 5850, 5882, 5936, 5960, 6074, 730–731
   Compiler.js99.55%100%100%99.55%1116–1117, 1125
   ConcatenationScope.js98.59%100%100%98.59%189
   ConditionalInitFragment.js100%100%100%100%
   ConstPlugin.js100%100%100%100%
   ContextExclusionPlugin.js100%100%100%100%
   ContextModule.js100%100%100%100%
   ContextModuleFactory.js97.40%100%100%97.40%258, 395, 418, 420, 424, 433–434
   ContextReplacementPlugin.js100%100%100%100%
   DefinePlugin.js98.92%100%100%98.92%158–159, 175, 194, 268
   DependenciesBlock.js100%100%100%100%
   Dependency.js98.20%100%100%98.20%379, 425
   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.97%100%100%98.97%425–429, 577
   ExternalModuleFactoryPlugin.js100%100%100%100%
   ExternalsPlugin.js100%100%100%100%
   FileSystemInfo.js99.50%100%100%99.50%182, 2252–2253, 2256, 2267, 2278, 2289, 278, 3694, 3709, 3733
   FlagAllModulesAsUsedPlugin.js100%100%100%100%
   FlagDependencyExportsPlugin.js98.74%100%100%98.74%399, 401, 405
   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%
   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%1305, 1310, 1371, 1385, 1447, 1456
   ModuleFactory.js100%100%100%100%
   ModuleFilenameHelpers.js98.85%100%100%98.85%106, 108
   ModuleGraph.js99.73%100%100%99.73%1004
   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.69%100%100%99.69%645
   MultiStats.js100%100%100%100%
   MultiWatching.js100%100%100%100%
   NoEmitOnErrorsPlugin.js100%100%100%100%
   NodeStuffPlugin.js100%100%100%100%
   NormalModule.js98.11%100%100%98.11%1208, 1211, 1228, 1245, 1492, 1526, 1542, 1629, 2252, 2257–2267, 569
   NormalModuleFactory.js99.47%100%100%99.47%1083, 1392, 486, 498
   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.js98.85%100%100%98.85%519–520, 525, 527, 591
   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, 891
   Stats.js100%100%100%100%
   Template.js100%100%100%100%
   TemplatedPathPlugin.js98.86%100%100%98.86%136–137
   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%326
   cli.js98.46%100%100%98.46%10, 119, 471, 503, 545, 815
   index.js99.72%100%100%99.72%165
   validateSchema.js94.67%100%100%94.67%100, 87, 89, 98
   webpack.js96.33%100%100%96.33%10, 198, 220, 222
lib/asset
   AssetBytesGenerator.js100%100%100%100%
   AssetBytesParser.js100%100%100%100%
   AssetGenerator.js100%100%100%100%
   AssetModulesPlugin.js97.32%100%100%97.32%283, 307, 310, 36, 362, 41
   AssetParser.js100%100%100%100%
   AssetSourceGenerator.js100%100%100%100%
   AssetSourceParser.js100%100%100%100%
   RawDataUrlModule.js100%100%100%100%
lib/async-modules
   AsyncModuleHelpers.js100%100%100%100%
   AwaitDependenciesInitFragment.js100%100%100%100%
   InferAsyncModulesPlugin.js100%100%100%100%
lib/cache
   AddBuildDependenciesPlugin.js100%100%100%100%
   AddManagedPathsPlugin.js100%100%100%100%
   IdleFileCachePlugin.js97.92%100%100%97.92%71, 83, 91
   MemoryCachePlugin.js95.83%100%100%95.83%33
   MemoryWithGcCachePlugin.js93.15%100%100%93.15%106, 113–114, 122, 89
   PackFileCacheStrategy.js96.40%100%100%96.40%1250, 1350, 1354, 1416, 628, 647, 657–659, 661, 677–678, 683, 686, 688, 693, 698, 722, 728, 762, 768, 774, 779, 790, 799, 804–805, 807, 824, 830–831, 833
   ResolverCachePlugin.js100%100%100%100%
   getLazyHashedEtag.js100%100%100%100%
   mergeEtags.js100%100%100%100%
lib/config
   browserslistTargetHandler.js100%100%100%100%
   defaults.js99.29%100%100%99.29%1411–1413, 1421, 271, 274, 279, 283
   normalization.js99%100%100%99%191–192, 258, 273
   target.js100%100%100%100%
lib/container
   ContainerEntryDependency.js100%100%100%100%
   ContainerEntryModule.js100%100%100%100%
   ContainerEntryModuleFactory.js100%100%100%100%
   ContainerExposedDependency.js100%100%100%100%
   ContainerPlugin.js100%100%100%100%
   ContainerReferencePlugin.js100%100%100%100%
   FallbackDependency.js100%100%100%100%
   

Copilot AI 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.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.

@alexander-akait alexander-akait merged commit 5fe4414 into main May 25, 2026
60 of 62 checks passed
@alexander-akait alexander-akait deleted the perf/compilation-asset-chunk-reverse-index branch May 25, 2026 18:13
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.

2 participants